1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-24 07:14:31 +01:00

Merge branch 'master' into cmdline-scenario

This commit is contained in:
IntelOrca
2014-10-08 23:32:51 +01:00
168 changed files with 28151 additions and 7637 deletions

View File

@@ -41,6 +41,18 @@
#define RCT2_CALLPROC_4(address, a1, a2, a3, a4, v1, v2, v3, v4) RCT2_CALLFUNC_4(address, void, a1, a2, a3, a4, v1, v2, v3, v4)
#define RCT2_CALLPROC_5(address, a1, a2, a3, a4, a5, v1, v2, v3, v4, v5) RCT2_CALLFUNC_5(address, void, a1, a2, a3, a4, a5, v1, v2, v3, v4, v5)
#pragma region Memory locations
// The following memory locations represent memory in RCT2 that is still used
// by OpenRCT2. Only when the memory is no longer needed due to them being
// stored in a new C module or changed behaviour of code that used them.
// This generally can happen once all functions that referenced the location
// are implemented in C. Sometimes memory locations are still used even if
// they aren't directly referenced, for example when a game is saved and
// loaded, large chunks of data is read and written to.
#define RCT2_ADDRESS_EASTEREGG_NAMES 0x00988C20
#define RCT2_ADDRESS_RIDE_PROPERTIES 0x00997C9D
#define RCT2_ADDRESS_LAND_TOOL_SIZE 0x009A9800
#define RCT2_ADDRESS_SAVE_PROMPT_MODE 0x009A9802
@@ -50,6 +62,8 @@
#define RCT2_ADDRESS_APP_PATH 0x009AA214
#define RCT2_ADDRESS_DSOUND_GUID 0x009AAC5D
#define RCT2_ADDRESS_CONFIG_SOUND_SW_BUFFER 0x009AAC6E
#define RCT2_ADDRESS_CONFIG_MUSIC 0x009AAC72
@@ -105,6 +119,8 @@
#define RCT2_ADDRESS_RUN_INTRO_TICK_PART 0x009AC319
#define RCT2_ADDRESS_RIDE_ENTRIES 0x009ACFA4
#define RCT2_ADDRESS_INSTALLED_OBJECT_LIST 0x009ADAE8
#define RCT2_ADDRESS_CURRENT_SOUND_DEVICE 0x009AF280
@@ -162,7 +178,7 @@
#define RCT2_ADDRESS_DSOUND_BUFFERS 0x009E1AB0
#define RCT2_ADDRESS_NUM_DSOUND_DEVICES 0x009E2B88
#define RCT2_ADDRESS_DSOUND_DEVICES 0x009E2B8C
#define RCT2_ADDRESS_SOUND_INFO_LIST_MAPPING 0x009E2B94
#define RCT2_ADDRESS_SOUND_EFFECTS_MAPPING 0x009E2B94
#define RCT2_ADDRESS_SOUNDLIST_BEGIN 0x009E2B98
#define RCT2_ADDRESS_SOUNDLIST_END 0x009E2B9C
#define RCT2_ADDRESS_DIRECTSOUND 0x009E2BA0
@@ -213,6 +229,8 @@
#define RCT2_ADDRESS_CONSTRUCT_PATH_FROM_Z 0x00F3EF8E
#define RCT2_ADDRESS_CONSTRUCT_PATH_DIRECTION 0x00F3EF90
#define RCT2_ADDRESS_VOLUME_ADJUST_ZOOM 0x00F438AC
#define RCT2_ADDRESS_STAFF_HIGHLIGHTED_INDEX 0x00F43908
#define RCT2_ADDRESS_CURRENT_MONTH_YEAR 0x00F663A8
@@ -277,6 +295,11 @@
#define RCT2_ADDRESS_SECURITY_COLOUR 0x01357BCF
#define RCT2_ADDRESS_ACTIVE_RESEARCH_TYPES 0x01357CF2
#define RCT2_ADDRESS_RESEARH_PROGRESS_STAGE 0x01357CF3
#define RCT2_ADDRESS_NEXT_RESEARCH_ITEM 0x013580E0
#define RCT2_ADDRESS_RESEARH_PROGRESS 0x013580E4
#define RCT2_ADDRESS_NEXT_RESEARCH_CATEGORY 0x013580E6
#define RCT2_ADDRESS_NEXT_RESEARCH_EXPECTED_DAY 0x013580E7
#define RCT2_ADDRESS_NEXT_RESEARCH_EXPECTED_MONTH 0x013580E8
@@ -286,6 +309,8 @@
#define RCT2_TOTAL_RIDE_VALUE 0x013580EE
#define RCT2_RESEARCH_ITEMS 0x01358844
#define RCT2_ADDRESS_SCENARIO_NAME 0x0135920A
#define RCT2_ADDRESS_SCENARIO_DETAILS 0x0135924A
@@ -421,6 +446,28 @@
#define RCT2_ADDRESS_INPUT_QUEUE 0x01424340
#define RCT2_ADDRESS_COMMON_FORMAT_ARGS 0x013CE952
#define RCT2_ADDRESS_STAFF_MODE_ARRAY 0x013CA672
#pragma endregion
#pragma region Obsolete
// The following addresses relate to memory locations that no longer used by
// OpenRCT2. This may be due to the data at those locations being stored in
// the new C modules or changed behaviour of code that used them.
#define RCT2_ADDRESS_Y_RELATED_GLOBAL_1 0x9E3D12 //uint16
#define RCT2_ADDRESS_Y_END_POINT_GLOBAL 0x9ABDAC //sint16
#define RCT2_ADDRESS_Y_START_POINT_GLOBAL 0xEDF808 //sint16
#define RCT2_ADDRESS_X_RELATED_GLOBAL_1 0x9E3D10 //uint16
#define RCT2_ADDRESS_X_END_POINT_GLOBAL 0x9ABDA8 //sint16
#define RCT2_ADDRESS_X_START_POINT_GLOBAL 0xEDF80C //sint16
#define RCT2_ADDRESS_DPI_LINE_LENGTH_GLOBAL 0x9ABDB0 //uint16 width+pitch
#pragma endregion
static void RCT2_CALLPROC_EBPSAFE(int address)
{
#ifdef _MSC_VER
@@ -436,7 +483,7 @@ static void RCT2_CALLPROC_EBPSAFE(int address)
#endif
}
static void RCT2_CALLPROC_X(int address, int _eax, int _ebx, int _ecx, int _edx, int _esi, int _edi, int _ebp)
static int RCT2_CALLPROC_X(int address, int _eax, int _ebx, int _ecx, int _edx, int _esi, int _edi, int _ebp)
{
#ifdef _MSC_VER
__asm {
@@ -449,6 +496,7 @@ static void RCT2_CALLPROC_X(int address, int _eax, int _ebx, int _ecx, int _edx,
mov edi, _edi
mov ebp, _ebp
call [esp]
lahf
add esp, 4
}
#else
@@ -465,6 +513,7 @@ static void RCT2_CALLPROC_X(int address, int _eax, int _ebx, int _ecx, int _edx,
mov edi, %[_edi] \n\
mov ebp, %[_ebp] \n\
call [esp] \n\
lahf \n\
add esp, 4 \n\
pop ebp \n\
pop ebx \n\

View File

@@ -19,16 +19,22 @@
*****************************************************************************/
#include <SDL.h>
#include "../addresses.h"
#include "../config.h"
#include "../interface/viewport.h"
#include "../interface/window.h"
#include "../platform/osinterface.h"
#include "../world/map.h"
#include "../world/sprite.h"
#include "audio.h"
#include "addresses.h"
#include "config.h"
#include "rct2.h"
#include "sprite.h"
#include "viewport.h"
#include "window.h"
#include "mixer.h"
int gAudioDeviceCount;
audio_device *gAudioDevices = NULL;
rct_vehicle_sound gVehicleSoundList[AUDIO_MAX_VEHICLE_SOUNDS];
rct_vehicle_sound_params gVehicleSoundParamsList[AUDIO_MAX_VEHICLE_SOUNDS];
rct_vehicle_sound_params *gVehicleSoundParamsListEnd;
void* gMusicChannels[4];
void audio_init(int i)
{
@@ -96,12 +102,12 @@ int audio_release()
*
* rct2: 0x00404C45
*/
int unmap_sound_info()
int unmap_sound_effects()
{
if (RCT2_GLOBAL(RCT2_ADDRESS_SOUND_INFO_LIST_MAPPING, LPVOID)) {
if (RCT2_GLOBAL(RCT2_ADDRESS_SOUND_EFFECTS_MAPPING, LPVOID)) {
sound_stop_all();
unmap_file(RCT2_GLOBAL(RCT2_ADDRESS_SOUND_INFO_LIST_MAPPING, LPVOID));
RCT2_GLOBAL(RCT2_ADDRESS_SOUND_INFO_LIST_MAPPING, LPVOID) = 0;
unmap_file(RCT2_GLOBAL(RCT2_ADDRESS_SOUND_EFFECTS_MAPPING, LPVOID));
RCT2_GLOBAL(RCT2_ADDRESS_SOUND_EFFECTS_MAPPING, LPVOID) = 0;
return 1;
}
return 0;
@@ -162,7 +168,7 @@ void audio_close()
{
if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_SOUND_DEVICE, uint32) != -1) {
stop_other_sounds();
stop_peep_sounds();
stop_crowd_sound();
stop_title_music();
if (RCT2_GLOBAL(0x009AF284, uint32) & (1 << 0)) {
stop_ride_music();
@@ -171,7 +177,7 @@ void audio_close()
RCT2_GLOBAL(0x014241BC, uint32) = 0;
}
RCT2_GLOBAL(0x014241BC, uint32) = 1;
unmap_sound_info();
unmap_sound_effects();
audio_release();
RCT2_GLOBAL(0x014241BC, uint32) = 0;
RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_SOUND_DEVICE, uint32) = -1;
@@ -215,14 +221,14 @@ int dsound_create_primary_buffer(int a, int device, int channels, int samples, i
}
dsdevice = &RCT2_GLOBAL(RCT2_ADDRESS_DSOUND_DEVICES, rct_dsdevice*)[device];
}
memset(&RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, rct_audio_info), 0, sizeof(rct_audio_info));
RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, rct_audio_info).var_0 = 1;
RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, rct_audio_info).channels = channels;
RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, rct_audio_info).samples = samples;
RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, rct_audio_info).var_8 = samples * RCT2_GLOBAL(0x01425B4C, uint16);
RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, rct_audio_info).bytes = bits * channels / 8;
RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, rct_audio_info).bits = bits;
RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, rct_audio_info).var_E = 0;
memset(&RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, WAVEFORMATEX), 0, sizeof(WAVEFORMATEX));
RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, WAVEFORMATEX).wFormatTag = 1;
RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, WAVEFORMATEX).nChannels = channels;
RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, WAVEFORMATEX).nSamplesPerSec = samples;
RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, WAVEFORMATEX).nAvgBytesPerSec = samples * RCT2_GLOBAL(0x01425B4C, uint16);
RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, WAVEFORMATEX).nBlockAlign = bits * channels / 8;
RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, WAVEFORMATEX).wBitsPerSample = bits;
RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, WAVEFORMATEX).cbSize = 0;
DSBUFFERDESC bufferdesc;
memset(&bufferdesc, 0, sizeof(bufferdesc));
bufferdesc.dwSize = sizeof(bufferdesc);
@@ -343,13 +349,13 @@ LPVOID map_file(LPCSTR lpFileName, DWORD dwCreationDisposition, DWORD dwNumberOf
*
* rct2: 0x00404C1A
*/
int map_sound_info(const char* filename)
int map_sound_effects(const char* filename)
{
if (RCT2_GLOBAL(RCT2_ADDRESS_SOUND_INFO_LIST_MAPPING, LPVOID)) {
if (RCT2_GLOBAL(RCT2_ADDRESS_SOUND_EFFECTS_MAPPING, LPVOID)) {
return 0;
} else {
RCT2_GLOBAL(RCT2_ADDRESS_SOUND_INFO_LIST_MAPPING, LPVOID) = map_file(filename, 0, 0);
return RCT2_GLOBAL(RCT2_ADDRESS_SOUND_INFO_LIST_MAPPING, LPVOID) != 0;
RCT2_GLOBAL(RCT2_ADDRESS_SOUND_EFFECTS_MAPPING, LPVOID) = map_file(filename, 0, 0);
return RCT2_GLOBAL(RCT2_ADDRESS_SOUND_EFFECTS_MAPPING, LPVOID) != 0;
}
}
@@ -447,7 +453,7 @@ int sub_4015E7(int channel)
int zero = 0;
rct_sound_channel* sound_channel = &RCT2_ADDRESS(0x014262E0, rct_sound_channel)[channel];
LPDIRECTSOUNDBUFFER dsbuffer = RCT2_ADDRESS(RCT2_ADDRESS_DSOUND_BUFFERS, LPDIRECTSOUNDBUFFER)[channel];
int result = dsbuffer->lpVtbl->Lock(dsbuffer, 0, sound_channel->var_150, (LPVOID*)&buf1, (LPDWORD)&buf1size, (LPVOID*)&buf2, (LPDWORD)&buf2size, 0);
int result = dsbuffer->lpVtbl->Lock(dsbuffer, 0, sound_channel->bufsize, (LPVOID*)&buf1, (LPDWORD)&buf1size, (LPVOID*)&buf2, (LPDWORD)&buf2size, 0);
if (SUCCEEDED(result)) {
if (buf1size) {
mmio_read(sound_channel->hmmio, buf1size, buf1, &sound_channel->mmckinfo1, &read);
@@ -466,8 +472,8 @@ int sub_4015E7(int channel)
} else {
sound_channel->var_168 = 1;
sound_channel->var_15C = read;
rct_audio_info* audio_info = sound_channel->hmem;
uint16 v = ((audio_info->var_E != 8) - 1) & 0x80;
LPWAVEFORMATEX waveformat = sound_channel->hmem;
uint16 v = ((waveformat->nBlockAlign != 8) - 1) & 0x80;
memset(&buf1[read], v, buf1size - r);
}
}
@@ -479,11 +485,26 @@ int sub_4015E7(int channel)
return result;
}
/**
*
* rct2: 0x00401AF3
*/
void sub_401AF3(int channel, const char* filename, int a3, int a4)
{
rct_sound_channel* sound_channel = &RCT2_ADDRESS(RCT2_ADDRESS_SOUND_CHANNEL_LIST, rct_sound_channel)[channel];
sound_channel->var_4 = 1;
memcpy(sound_channel->filename, filename, sizeof(sound_channel->filename));
sound_channel->var_10C = 0;
sound_channel->var_110 = a4;
sound_channel->var_114 = a3;
sound_channel->var_164 = 1;
}
/**
*
* rct2: 0x004016FF
*/
int sound_channel_load_file(int channel, char* filename, int offset)
int sound_channel_load_file(int channel, const char* filename, int offset)
{
rct_sound_channel* sound_channel = &RCT2_ADDRESS(0x014262E0, rct_sound_channel)[channel];
sound_channel->hmem;
@@ -499,11 +520,11 @@ int sound_channel_load_file(int channel, char* filename, int offset)
sound_channel_free(&sound_channel->hmmio, &sound_channel->hmem);
return -103;
}
sound_channel->var_150 = 120 * *((uint32*)sound_channel->hmem + 2) / 100;
sound_channel->bufsize = 120 * *((uint32*)sound_channel->hmem + 2) / 100;
DSBUFFERDESC bufferdesc;
memset(&bufferdesc, 0, sizeof(bufferdesc));
bufferdesc.dwFlags = RCT2_GLOBAL(0x009E1AA8, uint32) | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
bufferdesc.dwBufferBytes = sound_channel->var_150;
bufferdesc.dwBufferBytes = sound_channel->bufsize;
bufferdesc.lpwfxFormat = sound_channel->hmem;
bufferdesc.dwSize = sizeof(bufferdesc);
int ret = RCT2_GLOBAL(RCT2_ADDRESS_DIRECTSOUND, LPDIRECTSOUND)->lpVtbl->CreateSoundBuffer(RCT2_GLOBAL(RCT2_ADDRESS_DIRECTSOUND, LPDIRECTSOUND), &bufferdesc, &RCT2_ADDRESS(RCT2_ADDRESS_DSOUND_BUFFERS, LPDIRECTSOUNDBUFFER)[channel], 0);
@@ -524,18 +545,18 @@ int sound_channel_load_file(int channel, char* filename, int offset)
*
* rct2: 0x00405222
*/
MMRESULT mmio_open(char* filename, HMMIO* hmmio, HGLOBAL* hmem, LPMMCKINFO mmckinfo)
MMRESULT mmio_open(const char* filename, HMMIO* hmmio, HGLOBAL* hmem, LPMMCKINFO mmckinfo)
{
HGLOBAL* hmemold;
HGLOBAL hmemold2;
HMMIO hmmio1;
MMRESULT result;
MMCKINFO mmckinfo1;
rct_audio_info audio_info;
WAVEFORMATEX waveformat;
hmemold = hmem;
*hmem = 0;
hmmio1 = mmioOpenA(filename, 0, MMIO_ALLOCBUF);
hmmio1 = mmioOpenA((char*)filename, 0, MMIO_ALLOCBUF);
if (hmmio1) {
result = mmioDescend(hmmio1, mmckinfo, 0, 0);
if (result != MMSYSERR_NOERROR) {
@@ -555,20 +576,20 @@ MMRESULT mmio_open(char* filename, HMMIO* hmmio, HGLOBAL* hmem, LPMMCKINFO mmcki
result = 57601;
goto label20;
}
if (mmioRead(hmmio1, (HPSTR)&audio_info, 16) == 16) {
if (audio_info.var_0 == 1) {
if (mmioRead(hmmio1, (HPSTR)&waveformat, 16) == 16) {
if (waveformat.wFormatTag == 1) {
//strcpy(audio_info.var_0, "\x01");
hmem = 0;
label11:
hmemold2 = GlobalAlloc(0, (uint16)hmem + 18);
hmemold2 = GlobalAlloc(0, (SIZE_T)(hmem + 18));
*hmemold = hmemold2;
if (!hmemold2) {
result = 57344;
goto label20;
}
memcpy(hmemold2, &audio_info, 16);
memcpy(hmemold2, &waveformat, 16);
*((uint16*)*hmemold + 8) = (uint16)hmem;
if (!(uint16)hmem || mmioRead(hmmio1, (char*)*hmemold + 18, (uint16)hmem) == (uint16)hmem) {
if (!hmem || mmioRead(hmmio1, (char*)*hmemold + 18, (LONG)hmem) == (LONG)hmem) {
result = mmioAscend(hmmio1, &mmckinfo1, 0);
if (!result) {
goto label24;
@@ -698,10 +719,10 @@ void audio_timefunc(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, D
if (dwCurrentPlayCursor >= sound_channel->playpos) {
var1 = dwCurrentPlayCursor - sound_channel->playpos;
} else {
var1 = dwCurrentPlayCursor + sound_channel->var_150 - sound_channel->playpos;
var1 = dwCurrentPlayCursor + sound_channel->bufsize - sound_channel->playpos;
}
if (bufferlost) {
var2 = 2 * sound_channel->var_150 / 6;
var2 = 2 * sound_channel->bufsize / 6;
} else {
var2 = var1;
}
@@ -726,8 +747,8 @@ void audio_timefunc(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, D
}
sound_channel->dsbuffer->lpVtbl->Unlock(sound_channel->dsbuffer, buf1, buf1size, buf2, buf2size);
sound_channel->playpos += var2;
if (sound_channel->playpos >= sound_channel->var_150) {
sound_channel->playpos = sound_channel->playpos - sound_channel->var_150;
if (sound_channel->playpos >= sound_channel->bufsize) {
sound_channel->playpos = sound_channel->playpos - sound_channel->bufsize;
}
return;
}
@@ -768,7 +789,7 @@ void audio_timefunc(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, D
if (dwCurrentPlayCursor <= sound_channel->playpos) {
sound_channel->var_15C = sound_channel->playpos - dwCurrentPlayCursor;
} else {
sound_channel->var_15C = sound_channel->playpos + sound_channel->var_150 - dwCurrentPlayCursor;
sound_channel->var_15C = sound_channel->playpos + sound_channel->bufsize - dwCurrentPlayCursor;
}
goto label49;
}
@@ -795,8 +816,8 @@ void audio_timefunc(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, D
label68:
sound_channel->dsbuffer->lpVtbl->Unlock(sound_channel->dsbuffer, buf1, buf1size, buf2, buf2size);
sound_channel->playpos += var2;
if (sound_channel->playpos >= sound_channel->var_150) {
sound_channel->playpos -= sound_channel->var_150;
if (sound_channel->playpos >= sound_channel->bufsize) {
sound_channel->playpos -= sound_channel->bufsize;
}
if (bufferlost != 0) {
sound_channel->dsbuffer->lpVtbl->Play(sound_channel->dsbuffer, 0, 0, DSBPLAY_LOOPING);
@@ -830,7 +851,7 @@ void audio_timefunc(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, D
if (dwCurrentPlayCursor <= sound_channel->playpos) {
sound_channel->var_15C = sound_channel->playpos - dwCurrentPlayCursor;
} else {
sound_channel->var_15C = sound_channel->playpos + sound_channel->var_150 - dwCurrentPlayCursor;
sound_channel->var_15C = sound_channel->playpos + sound_channel->bufsize - dwCurrentPlayCursor;
}
goto label68;
}
@@ -875,6 +896,48 @@ int audio_create_timer()
return 1;
}
/**
*
* rct2: 0x006BA8E0
*/
void audio_init1()
{
int devicenum = 0;
if (RCT2_GLOBAL(0x009AAC5C, uint8)) {
rct_dsdevice* dsdevice = &RCT2_GLOBAL(RCT2_ADDRESS_DSOUND_DEVICES, rct_dsdevice*)[0];
while (dsdevice->guid.Data1 != RCT2_GLOBAL(RCT2_ADDRESS_DSOUND_GUID, GUID).Data1 ||
dsdevice->guid.Data2 != RCT2_GLOBAL(RCT2_ADDRESS_DSOUND_GUID, GUID).Data2 ||
dsdevice->guid.Data3 != RCT2_GLOBAL(RCT2_ADDRESS_DSOUND_GUID, GUID).Data3 ||
memcmp(dsdevice->guid.Data4, RCT2_GLOBAL(RCT2_ADDRESS_DSOUND_GUID, GUID).Data4, sizeof(dsdevice->guid.Data4)) != 0) {
dsdevice++;
devicenum++;
if (devicenum >= RCT2_GLOBAL(RCT2_ADDRESS_NUM_DSOUND_DEVICES, int)) {
devicenum = 0;
break;
}
}
}
audio_init2(devicenum);
int m = 0;
do {
rct_music_info3* music_info3 = &RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[m];
const char* path = get_file_path(music_info3->pathid);
RCT2_GLOBAL(0x014241BC, uint32) = 3;
HANDLE hfile = osinterface_file_open(path);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
if (hfile != INVALID_HANDLE_VALUE) {
RCT2_GLOBAL(0x014241BC, uint32) = 3;
osinterface_file_read(hfile, &RCT2_GLOBAL(0x009AF47E, uint32), 4);
osinterface_file_close(hfile);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
if (RCT2_GLOBAL(0x009AF47E, uint32) == 0x78787878) {
music_info3->var_0 = 0;
}
}
m++;
} while(m + 1 < 0x2E);
}
/**
*
* rct2: 0x006BA9B5
@@ -882,8 +945,8 @@ int audio_create_timer()
void audio_init2(int device)
{
audio_close();
for (int i = 0; i < 7; i++) {
rct_vehicle_sound* vehicle_sound = &RCT2_ADDRESS(RCT2_ADDRESS_VEHICLE_SOUND_LIST, rct_vehicle_sound)[i];
for (int i = 0; i < countof(gVehicleSoundList); i++) {
rct_vehicle_sound* vehicle_sound = &gVehicleSoundList[i];//&RCT2_ADDRESS(RCT2_ADDRESS_VEHICLE_SOUND_LIST, rct_vehicle_sound)[i];
vehicle_sound->id = 0xFFFF;
}
for (int i = 0; i < 7; i++) {
@@ -898,7 +961,7 @@ void audio_init2(int device)
}
const char * filepath = get_file_path(2);
RCT2_GLOBAL(0x014241BC, uint32) = 1;
int successmap = map_sound_info(filepath);
int successmap = map_sound_effects(filepath);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
if (!successmap) {
RCT2_GLOBAL(0x014241BC, uint32) = 1;
@@ -908,20 +971,17 @@ void audio_init2(int device)
}
RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_SOUND_DEVICE, uint32) = device;
rct_dsdevice dsdevice = RCT2_GLOBAL(RCT2_ADDRESS_DSOUND_DEVICES, rct_dsdevice*)[device];
RCT2_GLOBAL(0x009AAC5D, uint32) = dsdevice.guid.Data1;
RCT2_GLOBAL(0x009AAC61, uint32) = dsdevice.guid.Data2;
RCT2_GLOBAL(0x009AAC65, uint32) = dsdevice.guid.Data3;
RCT2_GLOBAL(0x009AAC69, uint32) = (uint32)dsdevice.guid.Data4;
RCT2_GLOBAL(RCT2_ADDRESS_DSOUND_GUID, GUID) = dsdevice.guid;
RCT2_GLOBAL(0x009AAC5C, uint8) = 1;
config_save();
RCT2_GLOBAL(0x014241BC, uint32) = 1;
int successtimer = audio_create_timer();
RCT2_GLOBAL(0x014241BC, uint32) = 0;
if (successtimer) {
if ((RCT2_GLOBAL(0x009AF284, uint32) & (1 << 0))) {
for (int i = 0; i < 2; i++) {
RCT2_GLOBAL(0x009AF46C + (i * 8), uint8) = -1;
}
RCT2_GLOBAL(0x009AF284, uint32) |= (1 << 0);
for (int i = 0; i < 2; i++) {
rct_music_info2* music_info2 = &RCT2_ADDRESS(0x009AF46C, rct_music_info2)[i];
music_info2->id = -1;
}
}
if (!(RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) & 1 << 4)) {
@@ -1030,10 +1090,10 @@ rct_sound* sound_next(rct_sound* sound)
*
* rct2: 0x00405206
*/
rct_sound_info* sound_get_info(uint16 sound_id)
rct_sound_effect* sound_get_effect(uint16 sound_id)
{
if (RCT2_GLOBAL(RCT2_ADDRESS_SOUND_INFO_LIST_MAPPING, LPVOID) && sound_id < RCT2_GLOBAL(RCT2_ADDRESS_SOUND_INFO_LIST_MAPPING, uint32*)[0]) {
return (rct_sound_info*)(RCT2_GLOBAL(RCT2_ADDRESS_SOUND_INFO_LIST_MAPPING, int) + RCT2_GLOBAL(RCT2_ADDRESS_SOUND_INFO_LIST_MAPPING, uint32*)[sound_id + 1]);
if (RCT2_GLOBAL(RCT2_ADDRESS_SOUND_EFFECTS_MAPPING, LPVOID) && sound_id < RCT2_GLOBAL(RCT2_ADDRESS_SOUND_EFFECTS_MAPPING, uint32*)[0]) {
return (rct_sound_effect*)(RCT2_GLOBAL(RCT2_ADDRESS_SOUND_EFFECTS_MAPPING, int) + RCT2_GLOBAL(RCT2_ADDRESS_SOUND_EFFECTS_MAPPING, uint32*)[sound_id + 1]);
}
return 0;
}
@@ -1042,11 +1102,11 @@ rct_sound_info* sound_get_info(uint16 sound_id)
*
* rct2: 0x00405054
*/
int sound_info_loadvars(rct_sound_info* sound_info, LPWAVEFORMATEX* waveformat, char** data, DWORD* buffersize)
int sound_effect_loadvars(rct_sound_effect* sound_effect, LPWAVEFORMATEX* waveformat, char** data, DWORD* buffersize)
{
*buffersize = sound_info->size;
*waveformat = &sound_info->format;
*data = (char*)&sound_info->data;
*buffersize = sound_effect->size;
*waveformat = &sound_effect->format;
*data = (char*)&sound_effect->data;
return 1;
}
@@ -1095,9 +1155,9 @@ int sound_prepare(int sound_id, rct_sound *sound, int channels, int software)
return 1;
}
}
rct_sound_info* sound_info = sound_get_info(sound_id);
if (sound_info) {
if (sound_info_loadvars(sound_info, &bufferdesc.lpwfxFormat, &buffer, &bufferdesc.dwBufferBytes)) {
rct_sound_effect* sound_effect = sound_get_effect(sound_id);
if (sound_effect) {
if (sound_effect_loadvars(sound_effect, &bufferdesc.lpwfxFormat, &buffer, &bufferdesc.dwBufferBytes)) {
bufferdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_STATIC;
if (channels) {
if (channels == 2) {
@@ -1134,17 +1194,68 @@ int sound_prepare(int sound_id, rct_sound *sound, int channels, int software)
/**
*
* rct2: 0x006BB76E
*
* @param sound_id (eax)
* @param ebx (ebx)
* @param x (cx)
* @param y (dx)
* @param z (bp)
*/
int sound_play_panned(int sound_id, int x)
int sound_play_panned(int sound_id, int ebx, sint16 x, sint16 y, sint16 z)
{
//RCT2_CALLPROC_X(0x006BB76E, sound_id, x, 0, 0, 0, 0, 0);
// this function is not complete, need to add in volume adjust
int result = 0;
if (RCT2_GLOBAL(0x009AF59D, uint8) & 1) {
RCT2_GLOBAL(0x00F438AD, uint8) = 0;
int volume = 0;
if (x == 0x8001) {
// stuff to adjust volume
if (ebx == 0x8001) {
sint16 x2 = x & 0xFFE0; // round by 32
sint16 y2 = y & 0xFFE0;
if (x2 < 0x1FFF && y2 < 0x1FFF) {
rct_map_element* mapelement = RCT2_ADDRESS(RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS, rct_map_element*)[((y2 * 256 + x2) & 0xFFFF) / 8];
while (mapelement->type & MAP_ELEMENT_TYPE_MASK) {
mapelement++;
}
if ((mapelement->base_height * 8) - 5 > z) {
RCT2_GLOBAL(0x00F438AD, uint8) = 10;
}
}
sint16 v11;
sint16 v12;
switch (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint32)) {
case 0:
v11 = y - x;
v12 = ((y + x) / 2) - z;
break;
case 1:
v11 = -x - y;
v12 = ((y - x) / 2) - z;
break;
case 2:
v11 = x - y;
v12 = ((-y - x) / 2) - z;
break;
case 3:
v11 = y + x;
v12 = ((x - y) / 2) - z;
break;
}
rct_window* window = RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*);
while (1) {
window--;
if (window < RCT2_ADDRESS(RCT2_ADDRESS_WINDOW_LIST, rct_window)) {
break;
}
rct_viewport* viewport = window->viewport;
if (viewport && viewport->flags & VIEWPORT_FLAG_SOUND_ON) {
sint16 v15 = v12 - viewport->view_y;
sint16 v16 = v11 - viewport->view_x;
ebx = viewport->x + (v16 >> viewport->zoom);
volume = RCT2_ADDRESS(0x0099282C, int)[sound_id] + ((-1024 * viewport->zoom - 1) << RCT2_GLOBAL(0x00F438AD, uint8)) + 1;
if (v15 < 0 || v15 >= viewport->view_height || v16 < 0 || v16 >= viewport->view_width || volume < -10000) {
return sound_id;
}
}
}
}
int i = 0;
rct_other_sound* other_sound = &RCT2_ADDRESS(0x009AF484, rct_other_sound)[i];
@@ -1157,10 +1268,10 @@ int sound_play_panned(int sound_id, int x)
}
other_sound->id = sound_id;
int pan;
if (x == 0x8000) {
if (ebx == 0x8000) {
pan = 0;
} else {
int x2 = x << 16;
int x2 = ebx << 16;
uint16 screenwidth = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16);
if (screenwidth < 64) {
screenwidth = 64;
@@ -1170,13 +1281,16 @@ int sound_play_panned(int sound_id, int x)
if (!RCT2_GLOBAL(0x009AAC6D, uint8)) {
pan = 0;
}
#ifdef USE_MIXER
Mixer_Play_Effect(sound_id, MIXER_LOOP_NONE, DStoMixerVolume(volume), DStoMixerPan(pan), 1, 1);
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_prepare(sound_id, &other_sound->sound, 1, RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_SOUND_SW_BUFFER, uint32));
RCT2_GLOBAL(0x014241BC, uint32) = 0;
RCT2_GLOBAL(0x014241BC, uint32) = 1;
result = sound_play(&other_sound->sound, 0, volume, pan, 0);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
}
return result;
}
@@ -1198,7 +1312,7 @@ int sub_401B63(int channel)
*
* rct2: 0x0040194E
*/
int sound_channel_load_file2(int channel, char* filename, int offset)
int sound_channel_load_file2(int channel, const char* filename, int offset)
{
if (!RCT2_GLOBAL(0x009E1AA4, int)) {
return 0;
@@ -1221,6 +1335,9 @@ void start_title_music()
{
if ((RCT2_GLOBAL(0x009AF284, uint32) & (1 << 0)) && RCT2_GLOBAL(0x009AF59D, uint8) & 1 && RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 1) {
if (!RCT2_GLOBAL(0x009AF600, uint8)) {
#ifdef USE_MIXER
gMusicChannels[3] = Mixer_Play_Music(PATH_ID_CSS17);
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
int result = sound_channel_load_file2(3, (char*)get_file_path(PATH_ID_CSS17), 0);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
@@ -1229,6 +1346,7 @@ void start_title_music()
sound_channel_play(3, 1, 0, 0, 0);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
}
#endif
RCT2_GLOBAL(0x009AF600, uint8) = 1;
}
} else {
@@ -1496,7 +1614,7 @@ void pause_sounds()
stop_other_sounds();
stop_vehicle_sounds();
stop_ride_music();
stop_peep_sounds();
stop_crowd_sound();
}
g_sounds_disabled = 1;
}
@@ -1536,18 +1654,26 @@ void stop_other_sounds()
void stop_vehicle_sounds()
{
if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_SOUND_DEVICE, sint32) != -1) {
for (int i = 0; i < 7; i++) {
rct_vehicle_sound* vehicle_sound = &RCT2_ADDRESS(RCT2_ADDRESS_VEHICLE_SOUND_LIST, rct_vehicle_sound)[i];
for (int i = 0; i < countof(gVehicleSoundList); i++) {
rct_vehicle_sound* vehicle_sound = &gVehicleSoundList[i];//&RCT2_ADDRESS(RCT2_ADDRESS_VEHICLE_SOUND_LIST, rct_vehicle_sound)[i];
if (vehicle_sound->id != 0xFFFF) {
if (vehicle_sound->var_18 != 0xFFFF) {
if (vehicle_sound->sound1_id != 0xFFFF) {
#ifdef USE_MIXER
Mixer_Stop_Channel(vehicle_sound->sound1_channel);
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_stop(&vehicle_sound->sound1);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
}
if (vehicle_sound->var_34 != 0xFFFF) {
if (vehicle_sound->sound2_id != 0xFFFF) {
#ifdef USE_MIXER
Mixer_Stop_Channel(vehicle_sound->sound2_channel);
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_stop(&vehicle_sound->sound2);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
}
}
vehicle_sound->id = 0xFFFF;
@@ -1563,12 +1689,12 @@ void stop_ride_music()
{
if ((RCT2_GLOBAL(0x009AF284, uint32) & (1 << 0))) {
for (int i = 0; i < 2; i++) {
uint8 * data = RCT2_ADDRESS(0x009AF46C + (i * 8), uint8);
if (data[0] != 0xFF) {
rct_music_info2* music_info2 = &RCT2_ADDRESS(0x009AF46C, rct_music_info2)[i];
if (music_info2->id != (uint8)-1) {
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_channel_stop(i);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
data[0] = 0xFF;
music_info2->id = -1;
}
}
}
@@ -1578,13 +1704,20 @@ void stop_ride_music()
*
* rct2: 0x006BD07F
*/
void stop_peep_sounds()
void stop_crowd_sound()
{
if ((RCT2_GLOBAL(0x009AF284, uint32) & (1 << 0))) {
if (RCT2_GLOBAL(0x009AF5FC, uint32) != 1) {
#ifdef USE_MIXER
if (gMusicChannels[2]) {
Mixer_Stop_Channel(gMusicChannels[2]);
gMusicChannels[2] = 0;
}
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_channel_stop(2);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
RCT2_GLOBAL(0x009AF5FC, uint32) = 1;
}
}
@@ -1598,9 +1731,16 @@ void stop_title_music()
{
if (RCT2_GLOBAL(0x009AF284, uint32) & (1 << 0)) {
if (RCT2_GLOBAL(0x009AF600, uint8) != 0) {
#ifdef USE_MIXER
if (gMusicChannels[3]) {
Mixer_Stop_Channel(gMusicChannels[3]);
gMusicChannels[3] = 0;
}
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_channel_stop(3);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
}
}
RCT2_GLOBAL(0x009AF600, uint8) = 0;
@@ -1640,4 +1780,196 @@ void unpause_sounds()
{
RCT2_GLOBAL(0x009AF59C, uint8)--;
g_sounds_disabled = 0;
}
}
/**
* Update zoom based volume attenuation for ride music
* rct2: 0x006BC348
*/
void sub_6BC348()
{
RCT2_GLOBAL(0x009AF42C, void*) = &RCT2_GLOBAL(0x009AF430, void*);
RCT2_GLOBAL(0x00F438A4, rct_viewport*) = (rct_viewport*)-1;
rct_window* window = RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*);
while (1) {
window--;
if (window < RCT2_ADDRESS(RCT2_ADDRESS_WINDOW_LIST, rct_window)) {
break;
}
if (window->viewport && window->viewport->flags & VIEWPORT_FLAG_SOUND_ON) {
RCT2_GLOBAL(0x00F438A4, rct_viewport*) = window->viewport;
RCT2_GLOBAL(0x00F438A8, rct_window*) = window;
RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 0;
if (window->viewport->zoom) {
RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 30;
if (window->viewport->zoom != 1) {
RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 60;
}
}
return;
}
}
}
/**
* Play/update ride music based on structs updated somewhere else
* rct2: 0x006BC6D8
*/
void sub_6BC6D8()
{
rct_music_info* edi;
int ebx;
if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 2)) {
if ((RCT2_GLOBAL(0x009AF284, uint32) & (1 << 0))) {
if (!RCT2_GLOBAL(0x009AF59C, uint8) && RCT2_GLOBAL(0x009AF59D, uint8) & 1 && RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_MUSIC, uint8) && !(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 1)) {
while (1) {
int v8 = 0;
int v9 = 1;
rct_music_info* music_info = &RCT2_GLOBAL(0x009AF430, rct_music_info);
while (music_info < RCT2_GLOBAL(0x009AF42C, rct_music_info*)) {
if (music_info->id != (uint8)-1) {
rct_music_info3* music_info3 = &RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[music_info->var_1] + 8;
if (RCT2_ADDRESS(0x009AA0B1, uint8*)[music_info3->var_0]) {
v8++;
if (v9 >= music_info->volume) {
v9 = music_info->volume;
edi = music_info;
}
}
}
music_info++;
}
if (v8 <= 1) {
break;
}
edi->id = -1;
}
while (1) {
int v8 = 0;
int v9 = 1;
rct_music_info* music_info = &RCT2_GLOBAL(0x009AF430, rct_music_info);
while (music_info < RCT2_GLOBAL(0x009AF42C, rct_music_info*)) {
if (music_info->id != (uint8)-1) {
v8++;
if (v9 >= music_info->volume) {
v9 = music_info->volume;
edi = music_info;
}
}
music_info++;
}
if (v8 <= 2) {
break;
}
edi->id = -1;
}
rct_music_info2* music_info2 = &RCT2_GLOBAL(0x009AF46C, rct_music_info2);
int channel = 0;
do {
if (music_info2->id != (uint8)-1) {
rct_music_info* music_info = &RCT2_GLOBAL(0x009AF430, rct_music_info);
while (music_info < RCT2_GLOBAL(0x009AF42C, rct_music_info*)) {
if (music_info->id == music_info2->id && music_info->var_1 == music_info2->var_1) {
RCT2_GLOBAL(0x014241BC, uint32) = 1;
int v16 = sub_401B63(channel);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
if (v16) {
goto label32;
}
break;
}
music_info++;
}
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_channel_stop(channel);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
music_info2->id = -1;
}
label32:
music_info2++;
channel++;
} while(channel < 2);
for (rct_music_info* music_info = &RCT2_GLOBAL(0x009AF430, rct_music_info); music_info < RCT2_GLOBAL(0x009AF42C, rct_music_info*); music_info++) {
if (music_info->id != (uint8)-1) {
rct_music_info2* music_info2 = &RCT2_GLOBAL(0x009AF46C, rct_music_info2);
int channel = 0;
while (music_info->id != music_info2->id || music_info->var_1 != music_info2->var_1) {
if (music_info2->id == (uint8)-1) {
ebx = channel;
}
music_info2++;
channel++;
if (channel >= 2) {
rct_music_info3* music_info3 = &RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[music_info->var_1];
const char* filename = get_file_path(music_info3->pathid);
RCT2_GLOBAL(0x014241BC, uint32) = 3;
HANDLE hfile = osinterface_file_open(filename);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
if (hfile != INVALID_HANDLE_VALUE) {
RCT2_GLOBAL(0x014241BC, uint32) = 3;
osinterface_file_read(hfile, &RCT2_GLOBAL(0x009AF47E, uint32), 4);
RCT2_GLOBAL(0x014241BC, uint32) = 3;
osinterface_file_close(hfile);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
}
if (hfile == INVALID_HANDLE_VALUE || RCT2_GLOBAL(0x009AF47E, uint32) != 0x78787878) {
int offset = music_info->offset - 10000;
if (offset < 0) {
offset = 0;
}
RCT2_GLOBAL(0x014241BC, uint32) = 1;
int musicloaded = sound_channel_load_file2(ebx, filename, offset & 0xFFFFFFF0);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
if (musicloaded) {
RCT2_GLOBAL(0x014241BC, uint32) = 1;
int musicplayed = sound_channel_play(ebx, 0, music_info->volume, music_info->pan, music_info->freq);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
if (musicplayed) {
rct_music_info3* music_info3 = &RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[music_info->var_1];
if (music_info3->var_9) {
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sub_401AF3(ebx, get_file_path(music_info3->pathid), 1, 0);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
}
rct_music_info2* music_info2 = &RCT2_ADDRESS(0x009AF46C, rct_music_info2)[ebx];
music_info2->volume = music_info->volume;
music_info2->pan = music_info->pan;
music_info2->freq = music_info->freq;
music_info2->id = music_info->id;
music_info2->var_1 = music_info->var_1;
}
} else {
RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_MUSIC, uint8) = 0;
}
}
return;
}
}
if (music_info->volume != music_info2->volume) {
music_info2->volume = music_info->volume;
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_channel_set_volume(channel, music_info->volume);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
}
if (music_info->pan != music_info2->pan) {
music_info2->pan = music_info->pan;
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_channel_set_pan(channel, music_info->pan);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
}
if (music_info->freq != music_info2->freq) {
music_info2->freq = music_info->freq;
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_channel_set_frequency(channel, music_info->freq);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
}
}
}
}
}
}
}

View File

@@ -21,8 +21,8 @@
#ifndef _AUDIO_H_
#define _AUDIO_H_
#include "rct2.h"
#include "sprite.h"
#include "../common.h"
#include "../world/sprite.h"
typedef struct {
char name[256];
@@ -31,9 +31,12 @@ typedef struct {
extern int gAudioDeviceCount;
extern audio_device *gAudioDevices;
#define AUDIO_MAX_VEHICLE_SOUNDS 14
void audio_init();
void audio_quit();
void audio_get_devices();
void audio_init1();
void audio_init2(int device);
#include <dsound.h>
@@ -59,20 +62,11 @@ typedef struct rct_sound {
struct rct_sound* next;
} rct_sound;
typedef struct {
uint16 var_0;
uint16 channels;
uint32 samples;
uint32 var_8;
uint16 bytes;
uint16 bits;
uint16 var_E;
} rct_audio_info;
typedef struct {
uint32 var_0;
uint32 var_4;
char filename[0x108]; // 0x8
char filename[MAX_PATH]; // 0x8
uint32 var_10C;
uint32 var_110;
uint32 var_114;
uint32 var_118;
@@ -81,7 +75,7 @@ typedef struct {
MMCKINFO mmckinfo1; // 0x124
MMCKINFO mmckinfo2; // 0x138
LPDIRECTSOUNDBUFFER dsbuffer; // 0x14C
uint32 var_150;
uint32 bufsize;
uint32 playpos; // 0x154
uint32 var_158;
uint32 var_15C;
@@ -93,39 +87,69 @@ typedef struct {
typedef struct {
uint32 size;
WAVEFORMATEX format;
char* data;
} rct_sound_info;
uint8* data;
} rct_sound_effect;
typedef struct {
uint16 id;
uint16 var_2;
rct_sound sound1; // 0x04
uint16 var_18;
uint16 var_1A;
uint16 var_1C;
uint16 var_1D;
uint16 sound1_id; // 0x18
sint16 sound1_volume; // 0x1A
sint16 sound1_pan; // 0x1C
uint16 sound1_freq;
rct_sound sound2; // 0x20
uint16 var_34;
uint16 pad_36;
uint16 var_38;
uint16 var_3A;
uint16 sound2_id; // 0x34
sint16 sound2_volume; // 0x36
sint16 sound2_pan; // 0x38
uint16 sound2_freq; // 0x3A
void* sound1_channel;
void* sound2_channel;
} rct_vehicle_sound;
typedef struct {
uint16 id;
sint16 pan; // 0x2
sint16 var_4;
uint16 frequency; // 0x6
sint16 var_8;
uint16 var_A; // 0xA
} rct_vehicle_sound_params;
typedef struct {
uint16 id;
rct_sound sound;
} rct_other_sound;
typedef struct {
uint16 id;
uint8 var_2;
uint8 var_3;
uint8 var_4;
uint16 var_5;
uint8 var_7;
uint16 var_8;
uint16 next; // 0xA
} rct_sound_unknown;
uint8 id;
uint8 var_1;
sint32 offset; //0x2
sint16 volume; //0x6
sint16 pan; //0x8
uint16 freq; //0xA
} rct_music_info;
typedef struct {
uint8 id;
uint8 var_1;
uint16 volume; //0x2
uint16 pan; //0x4
uint16 freq; //0x6
} rct_music_info2;
typedef struct {
uint8 var_0;
uint8 pad_1[0x7];
uint8 pathid; //0x8
uint8 var_9;
} rct_music_info3;
extern rct_vehicle_sound gVehicleSoundList[AUDIO_MAX_VEHICLE_SOUNDS];
extern rct_vehicle_sound_params gVehicleSoundParamsList[AUDIO_MAX_VEHICLE_SOUNDS];
extern rct_vehicle_sound_params *gVehicleSoundParamsListEnd;
extern void* gMusicChannels[4];
int get_dsound_devices();
int dsound_create_primary_buffer(int a, int device, int channels, int samples, int bits);
@@ -133,15 +157,18 @@ void audio_timefunc(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, D
int audio_release();
MMRESULT mmio_read(HMMIO hmmio, uint32 size, char* buffer, LPMMCKINFO mmckinfo, int* read);
MMRESULT mmio_seek(HMMIO* hmmio, LPMMCKINFO mmckinfo1, LPMMCKINFO mmckinfo2, int offset);
MMRESULT mmio_open(char* filename, HMMIO* hmmio, HGLOBAL* hmem, LPMMCKINFO mmckinfo);
MMRESULT mmio_open(const char* filename, HMMIO* hmmio, HGLOBAL* hmem, LPMMCKINFO mmckinfo);
int sub_40153B(int channel);
int sub_4015E7(int channel);
void sub_401AF3(int channel, const char* filename, int a3, int a4);
int sub_401B63(int channel);
void sub_6BC6D8();
int audio_remove_timer();
void audio_close();
LPVOID map_file(LPCSTR lpFileName, DWORD dwCreationDisposition, DWORD dwNumberOfBytesToMap);
int unmap_sound_info();
int unmap_sound_effects();
int sound_prepare(int sound_id, rct_sound *sound, int channels, int software);
int sound_play_panned(int sound_id, int x);
int sound_play_panned(int sound_id, int ebx, sint16 x, sint16 y, sint16 z);
int sound_play(rct_sound* sound, int looping, int volume, int pan, int frequency);
int sound_is_playing(rct_sound* sound);
int sound_set_frequency(rct_sound* sound, int frequency);
@@ -151,8 +178,8 @@ int sound_channel_play(int channel, int a2, int volume, int pan, int frequency);
int sound_channel_set_frequency(int channel, int frequency);
int sound_channel_set_pan(int channel, int pan);
int sound_channel_set_volume(int channel, int volume);
int sound_channel_load_file2(int channel, char* filename, int offset);
int sound_channel_load_file(int channel, char* filename, int offset);
int sound_channel_load_file2(int channel, const char* filename, int offset);
int sound_channel_load_file(int channel, const char* filename, int offset);
void sound_channel_free(HMMIO* hmmio, HGLOBAL* hmem);
int sound_stop(rct_sound *sound);
int sound_stop_all();
@@ -167,7 +194,7 @@ void stop_completed_sounds();
void stop_other_sounds();
void stop_vehicle_sounds();
void stop_ride_music();
void stop_peep_sounds();
void stop_crowd_sound();
void stop_title_music();
void start_title_music();
void unpause_sounds();
@@ -239,7 +266,8 @@ typedef enum {
SOUND_TRAM = 59,
SOUND_DOOR_OPEN = 60,
SOUND_DOOR_CLOSE = 61,
SOUND_62 = 62
SOUND_62 = 62,
SOUND_MAXID
} RCT2_SOUND;
#endif

572
src/audio/mixer.cpp Normal file
View File

@@ -0,0 +1,572 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#include <math.h>
#include <SDL.h>
#include <string.h>
extern "C" {
#include "../config.h"
#include "audio.h"
}
#include "mixer.h"
Mixer gMixer;
Sample::Sample()
{
data = 0;
length = 0;
issdlwav = false;
}
Sample::~Sample()
{
Unload();
}
bool Sample::Load(const char* filename)
{
Unload();
SDL_RWops* rw = SDL_RWFromFile(filename, "rb");
if (!rw) {
SDL_RWclose(rw);
return false;
}
SDL_AudioSpec audiospec;
memset(&audiospec, 0, sizeof(audiospec));
SDL_AudioSpec* spec = SDL_LoadWAV_RW(rw, false, &audiospec, &data, (Uint32*)&length);
if (spec != NULL) {
format.freq = spec->freq;
format.format = spec->format;
format.channels = spec->channels;
issdlwav = true;
} else {
return false;
}
return true;
}
bool Sample::LoadCSS1(const char* filename, unsigned int offset)
{
Unload();
SDL_RWops* rw = SDL_RWFromFile(filename, "rb");
if (!rw) {
return false;
}
Uint32 numsounds;
SDL_RWread(rw, &numsounds, sizeof(numsounds), 1);
if (offset > numsounds) {
SDL_RWclose(rw);
return false;
}
SDL_RWseek(rw, offset * 4, RW_SEEK_CUR);
Uint32 soundoffset;
SDL_RWread(rw, &soundoffset, sizeof(soundoffset), 1);
SDL_RWseek(rw, soundoffset, RW_SEEK_SET);
Uint32 soundsize;
SDL_RWread(rw, &soundsize, sizeof(soundsize), 1);
length = soundsize;
WAVEFORMATEX waveformat;
SDL_RWread(rw, &waveformat, sizeof(waveformat), 1);
format.freq = waveformat.nSamplesPerSec;
format.format = AUDIO_S16LSB;
format.channels = waveformat.nChannels;
data = new uint8[length];
SDL_RWread(rw, data, length, 1);
SDL_RWclose(rw);
return true;
}
void Sample::Unload()
{
if (data) {
if (issdlwav) {
SDL_FreeWAV(data);
} else {
delete[] data;
}
data = 0;
}
issdlwav = false;
length = 0;
}
bool Sample::Loaded()
{
return data != 0;
}
bool Sample::Convert(AudioFormat format)
{
if(Sample::format.format != format.format || Sample::format.channels != format.channels || Sample::format.freq != format.freq){
SDL_AudioCVT cvt;
if (SDL_BuildAudioCVT(&cvt, Sample::format.format, Sample::format.channels, Sample::format.freq, format.format, format.channels, format.freq) < 0) {
return false;
}
cvt.len = length;
cvt.buf = (Uint8*)new uint8[cvt.len * cvt.len_mult];
memcpy(cvt.buf, data, length);
if (SDL_ConvertAudio(&cvt) < 0) {
delete[] cvt.buf;
return false;
}
Unload();
data = cvt.buf;
length = cvt.len_cvt;
Sample::format = format;
return true;
}
return false;
}
const uint8* Sample::Data()
{
return data;
}
unsigned long Sample::Length()
{
return length;
}
Stream::Stream()
{
sourcetype = SOURCE_NONE;
}
unsigned long Stream::GetSome(unsigned long offset, const uint8** data, unsigned long length)
{
unsigned long size = length;
switch(sourcetype) {
case SOURCE_SAMPLE:
if (offset >= sample->Length()) {
return 0;
}
if (offset + length > sample->Length()) {
size = sample->Length() - offset;
}
*data = &sample->Data()[offset];
return size;
break;
}
return 0;
}
unsigned long Stream::Length()
{
switch(sourcetype) {
case SOURCE_SAMPLE:
return sample->Length();
break;
}
return 0;
}
void Stream::SetSource_Sample(Sample& sample)
{
sourcetype = SOURCE_SAMPLE;
Stream::sample = &sample;
}
const AudioFormat* Stream::Format()
{
switch(sourcetype) {
case SOURCE_SAMPLE:
return &sample->format;
break;
}
return 0;
}
Channel::Channel()
{
resampler = 0;
SetRate(1);
SetVolume(SDL_MIX_MAXVOLUME);
SetPan(0.5f);
done = true;
}
Channel::~Channel()
{
if (resampler) {
speex_resampler_destroy(resampler);
resampler = 0;
}
}
void Channel::Play(Stream& stream, int loop = MIXER_LOOP_NONE)
{
Channel::stream = &stream;
Channel::loop = loop;
offset = 0;
done = false;
}
void Channel::SetRate(double rate)
{
Channel::rate = rate;
if (Channel::rate < 0.001) {
Channel::rate = 0.001;
}
}
void Channel::SetVolume(int volume)
{
Channel::volume = volume;
if (volume > SDL_MIX_MAXVOLUME) {
Channel::volume = SDL_MIX_MAXVOLUME;
}
if (volume < 0) {
Channel::volume = 0;
}
}
void Channel::SetPan(float pan)
{
Channel::pan = pan;
if (pan > 1) {
Channel::pan = 1;
}
if (pan < 0) {
Channel::pan = 0;
}
volume_l = (float)sin((1.0 - Channel::pan) * M_PI / 2.0);
volume_r = (float)sin(Channel::pan * M_PI / 2.0);
}
bool Channel::IsPlaying()
{
return !done;
}
void Mixer::Init(const char* device)
{
Close();
SDL_AudioSpec want, have;
SDL_zero(want);
want.freq = 44100;
want.format = AUDIO_S16SYS;
want.channels = 2;
want.samples = 1024;
want.callback = Callback;
want.userdata = this;
deviceid = SDL_OpenAudioDevice(device, 0, &want, &have, 0);
format.format = have.format;
format.channels = have.channels;
format.freq = have.freq;
const char* filename = get_file_path(PATH_ID_CSS1);
for (int i = 0; i < SOUND_MAXID; i++) {
css1samples[i].LoadCSS1(filename, i);
css1samples[i].Convert(format); // convert to audio output format, saves some cpu usage but requires a bit more memory, optional
css1streams[i].SetSource_Sample(css1samples[i]);
}
effectbuffer = new uint8[(have.samples * format.BytesPerSample() * format.channels) + 200];
SDL_PauseAudioDevice(deviceid, 0);
}
void Mixer::Close()
{
Lock();
while (channels.begin() != channels.end()) {
Stop(*(*channels.begin()));
}
Unlock();
SDL_CloseAudioDevice(deviceid);
delete[] effectbuffer;
}
void Mixer::Lock()
{
SDL_LockAudioDevice(deviceid);
}
void Mixer::Unlock()
{
SDL_UnlockAudioDevice(deviceid);
}
Channel* Mixer::Play(Stream& stream, int loop, bool deleteondone)
{
Lock();
Channel* newchannel = new (std::nothrow) Channel();
if (newchannel) {
newchannel->Play(stream, loop);
newchannel->deleteondone = deleteondone;
channels.push_back(newchannel);
}
Unlock();
return newchannel;
}
void Mixer::Stop(Channel& channel)
{
Lock();
channels.remove(&channel);
delete &channel;
Unlock();
}
bool Mixer::LoadMusic(int pathid)
{
if (pathid >= PATH_ID_END) {
return false;
}
if (!musicsamples[pathid].Loaded()) {
const char* filename = get_file_path(pathid);
musicstreams[pathid].SetSource_Sample(musicsamples[pathid]);
return musicsamples[pathid].Load(filename);
} else {
return true;
}
}
void SDLCALL Mixer::Callback(void* arg, uint8* stream, int length)
{
Mixer* mixer = (Mixer*)arg;
memset(stream, 0, length);
std::list<Channel*>::iterator i = mixer->channels.begin();
while (i != mixer->channels.end()) {
mixer->MixChannel(*(*i), stream, length);
if ((*i)->done && (*i)->deleteondone) {
delete (*i);
i = mixer->channels.erase(i);
} else {
i++;
}
}
}
void Mixer::MixChannel(Channel& channel, uint8* data, int length)
{
if (channel.stream && !channel.done) {
if (!channel.resampler) {
channel.resampler = speex_resampler_init(format.channels, format.freq, format.freq, 0, 0);
}
AudioFormat channelformat = *channel.stream->Format();
int loaded = 0;
SDL_AudioCVT cvt;
cvt.len_ratio = 1;
do {
int samplesize = format.channels * format.BytesPerSample();
int samples = length / samplesize;
int samplesloaded = loaded / samplesize;
double rate = 1;
if (format.format == AUDIO_S16SYS) {
rate = channel.rate;
}
int samplestoread = (int)ceil((samples - samplesloaded) * rate);
int lengthloaded = 0;
if (channel.offset < channel.stream->Length()) {
bool mustconvert = false;
if (MustConvert(*channel.stream)) {
if (SDL_BuildAudioCVT(&cvt, channelformat.format, channelformat.channels, channelformat.freq, Mixer::format.format, Mixer::format.channels, Mixer::format.freq) == -1) {
break;
}
mustconvert = true;
}
const uint8* datastream = 0;
int readfromstream = (channel.stream->GetSome(channel.offset, &datastream, (int)(((samplestoread) * samplesize) / cvt.len_ratio)) / channelformat.BytesPerSample()) * channelformat.BytesPerSample();
if (readfromstream == 0) {
break;
}
int volume = channel.volume;
uint8* dataconverted = 0;
const uint8* tomix = 0;
if (mustconvert) {
if (Convert(cvt, datastream, readfromstream, &dataconverted)) {
tomix = dataconverted;
lengthloaded = (cvt.len_cvt / samplesize) * samplesize;
} else {
break;
}
} else {
tomix = datastream;
lengthloaded = readfromstream;
}
bool effectbufferloaded = false;
if (rate != 1 && format.format == AUDIO_S16SYS) {
int in_len = (int)(ceil((double)lengthloaded / samplesize));
int out_len = samples + 20; // needs some extra, otherwise resampler sometimes doesn't process all the input samples
speex_resampler_set_rate(channel.resampler, format.freq, (int)(format.freq * (1 / rate)));
speex_resampler_process_interleaved_int(channel.resampler, (const spx_int16_t*)tomix, (spx_uint32_t*)&in_len, (spx_int16_t*)effectbuffer, (spx_uint32_t*)&out_len);
effectbufferloaded = true;
tomix = effectbuffer;
lengthloaded = (out_len * samplesize);
}
if (channel.pan != 0.5f && format.channels == 2) {
if (!effectbufferloaded) {
memcpy(effectbuffer, tomix, lengthloaded);
effectbufferloaded = true;
tomix = effectbuffer;
}
switch (format.format) {
case AUDIO_S16SYS:
EffectPanS16(channel, (sint16*)effectbuffer, lengthloaded / samplesize);
break;
case AUDIO_U8:
EffectPanU8(channel, (uint8*)effectbuffer, lengthloaded / samplesize);
break;
}
}
int mixlength = lengthloaded;
if (loaded + mixlength > length) {
mixlength = length - loaded;
}
SDL_MixAudioFormat(&data[loaded], tomix, format.format, mixlength, volume);
if (dataconverted) {
delete[] dataconverted;
}
channel.offset += readfromstream;
}
loaded += lengthloaded;
if (channel.loop != 0 && channel.offset >= channel.stream->Length()) {
if (channel.loop != -1) {
channel.loop--;
}
channel.offset = 0;
}
} while(loaded < length && channel.loop != 0);
if (channel.loop == 0 && channel.offset >= channel.stream->Length()) {
channel.done = true;
}
}
}
void Mixer::EffectPanS16(Channel& channel, sint16* data, int length)
{
float left = channel.volume_l;
float right = channel.volume_r;
for (int i = 0; i < length * 2; i += 2) {
data[i] = (sint16)(data[i] * left);
data[i + 1] = (sint16)(data[i + 1] * right);
}
}
void Mixer::EffectPanU8(Channel& channel, uint8* data, int length)
{
float left = channel.volume_l;
float right = channel.volume_r;
for (int i = 0; i < length * 2; i += 2) {
data[i] = (uint8)(data[i] * left);
data[i + 1] = (uint8)(data[i + 1] * right);
}
}
bool Mixer::MustConvert(Stream& stream)
{
const AudioFormat* streamformat = stream.Format();
if (!streamformat) {
return false;
}
if (streamformat->format != format.format || streamformat->channels != format.channels || streamformat->freq != format.freq) {
return true;
}
return false;
}
bool Mixer::Convert(SDL_AudioCVT& cvt, const uint8* data, unsigned long length, uint8** dataout)
{
if (length == 0 || cvt.len_mult == 0) {
return false;
}
cvt.len = length;
cvt.buf = (Uint8*)new uint8[cvt.len * cvt.len_mult];
memcpy(cvt.buf, data, length);
if (SDL_ConvertAudio(&cvt) < 0) {
delete[] cvt.buf;
return false;
}
*dataout = cvt.buf;
return true;
}
void Mixer_Init(const char* device)
{
gMixer.Init(device);
}
void* Mixer_Play_Effect(int id, int loop, int volume, float pan, double rate, int deleteondone)
{
if (id >= SOUND_MAXID) {
return 0;
}
gMixer.Lock();
Channel* channel = gMixer.Play(gMixer.css1streams[id], loop, deleteondone != 0);
if (channel) {
channel->SetVolume(volume);
channel->SetPan(pan);
channel->SetRate(rate);
}
gMixer.Unlock();
return channel;
}
void Mixer_Stop_Channel(void* channel)
{
gMixer.Stop(*(Channel*)channel);
}
void Mixer_Channel_Volume(void* channel, int volume)
{
gMixer.Lock();
((Channel*)channel)->SetVolume(volume);
gMixer.Unlock();
}
void Mixer_Channel_Pan(void* channel, float pan)
{
gMixer.Lock();
((Channel*)channel)->SetPan(pan);
gMixer.Unlock();
}
void Mixer_Channel_Rate(void* channel, double rate)
{
gMixer.Lock();
((Channel*)channel)->SetRate(rate);
gMixer.Unlock();
}
int Mixer_Channel_IsPlaying(void* channel)
{
return ((Channel*)channel)->IsPlaying();
}
void* Mixer_Play_Music(int pathid)
{
if (gMixer.LoadMusic(pathid)) {
return gMixer.Play(gMixer.musicstreams[pathid], MIXER_LOOP_INFINITE, false);
}
return 0;
}

163
src/audio/mixer.h Normal file
View File

@@ -0,0 +1,163 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#ifndef _MIXER_H_
#define _MIXER_H_
#include "../common.h"
#include <SDL.h>
#define USE_MIXER
#define MIXER_LOOP_NONE 0
#define MIXER_LOOP_INFINITE -1
#ifdef __cplusplus
#include <list>
extern "C" {
#include <speex/speex_resampler.h>
}
struct AudioFormat {
int BytesPerSample() const { return (SDL_AUDIO_BITSIZE(format)) / 8; };
int freq;
SDL_AudioFormat format;
int channels;
};
class Sample
{
public:
Sample();
~Sample();
bool Load(const char* filename);
bool LoadCSS1(const char* filename, unsigned int offset);
void Unload();
bool Loaded();
bool Convert(AudioFormat format);
const uint8* Data();
unsigned long Length();
friend class Stream;
private:
AudioFormat format;
uint8* data;
unsigned long length;
bool issdlwav;
};
class Stream
{
public:
Stream();
unsigned long GetSome(unsigned long offset, const uint8** data, unsigned long length);
unsigned long Length();
void SetSource_Sample(Sample& sample);
const AudioFormat* Format();
friend class Mixer;
private:
enum {
SOURCE_NONE = 0,
SOURCE_SAMPLE
} sourcetype;
Sample* sample;
};
class Channel
{
public:
Channel();
~Channel();
void Play(Stream& stream, int loop);
void SetRate(double rate);
void SetVolume(int volume);
void SetPan(float pan);
bool IsPlaying();
friend class Mixer;
private:
int loop;
unsigned long offset;
double rate;
int volume;
float volume_l, volume_r;
float pan;
bool done;
bool deleteondone;
SpeexResamplerState* resampler;
Stream* stream;
};
class Mixer
{
public:
void Init(const char* device);
void Close();
void Lock();
void Unlock();
Channel* Play(Stream& stream, int loop, bool deleteondone);
void Stop(Channel& channel);
bool LoadMusic(int pathid);
Stream css1streams[SOUND_MAXID];
Stream musicstreams[PATH_ID_END];
private:
static void SDLCALL Callback(void* arg, uint8* data, int length);
void MixChannel(Channel& channel, uint8* buffer, int length);
void EffectPanS16(Channel& channel, sint16* data, int length);
void EffectPanU8(Channel& channel, uint8* data, int length);
bool MustConvert(Stream& stream);
bool Convert(SDL_AudioCVT& cvt, const uint8* data, unsigned long length, uint8** dataout);
SDL_AudioDeviceID deviceid;
AudioFormat format;
uint8* effectbuffer;
Sample css1samples[SOUND_MAXID];
Sample musicsamples[PATH_ID_END];
std::list<Channel*> channels;
};
extern "C"
{
#endif
void Mixer_Init(const char* device);
void* Mixer_Play_Effect(int id, int loop, int volume, float pan, double rate, int deleteondone);
void Mixer_Stop_Channel(void* channel);
void Mixer_Channel_Volume(void* channel, int volume);
void Mixer_Channel_Pan(void* channel, float pan);
void Mixer_Channel_Rate(void* channel, double rate);
int Mixer_Channel_IsPlaying(void* channel);
void* Mixer_Play_Music(int pathid);
static int DStoMixerVolume(int volume) { return (int)(SDL_MIX_MAXVOLUME * (SDL_pow(10, (float)volume / 2000))); };
static float DStoMixerPan(int pan) { return (((float)pan + -DSBPAN_LEFT) / DSBPAN_RIGHT) / 2; };
static double DStoMixerRate(int frequency) { return (double)frequency / 22050; };
#ifdef __cplusplus
}
#endif
#endif

26
src/common.h Normal file
View File

@@ -0,0 +1,26 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#ifndef _COMMON_H_
#define _COMMON_H_
#include "rct2.h"
#endif

View File

@@ -23,11 +23,8 @@
#include <ctype.h>
#include "addresses.h"
#include "config.h"
#include "language.h"
#include "rct2.h"
#include "osinterface.h"
#include "localisation/localisation.h"
#include "platform/osinterface.h"
// Current keyboard shortcuts
uint16 gShortcutKeys[SHORTCUT_COUNT];

View File

@@ -21,9 +21,9 @@
#ifndef _CONFIG_H_
#define _CONFIG_H_
#include "currency.h"
#include "rct2.h"
#include <windows.h> // for MAX_PATH
#include "common.h"
#include "localisation/currency.h"
enum {
CONFIG_FLAG_ALWAYS_SHOW_GRIDLINES = (1 << 0),
@@ -133,7 +133,7 @@ typedef struct general_configuration {
uint16 language;
} general_configuration_t;
static const struct { char *key; int value; } _currencyLookupTable[] = {
static const struct { const char *key; int value; } _currencyLookupTable[] = {
{ "GBP", CURRENCY_POUNDS },
{ "USD", CURRENCY_DOLLARS },
{ "FRF", CURRENCY_FRANC },
@@ -153,6 +153,11 @@ static const struct { char *key; int value; } _currencyLookupTable[] = {
{ "\xB5", CURRENCY_EUROS }
};
typedef struct shortcut_entry{
uint8 key;
uint8 modifier;
}shortcut_entry;
//typedef struct hotkey_configuration{
//};

View File

@@ -54,8 +54,8 @@ unsigned char up_arrow_cursor_mask[32 * 4] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
#define UP_ARROW_CURSOR_HOTX 7
#define UP_ARROW_CURSOR_HOTY 31
#define UP_ARROW_CURSOR_HOTX 15
#define UP_ARROW_CURSOR_HOTY 0
unsigned char up_down_arrow_cursor_data[32 * 4] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x02, 0xA0, 0x00,

409
src/drawing/drawing.c Normal file
View File

@@ -0,0 +1,409 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John, Peter Hill, Duncan Frost
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <windows.h>
#include <limits.h>
#include "../addresses.h"
#include "../common.h"
#include "../localisation/localisation.h"
#include "../interface/window.h"
#include "../platform/osinterface.h"
#include "drawing.h"
// HACK These were originally passed back through registers
int gLastDrawStringX;
int gLastDrawStringY;
uint8 _screenDirtyBlocks[5120];
//Originally 0x9ABE0C, 12 elements from 0xF3 are the peep top colour, 12 elements from 0xCA are peep trouser colour
const uint8 peep_palette[0x100] = {
0x00, 0xF3, 0xF4, 0xF5, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
};
//Originally 0x9ABE04
uint8 text_palette[0x8] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// Previously 0x97FCBC use it to get the correct palette from g1_elements
const uint16 palette_to_g1_offset[] = {
0x1333, 0x1334, 0x1335, 0x1336,
0x1337, 0x1338, 0x1339, 0x133A,
0x133B, 0x133C, 0x133D, 0x133E,
0x133F, 0x1340, 0x1341, 0x1342,
0x1343, 0x1344, 0x1345, 0x1346,
0x1347, 0x1348, 0x1349, 0x134A,
0x134B, 0x134C, 0x134D, 0x134E,
0x134F, 0x1350, 0x1351, 0x1352,
0x1353, 0x0C1C, 0x0C1D, 0x0C1E,
0x0C1F, 0x0C20, 0x0C22, 0x0C23,
0x0C24, 0x0C25, 0x0C26, 0x0C21,
0x1354, 0x1355, 0x1356, 0x1357,
0x1358, 0x1359, 0x135A, 0x135B,
0x135C, 0x135D, 0x135E, 0x135F,
0x1360, 0x1361, 0x1362, 0x1363,
0x1364, 0x1365, 0x1366, 0x1367,
0x1368, 0x1369, 0x136A, 0x136B,
0x136C, 0x136D, 0x136E, 0x136F,
0x1370, 0x1371, 0x1372, 0x1373,
0x1374, 0x1375, 0x1376, 0x1377,
0x1378, 0x1379, 0x137A, 0x137B,
0x137C, 0x137D, 0x137E, 0x137F,
0x1380, 0x1381, 0x1382, 0x1383,
0x1384, 0x1385, 0x1386, 0x1387,
0x1388, 0x1389, 0x138A, 0x138B,
0x138C, 0x138D, 0x138E, 0x138F,
0x1390, 0x1391, 0x1392, 0x1393,
0x1394, 0x1395, 0x1396, 0x1397,
0x1398, 0x1399, 0x139A, 0x139B,
0x139C, 0x139D, 0x139E, 0x139F,
0x13A0, 0x13A1, 0x13A2, 0x13A3,
0x13A4, 0x13A5, 0x13A6, 0x13A7,
0x13A8, 0x13A9, 0x13AA, 0x13AB,
0x13AC, 0x13AD, 0x13AE, 0x13AF,
0x13B0, 0x13B1, 0x13B2, 0x13B3,
0x13B4, 0x13B5, 0x13B6, 0x13B7,
};
static void gfx_draw_dirty_blocks(int x, int y, int columns, int rows);
/**
* Clears the screen with the specified colour.
* rct2: 0x00678A9F
*/
void gfx_clear(rct_drawpixelinfo *dpi, int colour)
{
int y, w, h;
char* ptr;
w = dpi->width >> dpi->zoom_level;
h = dpi->height >> dpi->zoom_level;
ptr = dpi->bits;
for (y = 0; y < h; y++) {
memset(ptr, colour, w);
ptr += w + dpi->pitch;
}
}
void gfx_draw_pixel(rct_drawpixelinfo *dpi, int x, int y, int colour)
{
gfx_fill_rect(dpi, x, y, x, y, colour);
}
/**
*
* rct2: 0x00683854
* a1 (ebx)
* product (cl)
*/
void gfx_transpose_palette(int pal, unsigned char product)
{
rct_g1_element g1 = RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[pal];
int width = g1.width;
int x = g1.x_offset;
uint8* dest_pointer = (uint8*)&(RCT2_ADDRESS(0x014124680,uint8)[x]);
uint8* source_pointer = g1.offset;
for (; width > 0; width--) {
dest_pointer[0] = (source_pointer[0] * product) >> 8;
dest_pointer[1] = (source_pointer[1] * product) >> 8;
dest_pointer[2] = (source_pointer[2] * product) >> 8;
source_pointer += 3;
dest_pointer += 4;
}
osinterface_update_palette((char*)0x01424680, 10, 236);//Odd would have expected dest_pointer
}
/**
*
* rct2: 0x006ED7E5
*/
void gfx_invalidate_screen()
{
int width = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16);
int height = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, sint16);
gfx_set_dirty_blocks(0, 0, width, height);
}
/**
*
* rct2: 0x006E732D
* left (ax)
* top (bx)
* right (dx)
* bottom (bp)
*/
void gfx_set_dirty_blocks(int left, int top, int right, int bottom)
{
int x, y;
uint8 *screenDirtyBlocks = RCT2_ADDRESS(0x00EDE408, uint8);
left = max(left, 0);
top = max(top, 0);
right = min(right, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16));
bottom = min(bottom, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, sint16));
if (left >= right)
return;
if (top >= bottom)
return;
right--;
bottom--;
left >>= RCT2_GLOBAL(0x009ABDF0, sint8);
right >>= RCT2_GLOBAL(0x009ABDF0, sint8);
top >>= RCT2_GLOBAL(0x009ABDF1, sint8);
bottom >>= RCT2_GLOBAL(0x009ABDF1, sint8);
for (y = top; y <= bottom; y++)
for (x = left; x <= right; x++)
screenDirtyBlocks[y * RCT2_GLOBAL(RCT2_ADDRESS_DIRTY_BLOCK_COLUMNS, sint32) + x] = 0xFF;
}
/**
*
* rct2: 0x006E73BE
*/
void gfx_draw_all_dirty_blocks()
{
int x, y, xx, yy, columns, rows;
uint8 *screenDirtyBlocks = RCT2_ADDRESS(0x00EDE408, uint8);
for (x = 0; x < RCT2_GLOBAL(RCT2_ADDRESS_DIRTY_BLOCK_COLUMNS, sint32); x++) {
for (y = 0; y < RCT2_GLOBAL(RCT2_ADDRESS_DIRTY_BLOCK_ROWS, sint32); y++) {
if (screenDirtyBlocks[y * RCT2_GLOBAL(RCT2_ADDRESS_DIRTY_BLOCK_COLUMNS, sint32) + x] == 0)
continue;
// Determine columns
for (xx = x; xx < RCT2_GLOBAL(RCT2_ADDRESS_DIRTY_BLOCK_COLUMNS, sint32); xx++)
if (screenDirtyBlocks[y * RCT2_GLOBAL(RCT2_ADDRESS_DIRTY_BLOCK_COLUMNS, sint32) + xx] == 0)
break;
columns = xx - x;
// Check rows
for (yy = y; yy < RCT2_GLOBAL(RCT2_ADDRESS_DIRTY_BLOCK_ROWS, sint32); yy++)
for (xx = x; xx < x + columns; xx++)
if (screenDirtyBlocks[yy * RCT2_GLOBAL(RCT2_ADDRESS_DIRTY_BLOCK_COLUMNS, sint32) + xx] == 0)
goto endRowCheck;
endRowCheck:
rows = yy - y;
gfx_draw_dirty_blocks(x, y, columns, rows);
}
}
}
static void gfx_draw_dirty_blocks(int x, int y, int columns, int rows)
{
int left, top, right, bottom;
uint8 *screenDirtyBlocks = RCT2_ADDRESS(0x00EDE408, uint8);
// Unset dirty blocks
for (top = y; top < y + rows; top++)
for (left = x; left < x + columns; left++)
screenDirtyBlocks[top * RCT2_GLOBAL(RCT2_ADDRESS_DIRTY_BLOCK_COLUMNS, sint32) + left] = 0;
// Determine region in pixels
left = max(0, x * RCT2_GLOBAL(RCT2_ADDRESS_DIRTY_BLOCK_WIDTH, sint16));
top = max(0, y * RCT2_GLOBAL(RCT2_ADDRESS_DIRTY_BLOCK_HEIGHT, sint16));
right = min(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16), left + (columns * RCT2_GLOBAL(RCT2_ADDRESS_DIRTY_BLOCK_WIDTH, sint16)));
bottom = min(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, sint16), top + (rows * RCT2_GLOBAL(RCT2_ADDRESS_DIRTY_BLOCK_HEIGHT, sint16)));
if (right <= left || bottom <= top)
return;
// Draw region
gfx_redraw_screen_rect(left, top, right, bottom);
}
/**
*
* rct2: 0x006E7499
* left (ax)
* top (bx)
* right (dx)
* bottom (bp)
*/
void gfx_redraw_screen_rect(short left, short top, short right, short bottom)
{
rct_window* w;
rct_drawpixelinfo *screenDPI = RCT2_ADDRESS(RCT2_ADDRESS_SCREEN_DPI, rct_drawpixelinfo);
rct_drawpixelinfo *windowDPI = RCT2_ADDRESS(RCT2_ADDRESS_WINDOW_DPI, rct_drawpixelinfo);
// Unsure what this does
RCT2_CALLPROC_X(0x00683326, left, top, right - 1, bottom - 1, 0, 0, 0);
windowDPI->bits = screenDPI->bits + left + ((screenDPI->width + screenDPI->pitch) * top);
windowDPI->x = left;
windowDPI->y = top;
windowDPI->width = right - left;
windowDPI->height = bottom - top;
windowDPI->pitch = screenDPI->width + screenDPI->pitch + left - right;
for (w = g_window_list; w < RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*); w++) {
if (w->flags & WF_TRANSPARENT)
continue;
if (right <= w->x || bottom <= w->y)
continue;
if (left >= w->x + w->width || top >= w->y + w->height)
continue;
window_draw(w, left, top, right, bottom);
}
}
/*
*
* rct2: 0x006EE53B
* left (ax)
* width (bx)
* top (cx)
* height (dx)
* drawpixelinfo (edi)
*/
rct_drawpixelinfo* clip_drawpixelinfo(rct_drawpixelinfo* dpi, int left, int width, int top, int height)
{
rct_drawpixelinfo* newDrawPixelInfo = rct2_malloc(sizeof(rct_drawpixelinfo));
int right = left + width;
int bottom = top + height;
newDrawPixelInfo->bits = dpi->bits;
newDrawPixelInfo->x = dpi->x;
newDrawPixelInfo->y = dpi->y;
newDrawPixelInfo->width = dpi->width;
newDrawPixelInfo->height = dpi->height;
newDrawPixelInfo->pitch = dpi->pitch;
newDrawPixelInfo->zoom_level = 0;
if (left > newDrawPixelInfo->x) {
uint16 clippedFromLeft = left - newDrawPixelInfo->x;
newDrawPixelInfo->width -= clippedFromLeft;
newDrawPixelInfo->x = left;
newDrawPixelInfo->pitch += clippedFromLeft;
newDrawPixelInfo->bits += clippedFromLeft;
}
int stickOutWidth = newDrawPixelInfo->x + newDrawPixelInfo->width - right;
if (stickOutWidth > 0) {
newDrawPixelInfo->width -= stickOutWidth;
newDrawPixelInfo->pitch += stickOutWidth;
}
if (top > newDrawPixelInfo->y) {
uint16 clippedFromTop = top - newDrawPixelInfo->y;
newDrawPixelInfo->height -= clippedFromTop;
newDrawPixelInfo->y = top;
uint32 bitsPlus = (newDrawPixelInfo->pitch + newDrawPixelInfo->width) * clippedFromTop;
newDrawPixelInfo->bits += bitsPlus;
}
int bp = newDrawPixelInfo->y + newDrawPixelInfo->height - bottom;
if (bp > 0) {
newDrawPixelInfo->height -= bp;
}
if (newDrawPixelInfo->width > 0 && newDrawPixelInfo->height > 0) {
newDrawPixelInfo->x -= left;
newDrawPixelInfo->y -= top;
return newDrawPixelInfo;
}
rct2_free(newDrawPixelInfo);
return NULL;
}
/***
*
* rct2: 0x00684027
*
* ebp used to be a parameter but it is always zero
* left : eax
* top : ebx
* width : ecx
* height : edx
* x_start: edi
* y_start: esi
*/
void gfx_draw_rain(int left, int top, int width, int height, sint32 x_start, sint32 y_start){
uint8* pattern = RCT2_GLOBAL(RCT2_ADDRESS_RAIN_PATTERN, uint8*);
uint8 pattern_x_space = *pattern++;
uint8 pattern_y_space = *pattern++;
uint8 pattern_start_x_offset = x_start % pattern_x_space;
uint8 pattern_start_y_offset = y_start % pattern_y_space;;
rct_drawpixelinfo* dpi = RCT2_ADDRESS(RCT2_ADDRESS_SCREEN_DPI, rct_drawpixelinfo);
uint32 pixel_offset = (dpi->pitch + dpi->width)*top + left;
uint8 pattern_y_pos = pattern_start_y_offset;
//Stores the colours of changed pixels
uint32* pixel_store = RCT2_ADDRESS(RCT2_ADDRESS_RAIN_PIXEL_STORE, uint32);
pixel_store += RCT2_GLOBAL(RCT2_ADDRESS_NO_RAIN_PIXELS, uint32);
for (; height != 0; height--){
uint8 pattern_x = pattern[pattern_y_pos * 2];
if (pattern_x != 0xFF){
if (RCT2_GLOBAL(0x9AC00C, uint32) <= 0x1F38){
int final_pixel_offset = width + pixel_offset;
int x_pixel_offset = pixel_offset;
x_pixel_offset += ((uint8)(pattern_x - pattern_start_x_offset)) % pattern_x_space;
uint8 pattern_pixel = pattern[pattern_y_pos * 2 + 1];
for (; x_pixel_offset < final_pixel_offset; x_pixel_offset += pattern_x_space){
uint8 current_pixel = dpi->bits[x_pixel_offset];
dpi->bits[x_pixel_offset] = pattern_pixel;
RCT2_GLOBAL(RCT2_ADDRESS_NO_RAIN_PIXELS, uint32)++;
//Store colour and position
*pixel_store++ = (x_pixel_offset << 8) | current_pixel;
}
}
}
pixel_offset += dpi->pitch + dpi->width;
pattern_y_pos++;
pattern_y_pos %= pattern_y_space;
}
}

View File

@@ -18,10 +18,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef _GFX_H_
#define _GFX_H_
#ifndef _DRAWING_H_
#define _DRAWING_H_
#include "rct2.h"
#include "../common.h"
// Size: 0x10
typedef struct {
@@ -58,45 +58,61 @@ enum{
IMAGE_TYPE_UNKNOWN = (1<<3)
};
extern const uint16 palette_to_g1_offset[];
extern const uint8 peep_palette[];
extern uint8 text_palette[];
extern int gLastDrawStringX;
extern int gLastDrawStringY;
int gfx_load_g1();
void gfx_load_character_widths();
int gfx_wrap_string(char* buffer, int width, int* num_lines, int* font_height);
void gfx_clear(rct_drawpixelinfo *dpi, int colour);
void gfx_draw_pixel(rct_drawpixelinfo *dpi, int x, int y, int colour);
void gfx_draw_line(rct_drawpixelinfo *dpi, int x1, int y1, int x2, int y2, int colour);
void gfx_fill_rect(rct_drawpixelinfo *dpi, int left, int top, int right, int bottom, int colour);
void gfx_fill_rect_inset(rct_drawpixelinfo* dpi, short left, short top, short right, short bottom, int colour, short _si);
void gfx_draw_sprite(rct_drawpixelinfo *dpi, int image_id, int x, int y, uint32 tertiary_colour);
void gfx_draw_sprite_palette_set(rct_drawpixelinfo *dpi, int image_id, int x, int y, uint8* palette_pointer, uint8* unknown_pointer);
void gfx_draw_string(rct_drawpixelinfo *dpi, char *buffer, int colour, int x, int y);
void gfx_transpose_palette(int pal, unsigned char product);
void gfx_draw_string_left(rct_drawpixelinfo *dpi, int format, void *args, int colour, int x, int y);
void gfx_draw_string_left_clipped(rct_drawpixelinfo *dpi, int format, void *args, int colour, int x, int y, int width);
void gfx_draw_string_centred_clipped(rct_drawpixelinfo *dpi, int format, void *args, int colour, int x, int y, int width);
void gfx_draw_string_right(rct_drawpixelinfo *dpi, int format, void *args, int colour, int x, int y);
void gfx_draw_string_centred(rct_drawpixelinfo *dpi, int format, int x, int y, int colour, void *args);
int gfx_draw_string_centred_wrapped(rct_drawpixelinfo *dpi, void *args, int x, int y, int width, int format, int colour);
int gfx_draw_string_left_wrapped(rct_drawpixelinfo *dpi, void *args, int x, int y, int width, int format, int colour);
int gfx_get_string_width(char *buffer);
int clip_text(char *buffer, int width);
void gfx_fill_rect_inset(rct_drawpixelinfo *dpi, short left, short top, short right, short bottom, int colour, short _si);
//
rct_drawpixelinfo* clip_drawpixelinfo(rct_drawpixelinfo* dpi, int left, int width, int top, int height);
void gfx_set_dirty_blocks(int left, int top, int right, int bottom);
void gfx_draw_all_dirty_blocks();
void gfx_redraw_screen_rect(short left, short top, short right, short bottom);
void gfx_invalidate_screen();
// palette
void gfx_transpose_palette(int pal, unsigned char product);
// other
void gfx_draw_rain(int left, int top, int width, int height, sint32 x_start, sint32 y_start);
void gfx_clear(rct_drawpixelinfo *dpi, int colour);
void gfx_draw_pixel(rct_drawpixelinfo *dpi, int x, int y, int colour);
rct_drawpixelinfo* clip_drawpixelinfo(rct_drawpixelinfo* dpi, int left, int width, int top, int height);
// line
void gfx_draw_line(rct_drawpixelinfo *dpi, int x1, int y1, int x2, int y2, int colour);
// rect
void gfx_fill_rect(rct_drawpixelinfo *dpi, int left, int top, int right, int bottom, int colour);
void gfx_fill_rect_inset(rct_drawpixelinfo* dpi, short left, short top, short right, short bottom, int colour, short _si);
// sprite
int gfx_load_g1();
void gfx_bmp_sprite_to_buffer(uint8* palette_pointer, uint8* unknown_pointer, uint8* source_pointer, uint8* dest_pointer, rct_g1_element* source_image, rct_drawpixelinfo *dest_dpi, int height, int width, int image_type);
void gfx_rle_sprite_to_buffer(uint8* source_bits_pointer, uint8* dest_bits_pointer, uint8* palette_pointer, rct_drawpixelinfo *dpi, int image_type, int source_y_start, int height, int source_x_start, int width);
void gfx_draw_sprite(rct_drawpixelinfo *dpi, int image_id, int x, int y, uint32 tertiary_colour);
void gfx_draw_sprite_palette_set(rct_drawpixelinfo *dpi, int image_id, int x, int y, uint8* palette_pointer, uint8* unknown_pointer);
// string
void gfx_load_character_widths();
int clip_text(char *buffer, int width);
int gfx_wrap_string(char* buffer, int width, int* num_lines, int* font_height);
int gfx_get_string_width(char *buffer);
void gfx_draw_string(rct_drawpixelinfo *dpi, char *buffer, int colour, int x, int y);
void gfx_draw_string_left(rct_drawpixelinfo *dpi, int format, void *args, int colour, int x, int y);
void gfx_draw_string_left_clipped(rct_drawpixelinfo *dpi, int format, void *args, int colour, int x, int y, int width);
int gfx_draw_string_left_wrapped(rct_drawpixelinfo *dpi, void *args, int x, int y, int width, int format, int colour);
void draw_string_left_underline(rct_drawpixelinfo *dpi, int format, void *args, int colour, int x, int y);
void gfx_draw_string_centred(rct_drawpixelinfo *dpi, int format, int x, int y, int colour, void *args);
void gfx_draw_string_centred_clipped(rct_drawpixelinfo *dpi, int format, void *args, int colour, int x, int y, int width);
void draw_string_centred_underline(rct_drawpixelinfo *dpi, int format, void *args, int colour, int x, int y);
int gfx_draw_string_centred_wrapped(rct_drawpixelinfo *dpi, void *args, int x, int y, int width, int format, int colour);
void draw_string_centred_raw(rct_drawpixelinfo *dpi, int x, int y, int numLines, char *text);
void gfx_draw_string_right(rct_drawpixelinfo *dpi, int format, void *args, int colour, int x, int y);
void draw_string_right_underline(rct_drawpixelinfo *dpi, int format, void *args, int colour, int x, int y);
// rain
void update_rain_animation();
#endif

150
src/drawing/line.c Normal file
View File

@@ -0,0 +1,150 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John, Peter Hill, Duncan Frost
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#include "drawing.h"
/*
* Draws a horizontal line of specified colour to a buffer.
* rct2: 0x0068474C
*/
void gfx_draw_line_on_buffer(rct_drawpixelinfo *dpi, char colour, int y, int x, int no_pixels)
{
y -= dpi->y;
//Check to make sure point is in the y range
if (y < 0)return;
if (y >= dpi->height)return;
//Check to make sure we are drawing at least a pixel
if (!no_pixels) no_pixels++;
x -= dpi->x;
//If x coord outside range leave
if (x < 0){
//Unless the number of pixels is enough to be in range
no_pixels += x;
if (no_pixels <= 0)return;
//Resets starting point to 0 as we don't draw outside the range
x = 0;
}
//Ensure that the end point of the line is within range
if (x + no_pixels - dpi->width > 0){
//If the end point has any pixels outside range
//cut them off. If there are now no pixels return.
no_pixels -= x + no_pixels - dpi->width;
if (no_pixels <= 0)return;
}
char* bits_pointer;
//Get the buffer we are drawing to and move to the first coordinate.
bits_pointer = dpi->bits + y*(dpi->pitch + dpi->width) + x;
//Draw the line to the specified colour
for (; no_pixels > 0; --no_pixels, ++bits_pointer){
*((uint8*)bits_pointer) = colour;
}
}
/**
* Draws a line on dpi if within dpi boundaries
* rct2: 0x00684466
* dpi (edi)
* x1 (ax)
* y1 (bx)
* x2 (cx)
* y2 (dx)
* colour (ebp)
*/
void gfx_draw_line(rct_drawpixelinfo *dpi, int x1, int y1, int x2, int y2, int colour)
{
// Check to make sure the line is within the drawing area
if ((x1 < dpi->x) && (x2 < dpi->x)){
return;
}
if ((y1 < dpi->y) && (y2 < dpi->y)){
return;
}
if ((x1 >(dpi->x + dpi->width)) && (x2 >(dpi->x + dpi->width))){
return;
}
if ((y1 > (dpi->y + dpi->height)) && (y2 > (dpi->y + dpi->height))){
return;
}
//Bresenhams algorithm
//If vertical plot points upwards
int steep = abs(y2 - y1) > abs(x2 - x1);
if (steep){
int temp_y2 = y2;
int temp_x2 = x2;
y2 = x1;
x2 = y1;
y1 = temp_x2;
x1 = temp_y2;
}
//If line is right to left swap direction
if (x1 > x2){
int temp_y2 = y2;
int temp_x2 = x2;
y2 = y1;
x2 = x1;
y1 = temp_y2;
x1 = temp_x2;
}
int delta_x = x2 - x1;
int delta_y = abs(y2 - y1);
int error = delta_x / 2;
int y_step;
int y = y1;
//Direction of step
if (y1 < y2)y_step = 1;
else y_step = -1;
for (int x = x1, x_start = x1, no_pixels = 1; x < x2; ++x,++no_pixels){
//Vertical lines are drawn 1 pixel at a time
if (steep)gfx_draw_line_on_buffer(dpi, colour, x, y, 1);
error -= delta_y;
if (error < 0){
//Non vertical lines are drawn with as many pixels in a horizontal line as possible
if (!steep)gfx_draw_line_on_buffer(dpi, colour, y, x_start, no_pixels);
//Reset non vertical line vars
x_start = x + 1;
no_pixels = 1;
y += y_step;
error += delta_x;
}
//Catch the case of the last line
if (x + 1 == x2 && !steep){
gfx_draw_line_on_buffer(dpi, colour, y, x_start, no_pixels);
}
}
return;
}

239
src/drawing/rain.c Normal file
View File

@@ -0,0 +1,239 @@
/*****************************************************************************
* Copyright (c) 2014
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#include "../addresses.h"
#include "../interface/window.h"
#include "drawing.h"
typedef void(*draw_rain_func)(int left, int top, int width, int height);
static void draw_light_rain(int left, int top, int width, int height);
static void draw_heavy_rain(int left, int top, int width, int height);
/**
*
* rct2: 0x009AC058
*/
const draw_rain_func draw_rain_function[] = {
NULL,
&draw_light_rain,
&draw_heavy_rain
};
/**
*
* rct2: 0x00684383
*/
static void call_draw_rain_func(rct_window* w, short left, short right, short top, short bottom, uint32 draw_rain_func)
{
rct_viewport* vp = w->viewport;
if (vp == NULL) {
return;
}
left = max(left, vp->x);
right = min(right, vp->width);
top = max(top, vp->y);
bottom = min(bottom, vp->height);
if (left >= right || top >= bottom) {
return;
}
int width = right - left;
int height = bottom - top;
draw_rain_function[draw_rain_func](left, top, width, height);
}
/**
*
* rct2: 0x006842AF
* From 0x00684383 on: split into call_draw_rain_func
*/
static void draw_rain_window(rct_window* original_w, short left, short right, short top, short bottom, uint32 draw_rain_func)
{
rct_window* newWindow = RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*);
rct_window* w = original_w + 1; // Start from second window
for (; ; w++) {
if (w >= newWindow) {
// Loop ended, draw rain for original_w
call_draw_rain_func(original_w, left, right, top, bottom, draw_rain_func);
return;
}
if (right <= w->x || bottom <= w->y) {
continue;
}
if (RCT_WINDOW_RIGHT(w) <= left || RCT_WINDOW_BOTTOM(w) <= top) {
continue;
}
if (left >= w->x) {
break;
}
draw_rain_window(original_w, left, w->x, top, bottom, draw_rain_func);
left = w->x;
draw_rain_window(original_w, left, right, top, bottom, draw_rain_func);
return;
}
sint16 w_right = RCT_WINDOW_RIGHT(w);
if (right > w_right) {
draw_rain_window(original_w, left, w_right, top, bottom, draw_rain_func);
left = w_right;
draw_rain_window(original_w, left, right, top, bottom, draw_rain_func);
return;
}
if (top < w->y) {
draw_rain_window(original_w, left, right, top, w->y, draw_rain_func);
top = w->y;
draw_rain_window(original_w, left, right, top, bottom, draw_rain_func);
return;
}
sint16 w_bottom = RCT_WINDOW_BOTTOM(w);
if (bottom > w_bottom) {
draw_rain_window(original_w, left, right, top, w_bottom, draw_rain_func);
top = w_bottom;
draw_rain_window(original_w, left, right, top, bottom, draw_rain_func);
return;
}
}
/**
*
* rct2: 0x00684266
*/
static void draw_rain_animation(uint32 draw_rain_func)
{
rct_drawpixelinfo *screenDPI = RCT2_ADDRESS(RCT2_ADDRESS_SCREEN_DPI, rct_drawpixelinfo);
short left = screenDPI->x;
short right = left + screenDPI->width;
short top = screenDPI->y;
short bottom = top + screenDPI->height;
rct_window* newWindow = (RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*));
for (rct_window* w = g_window_list; w < newWindow; w++) {
draw_rain_window(w, left, right, top, bottom, draw_rain_func);
}
}
/**
*
* rct2: 0x00684218
*/
void update_rain_animation()
{
if (RCT2_GLOBAL(0x009ABDF2, uint8) == 0)
return;
// Draw picked-up peep
if (RCT2_GLOBAL(RCT2_ADDRESS_PICKEDUP_PEEP_SPRITE, uint32) != 0xFFFFFFFF) {
gfx_draw_sprite(
(rct_drawpixelinfo*)RCT2_ADDRESS_SCREEN_DPI,
RCT2_GLOBAL(RCT2_ADDRESS_PICKEDUP_PEEP_SPRITE, uint32),
RCT2_GLOBAL(RCT2_ADDRESS_PICKEDUP_PEEP_X, sint16),
RCT2_GLOBAL(RCT2_ADDRESS_PICKEDUP_PEEP_Y, sint16), 0
);
}
// Get rain draw function and draw rain
uint32 draw_rain_func = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_RAIN_LEVEL, uint8);
if (draw_rain_func > 0 && !(RCT2_GLOBAL(0x009DEA6F, uint8) & 1))
draw_rain_animation(draw_rain_func);
}
/**
*
* rct2: 0x00684114
*/
static void draw_light_rain(int left, int top, int width, int height)
{
int x_start = -RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) + 8;
int y_start = (RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) * 3) + 7;
y_start = -y_start;
x_start += left;
y_start += top;
gfx_draw_rain(left, top, width, height, x_start, y_start);
x_start = -RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) + 0x18;
y_start = (RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) * 4) + 0x0D;
y_start = -y_start;
x_start += left;
y_start += top;
gfx_draw_rain(left, top, width, height, x_start, y_start);
}
/**
*
* rct2: 0x0068416D
*/
static void draw_heavy_rain(int left, int top, int width, int height)
{
int x_start = -RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int);
int y_start = RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) * 5;
y_start = -y_start;
x_start += left;
y_start += top;
gfx_draw_rain(left, top, width, height, x_start, y_start);
x_start = -RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) + 0x10;
y_start = (RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) * 6) + 5;
y_start = -y_start;
x_start += left;
y_start += top;
gfx_draw_rain(left, top, width, height, x_start, y_start);
x_start = -RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) + 8;
y_start = (RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) * 3) + 7;
y_start = -y_start;
x_start += left;
y_start += top;
gfx_draw_rain(left, top, width, height, x_start, y_start);
x_start = -RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) + 0x18;
y_start = (RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) * 4) + 0x0D;
y_start = -y_start;
x_start += left;
y_start += top;
gfx_draw_rain(left, top, width, height, x_start, y_start);
}

329
src/drawing/rect.c Normal file
View File

@@ -0,0 +1,329 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John, Peter Hill, Duncan Frost
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#include "../addresses.h"
#include "../common.h"
#include "drawing.h"
/**
*
* rct2: 0x00678AD4
* dpi (edi)
* left (ax)
* top (cx)
* right (bx)
* bottom (dx)
* colour (ebp)
*/
void gfx_fill_rect(rct_drawpixelinfo *dpi, int left, int top, int right, int bottom, int colour)
{
int left_, right_, top_, bottom_;
rct_drawpixelinfo* dpi_;
left_ = left;
right_ = right;
top_ = top;
bottom_ = bottom;
dpi_ = dpi;
if ((left > right) || (top > bottom) || (dpi->x > right) || (left >= (dpi->x + dpi->width)) ||
(bottom < dpi->y) || (top >= (dpi->y + dpi->height)))
return;
colour |= RCT2_GLOBAL(0x009ABD9C, uint32);
uint16 cross_pattern = 0;
int start_x = left - dpi->x;
if (start_x < 0){
start_x = 0;
cross_pattern ^= start_x;
}
int end_x = right - dpi->x;
end_x++;
if (end_x > dpi->width)
end_x = dpi->width;
int width = end_x - start_x;
int start_y = top - dpi->y;
if (start_y < 0){
start_y = 0;
cross_pattern ^= start_y;
}
int end_y = bottom - dpi->y;
end_y++;
if (end_y > dpi->height)
end_y = dpi->height;
int height = end_y - start_y;
if (colour&0x1000000){
// 00678B2E 00678BE5
//Cross hatching
uint8* dest_pointer = (start_y * (dpi->width + dpi->pitch)) + start_x + dpi->bits;
uint32 ecx;
for (int i = 0; i < height; ++i) {
uint8* next_dest_pointer = dest_pointer + dpi->width + dpi->pitch;
ecx = cross_pattern;
// Rotate right
ecx = (ecx >> 1) | (ecx << (sizeof(ecx) * CHAR_BIT - 1));
ecx = (ecx & 0xFFFF0000) | width;
// Fill every other pixel with the colour
for (; (ecx & 0xFFFF) > 0; ecx--) {
ecx = ecx ^ 0x80000000;
if ((int)ecx < 0) {
*dest_pointer = colour & 0xFF;
}
dest_pointer++;
}
cross_pattern ^= 1;
dest_pointer = next_dest_pointer;
}
return;
}
if (colour & 0x2000000){
//0x2000000
// 00678B7E 00678C83
// Location in screen buffer?
uint8* dest_pointer = dpi->bits + (uint32)((start_y >> (dpi->zoom_level)) * ((dpi->width >> dpi->zoom_level) + dpi->pitch) + (start_x >> dpi->zoom_level));
// Find colour in colour table?
uint16 eax = palette_to_g1_offset[(colour & 0xFF)];
rct_g1_element g1_element = RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[eax];
// Fill the rectangle with the colours from the colour table
for (int i = 0; i < height>>dpi->zoom_level; ++i) {
uint8* next_dest_pointer = dest_pointer + (dpi->width >> dpi->zoom_level) + dpi->pitch;
for (int j = 0; j < width; ++j) {
*dest_pointer = g1_element.offset[*dest_pointer];
dest_pointer++;
}
dest_pointer = next_dest_pointer;
}
return;
}
if (colour & 0x4000000){
//0x4000000
// 00678B8A 00678E38
char* dest_pointer;
dest_pointer = start_y * (dpi->width + dpi->pitch) + start_x + dpi->bits;
//The pattern loops every 15 lines this is which
//part the pattern is on.
int pattern_y = (start_y + dpi->y) % 16;
//The pattern loops every 15 pixels this is which
//part the pattern is on.
int start_pattern_x = (start_x + dpi_->x) % 16;
int pattern_x = start_pattern_x;
uint16* pattern_pointer;
pattern_pointer = RCT2_ADDRESS(0x0097FEFC,uint16*)[colour >> 28]; // or possibly uint8)[esi*4] ?
for (int no_lines = height; no_lines > 0; no_lines--) {
char* next_dest_pointer = dest_pointer + dpi->width + dpi->pitch;
uint16 pattern = pattern_pointer[pattern_y];
for (int no_pixels = width; no_pixels > 0; --no_pixels) {
if (pattern & (1 << pattern_x))
*dest_pointer = colour & 0xFF;
pattern_x = (pattern_x + 1) % 16;
dest_pointer++;
}
pattern_x = start_pattern_x;
pattern_y = (pattern_y + 1) % 16;
dest_pointer = next_dest_pointer;
}
return;
}
if (colour & 0x8000000){
//0x8000000
// 00678B3A 00678EC9 still to be implemented
//RCT2_CALLPROC_X(0x00678AD4, left, right, top, bottom, 0, dpi, colour);
int esi = left - RCT2_GLOBAL(0x1420070,sint16);
RCT2_GLOBAL(0xEDF824,uint32) = esi;
esi = top - RCT2_GLOBAL(0x1420072,sint16);
RCT2_GLOBAL(0xEDF828,uint32) = esi;
left -= dpi->x;//0x4
if ( left < 0 ){
RCT2_GLOBAL(0xEDF824,sint32) -= left;
left = 0;
}
right -= dpi->x;
right++;
if ( right > dpi->width ){
right = dpi->width;
}
right -= left;
top -= dpi->y;
if ( top < 0 ){
RCT2_GLOBAL(0xEDF828,sint32) -= top;
top = 0;
}
bottom -= dpi->y;
bottom++;
if (bottom > dpi->height){
bottom = dpi->height;
}
bottom -= top;
RCT2_GLOBAL(0xEDF824,sint32) &= 0x3F;
RCT2_GLOBAL(0xEDF828,sint32) &= 0x3F;
esi = dpi->width;
esi += dpi->pitch;
esi *= top;
esi += left;
esi += (uint32)dpi->bits;
RCT2_GLOBAL(0xEDF82C,sint32) = right;
RCT2_GLOBAL(0xEDF830,sint32) = bottom;
left = dpi->width;
left+= dpi->pitch;
left-= right;
RCT2_GLOBAL(0xEDF834,sint32) = left;
colour &= 0xFF;
colour--;
right = colour;
colour <<= 8;
right |= colour;
RCT2_GLOBAL(0xEDF838,sint32) = right;
//right <<= 4;
int edi = esi;
esi = RCT2_GLOBAL(0xEDF828,sint32);
esi *= 0x40;
left = 0;
esi += (uint32)(RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS,rct_g1_element)[right]).offset;//???
//Not finished
//Start of loop
return;
}
//0x0000000
uint8* dest_pointer = start_y * (dpi->width + dpi->pitch) + start_x + dpi->bits;
for (int i = 0; i < height; ++i) {
memset(dest_pointer, (colour & 0xFF), width);
dest_pointer += dpi->width + dpi->pitch;
}
// RCT2_CALLPROC_X(0x00678AD4, left, right, top, bottom, 0, dpi, colour);
}
/**
* Draw a rectangle, with optional border or fill
*
* rct2: 0x006E6F81
* dpi (edi)
* left (ax)
* top (cx)
* right (bx)
* bottom (dx)
* colour (ebp)
* flags (si)
*/
void gfx_fill_rect_inset(rct_drawpixelinfo* dpi, short left, short top, short right, short bottom, int colour, short flags)
{
uint8 shadow, fill, hilight;
// Flags
int no_border, no_fill, pressed;
no_border = 8;
no_fill = 0x10;
pressed = 0x20;
if (colour & 0x180) {
if (colour & 0x100) {
colour = colour & 0x7F;
} else {
colour = RCT2_ADDRESS(0x009DEDF4,uint8)[colour];
}
colour = colour | 0x2000000; //Transparent
if (flags & no_border) {
gfx_fill_rect(dpi, left, top, bottom, right, colour);
} else if (flags & pressed) {
// Draw outline of box
gfx_fill_rect(dpi, left, top, left, bottom, colour + 1);
gfx_fill_rect(dpi, left, top, right, top, colour + 1);
gfx_fill_rect(dpi, right, top, right, bottom, colour + 2);
gfx_fill_rect(dpi, left, bottom, right, bottom, colour + 2);
if (!(flags & no_fill)) {
gfx_fill_rect(dpi, left+1, top+1, right-1, bottom-1, colour);
}
} else {
// Draw outline of box
gfx_fill_rect(dpi, left, top, left, bottom, colour + 2);
gfx_fill_rect(dpi, left, top, right, top, colour + 2);
gfx_fill_rect(dpi, right, top, right, bottom, colour + 1);
gfx_fill_rect(dpi, left, bottom, right, bottom, colour + 1);
if (!(flags & no_fill)) {
gfx_fill_rect(dpi, left+1, top+1, right-1, bottom-1, colour);
}
}
} else {
if (flags & 0x80) {
shadow = RCT2_ADDRESS(0x0141FC46, uint8)[colour * 8];
fill = RCT2_ADDRESS(0x0141FC48, uint8)[colour * 8];
hilight = RCT2_ADDRESS(0x0141FC4A, uint8)[colour * 8];
} else {
shadow = RCT2_ADDRESS(0x0141FC47, uint8)[colour * 8];
fill = RCT2_ADDRESS(0x0141FC49, uint8)[colour * 8];
hilight = RCT2_ADDRESS(0x0141FC4B, uint8)[colour * 8];
}
if (flags & no_border) {
gfx_fill_rect(dpi, left, top, right, bottom, fill);
} else if (flags & pressed) {
// Draw outline of box
gfx_fill_rect(dpi, left, top, left, bottom, shadow);
gfx_fill_rect(dpi, left + 1, top, right, top, shadow);
gfx_fill_rect(dpi, right, top + 1, right, bottom - 1, hilight);
gfx_fill_rect(dpi, left + 1, bottom, right, bottom, hilight);
if (!(flags & no_fill)) {
if (!(flags & 0x40)) {
if (flags & 0x04) {
fill = RCT2_ADDRESS(0x0141FC49, uint8)[0];
} else {
fill = RCT2_ADDRESS(0x0141FC4A, uint8)[colour * 8];
}
}
gfx_fill_rect(dpi, left+1, top+1, right-1, bottom-1, fill);
}
} else {
// Draw outline of box
gfx_fill_rect(dpi, left, top, left, bottom - 1, hilight);
gfx_fill_rect(dpi, left + 1, top, right - 1, top, hilight);
gfx_fill_rect(dpi, right, top, right, bottom - 1, shadow);
gfx_fill_rect(dpi, left, bottom, right, bottom, shadow);
if (!(flags & no_fill)) {
if (flags & 0x04) {
fill = RCT2_ADDRESS(0x0141FC49, uint8)[0];
}
gfx_fill_rect(dpi, left+1, top+1, right-1, bottom-1, fill);
}
}
}
}

577
src/drawing/sprite.c Normal file
View File

@@ -0,0 +1,577 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John, Peter Hill, Duncan Frost
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#include "../addresses.h"
#include "../common.h"
#include "drawing.h"
typedef struct {
uint32 num_entries;
uint32 total_size;
} rct_g1_header;
void *_g1Buffer = NULL;
/**
*
* rct2: 0x00678998
*/
int gfx_load_g1()
{
FILE *file;
rct_g1_header header;
unsigned int i;
rct_g1_element *g1Elements = RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element);
file = fopen(get_file_path(PATH_ID_G1), "rb");
if (file != NULL) {
if (fread(&header, 8, 1, file) == 1) {
// number of elements is stored in g1.dat, but because the entry headers are static, this can't be variable until
// made into a dynamic array
header.num_entries = 29294;
// Read element headers
fread(g1Elements, header.num_entries * sizeof(rct_g1_element), 1, file);
// Read element data
_g1Buffer = rct2_malloc(header.total_size);
fread(_g1Buffer, header.total_size, 1, file);
fclose(file);
// Fix entry data offsets
for (i = 0; i < header.num_entries; i++)
g1Elements[i].offset += (int)_g1Buffer;
// Successful
return 1;
}
fclose(file);
}
// Unsuccessful
RCT2_ERROR("Unable to load g1.dat");
return 0;
}
/**
* Copies a sprite onto the buffer. There is no compression used on the sprite
* image.
* rct2: 0x0067A690
*/
void gfx_bmp_sprite_to_buffer(uint8* palette_pointer, uint8* unknown_pointer, uint8* source_pointer, uint8* dest_pointer, rct_g1_element* source_image, rct_drawpixelinfo *dest_dpi, int height, int width, int image_type){
uint8 zoom_level = dest_dpi->zoom_level;
uint8 zoom_amount = 1 << zoom_level;
//Requires use of palette?
if (image_type & IMAGE_TYPE_USE_PALETTE){
//Mix with another image?? and colour adjusted
if (unknown_pointer!= NULL){ //Not tested. I can't actually work out when this code runs.
unknown_pointer += source_pointer - source_image->offset;// RCT2_GLOBAL(0x9E3CE0, uint32);
for (; height > 0; height -= zoom_amount){
uint8* next_source_pointer = source_pointer + (uint32)(source_image->width * zoom_amount);
uint8* next_unknown_pointer = unknown_pointer + (uint32)(source_image->width * zoom_amount);
uint8* next_dest_pointer = dest_pointer + (dest_dpi->width / zoom_amount) + dest_dpi->pitch;
for (int no_pixels = width; no_pixels > 0; no_pixels -= zoom_amount, source_pointer += zoom_amount, unknown_pointer += zoom_amount, dest_pointer++){
uint8 pixel = *source_pointer;
pixel = palette_pointer[pixel];
pixel &= *unknown_pointer;
if (pixel){
*dest_pointer = pixel;
}
}
source_pointer = next_source_pointer;
dest_pointer = next_dest_pointer;
unknown_pointer = next_unknown_pointer;
}
return;
}
//image colour adjusted?
for (; height > 0; height -= zoom_amount){
uint8* next_source_pointer = source_pointer + (uint32)(source_image->width * zoom_amount);
uint8* next_dest_pointer = dest_pointer + (dest_dpi->width / zoom_amount) + dest_dpi->pitch;
for (int no_pixels = width; no_pixels > 0; no_pixels -= zoom_amount, source_pointer += zoom_amount, dest_pointer++){
uint8 pixel = *source_pointer;
pixel = palette_pointer[pixel];
if (pixel){
*dest_pointer = pixel;
}
}
source_pointer = next_source_pointer;
dest_pointer = next_dest_pointer;
}
return;
}
//Mix with background. It only uses source pointer for
//telling if it needs to be drawn not for colour.
if (image_type & IMAGE_TYPE_MIX_BACKGROUND){//Not tested
for (; height > 0; height -= zoom_amount){
uint8* next_source_pointer = source_pointer + (uint32)(source_image->width * zoom_amount);
uint8* next_dest_pointer = dest_pointer + (dest_dpi->width / zoom_amount) + dest_dpi->pitch;
for (int no_pixels = width; no_pixels > 0; no_pixels -= zoom_amount, source_pointer += zoom_amount, dest_pointer++){
uint8 pixel = *source_pointer;
if (pixel){
pixel = *dest_pointer;
pixel = palette_pointer[pixel];
*dest_pointer = pixel;
}
}
source_pointer = next_source_pointer;
dest_pointer = next_dest_pointer;
}
return;
}
//Basic bitmap no fancy stuff
if (!(source_image->flags & G1_FLAG_BMP)){//Not tested
for (; height > 0; height -= zoom_amount){
uint8* next_source_pointer = source_pointer + (uint32)(source_image->width * zoom_amount);
uint8* next_dest_pointer = dest_pointer + (dest_dpi->width / zoom_amount) + dest_dpi->pitch;
for (int no_pixels = width; no_pixels > 0; no_pixels -= zoom_amount, dest_pointer++, source_pointer += zoom_amount){
*dest_pointer = *source_pointer;
}
dest_pointer = next_dest_pointer;
source_pointer = next_source_pointer;
}
return;
}
if (RCT2_GLOBAL(0x9E3CDC, uint32) != 0){//Not tested. I can't actually work out when this code runs.
unknown_pointer += source_pointer - source_image->offset;
for (; height > 0; height -= zoom_amount){
uint8* next_source_pointer = source_pointer + (uint32)(source_image->width * zoom_amount);
uint8* next_unknown_pointer = unknown_pointer + (uint32)(source_image->width * zoom_amount);
uint8* next_dest_pointer = dest_pointer + (dest_dpi->width / zoom_amount) + dest_dpi->pitch;
for (int no_pixels = width; no_pixels > 0; no_pixels -= zoom_amount, dest_pointer++, source_pointer += zoom_amount, unknown_pointer += zoom_amount){
uint8 pixel = *source_pointer;
pixel &= *unknown_pointer;
if (pixel){
*dest_pointer = pixel;
}
}
dest_pointer = next_dest_pointer;
source_pointer = next_source_pointer;
unknown_pointer = next_unknown_pointer;
}
}
//Basic bitmap with no draw pixels
for (; height > 0; height -= zoom_amount){
uint8* next_source_pointer = source_pointer + (uint32)(source_image->width * zoom_amount);
uint8* next_dest_pointer = dest_pointer + (dest_dpi->width / zoom_amount) + dest_dpi->pitch;
for (int no_pixels = width; no_pixels > 0; no_pixels -= zoom_amount, dest_pointer++, source_pointer += zoom_amount){
uint8 pixel = *source_pointer;
if (pixel){
*dest_pointer = pixel;
}
}
dest_pointer = next_dest_pointer;
source_pointer = next_source_pointer;
}
return;
}
/**
* Transfers readied images onto buffers
* This function copies the sprite data onto the screen
* rct2: 0x0067AA18
*/
void gfx_rle_sprite_to_buffer(uint8* source_bits_pointer, uint8* dest_bits_pointer, uint8* palette_pointer, rct_drawpixelinfo *dpi, int image_type, int source_y_start, int height, int source_x_start, int width){
int zoom_level = dpi->zoom_level;
int zoom_amount = 1 << zoom_level;
uint8* next_source_pointer;
uint8* next_dest_pointer = dest_bits_pointer;
//For every line in the image
for (int y = source_y_start; y < (height + source_y_start); y += zoom_amount){
//The first part of the source pointer is a list of offsets to different lines
//This will move the pointer to the correct source line.
next_source_pointer = source_bits_pointer + ((uint16*)source_bits_pointer)[y];
uint8 last_data_line = 0;
//For every data section in the line
while (!last_data_line){
uint8* source_pointer = next_source_pointer;
uint8* dest_pointer = next_dest_pointer;
int no_pixels = *source_pointer++;
//gap_size is the number of non drawn pixels you require to
//jump over on your destination
uint8 gap_size = *source_pointer++;
//The last bit in no_pixels tells you if you have reached the end of a line
last_data_line = no_pixels & 0x80;
//Clear the last data line bit so we have just the no_pixels
no_pixels &= 0x7f;
//Have our next source pointer point to the next data section
next_source_pointer = source_pointer + no_pixels;
//Calculates the start point of the image
int x_start = gap_size - source_x_start;
if (x_start > 0){
//Since the start is positive
//We need to move the drawing surface to the correct position
dest_pointer += x_start / zoom_amount;
}
else{
//If the start is negative we require to remove part of the image.
//This is done by moving the image pointer to the correct position.
source_pointer -= x_start;
//The no_pixels will be reduced in this operation
no_pixels += x_start;
//If there are no pixels there is nothing to draw this data section
if (no_pixels <= 0) continue;
//Reset the start position to zero as we have taken into account all moves
x_start = 0;
}
int x_end = x_start + no_pixels;
//If the end position is further out than the whole image
//end position then we need to shorten the line again
if (x_end > width){
//Shorten the line
no_pixels -= x_end - width;
//If there are no pixels there is nothing to draw.
if (no_pixels <= 0) continue;
}
//Finally after all those checks, copy the image onto the drawing surface
//If the image type is not a basic one we require to mix the pixels
if (image_type & IMAGE_TYPE_USE_PALETTE){//In the .exe these are all unraveled loops
for (; no_pixels > 0; no_pixels -= zoom_amount, source_pointer += zoom_amount, dest_pointer++){
uint8 al = *source_pointer;
uint8 ah = *dest_pointer;
if (image_type & IMAGE_TYPE_MIX_BACKGROUND)
al = palette_pointer[(((uint16)al << 8) | ah) - 0x100];
else
al = palette_pointer[al];
*dest_pointer = al;
}
}
else if (image_type & IMAGE_TYPE_MIX_BACKGROUND){//In the .exe these are all unraveled loops
//Doesnt use source pointer ??? mix with background only?
//Not Tested
for (; no_pixels > 0; no_pixels -= zoom_amount, dest_pointer++){
uint8 pixel = *dest_pointer;
pixel = palette_pointer[pixel];
*dest_pointer = pixel;
}
}
else
{
for (; no_pixels > 0; no_pixels -= zoom_amount, source_pointer += zoom_amount, dest_pointer++){
*dest_pointer = *source_pointer;
}
}
}
//Add a line to the drawing surface pointer
next_dest_pointer += dpi->width / zoom_amount + dpi->pitch;
}
}
/**
*
* rct2: 0x0067A28E
* image_id (ebx)
* image_id as below
* 0b_111X_XXXX_XXXX_XXXX_XXXX_XXXX_XXXX_XXXX image_type
* 0b_XXX1_11XX_XXXX_XXXX_XXXX_XXXX_XXXX_XXXX image_sub_type (unknown pointer)
* 0b_XXX1_1111_XXXX_XXXX_XXXX_XXXX_XXXX_XXXX secondary_colour
* 0b_XXXX_XXXX_1111_1XXX_XXXX_XXXX_XXXX_XXXX primary_colour
* 0b_XXXX_X111_1111_1XXX_XXXX_XXXX_XXXX_XXXX palette_ref
* 0b_XXXX_XXXX_XXXX_X111_1111_1111_1111_1111 image_id (offset to g1)
* x (cx)
* y (dx)
* dpi (esi)
* tertiary_colour (ebp)
*/
void gfx_draw_sprite(rct_drawpixelinfo *dpi, int image_id, int x, int y, uint32 tertiary_colour)
{
//RCT2_CALLPROC_X(0x0067A28E, 0, image_id, x, y, 0, (int)dpi, tertiary_colour);
//return;
int image_type = (image_id & 0xE0000000) >> 28;
int image_sub_type = (image_id & 0x1C000000) >> 26;
uint8* palette_pointer = NULL;
uint8 palette[0x100];
RCT2_GLOBAL(0x00EDF81C, uint32) = image_id & 0xE0000000;
uint8* unknown_pointer = (uint8*)(RCT2_ADDRESS(0x9E3CE4, uint32*)[image_sub_type]);
RCT2_GLOBAL(0x009E3CDC, uint32) = (uint32)unknown_pointer;
if (image_type && !(image_type & IMAGE_TYPE_UNKNOWN)) {
uint8 palette_ref = (image_id >> 19) & 0xFF;
if (image_type & IMAGE_TYPE_MIX_BACKGROUND){
unknown_pointer = NULL;
RCT2_GLOBAL(0x009E3CDC, uint32) = 0;
}
else{
palette_ref &= 0x7F;
}
uint16 palette_offset = palette_to_g1_offset[palette_ref];
palette_pointer = RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[palette_offset].offset;
RCT2_GLOBAL(0x9ABDA4, uint32) = (uint32)palette_pointer;
}
else if (image_type && !(image_type & IMAGE_TYPE_USE_PALETTE)){
RCT2_GLOBAL(0x9E3CDC, uint32) = 0;
unknown_pointer = NULL;
uint32 primary_offset = palette_to_g1_offset[(image_id >> 19) & 0x1F];
uint32 secondary_offset = palette_to_g1_offset[(image_id >> 24) & 0x1F];
uint32 tertiary_offset = palette_to_g1_offset[tertiary_colour];
rct_g1_element* primary_colour = &RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[primary_offset];
rct_g1_element* secondary_colour = &RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[secondary_offset];
rct_g1_element* tertiary_colour = &RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[tertiary_offset];
memcpy((uint8*)0x9ABFFF, &primary_colour->offset[0xF3], 12);
memcpy((uint8*)0x9ABFD6, &secondary_colour->offset[0xF3], 12);
memcpy((uint8*)0x9ABF3A, &tertiary_colour->offset[0xF3], 12);
//image_id
RCT2_GLOBAL(0xEDF81C, uint32) |= 0x20000000;
image_id |= IMAGE_TYPE_USE_PALETTE << 28;
RCT2_GLOBAL(0x9ABDA4, uint32) = 0x9ABF0C;
palette_pointer = (uint8*)0x9ABF0C;
}
else if (image_type){
RCT2_GLOBAL(0x9E3CDC, uint32) = 0;
unknown_pointer = NULL;
//Copy the peep palette into a new palette.
//Not really required but its nice to make a copy
memcpy(palette, peep_palette, 0x100);
//Top
int top_type = (image_id >> 19) & 0x1f;
uint32 top_offset = palette_to_g1_offset[top_type]; //RCT2_ADDRESS(0x97FCBC, uint32)[top_type];
rct_g1_element top_palette = RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[top_offset];
memcpy(palette + 0xF3, top_palette.offset + 0xF3, 12);
//Trousers
int trouser_type = (image_id >> 24) & 0x1f;
uint32 trouser_offset = palette_to_g1_offset[trouser_type]; //RCT2_ADDRESS(0x97FCBC, uint32)[trouser_type];
rct_g1_element trouser_palette = RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[trouser_offset];
memcpy(palette + 0xCA, trouser_palette.offset + 0xF3, 12);
//For backwards compatibility until the zooming function is done
RCT2_GLOBAL(0x9ABDA4, uint8*) = palette;
palette_pointer = palette;
}
gfx_draw_sprite_palette_set(dpi, image_id, x, y, palette_pointer, unknown_pointer);
}
/*
* rct: 0x0067A46E
* image_id (ebx) and also (0x00EDF81C)
* palette_pointer (0x9ABDA4)
* unknown_pointer (0x9E3CDC)
* dpi (edi)
* x (cx)
* y (dx)
*/
void gfx_draw_sprite_palette_set(rct_drawpixelinfo *dpi, int image_id, int x, int y, uint8* palette_pointer, uint8* unknown_pointer){
int image_element = 0x7FFFF&image_id;
int image_type = (image_id & 0xE0000000) >> 28;
rct_g1_element* g1_source = &(RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[image_element]);
//Zooming code has been integrated into main code.
//if (dpi->zoom_level >= 1){ //These have not been tested
// //something to do with zooming
// if (dpi->zoom_level == 1){
// RCT2_CALLPROC_X(0x0067A28E, 0, image_id, x, y, 0, (int)dpi, 0);
// return;
// }
// if (dpi->zoom_level == 2){
// RCT2_CALLPROC_X(0x0067DADA, 0, (int)g1_source, x, y, 0, (int)dpi, 0);
// return;
// }
// RCT2_CALLPROC_X(0x0067FAAE, 0, (int)g1_source, x, y, 0, (int)dpi, 0);
// return;
//}
if ( dpi->zoom_level && (g1_source->flags & (1<<4)) ){
rct_drawpixelinfo zoomed_dpi = {
.bits = dpi->bits,
.x = dpi->x >> 1,
.y = dpi->y >> 1,
.height = dpi->height>>1,
.width = dpi->width>>1,
.pitch = dpi->pitch,
.zoom_level = dpi->zoom_level - 1
};
gfx_draw_sprite_palette_set(&zoomed_dpi, (image_type << 28) | (image_element - g1_source->zoomed_offset), x >> 1, y >> 1, palette_pointer, unknown_pointer);
return;
}
if ( dpi->zoom_level && (g1_source->flags & (1<<5)) ){
return;
}
//Its used super often so we will define it to a seperate variable.
int zoom_level = dpi->zoom_level;
int zoom_amount = 1 << zoom_level;
int zoom_mask = 0xFFFFFFFF << zoom_level;
//This will be the height of the drawn image
int height = g1_source->height;
//This is the start y coordinate on the destination
sint16 dest_start_y = ((y + g1_source->y_offset)&zoom_mask) - dpi->y;
//This is the start y coordinate on the source
int source_start_y = 0;
if (dest_start_y < 0){
//If the destination y is negative reduce the height of the
//image as we will cut off the bottom
height += dest_start_y;
//If the image is no longer visible nothing to draw
if (height <= 0){
return;
}
//The source image will start a further up the image
source_start_y -= dest_start_y;
//The destination start is now reset to 0
dest_start_y = 0;
}
int dest_end_y = dest_start_y + height;
if (dest_end_y > dpi->height){
//If the destination y is outside of the drawing
//image reduce the height of the image
height -= dest_end_y - dpi->height;
}
//If the image no longer has anything to draw
if (height <= 0)return;
dest_start_y /= zoom_amount;
dest_end_y /= zoom_amount;
//This will be the width of the drawn image
int width = g1_source->width;
//This is the source start x coordinate
int source_start_x = 0;
//This is the destination start x coordinate
sint16 dest_start_x = ((x + g1_source->x_offset) & zoom_mask) - dpi->x;
if (dest_start_x < 0){
//If the destination is negative reduce the width
//image will cut off the side
width += dest_start_x;
//If there is no image to draw
if (width <= 0){
return;
}
//The source start will also need to cut off the side
source_start_x -= dest_start_x;
//Reset the destination to 0
dest_start_x = 0;
}
int dest_end_x = dest_start_x + width;
if (dest_end_x > dpi->width){
//If the destination x is outside of the drawing area
//reduce the image width.
width -= dest_end_x - dpi->width;
//If there is no image to draw.
if (width <= 0)return;
}
dest_start_x /= zoom_amount;
dest_end_x /= zoom_amount;
uint8* dest_pointer = (uint8*)dpi->bits;
//Move the pointer to the start point of the destination
dest_pointer += ((dpi->width / zoom_amount) + dpi->pitch)*dest_start_y + dest_start_x;
if (g1_source->flags & G1_FLAG_RLE_COMPRESSION){
//We have to use a different method to move the source pointer for
//rle encoded sprites so that will be handled within this function
gfx_rle_sprite_to_buffer(g1_source->offset, dest_pointer, palette_pointer, dpi, image_type, source_start_y, height, source_start_x, width);
return;
}
uint8* source_pointer = g1_source->offset;
//Move the pointer to the start point of the source
source_pointer += g1_source->width*source_start_y + source_start_x;
if (!(g1_source->flags & 0x02)){
gfx_bmp_sprite_to_buffer(palette_pointer, unknown_pointer, source_pointer, dest_pointer, g1_source, dpi, height, width, image_type);
return;
}
//0x67A60A Not tested
int total_no_pixels = g1_source->width*g1_source->height;
source_pointer = g1_source->offset;
uint8* new_source_pointer_start = malloc(total_no_pixels);
uint8* new_source_pointer = new_source_pointer_start;// 0x9E3D28;
int ebx, ecx;
while (total_no_pixels>0){
sint8 no_pixels = *source_pointer;
if (no_pixels >= 0){
source_pointer++;
total_no_pixels -= no_pixels;
memcpy((char*)new_source_pointer, (char*)source_pointer, no_pixels);
new_source_pointer += no_pixels;
source_pointer += no_pixels;
continue;
}
ecx = no_pixels;
no_pixels &= 0x7;
ecx >>= 3;//SAR
int eax = ((int)no_pixels)<<8;
ecx = -ecx;//Odd
eax = eax & 0xFF00 + *(source_pointer+1);
total_no_pixels -= ecx;
source_pointer += 2;
ebx = (uint32)new_source_pointer - eax;
eax = (uint32)source_pointer;
source_pointer = (uint8*)ebx;
ebx = eax;
eax = 0;
memcpy((char*)new_source_pointer, (char*)source_pointer, ecx);
new_source_pointer += ecx;
source_pointer += ecx;
source_pointer = (uint8*)ebx;
}
source_pointer = new_source_pointer_start + g1_source->width*source_start_y + source_start_x;
gfx_bmp_sprite_to_buffer(palette_pointer, unknown_pointer, source_pointer, dest_pointer, g1_source, dpi, height, width, image_type);
free(new_source_pointer_start);
return;
}

1041
src/drawing/string.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -19,21 +19,22 @@
*****************************************************************************/
#include "addresses.h"
#include "date.h"
#include "audio/audio.h"
#include "drawing/drawing.h"
#include "editor.h"
#include "game.h"
#include "gfx.h"
#include "map.h"
#include "news_item.h"
#include "interface/window.h"
#include "interface/viewport.h"
#include "localisation/date.h"
#include "localisation/localisation.h"
#include "management/finance.h"
#include "management/news_item.h"
#include "object.h"
#include "park.h"
#include "ride.h"
#include "window.h"
#include "viewport.h"
#include "finance.h"
#include "audio.h"
#include "sprite.h"
#include "string_ids.h"
#include "peep/staff.h"
#include "ride/ride.h"
#include "world/map.h"
#include "world/park.h"
#include "world/sprite.h"
static void set_all_land_owned();
@@ -62,7 +63,7 @@ void editor_load()
finance_init();
date_reset();
window_guest_list_init_vars_b();
window_staff_init_vars();
window_staff_list_init_vars();
RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) = SCREEN_FLAGS_SCENARIO_EDITOR;
RCT2_GLOBAL(0x0141F570, uint8) = 0;
RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) |= PARK_FLAGS_SHOW_REAL_GUEST_NAMES;
@@ -111,7 +112,7 @@ void trackdesigner_load()
finance_init();
date_reset();
window_guest_list_init_vars_b();
window_staff_init_vars();
window_staff_list_init_vars();
RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) = SCREEN_FLAGS_TRACK_DESIGNER;
RCT2_GLOBAL(0x0141F570, uint8) = 0;
window_new_ride_init_vars();
@@ -149,7 +150,7 @@ void trackmanager_load()
finance_init();
date_reset();
window_guest_list_init_vars_b();
window_staff_init_vars();
window_staff_list_init_vars();
RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) = SCREEN_FLAGS_TRACK_MANAGER;
RCT2_GLOBAL(0x0141F570, uint8) = 0;
window_new_ride_init_vars();
@@ -192,10 +193,11 @@ static void set_all_land_owned()
*/
void sub_6BD3A4() {
for (short i = 0; i < 200; i++) {
RCT2_ADDRESS(0x013CA672, uint8)[i] = 0;
RCT2_ADDRESS(RCT2_ADDRESS_STAFF_MODE_ARRAY, uint8)[i] = STAFF_MODE_NONE;
}
for (short i = 200; i < 204; i++) {
RCT2_ADDRESS(0x013CA672, uint8)[i] = 1;
RCT2_ADDRESS(RCT2_ADDRESS_STAFF_MODE_ARRAY, uint8)[i] = STAFF_MODE_WALK;
}
RCT2_CALLPROC_EBPSAFE(0x006C0C3F);
//RCT2_CALLPROC_EBPSAFE(0x006C0C3F);
sub_6C0C3F();
}

View File

@@ -19,113 +19,36 @@
*****************************************************************************/
#include "addresses.h"
#include "audio.h"
#include "climate.h"
#include "audio/audio.h"
#include "config.h"
#include "rct2.h"
#include "game.h"
#include "finance.h"
#include "input.h"
#include "news_item.h"
#include "localisation/localisation.h"
#include "interface/screenshot.h"
#include "interface/viewport.h"
#include "interface/widget.h"
#include "interface/window.h"
#include "management/finance.h"
#include "management/news_item.h"
#include "management/research.h"
#include "object.h"
#include "osinterface.h"
#include "park.h"
#include "peep.h"
#include "sawyercoding.h"
#include "peep/peep.h"
#include "peep/staff.h"
#include "platform/osinterface.h"
#include "ride/ride.h"
#include "ride/vehicle.h"
#include "scenario.h"
#include "screenshot.h"
#include "sprite.h"
#include "string_ids.h"
#include "title.h"
#include "tutorial.h"
#include "vehicle.h"
#include "viewport.h"
#include "widget.h"
#include "window.h"
#include "staff.h"
#include "window_error.h"
#include "window_tooltip.h"
#include "util/sawyercoding.h"
#include "windows/error.h"
#include "windows/tooltip.h"
#include "world/climate.h"
#include "world/park.h"
#include "world/sprite.h"
int gGameSpeed = 1;
typedef void(*draw_rain_func)(int left, int top, int width, int height);
/**
*
* rct2: 0x00684114
*/
void draw_light_rain(int left, int top, int width, int height){
int x_start = -RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) + 8;
int y_start = (RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) * 3) + 7;
y_start = -y_start;
x_start += left;
y_start += top;
gfx_draw_rain(left, top, width, height, x_start, y_start);
x_start = -RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) + 0x18;
y_start = (RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) * 4) + 0x0D;
y_start = -y_start;
x_start += left;
y_start += top;
gfx_draw_rain(left, top, width, height, x_start, y_start);
}
/**
*
* rct2: 0x0068416D
*/
void draw_heavy_rain(int left, int top, int width, int height){
int x_start = -RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int);
int y_start = RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) * 5;
y_start = -y_start;
x_start += left;
y_start += top;
gfx_draw_rain(left, top, width, height, x_start, y_start);
x_start = -RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) + 0x10;
y_start = (RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) * 6) + 5;
y_start = -y_start;
x_start += left;
y_start += top;
gfx_draw_rain(left, top, width, height, x_start, y_start);
x_start = -RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) + 8;
y_start = (RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) * 3) + 7;
y_start = -y_start;
x_start += left;
y_start += top;
gfx_draw_rain(left, top, width, height, x_start, y_start);
x_start = -RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) + 0x18;
y_start = (RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) * 4) + 0x0D;
y_start = -y_start;
x_start += left;
y_start += top;
gfx_draw_rain(left, top, width, height, x_start, y_start);
}
/**
*
* rct2: 0x009AC058
*/
const draw_rain_func draw_rain_function[] = {
NULL,
&draw_light_rain, // Light rain
&draw_heavy_rain // Heavy rain
};
/**
*
* rct2: 0x0066B5C0 (part of 0x0066B3E8)
@@ -138,148 +61,130 @@ void game_create_windows()
RCT2_CALLPROC_EBPSAFE(0x0066B905);
}
/**
*
* rct2: 0x006838BD
*/
void update_water_animation()
{
RCT2_CALLPROC_EBPSAFE(0x006838BD);
}
/**
*
* rct2: 0x00684383
* rct2: 0x006838BD
*/
void call_draw_rain_func(rct_window* w, short left, short right, short top, short bottom, uint32 draw_rain_func)
void update_palette_effects()
{
rct_viewport* vp = w->viewport;
if (vp == NULL) {
return;
}
left = max(left, vp->x);
right = min(right, vp->width);
top = max(top, vp->y);
bottom = min(bottom, vp->height);
if (left >= right || top >= bottom) {
return;
}
int width = right - left;
int height = bottom - top;
draw_rain_function[draw_rain_func](left, top, width, height);
}
/**
*
* rct2: 0x006842AF
* From 0x00684383 on: split into call_draw_rain_func
*/
void draw_rain_window(rct_window* original_w, short left, short right, short top, short bottom, uint32 draw_rain_func)
{
rct_window* newWindow = RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*);
rct_window* w = original_w + 1; // Start from second window
for (; ; w++) {
if (w >= newWindow) {
// Loop ended, draw rain for original_w
call_draw_rain_func(original_w, left, right, top, bottom, draw_rain_func);
return;
if (RCT2_GLOBAL(RCT2_ADDRESS_LIGHTNING_ACTIVE, uint8) == 1) {
// change palette to lighter color during lightning
int palette = 1532;
if (RCT2_GLOBAL(0x009ADAE0, sint32) != -1) {
palette = RCT2_GLOBAL(RCT2_GLOBAL(0x009ADAE0, int) + 2, int);
}
rct_g1_element g1_element = RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[palette];
int xoffset = g1_element.x_offset;
xoffset = xoffset * 4;
for (int i = 0; i < g1_element.width; i++) {
RCT2_ADDRESS(0x01424680 + xoffset, uint8)[(i * 4) + 0] = -((0xFF - g1_element.offset[(i * 3) + 0]) / 2) - 1;
RCT2_ADDRESS(0x01424680 + xoffset, uint8)[(i * 4) + 1] = -((0xFF - g1_element.offset[(i * 3) + 1]) / 2) - 1;
RCT2_ADDRESS(0x01424680 + xoffset, uint8)[(i * 4) + 2] = -((0xFF - g1_element.offset[(i * 3) + 2]) / 2) - 1;
}
RCT2_GLOBAL(0x014241BC, uint32) = 2;
osinterface_update_palette(RCT2_ADDRESS(0x01424680, uint8), 10, 236);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
RCT2_GLOBAL(RCT2_ADDRESS_LIGHTNING_ACTIVE, uint8)++;
} else {
if (RCT2_GLOBAL(RCT2_ADDRESS_LIGHTNING_ACTIVE, uint8) == 2) {
// change palette back to normal after lightning
int palette = 1532;
if (RCT2_GLOBAL(0x009ADAE0, sint32) != -1) {
palette = RCT2_GLOBAL(RCT2_GLOBAL(0x009ADAE0, int) + 2, int);
}
rct_g1_element g1_element = RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[palette];
int xoffset = g1_element.x_offset;
xoffset = xoffset * 4;
for (int i = 0; i < g1_element.width; i++) {
RCT2_ADDRESS(0x01424680 + xoffset, uint8)[(i * 4) + 0] = g1_element.offset[(i * 3) + 0];
RCT2_ADDRESS(0x01424680 + xoffset, uint8)[(i * 4) + 1] = g1_element.offset[(i * 3) + 1];
RCT2_ADDRESS(0x01424680 + xoffset, uint8)[(i * 4) + 2] = g1_element.offset[(i * 3) + 2];
}
}
if (right <= w->x || bottom <= w->y) {
continue;
// animate the water/lava/chain movement palette
int q = 0;
int weather_colour = RCT2_ADDRESS(0x98195C, uint32)[RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_WEATHER_GLOOM, uint8)];
if (weather_colour != -1) {
q = 1;
if (weather_colour != 0x2000031) {
q = 2;
}
}
uint32 j = RCT2_GLOBAL(0x009DE584, uint32);
j = (((uint16)((~j / 2) * 128) * 15) >> 16);
int p = 1533;
if (RCT2_GLOBAL(0x009ADAE0, int) != -1) {
p = RCT2_GLOBAL(RCT2_GLOBAL(0x009ADAE0, int) + 0x6, int);
}
rct_g1_element g1_element = RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[q + p];
uint8* vs = &g1_element.offset[j * 3];
uint8* vd = RCT2_ADDRESS(0x01424A18, uint8);
int n = 5;
for (int i = 0; i < n; i++) {
vd[0] = vs[0];
vd[1] = vs[1];
vd[2] = vs[2];
vs += 9;
if (vs >= &g1_element.offset[9 * n]) {
vs -= 9 * n;
}
vd += 4;
}
if (RCT_WINDOW_RIGHT(w) <= left || RCT_WINDOW_BOTTOM(w) <= top) {
continue;
p = 1536;
if (RCT2_GLOBAL(0x009ADAE0, int) != -1) {
p = RCT2_GLOBAL(RCT2_GLOBAL(0x009ADAE0, int) + 0xA, int);
}
g1_element = RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[q + p];
vs = &g1_element.offset[j * 3];
n = 5;
for (int i = 0; i < n; i++) {
vd[0] = vs[0];
vd[1] = vs[1];
vd[2] = vs[2];
vs += 9;
if (vs >= &g1_element.offset[9 * n]) {
vs -= 9 * n;
}
vd += 4;
}
if (left >= w->x) {
break;
j = ((uint16)(RCT2_GLOBAL(0x009DE584, uint32) * -960) * 3) >> 16;
p = 1539;
g1_element = RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[q + p];
vs = &g1_element.offset[j * 3];
vd += 12;
n = 3;
for (int i = 0; i < n; i++) {
vd[0] = vs[0];
vd[1] = vs[1];
vd[2] = vs[2];
vs += 3;
if (vs >= &g1_element.offset[3 * n]) {
vs -= 3 * n;
}
vd += 4;
}
draw_rain_window(original_w, left, w->x, top, bottom, draw_rain_func);
left = w->x;
draw_rain_window(original_w, left, right, top, bottom, draw_rain_func);
return;
RCT2_GLOBAL(0x014241BC, uint32) = 2;
osinterface_update_palette(RCT2_ADDRESS(0x01424680, uint8), 230, 16);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
if (RCT2_GLOBAL(RCT2_ADDRESS_LIGHTNING_ACTIVE, uint8) == 2) {
RCT2_GLOBAL(0x014241BC, uint32) = 2;
osinterface_update_palette(RCT2_ADDRESS(0x01424680, uint8), 10, 236);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
RCT2_GLOBAL(RCT2_ADDRESS_LIGHTNING_ACTIVE, uint8) = 0;
}
}
sint16 w_right = RCT_WINDOW_RIGHT(w);
if (right > w_right) {
draw_rain_window(original_w, left, w_right, top, bottom, draw_rain_func);
left = w_right;
draw_rain_window(original_w, left, right, top, bottom, draw_rain_func);
return;
}
if (top < w->y) {
draw_rain_window(original_w, left, right, top, w->y, draw_rain_func);
top = w->y;
draw_rain_window(original_w, left, right, top, bottom, draw_rain_func);
return;
}
sint16 w_bottom = RCT_WINDOW_BOTTOM(w);
if (bottom > w_bottom) {
draw_rain_window(original_w, left, right, top, w_bottom, draw_rain_func);
top = w_bottom;
draw_rain_window(original_w, left, right, top, bottom, draw_rain_func);
return;
if (RCT2_GLOBAL(0x009E2C4C, uint32) == 2 || RCT2_GLOBAL(0x009E2C4C, uint32) == 1) {
if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_CAP_BPP, uint32) != 8) {
RCT2_GLOBAL(0x009E2C78, int) = 1;
}
}
}
/**
*
* rct2: 0x00684266
*/
void draw_rain_animation(uint32 draw_rain_func)
{
rct_drawpixelinfo *screenDPI = RCT2_ADDRESS(RCT2_ADDRESS_SCREEN_DPI, rct_drawpixelinfo);
short left = screenDPI->x;
short right = left + screenDPI->width;
short top = screenDPI->y;
short bottom = top + screenDPI->height;
rct_window* newWindow = (RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*));
for (rct_window* w = g_window_list; w < newWindow; w++) {
draw_rain_window(w, left, right, top, bottom, draw_rain_func);
}
}
/**
*
* rct2: 0x00684218
*/
void update_rain_animation()
{
if (RCT2_GLOBAL(0x009ABDF2, uint8) == 0)
return;
// Draw picked-up peep
if (RCT2_GLOBAL(RCT2_ADDRESS_PICKEDUP_PEEP_SPRITE, uint32) != 0xFFFFFFFF) {
gfx_draw_sprite(
(rct_drawpixelinfo*)RCT2_ADDRESS_SCREEN_DPI,
RCT2_GLOBAL(RCT2_ADDRESS_PICKEDUP_PEEP_SPRITE, uint32),
RCT2_GLOBAL(RCT2_ADDRESS_PICKEDUP_PEEP_X, sint16),
RCT2_GLOBAL(RCT2_ADDRESS_PICKEDUP_PEEP_Y, sint16), 0
);
}
// Get rain draw function and draw rain
uint32 draw_rain_func = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_RAIN_LEVEL, uint8);
if (draw_rain_func > 0 && !(RCT2_GLOBAL(0x009DEA6F, uint8) & 1))
draw_rain_animation(draw_rain_func);
}
void game_update()
{
@@ -349,7 +254,7 @@ void game_update()
RCT2_GLOBAL(0x0141F568, uint8) = RCT2_GLOBAL(0x0013CA740, uint8);
game_handle_input();
update_water_animation();
update_palette_effects();
update_rain_animation();
if (RCT2_GLOBAL(0x009AAC73, uint8) != 255) {
@@ -379,11 +284,11 @@ void game_logic_update()
RCT2_CALLPROC_EBPSAFE(0x00672AA4); // update text effects
RCT2_CALLPROC_EBPSAFE(0x006ABE4C); // update rides
park_update();
RCT2_CALLPROC_EBPSAFE(0x00684C7A);
RCT2_CALLPROC_EBPSAFE(0x006B5A2A);
RCT2_CALLPROC_EBPSAFE(0x006B6456); // update ride measurements
research_update();
RCT2_CALLPROC_EBPSAFE(0x006B5A2A); // update ride ratings
ride_measurements_update();
RCT2_CALLPROC_EBPSAFE(0x0068AFAD);
RCT2_CALLPROC_EBPSAFE(0x006BBC6B); // vehicle and scream sounds
vehicle_sounds_update();//RCT2_CALLPROC_EBPSAFE(0x006BBC6B); // vehicle and scream sounds
peep_update_crowd_noise();
climate_update_sound();
news_item_update_current();
@@ -724,13 +629,18 @@ int game_load_save(const char *path)
if (s6Header->num_packed_objects > 0) {
j = 0;
for (i = 0; i < s6Header->num_packed_objects; i++)
j += object_load_packed();
j += object_load_packed(file);
if (j > 0)
object_list_load();
}
}
object_read_and_load_entries(file);
if (!object_read_and_load_entries(file)){
fclose(file);
RCT2_GLOBAL(0x009AC31B, uint8) = 255;
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_STRING_ID, uint16) = STR_FILE_CONTAINS_INVALID_DATA;
return 0;
};
// Read flags (16 bytes)
sawyercoding_read_chunk(file, (uint8*)RCT2_ADDRESS_CURRENT_MONTH_YEAR);
@@ -924,7 +834,7 @@ static uint32 game_do_command_table[58] = {
0x006A67C0,
0x00663CCD, // 20
0x006B53E9,
0x00698D6C,
0x00698D6C, // text input
0x0068C542,
0x0068C6D1,
0x0068BC01,

View File

@@ -32,7 +32,7 @@ enum GAME_COMMAND {
GAME_COMMAND_7,
GAME_COMMAND_SET_RIDE_OPEN, // 8
GAME_COMMAND_9,
GAME_COMMAND_10,
GAME_COMMAND_SET_RIDE_NAME,
GAME_COMMAND_11,
GAME_COMMAND_12,
GAME_COMMAND_13,
@@ -90,8 +90,7 @@ void game_create_windows();
void game_update();
void game_logic_update();
void sub_0x0069E9A7();
void update_rain_animation();
void update_water_animation();
void update_palette_effects();
int game_do_command(int eax, int ebx, int ecx, int edx, int esi, int edi, int ebp);
int game_do_command_p(int command, int *eax, int *ebx, int *ecx, int *edx, int *edi, int *esi, int *ebp);

2391
src/gfx.c

File diff suppressed because it is too large Load Diff

201
src/hook.c Normal file
View File

@@ -0,0 +1,201 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#include <windows.h>
#include "hook.h"
void* g_hooktableaddress = 0;
int g_hooktableoffset = 0;
int g_maxhooks = 1000;
void hookfunc(int address, int newaddress, int stacksize, int registerargs[], int registersreturned)
{
int i = 0;
char data[100];
int registerssaved = 7;
int n = registersreturned;
for (; n; registerssaved--) {
n &= n - 1;
}
int numrargs = 0;
for (int j = 0; ; j++) {
if (registerargs[j] != END) {
numrargs++;
} else {
break;
}
}
int rargssize = numrargs * 4;
data[i++] = 0x50; // push eax
// move stack down for possible existing arguments
for (int j = 0; j < stacksize; j++) {
data[i++] = 0x8B; // mov eax, [esp+x]
data[i++] = 0x44;
data[i++] = 0xE4;
data[i++] = (signed char)((4 * (stacksize - j)) + 4);
data[i++] = 0x89; // mov [esp+x], eax
data[i++] = 0x44;
data[i++] = 0xE4;
data[i++] = (signed char)((4 * (stacksize - j)) - ((registerssaved + stacksize) * 4));
}
if (numrargs > 0) {
// push the registers to be on the stack to access as arguments
data[i++] = 0x83; // add esp, x
data[i++] = 0xC4;
data[i++] = -((registerssaved + stacksize) * 4) + 4;
for (signed int j = numrargs - 1; j >= 0; j--) {
switch (registerargs[j]) {
case EAX: data[i++] = 0x50; break;
case EBX: data[i++] = 0x53; break;
case ECX: data[i++] = 0x51; break;
case EDX: data[i++] = 0x52; break;
case ESI: data[i++] = 0x56; break;
case EDI: data[i++] = 0x57; break;
case EBP: data[i++] = 0x55; break;
}
}
data[i++] = 0x83; // add esp, x
data[i++] = 0xC4;
data[i++] = rargssize + ((registerssaved + stacksize) * 4) - 4;
}
data[i++] = 0xE8; // call
data[i++] = 0x00;
data[i++] = 0x00;
data[i++] = 0x00;
data[i++] = 0x00;
int sizec = i;
data[i++] = 0x8B; // push eax, [esp] - puts eip in eax
data[i++] = 0x04;
data[i++] = 0xE4;
data[i++] = 0x83; // add eax, x
data[i++] = 0xC0;
int sizeoffset = i;
data[i++] = 0; // set to returnlocation offset later
data[i++] = 0x89; // mov [esp-20h], eax - put return address on stack
data[i++] = 0x44;
data[i++] = 0xE4;
data[i++] = (signed char)(-(registerssaved * 4) - rargssize - (stacksize * 4)) + 4;
data[i++] = 0x83; // add esp, x
data[i++] = 0xC4;
data[i++] = 4;
data[i++] = 0x58; // pop eax
if (!(registersreturned & EAX)) {
data[i++] = 0x50; // push eax
}
if (!(registersreturned & EBX)) {
data[i++] = 0x53; // push ebx
}
if (!(registersreturned & ECX)) {
data[i++] = 0x51; // push ecx
}
if (!(registersreturned & EDX)) {
data[i++] = 0x52; // push edx
}
if (!(registersreturned & EBP)) {
data[i++] = 0x55; // push ebp
}
if (!(registersreturned & ESI)) {
data[i++] = 0x56; // push esi
}
if (!(registersreturned & EDI)) {
data[i++] = 0x57; // push edi
}
data[i++] = 0x83; // sub esp, x
data[i++] = 0xEC;
data[i++] = 4 + (stacksize * 4) + rargssize;
data[i++] = 0xEA; // jmp
*((int *)&data[i]) = newaddress; i += 4;
data[i++] = 0x23;
data[i++] = 0x00;
data[sizeoffset] = i - sizec;
// returnlocation:
data[i++] = 0x83; // sub esp, x
data[i++] = 0xEC;
data[i++] = (signed char)(stacksize * -4) - rargssize;
if (!(registersreturned & EDI)) {
data[i++] = 0x5F; // pop edi
}
if (!(registersreturned & ESI)) {
data[i++] = 0x5E; // pop esi
}
if (!(registersreturned & EBP)) {
data[i++] = 0x5D; // pop ebp
}
if (!(registersreturned & EDX)) {
data[i++] = 0x5A; // pop edx
}
if (!(registersreturned & ECX)) {
data[i++] = 0x59; // pop ecx
}
if (!(registersreturned & EBX)) {
data[i++] = 0x5B; // pop ebx
}
if (!(registersreturned & EAX)) {
data[i++] = 0x58; // pop eax
}
data[i++] = 0xC3; // retn
WriteProcessMemory(GetCurrentProcess(), (LPVOID)address, data, i, 0);
}
void addhook(int address, int newaddress, int stacksize, int registerargs[], int registersreturned)
{
if (!g_hooktableaddress) {
g_hooktableaddress = VirtualAllocEx(GetCurrentProcess(), NULL, g_maxhooks * 100, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
}
if (g_hooktableoffset > g_maxhooks) {
return;
}
unsigned int hookaddress = (unsigned int)g_hooktableaddress + (g_hooktableoffset * 100);
char data[9];
int i = 0;
data[i++] = 0xEA; // jmp
*((int *)&data[i]) = hookaddress; i += 4;
data[i++] = 0x23;
data[i++] = 0x00;
data[i++] = 0xC3; // retn
WriteProcessMemory(GetCurrentProcess(), (LPVOID)address, data, i, 0);
hookfunc(hookaddress, newaddress, stacksize, registerargs, registersreturned);
g_hooktableoffset++;
}

37
src/hook.h Normal file
View File

@@ -0,0 +1,37 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#ifndef _HOOK_H_
#define _HOOK_H_
enum REGISTER_ARGS {
EAX = 1 << 0,
EBX = 1 << 1,
ECX = 1 << 2,
EDX = 1 << 3,
ESI = 1 << 4,
EDI = 1 << 5,
EBP = 1 << 6,
END = 0
};
void addhook(int address, int newaddress, int stacksize, int registerargs[], int registersreturned);
#endif

View File

@@ -21,18 +21,19 @@
#include <SDL_keycode.h>
#include <windows.h>
#include "addresses.h"
#include "audio.h"
#include "audio/audio.h"
#include "config.h"
#include "game.h"
#include "input.h"
#include "map.h"
#include "osinterface.h"
#include "sprite.h"
#include "interface/viewport.h"
#include "interface/widget.h"
#include "interface/window.h"
#include "platform/osinterface.h"
#include "tutorial.h"
#include "viewport.h"
#include "widget.h"
#include "window.h"
#include "window_tooltip.h"
#include "windows/tooltip.h"
#include "windows/dropdown.h"
#include "world/map.h"
#include "world/sprite.h"
POINT _dragPosition;
@@ -48,7 +49,7 @@ static void input_mouseover(int x, int y, rct_window *w, int widgetIndex);
static void input_mouseover_widget_check(rct_windowclass windowClass, rct_windownumber windowNumber, int widgetIndex);
static void input_mouseover_widget_flatbutton_invalidate();
void process_mouse_over(int x, int y);
void sub_6ED801(int x, int y);
void process_mouse_tool(int x, int y);
void invalidate_scroll();
static rct_mouse_data* get_mouse_input();
@@ -530,10 +531,12 @@ static void input_leftmousedown(int x, int y, rct_window *w, int widgetIndex)
}
break;
default:
if (!widget_is_enabled(w, widgetIndex))
break;
if (!widget_is_enabled(w, widgetIndex))
break;
if (widget_is_disabled(w, widgetIndex))
break;
sound_play_panned(SOUND_CLICK_1, w->x + (widget->left + widget->right) / 2);
sound_play_panned(SOUND_CLICK_1, w->x + (widget->left + widget->right) / 2, 0, 0, 0);
// Set new cursor down widget
RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WINDOWCLASS, rct_windowclass) = windowClass;
@@ -549,6 +552,145 @@ static void input_leftmousedown(int x, int y, rct_window *w, int widgetIndex)
}
}
/* rct2: 0x6E8DA7 */
void input_state_widget_pressed( int x, int y, int state, int widgetIndex, rct_window* w, rct_widget* widget ){
//RCT2_CALLPROC_X(0x006E8DA7, x, y, state, widgetIndex, (int)w, (int)widget, 0);
//return;
RCT2_GLOBAL(0x1420054, uint16) = x;
RCT2_GLOBAL(0x1420056, uint16) = y;
rct_windowclass cursor_w_class;
rct_windownumber cursor_w_number;
cursor_w_class = RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WINDOWCLASS, rct_windowclass);
cursor_w_number = RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WINDOWNUMBER, rct_windownumber);
int cursor_widgetIndex = RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WIDGETINDEX, uint32);
rct_window* cursor_w = window_find_by_id(cursor_w_class, cursor_w_number);
if (!cursor_w){
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = 0;
return;
}
switch (state){
case 0:
if (!w || cursor_w_class != w->classification || cursor_w_number != w->number || widgetIndex != cursor_widgetIndex)
break;
if (w->disabled_widgets & (1ULL << widgetIndex))
break;
if (RCT2_GLOBAL(0x9DE528, uint16) != 0) RCT2_GLOBAL(0x9DE528, uint16)++;
if (w->var_020 & (1ULL << widgetIndex) &&
RCT2_GLOBAL(0x9DE528, uint16) >= 0x10 &&
(!(RCT2_GLOBAL(0x9DE528, uint16) & 0x3))){
RCT2_CALLPROC_WE_MOUSE_DOWN(w->event_handlers[WE_MOUSE_DOWN], widgetIndex, w, widget);
}
if (RCT2_GLOBAL(0x9DE518, uint32) & 1) return;
RCT2_GLOBAL(0x9DE518, uint32) |= 1;
widget_invalidate(cursor_w_class, cursor_w_number, widgetIndex);
return;
case 3:
case 2:
if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) == 5){
if (w) {
int dropdown_index = 0;
if (w->classification == WC_DROPDOWN){
dropdown_index = dropdown_index_from_point(x, y, w);
if (dropdown_index == -1)goto dropdown_cleanup;
// _dropdown_unknown?? highlighted?
if (dropdown_index < 32 && RCT2_GLOBAL(0x009DED34, sint32) & (1 << dropdown_index))goto dropdown_cleanup;
// gDropdownItemsFormat[dropdown_index] will not work until all windows that use dropdown decompiled
if (RCT2_ADDRESS(0x9DEBA4, uint16)[dropdown_index] == 0)goto dropdown_cleanup;
}
else{
if (cursor_w_class != w->classification || cursor_w_number != w->number || widgetIndex != cursor_widgetIndex)
goto dropdown_cleanup;
dropdown_index = -1;
if (RCT2_GLOBAL(0x9DE518, uint32) & 2){
if (!(RCT2_GLOBAL(0x9DE518, uint32) & 4)){
RCT2_GLOBAL(0x9DE518, uint32) |= (1 << 2);
return;
}
}
}
window_close_by_id(WC_DROPDOWN, 0);
cursor_w = window_find_by_id(cursor_w_class, cursor_w_number);
if (RCT2_GLOBAL(0x9DE518, uint32) & 1){
RCT2_GLOBAL(0x9DE518, uint32) &= 0xFFFE;
widget_invalidate(cursor_w_class, cursor_w_number, cursor_widgetIndex);
}
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = 1;
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_TIMEOUT, uint16) = 0;
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WIDGET_INDEX, uint16) = cursor_widgetIndex;
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WINDOW_CLASS, rct_windowclass) = cursor_w_class;
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WINDOW_NUMBER, rct_windownumber) = cursor_w_number;
RCT2_CALLPROC_X(cursor_w->event_handlers[WE_DROPDOWN], dropdown_index, 0, 0, cursor_widgetIndex, (int)cursor_w, 0, 0);
}
dropdown_cleanup:
window_close_by_id(WC_DROPDOWN, 0);
}
if (state == 3) return;
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = 1;
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_TIMEOUT, uint16) = 0;
RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_WIDGET_INDEX, uint16) = cursor_widgetIndex;
if (!w)
break;
int mid_point_x = (widget->left + widget->right) / 2 + w->x;
sound_play_panned(5, mid_point_x, 0, 0, 0);
if (cursor_w_class != w->classification || cursor_w_number != w->number || widgetIndex != cursor_widgetIndex)
break;
if (w->disabled_widgets & (1ULL << widgetIndex))
break;
widget_invalidate(cursor_w_class, cursor_w_number, widgetIndex);
window_event_helper(w, widgetIndex, WE_MOUSE_UP);
default:
return;
}
RCT2_GLOBAL(0x9DE528, uint16) = 0;
if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) != 5){
// Hold down widget and drag outside of area??
if (RCT2_GLOBAL(0x9DE518, uint32) & 1){
RCT2_GLOBAL(0x9DE518, uint32) &= 0xFFFE;
widget_invalidate(cursor_w_class, cursor_w_number, cursor_widgetIndex);
}
return;
}
if (!w) return;
if (w->classification == WC_DROPDOWN){
int dropdown_index = dropdown_index_from_point(x, y, w);
if (dropdown_index == -1) return;
// _dropdown_unknown?? highlighted?
if (dropdown_index < 32 && RCT2_GLOBAL(0x009DED34, sint32) & (1 << dropdown_index))return;
// gDropdownItemsFormat[dropdown_index] will not work until all windows that use dropdown decompiled
if (RCT2_ADDRESS(0x9DEBA4, uint16)[dropdown_index] == 0)return;
// _dropdown_highlighted_index
RCT2_GLOBAL(0x009DEBA2, sint16) = dropdown_index;
window_invalidate_by_id(WC_DROPDOWN, 0);
}
}
/**
*
* rct2: 0x006E8655
@@ -612,7 +754,8 @@ static void game_handle_input_mouse(int x, int y, int state)
break;
case INPUT_STATE_WIDGET_PRESSED:
RCT2_CALLPROC_X(0x006E8DA7, x, y, state, widgetIndex, (int)w, (int)widget, 0);
input_state_widget_pressed(x, y, state, widgetIndex, w, widget);
//RCT2_CALLPROC_X(0x006E8DA7, x, y, state, widgetIndex, (int)w, (int)widget, 0);
break;
case INPUT_STATE_DRAGGING:
// RCT2_CALLPROC_X(0x006E8C5C, x, y, state, widgetIndex, w, widget, 0);
@@ -728,7 +871,8 @@ static void game_handle_input_mouse(int x, int y, int state)
break;
}
case INPUT_STATE_DROPDOWN_ACTIVE:
RCT2_CALLPROC_X(0x006E8DA7, x, y, state, widgetIndex, (int)w, (int)widget, 0);
input_state_widget_pressed(x, y, state, widgetIndex, w, widget);
//RCT2_CALLPROC_X(0x006E8DA7, x, y, state, widgetIndex, (int)w, (int)widget, 0);
break;
case INPUT_STATE_VIEWPORT_LEFT:
//RCT2_CALLPROC_X(0x006E87B4, x, y, state, widgetIndex, (int)w, (int)widget, 0);
@@ -756,7 +900,7 @@ static void game_handle_input_mouse(int x, int y, int state)
}
else if (state == 2){
RCT2_GLOBAL(0x9DE51D, uint8) = 0;
RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = 0;
if (RCT2_GLOBAL(0x9DE52E, rct_windownumber) != w->number)break;
if ((RCT2_GLOBAL(0x9DE518, uint32)&(1 << 3))){
w = window_find_by_id(RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, rct_windowclass), RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER, rct_windownumber));
@@ -775,7 +919,7 @@ static void game_handle_input_mouse(int x, int y, int state)
RCT2_CALLPROC_X(0x6ACAC2, eax, ebx, ecx, (int)spr, esi, edi, ebp);
}
else if (spr->unknown.sprite_identifier == SPRITE_IDENTIFIER_PEEP){
window_peep_open(&spr->peep);
window_guest_open(&spr->peep);
}
else if (spr->unknown.sprite_identifier == SPRITE_IDENTIFIER_FLOATING_TEXT){
//Unknown for now
@@ -789,7 +933,7 @@ static void game_handle_input_mouse(int x, int y, int state)
eax = RCT2_ADDRESS(0x0099BA64, uint8)[16 * map_element->properties.track.type];
if (!(eax & 0x10)){//If not station track
//Open ride window in overview mode.
RCT2_CALLPROC_X(0x6ACC28, map_element->properties.track.ride_index, ebx, ecx, (int)map_element, esi, edi, ebp);
window_ride_main_open(map_element->properties.track.ride_index);
break;
}
}
@@ -918,7 +1062,7 @@ static void input_mouseover(int x, int y, rct_window *w, int widgetIndex)
{
rct_windowclass windowClass = 255;
rct_windownumber windowNumber = 0;
rct_widget *widget;
rct_widget *widget = NULL;
if (w != NULL) {
windowClass = w->classification;
@@ -1546,7 +1690,7 @@ void game_handle_input()
// RCT2_CALLPROC_X(0x006E8655, eax, ebx, 0, 0, 0, 0, 0); // window_process_mouse_input
process_mouse_over(eax, ebx);
//RCT2_CALLPROC_X(0x006ED833, eax, ebx, 0, 0, 0, 0, 0);
sub_6ED801(eax, ebx);
process_mouse_tool(eax, ebx);
//RCT2_CALLPROC_EBPSAFE(0x006ED801);
}
}
@@ -1601,15 +1745,17 @@ static void game_get_next_input(int *x, int *y, int *state)
*
* rct2: 0x006ED801
*/
void sub_6ED801(int x, int y){
if (RCT2_GLOBAL(0x9DE518, uint32) & (1 << 3)){
void process_mouse_tool(int x, int y)
{
if (RCT2_GLOBAL(0x9DE518, uint32) & (1 << 3))
{
rct_window* w = window_find_by_id(RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, uint8), RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER, uint16));
if (w == NULL){
if (!w)
tool_cancel();
}
else{
else
RCT2_CALLPROC_X(w->event_handlers[WE_TOOL_UPDATE], x, y, 0, RCT2_GLOBAL(0x9DE546, uint16), (int)w, 0, 0);
}
}
}

View File

@@ -23,6 +23,7 @@
void game_handle_input();
void game_handle_keyboard_input();
void handle_shortcut_command(int shortcutIndex);
void store_mouse_input(int state);

View File

@@ -18,10 +18,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "addresses.h"
#include "date.h"
#include "../addresses.h"
#include "../localisation/date.h"
#include "../localisation/localisation.h"
#include "graph.h"
#include "string_ids.h"
static void graph_draw_months_uint8(rct_drawpixelinfo *dpi, uint8 *history, int count, int baseX, int baseY)
{

View File

@@ -21,8 +21,8 @@
#ifndef _GRAPH_H_
#define _GRAPH_H_
#include "gfx.h"
#include "rct2.h"
#include "../common.h"
#include "../drawing/drawing.h"
void graph_draw_uint8(rct_drawpixelinfo *dpi, uint8 *history, int count, int baseX, int baseY);
void graph_draw_money32(rct_drawpixelinfo *dpi, money32 *history, int count, int baseX, int baseY, int modifier, int offset);

View File

@@ -19,19 +19,18 @@
*****************************************************************************/
#pragma pack(1)
#include <lodepng.h>
#include <stdio.h>
#include "osinterface.h"
#include "addresses.h"
#include "config.h"
#include "gfx.h"
#include "game.h"
#include "rct2.h"
#include "screenshot.h"
#include "string_ids.h"
#include "window_error.h"
#include <windows.h> // For MAX_PATH
#include <lodepng/lodepng.h>
#include <stdio.h>
#include "../platform/osinterface.h"
#include "../addresses.h"
#include "../config.h"
#include "../drawing/drawing.h"
#include "../game.h"
#include "../localisation/localisation.h"
#include "../windows/error.h"
#include "screenshot.h"
static int screenshot_dump_bmp();

View File

@@ -19,13 +19,13 @@
*****************************************************************************/
#include <assert.h>
#include "addresses.h"
#include "config.h"
#include "gfx.h"
#include "map.h"
#include "string_ids.h"
#include "sprite.h"
#include "sprites.h"
#include "../addresses.h"
#include "../config.h"
#include "../drawing/drawing.h"
#include "../localisation/localisation.h"
#include "../sprites.h"
#include "../world/map.h"
#include "../world/sprite.h"
#include "viewport.h"
#include "window.h"
@@ -281,7 +281,7 @@ void viewport_update_position(rct_window *window)
if (window->viewport_target_sprite != -1){
rct_sprite* sprite = &g_sprite_list[window->viewport_target_sprite];
int height = map_element_height(0xFFFF & sprite->unknown.x, 0xFFFF & sprite->unknown.y) - 16;
int height = map_element_height(0xFFFF & sprite->unknown.x, 0xFFFF & sprite->unknown.y) & 0xFFFF - 16;
int underground = sprite->unknown.z < height;
RCT2_CALLPROC_X(0x6E7A15, sprite->unknown.x, sprite->unknown.y, sprite->unknown.z, underground, (int)window, (int)viewport, 0);
@@ -1088,3 +1088,27 @@ void viewport_set_visibility(uint8 mode)
window_invalidate(window);
}
}
/**
*
* rct2: 0x00685ADC
* screenX: eax
* screenY: ebx
* flags: edx
* x: ax
* y: cx
* z: bl
* mapElement: edx
*/
void get_map_coordinates_from_pos(int screenX, int screenY, int flags, int *x, int *y, int *z, rct_map_element **mapElement)
{
int eax, ebx, ecx, edx, esi, edi, ebp;
eax = screenX;
ebx = screenY;
edx = flags;
RCT2_CALLFUNC_X(0x00685ADC, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp);
if (x != NULL) *x = *((uint16*)&eax);
if (y != NULL) *y = *((uint16*)&ecx);
if (z != NULL) *z = *((uint8*)&ebx);
if (mapElement != NULL) *mapElement = (rct_map_element*)edx;
}

View File

@@ -65,4 +65,6 @@ void show_construction_rights();
void hide_construction_rights();
void viewport_set_visibility(uint8 mode);
void get_map_coordinates_from_pos(int screenX, int screenY, int flags, int *x, int *y, int *z, rct_map_element **mapElement);
#endif

View File

@@ -21,9 +21,9 @@
#include <windows.h>
#include <memory.h>
#include <stdlib.h>
#include "addresses.h"
#include "gfx.h"
#include "sprites.h"
#include "../addresses.h"
#include "../drawing/drawing.h"
#include "../sprites.h"
#include "widget.h"
#include "window.h"
@@ -442,6 +442,7 @@ static void widget_text_unknown(rct_drawpixelinfo *dpi, rct_window *w, int widge
widget->right - widget->left - 2
);
} else {
colour &= ~(1 << 7);
if (widget_is_disabled(w, widgetIndex))
colour |= 0x40;
gfx_draw_string_centred_clipped(
@@ -808,6 +809,9 @@ static void widget_scroll_draw(rct_drawpixelinfo *dpi, rct_window *w, int widget
if (scroll->flags & VSCROLLBAR_VISIBLE)
r -= 11;
b++;
r++;
// Create a new inner scroll dpi
scroll_dpi = *dpi;

View File

@@ -19,17 +19,16 @@
*****************************************************************************/
#include <string.h>
#include "addresses.h"
#include "audio.h"
#include "game.h"
#include "gfx.h"
#include "map.h"
#include "osinterface.h"
#include "rct2.h"
#include "../addresses.h"
#include "../audio/audio.h"
#include "../game.h"
#include "../drawing/drawing.h"
#include "../platform/osinterface.h"
#include "../world/map.h"
#include "../world/sprite.h"
#include "widget.h"
#include "window.h"
#include "viewport.h"
#include "sprite.h"
#define RCT2_FIRST_WINDOW (RCT2_ADDRESS(RCT2_ADDRESS_WINDOW_LIST, rct_window))
#define RCT2_LAST_WINDOW (RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*) - 1)
@@ -39,6 +38,28 @@
rct_window* g_window_list = RCT2_ADDRESS(RCT2_ADDRESS_WINDOW_LIST, rct_window);
// converted from uint16 values at 0x009A41EC - 0x009A4230
// these are percentage coordinates of the viewport to center to, if a window is obscuring a location, the next is tried
float window_scroll_locations[][2] = {
0.5f, 0.5f,
0.75f, 0.5f,
0.25f, 0.5f,
0.5f, 0.75f,
0.5f, 0.25f,
0.75f, 0.75f,
0.75f, 0.25f,
0.25f, 0.75f,
0.25f, 0.25f,
0.125f, 0.5f,
0.875f, 0.5f,
0.5f, 0.125f,
0.5f, 0.875f,
0.875f, 0.125f,
0.875f, 0.875f,
0.125f, 0.875f,
0.125f, 0.125f,
};
static void window_all_wheel_input();
static int window_draw_split(rct_window *w, int left, int top, int right, int bottom);
@@ -234,7 +255,7 @@ static int window_wheel_input(rct_window *w, int wheel)
continue;
// Originally always checked first scroll view, bug maybe?
scroll = &w->scrolls[i * sizeof(rct_scroll)];
scroll = &w->scrolls[i];
if (scroll->flags & (HSCROLLBAR_VISIBLE | VSCROLLBAR_VISIBLE)) {
window_scroll_wheel_input(w, i, wheel);
return 1;
@@ -308,7 +329,7 @@ static void window_all_wheel_input()
if (widgetIndex != -1) {
widget = &w->widgets[widgetIndex];
if (widget->type == WWT_SCROLL) {
scroll = &w->scrolls[RCT2_GLOBAL(0x01420075, uint8) * sizeof(rct_scroll)];
scroll = &w->scrolls[RCT2_GLOBAL(0x01420075, uint8)];
if (scroll->flags & (HSCROLLBAR_VISIBLE | VSCROLLBAR_VISIBLE)) {
window_scroll_wheel_input(w, window_get_scroll_index(w, widgetIndex), wheel);
return;
@@ -383,7 +404,7 @@ rct_window *window_create(int x, int y, int width, int height, uint32 *event_han
// Play sounds and flash the window
if (!(flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT))){
w->flags |= WF_WHITE_BORDER_MASK;
sound_play_panned(SOUND_WINDOW_OPEN, x + (width / 2));
sound_play_panned(SOUND_WINDOW_OPEN, x + (width / 2), 0, 0, 0);
}
w->number = 0;
@@ -658,7 +679,8 @@ void window_invalidate(rct_window *window)
/**
*
* rct2: 0x006EC3AC
*
* See also widget_invalidate that will probably be used
* when cls is > 0x7F.
* @param cls (ax)
* @param number (bx)
*/
@@ -719,6 +741,7 @@ void window_init_scroll_widgets(rct_window *w)
}
scroll = &w->scrolls[scroll_index];
scroll->flags = 0;
window_get_scroll_size(w, scroll_index, &width, &height);
scroll->h_left = 0;
scroll->h_right = width + 1;
@@ -951,12 +974,87 @@ void window_scroll_to_viewport(rct_window *w)
}
/**
*
* rct2: 0x006E7C9C
*/
*
* rct2: 0x006E7C9C
* @param w (esi)
* @param x (eax)
* @param y (ecx)
* @param z (edx)
*/
void window_scroll_to_location(rct_window *w, int x, int y, int z)
{
RCT2_CALLPROC_X(0x006E7C9C, x, 0, y, z, (int)w, 0, 0);
if (w->viewport) {
sint16 height = map_element_height(x, y);
if (z < height - 16) {
if (!(w->viewport->flags & 1 << 0)) {
w->viewport->flags |= 1 << 0;
window_invalidate(w);
}
} else {
if (w->viewport->flags & 1 << 0) {
w->viewport->flags &= ~(1 << 0);
window_invalidate(w);
}
}
sint16 sx;
sint16 sy;
switch (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8)) {
case 0:
sx = y - x;
sy = ((x + y) / 2) - z;
break;
case 1:
sx = -y - x;
sy = ((-x + y) / 2) - z;
break;
case 2:
sx = -y + x;
sy = ((-x - y) / 2) - z;
break;
case 3:
sx = y + x;
sy = ((x - y) / 2) - z;
break;
}
int i = 0;
if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TITLE_DEMO)) {
int found = 0;
while (!found) {
sint16 x2 = w->viewport->x + (sint16)(w->viewport->width * window_scroll_locations[i][0]);
sint16 y2 = w->viewport->y + (sint16)(w->viewport->height * window_scroll_locations[i][1]);
rct_window* w2 = w;
while (1) {
w2++;
if (w2 >= RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*)) {
found = 1;
break;
}
sint16 x1 = w2->x - 10;
sint16 y1 = w2->y - 10;
if (x2 >= x1 && x2 <= w2->width + x1 + 20) {
if (y2 >= y1 && y2 <= w2->height + y1 + 20) {
// window is covering this area, try the next one
i++;
found = 0;
break;
}
}
}
if (i >= countof(window_scroll_locations)) {
i = 0;
found = 1;
}
}
}
// rct2: 0x006E7C76
if (w->viewport_target_sprite == -1) {
if (!(w->flags & WF_2)) {
w->saved_view_x = sx - (sint16)(w->viewport->view_width * window_scroll_locations[i][0]);
w->saved_view_y = sy - (sint16)(w->viewport->view_height * window_scroll_locations[i][1]);
w->flags |= WF_SCROLLING_TO_LOCATION;
}
}
}
}
/**
@@ -1232,8 +1330,8 @@ void window_draw_widgets(rct_window *w, rct_drawpixelinfo *dpi)
widgetIndex = 0;
for (widget = w->widgets; widget->type != WWT_LAST; widget++) {
// Check if widget is outside the draw region
if (w->x + widget->left < dpi->x + dpi->width && w->x + widget->right > dpi->x)
if (w->y + widget->top < dpi->y + dpi->height && w->y + widget->bottom > dpi->y)
if (w->x + widget->left < dpi->x + dpi->width && w->x + widget->right >= dpi->x)
if (w->y + widget->top < dpi->y + dpi->height && w->y + widget->bottom >= dpi->y)
widget_draw(dpi, w, widgetIndex);
widgetIndex++;
@@ -1575,13 +1673,13 @@ void RCT2_CALLPROC_WE_MOUSE_DOWN(int address, int widgetIndex, rct_window*w, rc
}
/* Based on rct2: 0x6987ED and another version from window_park */
void window_align_tabs( rct_window *w, uint8 start_tab_id, uint8 end_tab_id )
void window_align_tabs(rct_window *w, uint8 start_tab_id, uint8 end_tab_id)
{
int x = w->widgets[start_tab_id].left;
int i, x = w->widgets[start_tab_id].left;
int tab_width = w->widgets[start_tab_id].right - w->widgets[start_tab_id].left;
for (int i = start_tab_id; i < end_tab_id; ++i){
if ( !(w->disabled_widgets & (1LL << i)) ){
for (i = start_tab_id; i <= end_tab_id; i++) {
if (!(w->disabled_widgets & (1LL << i))) {
w->widgets[i].left = x;
w->widgets[i].right = x + tab_width;
x += tab_width + 1;
@@ -1589,3 +1687,14 @@ void window_align_tabs( rct_window *w, uint8 start_tab_id, uint8 end_tab_id )
}
}
/**
*
* rct2: 0x006CBCC3
*/
void window_close_construction_windows()
{
window_close_by_id(WC_RIDE_CONSTRUCTION, 0);
window_close_by_id(WC_FOOTPATH, 0);
window_close_by_id(WC_TRACK_DESIGN_LIST, 0);
window_close_by_id(WC_TRACK_DESIGN_PLACE, 0);
}

View File

@@ -21,10 +21,11 @@
#ifndef _WINDOW_H_
#define _WINDOW_H_
#include "gfx.h"
#include "park.h"
#include "peep.h"
#include "rct2.h"
#include "../common.h"
#include "../drawing/drawing.h"
#include "../peep/peep.h"
#include "../ride/ride.h"
#include "../world/park.h"
struct rct_window;
union rct_window_event;
@@ -104,7 +105,7 @@ typedef struct{
sint16 y; //0x484 & VIEWPORT_FOCUS_Y_MASK
sint16 z; //0x486
uint8 rotation;//0x488
uint8 pad_489;
uint8 zoom;//0x489
} coordinate_focus;
// Type is viewport_target_sprite_id & 0x80000000 != 0
@@ -115,7 +116,7 @@ typedef struct{
uint8 type; //0x485 & VIEWPORT_FOCUS_TYPE_MASK
uint16 pad_486;
uint8 rotation; //0x488
uint8 pad_489;
uint8 zoom; //0x489
} sprite_focus;
#define VIEWPORT_FOCUS_TYPE_MASK 0xC0
@@ -123,7 +124,7 @@ enum{
VIEWPORT_FOCUS_TYPE_COORDINATE = (1<<6),
VIEWPORT_FOCUS_TYPE_SPRITE = (1<<7)
};
#define VIEWPORT_FOCUS_Y_MASK 0x3FFF;
#define VIEWPORT_FOCUS_Y_MASK 0x3FFF
typedef struct{
@@ -158,8 +159,9 @@ typedef struct{
} map_variables;
typedef struct {
sint16 var_480;
sint16 view;
sint32 var_482;
sint32 var_486;
} ride_variables;
typedef struct {
@@ -167,6 +169,16 @@ typedef struct {
sint16 hover_counter;
} scenery_variables;
typedef struct {
uint16 var_480;
uint16 var_482;
uint16 var_484;
} track_list_variables;
typedef struct {
uint16 var_480;
} error_variables;
/**
* Window structure
* size: 0x4C0
@@ -205,6 +217,8 @@ typedef struct rct_window {
map_variables map;
ride_variables ride;
scenery_variables scenery;
track_list_variables track_list;
error_variables error;
};
sint16 page; // 0x48A
sint16 var_48C;
@@ -234,7 +248,12 @@ typedef enum {
WE_RESIZE = 2,
WE_MOUSE_DOWN = 3,
WE_DROPDOWN = 4,
WE_UNKNOWN_05 = 5,
WE_UNKNOWN_05 = 5,
// Unknown 05: Used to update tabs that are not being animated
// see window_peep. When the overview tab is not highlighted the
// items being carried such as hats/balloons still need to be shown
// and removed. Probably called after anything that affects items
// being carried.
WE_UPDATE = 6,
WE_UNKNOWN_07 = 7,
WE_UNKNOWN_08 = 8,
@@ -330,14 +349,15 @@ enum {
WC_TOOLTIP = 5,
WC_DROPDOWN = 6,
WC_ABOUT = 8,
WC_MUSIC_CREDITS = 9,
WC_PUBLISHER_CREDITS = 10,
WC_PUBLISHER_CREDITS = 9,
WC_MUSIC_CREDITS = 10,
WC_ERROR = 11,
WC_RIDE = 12,
WC_RIDE_CONSTRUCTION = 13,
WC_SAVE_PROMPT = 14,
WC_RIDE_LIST = 15,
WC_CONSTRUCT_RIDE = 16,
WC_DEMOLISH_RIDE_PROMPT = 17,
WC_SCENERY = 18,
WC_OPTIONS = 19,
WC_FOOTPATH = 20,
@@ -354,6 +374,7 @@ enum {
WC_RECENT_NEWS = 31,
WC_SCENARIO_SELECT = 32,
WC_TRACK_DESIGN_LIST = 33,
WC_TRACK_DESIGN_PLACE = 34,
WC_NEW_CAMPAIGN = 35,
WC_KEYBOARD_SHORTCUT_LIST = 36,
WC_CHANGE_KEYBOARD_SHORTCUT = 37,
@@ -364,6 +385,8 @@ enum {
WC_EDITOR_INVENTION_LIST = 43,
WC_EDITOR_SCENARIO_OPTIONS = 45,
WC_EDTIOR_OBJECTIVE_OPTIONS = 46,
WC_47,
WC_48,
WC_CLEAR_SCENERY = 50,
WC_MANAGE_TRACK_DESIGN = 89,
WC_CHEATS = 110,
@@ -380,6 +403,9 @@ enum PROMPT_MODE {
// rct2: 0x01420078
extern rct_window* g_window_list;
// rct2: 0x00F635EE
extern ride_list_item _window_track_list_item;
void window_dispatch_update_all();
void window_update_all();
rct_window *window_create(int x, int y, int width, int height, uint32 *event_handlers, rct_windowclass cls, uint16 flags);
@@ -426,6 +452,8 @@ void window_set_resize(rct_window *w, int minWidth, int minHeight, int maxWidth,
int tool_set(rct_window *w, int widgetIndex, int tool);
void tool_cancel();
void window_close_construction_windows();
// Open window functions
void window_main_open();
void window_resize_gui(int width, int height);
@@ -440,15 +468,18 @@ void window_title_exit_open();
void window_title_logo_open();
void window_news_open();
void window_scenarioselect_open();
void window_track_list_open(ride_list_item item);
void window_clear_scenery_open();
void window_land_open();
void window_water_open();
void window_staff_open();
void window_staff_list_open();
void window_guest_list_open();
void window_map_open();
void window_options_open();
void window_peep_open(rct_peep* peep);
void window_staff_peep_open(rct_peep* peep);
void window_shortcut_keys_open();
void window_shortcut_change_open(int selected_key);
void window_guest_open(rct_peep* peep);
void window_staff_open(rct_peep* peep);
void window_park_awards_open();
void window_park_entrance_open();
void window_park_guests_open();
@@ -459,11 +490,15 @@ void window_finances_research_open();
void window_new_campaign_open(sint16 campaignType);
void window_ride_main_open(int rideIndex);
void window_ride_list_open();
void window_track_place_open();
void window_new_ride_open();
void window_banner_open();
void window_cheats_open();
void window_research_open();
void window_scenery_open();
void window_music_credits_open();
void window_publisher_credits_open();
void window_track_manage_open();
void window_guest_list_init_vars_a();
void window_guest_list_init_vars_b();
@@ -472,8 +507,9 @@ void window_bubble_list_item(rct_window* w, int item_position);
void window_align_tabs( rct_window *w, uint8 start_tab_id, uint8 end_tab_id );
void window_new_ride_init_vars();
void window_new_ride_focus(ride_list_item rideItem);
void window_staff_init_vars();
void window_staff_list_init_vars();
void window_event_helper(rct_window* w, short widgetIndex, WINDOW_EVENTS event);
void RCT2_CALLPROC_WE_MOUSE_DOWN(int address, int widgetIndex, rct_window*w, rct_widget* widget);
@@ -490,6 +526,12 @@ void RCT2_CALLPROC_WE_MOUSE_DOWN(int address, int widgetIndex, rct_window*w, rct
__asm mov dropdownIndex, ax \
__asm mov widgetIndex, dx \
__asm mov w, esi
#define window_text_input_get_registers(w, widgetIndex, _cl, text) \
__asm mov widgetIndex, dx \
__asm mov _cl, cl \
__asm mov w, esi \
__asm mov text, edi
#define window_scrollmouse_get_registers(w, x, y) \
__asm mov x, cx \
@@ -502,36 +544,54 @@ void RCT2_CALLPROC_WE_MOUSE_DOWN(int address, int widgetIndex, rct_window*w, rct
__asm mov widgetIndex, dx \
__asm mov w, esi
#define window_textinput_get_registers(w, widgetIndex, result, text) \
__asm mov result, cl \
__asm mov widgetIndex, dx \
__asm mov w, esi \
__asm mov text, edi
#define window_paint_get_registers(w, dpi) \
__asm mov w, esi \
__asm mov dpi, edi
#else
#define window_get_register(w) \
__asm__ ( "mov %[w], esi " : [w] "+m" (w) );
__asm__ ( "mov %["#w"], esi " : [w] "+m" (w) );
#define window_widget_get_registers(w, widgetIndex) \
__asm__ ( "mov %[widgetIndex], dx " : [widgetIndex] "+m" (widgetIndex) ); \
__asm__ ( "mov %[w], esi " : [w] "+m" (w) );
__asm__ ( "mov %["#widgetIndex"], dx " : [widgetIndex] "+m" (widgetIndex) ); \
__asm__ ( "mov %["#w"], esi " : [w] "+m" (w) );
#define window_dropdown_get_registers(w, widgetIndex, dropdownIndex) \
__asm__ ( "mov %[dropdownIndex], ax " : [dropdownIndex] "+m" (dropdownIndex) ); \
__asm__ ( "mov %["#dropdownIndex"], ax " : [dropdownIndex] "+m" (dropdownIndex) ); \
__asm__ ( "mov %["#widgetIndex"], dx " : [widgetIndex] "+m" (widgetIndex) ); \
__asm__ ( "mov %["#w"], esi " : [w] "+m" (w) );
#define window_text_input_get_registers(w, widgetIndex, _cl, text) \
__asm__ ( "mov %[_cl], cl " : [_cl] "+m" (_cl) ); \
__asm__ ( "mov %[widgetIndex], dx " : [widgetIndex] "+m" (widgetIndex) ); \
__asm__ ( "mov %[w], esi " : [w] "+m" (w) );
__asm__ ( "mov %[w], esi " : [w] "+m" (w) ); \
__asm__ ( "mov %[text], edi " : [text] "+m" (text) );
#define window_scrollmouse_get_registers(w, x, y) \
__asm__ ( "mov %[x], cx " : [x] "+m" (x) ); \
__asm__ ( "mov %[y], dx " : [y] "+m" (y) ); \
__asm__ ( "mov %[w], esi " : [w] "+m" (w) );
__asm__ ( "mov %["#x"], cx " : [x] "+m" (x) ); \
__asm__ ( "mov %["#y"], dx " : [y] "+m" (y) ); \
__asm__ ( "mov %["#w"], esi " : [w] "+m" (w) );
#define window_tool_get_registers(w, widgetIndex, x, y) \
__asm__ ( "mov %[x], ax " : [x] "+m" (x) ); \
__asm__ ( "mov %[y], bx " : [y] "+m" (y) ); \
__asm__ ( "mov %["#x"], ax " : [x] "+m" (x) ); \
__asm__ ( "mov %["#y"], bx " : [y] "+m" (y) ); \
__asm__ ( "mov %["#widgetIndex"], dx " : [widgetIndex] "+m" (widgetIndex) ); \
__asm__ ( "mov %["#w"], esi " : [w] "+m" (w) );
#define window_textinput_get_registers(w, widgetIndex, result, text) \
__asm__ ( "mov %[result], cl " : [result] "+m" (result) ); \
__asm__ ( "mov %[widgetIndex], dx " : [widgetIndex] "+m" (widgetIndex) ); \
__asm__ ( "mov %[w], esi " : [w] "+m" (w) );
__asm__ ( "mov %[w], esi " : [w] "+m" (w) ); \
__asm__ ( "mov %[text], edi " : [text] "+m" (text) );
#define window_paint_get_registers(w, dpi) \
__asm__ ( "mov %[w], esi " : [w] "+m" (w) ); \
__asm__ ( "mov %[dpi], edi " : [dpi] "+m" (dpi) );
__asm__ ( "mov %["#w"], esi " : [w] "+m" (w) ); \
__asm__ ( "mov %["#dpi"], edi " : [dpi] "+m" (dpi) );
#endif
#endif

View File

@@ -19,13 +19,12 @@
*****************************************************************************/
#include "addresses.h"
#include "audio.h"
#include "gfx.h"
#include "audio/audio.h"
#include "drawing/drawing.h"
#include "intro.h"
#include "rct2.h"
#include "osinterface.h"
#include "localisation/localisation.h"
#include "platform/osinterface.h"
#include "sprites.h"
#include "string_ids.h"
static void screen_intro_process_mouse_input();
static void screen_intro_process_keyboard_input();

View File

@@ -18,9 +18,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "addresses.h"
#include "../addresses.h"
#include "date.h"
#include "rct2.h"
// rct2: 0x00993988
const sint16 days_in_month[MONTH_COUNT] = { 31, 30, 31, 30, 31, 31, 30, 31 };

View File

@@ -21,7 +21,7 @@
#ifndef _DATE_H_
#define _DATE_H_
#include "rct2.h"
#include "../common.h"
enum {
MONTH_MARCH,

View File

@@ -0,0 +1,127 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#ifndef _FORMAT_CODES_H_
#define _FORMAT_CODES_H_
char format_get_code(const char *token);
const char *format_get_token(char code);
enum {
// Font format codes
// The next byte specifies the X coordinate
FORMAT_MOVE_X = 1,
// The next byte specifies the palette
FORMAT_ADJUST_PALETTE,
// Moves to the next line
FORMAT_NEWLINE = 5,
// Moves less than NEWLINE
FORMAT_NEWLINE_SMALLER,
FORMAT_TINYFONT,
FORMAT_BIGFONT,
FORMAT_MEDIUMFONT,
FORMAT_SMALLFONT,
FORMAT_OUTLINE,
FORMAT_OUTLINE_OFF,
// Changes the colour of the text to a predefined window colour.
FORMAT_WINDOW_COLOUR_1,
FORMAT_WINDOW_COLOUR_2,
FORMAT_WINDOW_COLOUR_3,
// The next 2 bytes specify the X and Y coordinates
FORMAT_NEWLINE_X_Y = 17,
// The next 4 bytes specify the sprite
FORMAT_INLINE_SPRITE = 23,
// Non ascii-characters
FORMAT_ENDQUOTES = 34,
// Argument format codes
FORMAT_ARGUMENT_CODE_START = 123,
FORMAT_COMMA32 = 123,
FORMAT_INT32,
FORMAT_COMMA2DP32,
FORMAT_COMMA16,
FORMAT_UINT16,
FORMAT_CURRENCY2DP,
FORMAT_CURRENCY,
FORMAT_STRINGID,
FORMAT_STRINGID2,
FORMAT_STRING,
FORMAT_MONTHYEAR,
FORMAT_MONTH,
FORMAT_VELOCITY,
FORMAT_POP16,
FORMAT_PUSH16,
FORMAT_DURATION,
FORMAT_REALTIME,
FORMAT_LENGTH,
FORMAT_SPRITE,
// Colour format codes
FORMAT_COLOUR_CODE_START = 142,
FORMAT_BLACK = 142,
FORMAT_GREY,
FORMAT_WHITE,
FORMAT_RED,
FORMAT_GREEN,
FORMAT_YELLOW,
FORMAT_TOPAZ,
FORMAT_CELADON,
FORMAT_BABYBLUE,
FORMAT_PALELAVENDER,
FORMAT_PALEGOLD,
FORMAT_LIGHTPINK,
FORMAT_PEARLAQUA,
FORMAT_PALESILVER,
FORMAT_COLOUR_CODE_END = FORMAT_PALESILVER,
// Extra non-ascii characters
FORMAT_AMINUSCULE = 159,
FORMAT_UP,
FORMAT_POUND = 163,
FORMAT_YEN = 165,
FORMAT_COPYRIGHT = 169,
FORMAT_DOWN,
FORMAT_LEFTGUILLEMET,
FORMAT_TICK,
FORMAT_CROSS,
FORMAT_RIGHT = 175,
FORMAT_DEGREE,
FORMAT_SQUARED = 178,
FORMAT_OPENQUOTES = 180,
FORMAT_EURO = 181,
FORMAT_APPROX = 184,
FORMAT_POWERNEGATIVEONE,
FORMAT_BULLET,
FORMAT_RIGHTGUILLEMET,
FORMAT_SMALLUP,
FORMAT_SMALLDOWN,
FORMAT_LEFT,
FORMAT_INVERTEDQUESTION
};
#endif

View File

@@ -20,9 +20,8 @@
#include <stdio.h>
#include <string.h>
#include "addresses.h"
#include "language.h"
#include "string_ids.h"
#include "../addresses.h"
#include "localisation.h"
const char *language_names[LANGUAGE_COUNT] = {
"", // LANGUAGE_UNDEFINED
@@ -32,7 +31,8 @@ const char *language_names[LANGUAGE_COUNT] = {
"Fran\u00E7ais", // LANGUAGE_FRENCH
"Magyar", // LANGUAGE_HUNGARIAN
"Polski", // LANGUAGE_POLISH
"Espa\u00F1ol" // LANGUAGE_SPANISH
"Espa\u00F1ol", // LANGUAGE_SPANISH
"Svenska" // LANGUAGE_SWEDISH
};
const char *language_filenames[LANGUAGE_COUNT] = {
@@ -43,7 +43,8 @@ const char *language_filenames[LANGUAGE_COUNT] = {
"french", // LANGUAGE_FRENCH
"hungarian", // LANGUAGE_HUNGARIAN
"polish", // LANGUAGE_POLISH
"spanish_sp" // LANGUAGE_SPANISH
"spanish_sp", // LANGUAGE_SPANISH
"swedish" // LANGUAGE_SWEDISH
};
int gCurrentLanguage = LANGUAGE_UNDEFINED;
@@ -132,7 +133,8 @@ static int language_open_file(const char *filename)
language_strings = calloc(STR_COUNT, sizeof(char*));
char *dst, *token;
char *dst = NULL;
char *token = NULL;
char tokenBuffer[64];
int i, stringIndex = 0, mode = 0, string_no;
for (i = 0; i < language_buffer_size; i++) {

View File

@@ -21,8 +21,7 @@
#ifndef _LANGUAGE_H_
#define _LANGUAGE_H_
#include "rct2.h"
#include "string_ids.h"
#include "../common.h"
enum {
LANGUAGE_UNDEFINED,
@@ -33,6 +32,7 @@ enum {
LANGUAGE_HUNGARIAN,
LANGUAGE_POLISH,
LANGUAGE_SPANISH,
LANGUAGE_SWEDISH,
LANGUAGE_COUNT
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#ifndef LOCALISATION_H
#define LOCALISATION_H
#include "currency.h"
#include "format_codes.h"
#include "language.h"
#include "string_ids.h"
void format_string(char *dest, rct_string_id format, void *args);
void generate_string_file();
void reset_saved_strings();
void error_string_quit(int error, rct_string_id format);
int get_string_length(char* buffer);
// Real name data
extern const char real_name_initials[16];
extern const char *real_names[1024];
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -21,116 +21,6 @@
#ifndef _STRING_IDS_H_
#define _STRING_IDS_H_
typedef unsigned short rct_string_id;
void format_string(char *dest, rct_string_id format, void *args);
void generate_string_file();
void reset_saved_strings();
void error_string_quit(int error, rct_string_id format);
char format_get_code(const char *token);
const char *format_get_token(char code);
enum {
// Font format codes
// The next byte specifies the X coordinate
FORMAT_MOVE_X = 1,
// The next byte specifies the palette
FORMAT_ADJUST_PALETTE,
// Moves to the next line
FORMAT_NEWLINE = 5,
// Moves less than NEWLINE
FORMAT_NEWLINE_SMALLER,
FORMAT_TINYFONT,
FORMAT_BIGFONT,
FORMAT_MEDIUMFONT,
FORMAT_SMALLFONT,
FORMAT_OUTLINE,
FORMAT_OUTLINE_OFF,
// Changes the colour of the text to a predefined window colour.
FORMAT_WINDOW_COLOUR_1,
FORMAT_WINDOW_COLOUR_2,
FORMAT_WINDOW_COLOUR_3,
// The next 2 bytes specify the X and Y coordinates
FORMAT_NEWLINE_X_Y = 17,
// The next 4 bytes specify the sprite
FORMAT_INLINE_SPRITE = 23,
// Non ascii-characters
FORMAT_ENDQUOTES = 34,
// Argument format codes
FORMAT_ARGUMENT_CODE_START = 123,
FORMAT_COMMA32 = 123,
FORMAT_INT32,
FORMAT_COMMA2DP32,
FORMAT_COMMA16,
FORMAT_UINT16,
FORMAT_CURRENCY2DP,
FORMAT_CURRENCY,
FORMAT_STRINGID,
FORMAT_STRINGID2,
FORMAT_STRING,
FORMAT_MONTHYEAR,
FORMAT_MONTH,
FORMAT_VELOCITY,
FORMAT_POP16,
FORMAT_PUSH16,
FORMAT_DURATION,
FORMAT_REALTIME,
FORMAT_LENGTH,
FORMAT_SPRITE,
// Colour format codes
FORMAT_COLOUR_CODE_START = 142,
FORMAT_BLACK = 142,
FORMAT_GREY,
FORMAT_WHITE,
FORMAT_RED,
FORMAT_GREEN,
FORMAT_YELLOW,
FORMAT_TOPAZ,
FORMAT_CELADON,
FORMAT_BABYBLUE,
FORMAT_PALELAVENDER,
FORMAT_PALEGOLD,
FORMAT_LIGHTPINK,
FORMAT_PEARLAQUA,
FORMAT_PALESILVER,
FORMAT_COLOUR_CODE_END = FORMAT_PALESILVER,
// Extra non-ascii characters
FORMAT_AMINUSCULE = 159,
FORMAT_UP,
FORMAT_POUND = 163,
FORMAT_YEN = 165,
FORMAT_COPYRIGHT = 169,
FORMAT_DOWN,
FORMAT_LEFTGUILLEMET,
FORMAT_TICK,
FORMAT_CROSS,
FORMAT_RIGHT = 175,
FORMAT_DEGREE,
FORMAT_SQUARED = 178,
FORMAT_OPENQUOTES = 180,
FORMAT_EURO = 181,
FORMAT_APPROX = 184,
FORMAT_POWERNEGATIVEONE,
FORMAT_BULLET,
FORMAT_RIGHTGUILLEMET,
FORMAT_SMALLUP,
FORMAT_SMALLDOWN,
FORMAT_LEFT,
FORMAT_INVERTEDQUESTION
};
enum {
STR_NONE = -1,
@@ -251,6 +141,20 @@ enum {
STR_LOWER_COST_AMOUNT = 985,
STR_COST_AMOUNT = 986,
STR_CONSTRUCTION = 990,
STR_DEMOLISH_RIDE_TIP = 992,
STR_OVERALL_VIEW = 996,
STR_VIEW_SELECTION = 997,
STR_CANT_OPEN = 1002,
STR_CANT_TEST = 1003,
STR_CANT_CLOSE = 1004,
STR_CANT_START_CONSTRUCTION_ON = 1005,
STR_MUST_BE_CLOSED_FIRST = 1006,
STR_UNABLE_TO_CREATE_ENOUGH_VEHICLES = 1007,
STR_OPEN_CLOSE_OR_TEST_RIDE = 1008,
STR_OPEN_OR_CLOSE_ALL_RIDES = 1009,
STR_OPEN_OR_CLOSE_PARK_TIP = 1010,
STR_CLOSE_ALL = 1011,
@@ -258,6 +162,8 @@ enum {
STR_CLOSE_PARK = 1013,
STR_OPEN_PARK = 1014,
STR_CANT_CHANGE_OPERATING_MODE = 1017,
STR_LOCATE_SUBJECT_TIP = 1027,
STR_LOAD_GAME_DIALOG_TITLE = 1036,
@@ -267,6 +173,99 @@ enum {
STR_RCT2_LANDSCAPE_FILE = 1045,
STR_RIDES_IN_PARK_TIP = 1053,
STR_NAME_RIDE_TIP = 1054,
STR_RIDE_ATTRACTION_NAME = 1057,
STR_ENTER_NEW_NAME_FOR_THIS_RIDE_ATTRACTION = 1058,
STR_CANT_RENAME_RIDE_ATTRACTION = 1059,
STR_INVALID_RIDE_ATTRACTION_NAME = 1060,
STR_RIDE_MODE_START = 1061,
STR_RIDE_MODE_NORMAL = STR_RIDE_MODE_START + 0,
STR_RIDE_MODE_CONTINUOUS_CIRCUIT = STR_RIDE_MODE_START + 1,
STR_RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE = STR_RIDE_MODE_START + 2,
STR_RIDE_MODE_POWERED_LAUNCH = STR_RIDE_MODE_START + 3,
STR_RIDE_MODE_SHUTTLE = STR_RIDE_MODE_START + 4,
STR_RIDE_MODE_BOAT_HIRE = STR_RIDE_MODE_START + 5,
STR_RIDE_MODE_UPWARD_LAUNCH = STR_RIDE_MODE_START + 6,
STR_RIDE_MODE_ROTATING_LIFT = STR_RIDE_MODE_START + 7,
STR_RIDE_MODE_STATION_TO_STATION = STR_RIDE_MODE_START + 8,
STR_RIDE_MODE_SINGLE_RIDE_PER_ADMISSION = STR_RIDE_MODE_START + 9,
STR_RIDE_MODE_UNLIMITED_RIDES_PER_ADMISSION = STR_RIDE_MODE_START + 10,
STR_RIDE_MODE_MAZE = STR_RIDE_MODE_START + 11,
STR_RIDE_MODE_RACE = STR_RIDE_MODE_START + 12,
STR_RIDE_MODE_BUMPER_CAR = STR_RIDE_MODE_START + 13,
STR_RIDE_MODE_SWING = STR_RIDE_MODE_START + 14,
STR_RIDE_MODE_SHOP_STALL = STR_RIDE_MODE_START + 15,
STR_RIDE_MODE_ROTATION = STR_RIDE_MODE_START + 16,
STR_RIDE_MODE_FORWARD_ROTATION = STR_RIDE_MODE_START + 17,
STR_RIDE_MODE_BACKWARD_ROTATION = STR_RIDE_MODE_START + 18,
STR_RIDE_MODE_FILM_AVENGING_AVIATORS = STR_RIDE_MODE_START + 19,
STR_RIDE_MODE_3D_FILM_MOUSE_TAILS = STR_RIDE_MODE_START + 20,
STR_RIDE_MODE_SPACE_RINGS = STR_RIDE_MODE_START + 21,
STR_RIDE_MODE_BEGINNERS = STR_RIDE_MODE_START + 22,
STR_RIDE_MODE_LIM_POWERED_LAUNCH = STR_RIDE_MODE_START + 23,
STR_RIDE_MODE_FILM_THRILL_RIDERS = STR_RIDE_MODE_START + 24,
STR_RIDE_MODE_3D_FILM_STORM_CHASERS = STR_RIDE_MODE_START + 25,
STR_RIDE_MODE_3D_FILM_SPACE_RAIDERS = STR_RIDE_MODE_START + 26,
STR_RIDE_MODE_INTENSE = STR_RIDE_MODE_START + 27,
STR_RIDE_MODE_BERSERK = STR_RIDE_MODE_START + 28,
STR_RIDE_MODE_HAUNTED_HOUSE = STR_RIDE_MODE_START + 29,
STR_RIDE_MODE_CIRCUS_SHOW = STR_RIDE_MODE_START + 30,
STR_RIDE_MODE_DOWNWARD_LAUNCH = STR_RIDE_MODE_START + 31,
STR_RIDE_MODE_CROOKED_HOUSE = STR_RIDE_MODE_START + 32,
STR_RIDE_MODE_FREEFALL_DROP = STR_RIDE_MODE_START + 33,
STR_RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED = STR_RIDE_MODE_START + 34,
STR_RIDE_MODE_POWERED_LAUNCH_35 = STR_RIDE_MODE_START + 35,
STR_RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED_MODE = STR_RIDE_MODE_START + 36,
STR_MOVING_TO_END_OF = 1098,
STR_WAITING_FOR_PASSENGERS_AT = 1099,
STR_WAITING_TO_DEPART = 1100,
STR_DEPARTING = 1101,
STR_TRAVELLING_AT_0 = 1102,
STR_ARRIVING_AT = 1103,
STR_UNLOADING_PASSENGERS_AT = 1104,
STR_TRAVELLING_AT_1 = 1105,
STR_CRASHING = 1106,
STR_CRASHED_0 = 1107,
STR_TRAVELLING_AT_2 = 1108,
STR_SWINGING = 1109,
STR_ROTATING_0 = 1110,
STR_ROTATING_1 = 1111,
STR_OPERATING_0 = 1112,
STR_SHOWING_FILM = 1113,
STR_ROTATING_2 = 1114,
STR_OPERATING_1 = 1115,
STR_OPERATING_2 = 1116,
STR_DOING_CIRCUS_SHOW = 1117,
STR_OPERATING_3 = 1118,
STR_WAITING_FOR_CABLE_LIFT = 1119,
STR_TRAVELLING_AT_3 = 1120,
STR_STOPPING_0 = 1121,
STR_WAITING_FOR_PASSENGERS = 1122,
STR_WAITING_TO_START = 1123,
STR_STARTING = 1124,
STR_OPERATING = 1125,
STR_STOPPING_1 = 1126,
STR_UNLOADING_PASSENGERS = 1127,
STR_STOPPED_BY_BLOCK_BRAKES = 1128,
STR_ALL_VEHICLES_IN_SAME_COLOURS = 1129,
STR_DIFFERENT_COLOURS_PER = 1130,
STR_DIFFERENT_COLOURS_PER_VEHICLE = 1131,
STR_SELECT_MAIN_COLOUR_TIP = 1136,
STR_SELECT_ADDITIONAL_COLOUR_1_TIP = 1137,
STR_SELECT_ADDITIONAL_COLOUR_2_TIP = 1138,
STR_SELECT_SUPPORT_STRUCTURE_COLOUR_TIP = 1139,
STR_SELECT_VEHICLE_COLOUR_SCHEME_TIP = 1140,
STR_SELECT_VEHICLE_TO_MODIFY_TIP = 1141,
STR_QUARTER_LOAD = 1148,
STR_HALF_LOAD = 1149,
STR_THREE_QUARTER_LOAD = 1150,
STR_FULL_LOAD = 1151,
STR_ANY_LOAD = 1152,
STR_PLACE_SCENERY_TIP = 1159,
STR_ADJUST_WATER_TIP = 1160,
@@ -288,9 +287,9 @@ enum {
STR_SLOPE_UP_TIP = 1188,
STR_CONSTRUCT_THE_SELECTED_FOOTPATH_SECTION_TIP = 1189,
STR_REMOVE_PREVIOUS_FOOTPATH_SECTION_TIP = 1190,
STR_COST = 1191,
STR_BLACK_STRING = 1191,
STR_LOSS = 1192,
STR_WINDOW_COLOUR_2_STRING = 1193,
STR_CLOSED = 1194,
STR_TEST_RUN = 1195,
STR_OPEN = 1196,
@@ -304,10 +303,24 @@ enum {
STR_QUEUE_PEOPLE = 1203,
STR_QUEUE_TIME_LABEL = 1204,
STR_QUEUE_TIME_PLURAL_LABEL = 1205,
STR_WAIT_FOR = 1206,
STR_LEAVE_IF_ANOTHER_TRAIN_ARRIVES = 1207,
STR_LEAVE_IF_ANOTHER_BOAT_ARRIVES = 1208,
STR_WAIT_FOR_PASSENGERS_BEFORE_DEPARTING_TIP = 1209,
STR_LEAVE_IF_ANOTHER_VEHICLE_ARRIVES_TIP = 1210,
STR_MINIMUM_WAITING_TIME = 1211,
STR_MAXIMUM_WAITING_TIME = 1212,
STR_MINIMUM_LENGTH_BEFORE_DEPARTING_TIP = 1213,
STR_MAXIMUM_LENGTH_BEFORE_DEPARTING_TIP = 1214,
STR_SYNCHRONISE_WITH_ADJACENT_STATIONS = 1215,
STR_SYNCHRONISE_WITH_ADJACENT_STATIONS_TIP = 1216,
STR_NUMERIC_UP = 1218,
STR_NUMERIC_DOWN = 1219,
STR_EXIT_ONLY = 1220,
STR_NO_ENTRANCE = 1221,
STR_NO_EXIT = 1222,
STR_TRANSPORT_RIDES_TIP = 1223,
STR_GENTLE_RIDES_TIP = 1224,
STR_ROLLER_COASTERS_TIP = 1225,
@@ -316,13 +329,53 @@ enum {
STR_SHOPS_STALLS_TIP = 1228,
STR_ROTATE_OBJECTS_90 = 1327,
STR_LAUNCH_SPEED = 1329,
STR_LAUNCH_SPEED_TIP = 1330,
STR_NO_TEST_RESULTS_YET = 1339,
STR_MAX_SPEED = 1340,
STR_RIDE_TIME = 1341,
STR_RIDE_LENGTH = 1344,
STR_AVERAGE_SPEED = 1347,
STR_MAX_POSITIVE_VERTICAL_G = 1348,
STR_MAX_POSITIVE_VERTICAL_G_RED = 1349,
STR_MAX_NEGATIVE_VERTICAL_G = 1350,
STR_MAX_NEGATIVE_VERTICAL_G_RED = 1351,
STR_MAX_LATERAL_G = 1352,
STR_MAX_LATERAL_G_RED = 1353,
STR_HIGHEST_DROP_HEIGHT = 1354,
STR_DROPS = 1355,
STR_INVERSIONS = 1356,
STR_HOLES = 1357,
STR_TOTAL_AIR_TIME = 1358,
STR_QUEUE_TIME_MINUTE = 1359,
STR_QUEUE_TIME_MINUTES = 1360,
STR_VIEW_OF_RIDE_ATTRACTION_TIP = 1392,
STR_VEHICLE_DETAILS_AND_OPTIONS_TIP = 1393,
STR_OPERATING_OPTIONS_TIP = 1394,
STR_MAINTENANCE_OPTIONS_TIP = 1395,
STR_COLOUR_SCHEME_OPTIONS_TIP = 1396,
STR_SOUND_AND_MUSIC_OPTIONS_TIP = 1397,
STR_MEASUREMENTS_AND_TEST_DATA_TIP = 1398,
STR_GRAPHS_TIP = 1399,
STR_ROTATE_90_TIP = 1404,
STR_MIRROR_IMAGE_TIP = 1405,
STR_TOGGLE_SCENERY_TIP = 1406,
STR_BUILD_THIS = 1407,
STR_COST_LABEL = 1408,
STR_DATA_LOGGING_NOT_AVAILABLE_FOR_THIS_TYPE_OF_RIDE = 1412,
STR_DATA_LOGGING_WILL_START_WHEN_NEXT_LEAVES = 1413,
STR_LOGGING_DATA_FROM_TIP = 1422,
STR_QUEUE_LINE_PATH_TIP = 1423,
STR_FOOTPATH_TIP = 1424,
STR_CUSTOMERS_PER_HOUR = 1427,
STR_RIDE_INCOME_ADMISSION_PRICE = 1428,
STR_FREE = 1430,
STR_WALKING = 1431,
STR_HEADING_FOR = 1432,
@@ -346,15 +399,37 @@ enum {
STR_GUESTS = 1463,
STR_STAFF = 1468,
STR_SPEED = 1471,
STR_SPEED_TIP = 1472,
STR_EXCITEMENT_RATING = 1473,
STR_EXCITEMENT_RATING_NOT_YET_AVAILABLE = 1474,
STR_INTENSITY_RATING = 1475,
STR_INTENSITY_RATING_NOT_YET_AVAILABLE = 1476,
STR_INTENSITY_RATING_RED = 1477,
STR_NAUSEA_RATING = 1478,
STR_NAUSEA_RATING_NOT_YET_AVAILABLE = 1479,
STR_THOUGHT_START = 1480,
STR_CONSTRUCT_FOOTPATH_ON_LAND_TIP = 1655,
STR_CONSTRUCT_BRIDGE_OR_TUNNEL_FOOTPATH_TIP = 1656,
STR_SATISFACTION_UNKNOWN = 1668,
STR_SATISFACTION_PERCENT = 1669,
STR_TOTAL_CUSTOMERS = 1670,
STR_TOTAL_PROFIT = 1671,
STR_POPULARITY_UNKNOWN = 1677,
STR_POPULARITY_PERCENT = 1678,
STR_GUESTS_TIP = 1693,
STR_STAFF_TIP = 1694,
STR_INCOME_AND_COSTS_TIP = 1695,
STR_CUSTOMER_INFORMATION_TIP = 1696,
STR_TOO_MANY_PEOPLE_IN_GAME = 1699,
STR_HIRE_HANDYMAN = 1700,
STR_HIRE_MECHANIC = 1701,
@@ -375,12 +450,25 @@ enum {
STR_CANT_OPEN_PARK = 1723,
STR_CANT_CLOSE_PARK = 1724,
STR_NUMBER_OF_LAPS = 1734,
STR_NUMBER_OF_LAPS_TIP = 1735,
STR_RACE_WON_BY_GUEST = 1739,
STR_RACE_WON_BY = 1740,
STR_MAX_PEOPLE_ON_RIDE = 1742,
STR_MAX_PEOPLE_ON_RIDE_TIP = 1743,
STR_TIME_LIMIT = 1747,
STR_TIME_LIMIT_TIP = 1748,
STR_INDIVIDUAL_GUESTS_TIP = 1752,
STR_SUMMARISED_GUESTS_TIP = 1753,
STR_ADMISSION_PRICE = 1756,
STR_RELIABILITY_LABEL_1757 = 1757,
STR_NUMBER_OF_SWINGS = 1769,
STR_NUMBER_OF_SWINGS_TIP = 1770,
STR_OFF = 1775,
STR_ON = 1776,
@@ -393,6 +481,18 @@ enum {
STR_FIXING_RIDE = 1794,
STR_ANSWERING_RADIO_CALL = 1795,
STR_SAFETY_CUT_OUT = 1800,
STR_RESTRAINTS_STUCK_CLOSED = 1801,
STR_RESTRAINTS_STUCK_OPEN = 1802,
STR_DOORS_STUCK_CLOSED = 1803,
STR_DOORS_STUCK_OPEN = 1804,
STR_VEHICLE_MALFUNCTION = 1805,
STR_BRAKES_FAILURE = 1806,
STR_CONTROL_FAILURE = 1807,
STR_LAST_BREAKDOWN = 1808,
STR_CURRENT_BREAKDOWN = 1809,
STR_ACTIONS = 1814,
STR_THOUGHTS = STR_ACTIONS + 1,
STR_INFORMATION_TYPE_TIP = 1816,
@@ -403,6 +503,10 @@ enum {
STR_GUESTS_FILTER_THINKING = STR_GUESTS_FILTER + 1,
STR_GUESTS_FILTER_THINKING_ABOUT = STR_GUESTS_FILTER + 2,
STR_SHOW_GUESTS_THOUGHTS_ABOUT_THIS_RIDE_ATTRACTION_TIP = 1823,
STR_SHOW_GUESTS_ON_THIS_RIDE_ATTRACTION_TIP = 1824,
STR_SHOW_GUESTS_QUEUING_FOR_THIS_RIDE_ATTRACTION_TIP = 1825,
STR_STATUS = 1826,
STR_POPULARITY = 1827,
STR_SATISFACTION = 1828,
@@ -424,6 +528,18 @@ enum {
STR_RIDE_LIST_INFORMATION_TYPE_TIP = 1844,
STR_NUM_GUESTS = 1846,
STR_PLAY_MUSIC = 1849,
STR_SELECT_MUSIC_TIP = 1850,
STR_RUNNING_COST_PER_HOUR = 1851,
STR_RUNNING_COST_UNKNOWN = 1852,
STR_BUILT_THIS_YEAR = 1853,
STR_BUILT_LAST_YEAR = 1854,
STR_BUILT_YEARS_AGO = 1855,
STR_PROFIT_PER_ITEM_SOLD = 1856,
STR_LOSS_PER_ITEM_SOLD = 1857,
STR_COST_PER_MONTH = 1858,
STR_HANDYMAN_PLURAL = 1859,
STR_MECHANIC_PLURAL = 1860,
STR_SECURITY_GUARD_PLURAL = 1861,
@@ -434,8 +550,32 @@ enum {
STR_ENTERTAINER_SINGULAR = 1866,
STR_STAFF_LIST_COUNTER = 1867,
STR_NUMBER_OF_ROTATIONS = 1869,
STR_NUMBER_OF_ROTATIONS_TIP = 1870,
STR_INCOME_PER_HOUR = 1873,
STR_PROFIT_PER_HOUR = 1874,
STR_INSPECT_RIDES = 1876,
STR_FIX_RIDES = 1877,
STR_INSPECTION = 1878,
STR_EVERY_10_MINUTES = 1879,
STR_EVERY_20_MINUTES = 1880,
STR_EVERY_30_MINUTES = 1881,
STR_EVERY_45_MINUTES = 1882,
STR_EVERY_HOUR = 1883,
STR_EVERY_2_HOURS = 1884,
STR_NEVER = 1885,
STR_INSPECTING_RIDE = 1886,
STR_TIME_SINCE_LAST_INSPECTION_MINUTES = 1887,
STR_TIME_SINCE_LAST_INSPECTION_MORE_THAN_4_HOURS = 1888,
STR_DOWN_TIME_LABEL_1889 = 1889,
STR_SELECT_HOW_OFTEN_A_MECHANIC_SHOULD_CHECK_THIS_RIDE = 1890,
STR_ITEMS_SOLD = 1894,
STR_BUILD_RIDE_TIP = 1895,
STR_FINANCES_SUMMARY_EXPENDITURE_INCOME = 1896,
STR_FINANCES_SUMMARY_RIDE_CONSTRUCTION = 1897,
@@ -480,6 +620,16 @@ enum {
STR_MARKETING = 1953,
STR_RESEARCH_FUNDING = 1954,
STR_NUMBER_OF_CIRCUITS = 1955,
STR_NUMBER_OF_CIRCUITS_TIP = 1956,
STR_ON_RIDE_PHOTO_PRICE = 1963,
STR_MAIN_COLOUR_SCHEME = 2971,
STR_ALTERNATIVE_COLOUR_SCHEME_1 = 2972,
STR_ALTERNATIVE_COLOUR_SCHEME_2 = 2973,
STR_ALTERNATIVE_COLOUR_SCHEME_3 = 2974,
STR_ITEM_START = 1988,
STR_ITEM_SINGULAR_START = 2044,
STR_ITEM2_START = 2090,
@@ -566,6 +716,17 @@ enum {
STR_CHANGE_BASE_LAND_TIP = 2294,
STR_CHANGE_VERTICAL_LAND_TIP = 2295,
STR_SELECT_DESIGN = 2307,
STR_TRACK_DESIGNS = 2308,
STR_BUILD_CUSTOM_DESIGN = 2310,
STR_TRACK_LIST_EXCITEMENT_RATING = 2311,
STR_TRACK_LIST_INTENSITY_RATING = 2312,
STR_TRACK_LIST_NAUSEA_RATING = 2313,
STR_TRACK_LIST_RIDE_LENGTH = 2314,
STR_TRACK_LIST_COST_AROUND = 2315,
STR_TRACK_LIST_SPACE_REQUIRED = 2316,
STR_SOUND_QUALITY = 2317,
STR_SOUND_LOW = 2318,
STR_SOUND_MEDIUM = 2319,
@@ -608,12 +769,12 @@ enum {
STR_LOW = 2369,
STR_AVERAGE = 2370,
STR_HIGH = 2371,
//STR_LOW = 2372,
STR_MEDIUM = 2373,
//STR_HIGH = 2374,
STR_VERY_HIGH = 2375,
STR_EXTREME = 2376,
STR_ULTRA_EXTREME = 2377,
STR_RATING_LOW = 2372,
STR_RATING_MEDIUM = 2373,
STR_RATING_HIGH = 2374,
STR_RATING_VERY_HIGH = 2375,
STR_RATING_EXTREME = 2376,
STR_RATING_ULTRA_EXTREME = 2377,
STR_ADJUST_SMALLER_LAND_TIP = 2378,
STR_ADJUST_LARGER_LAND_TIP = 2379,
@@ -708,6 +869,13 @@ enum {
STR_RESEARCH_NEW_SHOPS_AND_STALLS = 2475,
STR_RESEARCH_NEW_SCENERY_AND_THEMING = 2476,
STR_SELECT_OPERATING_MODE = 2477,
STR_SHOW_GRAPH_OF_VELOCITY_AGAINST_TIME_TIP = 2478,
STR_SHOW_GRAPH_OF_ALTITUDE_AGAINST_TIME_TIP = 2479,
STR_SHOW_GRAPH_OF_VERTICAL_ACCELERATION_AGAINST_TIME_TIP = 2480,
STR_SHOW_GRAPH_OF_LATERAL_ACCELERATION_AGAINST_TIME_TIP = 2481,
STR_PROFIT_PER_WEEK_AND_PARK_VALUE_TIP = 2482,
STR_FINANCES_WEEKLY_PROFIT_POSITIVE = 2483,
STR_FINANCES_WEEKLY_PROFIT_LOSS = 2484,
@@ -718,6 +886,15 @@ enum {
STR_REAL_NAME_TIP = 2488,
STR_HOTKEY = 2489,
STR_SHORTCUT_DESCRIPTION_0 = 2493,
STR_SHORTCUT_DESCRIPTION_31 = 2524,
STR_INDIVIDUAL_KEYS_BASE = 2525,
STR_SHORTCUT_ENTRY_FORMAT = 2781,
STR_SHIFT_PLUS = 2782,
STR_CTRL_PLUS = 2783,
STR_FINACNES_PARK_VALUE = 2787,
STR_ENTER_NAME_INTO_SCENARIO_CHART = 2790,
@@ -807,19 +984,83 @@ enum {
STR_LICENCE_AGREEMENT_NOTICE_1 = 2969,
STR_LICENCE_AGREEMENT_NOTICE_2 = 2970,
STR_COLOUR_SCHEME_TO_CHANGE_TIP = 2975,
STR_PAINT_INDIVIDUAL_AREA_TIP = 2976,
STR_UNABLE_TO_LOAD_FILE = 3010,
STR_FILE_CONTAINS_INVALID_DATA = 3011,
STR_MUSIC_STYLE_START = 3012,
STR_DODGEMS_BEAT_STYLE = STR_MUSIC_STYLE_START + 0,
STR_FAIRGROUND_ORGAN_STYLE = STR_MUSIC_STYLE_START + 1,
STR_ROMAN_FANFARE_STYLE = STR_MUSIC_STYLE_START + 2,
STR_ORIENTAL_STYLE = STR_MUSIC_STYLE_START + 3,
STR_MARTIAN_STYLE = STR_MUSIC_STYLE_START + 4,
STR_JUNGLE_DRUMS_STYLE = STR_MUSIC_STYLE_START + 5,
STR_EGYPTIAN_STYLE = STR_MUSIC_STYLE_START + 6,
STR_TOYLAND_STYLE = STR_MUSIC_STYLE_START + 7,
// STR_??? = STR_MUSIC_STYLE_START + 8,
STR_SPACE_STYLE = STR_MUSIC_STYLE_START + 9,
STR_HORROR_STYLE = STR_MUSIC_STYLE_START + 10,
STR_TECHNO_STYLE = STR_MUSIC_STYLE_START + 11,
STR_GENTLE_STYLE = STR_MUSIC_STYLE_START + 12,
STR_SUMMER_STYLE = STR_MUSIC_STYLE_START + 13,
STR_WATER_STYLE = STR_MUSIC_STYLE_START + 14,
STR_WILD_WEST_STYLE = STR_MUSIC_STYLE_START + 15,
STR_JURASSIC_STYLE = STR_MUSIC_STYLE_START + 16,
STR_ROCK_STYLE = STR_MUSIC_STYLE_START + 17,
STR_RAGTIME_STYLE = STR_MUSIC_STYLE_START + 18,
STR_FANTASY_STYLE = STR_MUSIC_STYLE_START + 19,
STR_ROCK_STYLE_2 = STR_MUSIC_STYLE_START + 20,
STR_ICE_STYLE = STR_MUSIC_STYLE_START + 21,
STR_SNOW_STYLE = STR_MUSIC_STYLE_START + 22,
STR_CUSTOM_MUSIC_1 = STR_MUSIC_STYLE_START + 23,
STR_CUSTOM_MUSIC_2 = STR_MUSIC_STYLE_START + 24,
STR_MEDIEVAL_STYLE = STR_MUSIC_STYLE_START + 25,
STR_URBAN_STYLE = STR_MUSIC_STYLE_START + 26,
STR_ORGAN_STYLE = STR_MUSIC_STYLE_START + 27,
STR_MECHANICAL_STYLE = STR_MUSIC_STYLE_START + 28,
STR_MODERN_STYLE = STR_MUSIC_STYLE_START + 29,
STR_PIRATES_STYLE = STR_MUSIC_STYLE_START + 30,
STR_ROCK_STYLE_3 = STR_MUSIC_STYLE_START + 31,
STR_CANDY_STYLE = STR_MUSIC_STYLE_START + 32,
STR_SELECT_MUSIC_STYLE_TIP = 3045,
STR_WHITE = 3055,
STR_TRANSLUCENT = 3056,
STR_CONSTRUCTION_MARKER = 3057,
STR_BRICK_WALLS = 3058,
STR_HEDGES = 3059,
STR_ICE_BLOCKS = 3060,
STR_WOODEN_FENCES = 3061,
STR_BEGINNER_PARKS = 3064,
STR_CHALLENGING_PARKS = STR_BEGINNER_PARKS + 1,
STR_EXPERT_PARKS = STR_BEGINNER_PARKS + 2,
STR_REAL_PARKS = STR_BEGINNER_PARKS + 3,
STR_OTHER_PARKS = STR_BEGINNER_PARKS + 4,
STR_SAME_PRICE_THROUGHOUT_PARK = 3071,
STR_SAME_PRICE_THROUGHOUT_PARK_TIP = 3072,
STR_PLAIN_ENTRANCE = 3078,
STR_WOODEN_ENTRANCE = 3079,
STR_CANVAS_TENT_ENTRANCE = 3080,
STR_CASTLE_ENTRANCE_GREY = 3081,
STR_CASTLE_ENTRANCE_BROWN = 3082,
STR_JUNGLE_ENTRANCE = 3083,
STR_LOG_CABIN_ENTRANCE = 3084,
STR_CLASSICAL_ROMAN_ENTRANCE = 3085,
STR_ABSTRACT_ENTRANCE = 3086,
STR_SNOW_ICE_ENTRANCE = 3087,
STR_PAGODA_ENTRANCE = 3088,
STR_SPACE_ENTRANCE = 3089,
STR_SELECT_STYLE_OF_ENTRANCE_EXIT_STATION_TIP = 3090,
STR_SELECT_LIFT_HILL_CHAIN_SPEED_TIP = 3097,
STR_SELECT_COLOUR = 3099,
STR_SELECT_SECONDARY_COLOUR = 3100,
STR_SELECT_TERNARY_COLOUR = 3101,
@@ -827,6 +1068,37 @@ enum {
STR_LIST_RIDES_TIP = 3104,
STR_LIST_SHOPS_AND_STALLS_TIP = 3105,
STR_LIST_KIOSKS_AND_FACILITIES_TIP = 3106,
STR_CLOSE_RIDE = 3107,
STR_TEST_RIDE = 3108,
STR_OPEN_RIDE = 3109,
STR_BLOCK_SECTIONS = 3110,
STR_CLICK_ON_DESIGN_TO_BUILD_IT_TIP = 3111,
STR_CLICK_ON_DESIGN_TO_RENAME_OR_DELETE_IT = 3112,
STR_SELECT_A_DIFFERENT_DESIGN = 3113,
STR_GO_BACK_TO_DESIGN_SELECTION_WINDOW_TIP = 3114,
STR_SAVE_TRACK_DESIGN = 3115,
STR_SAVE_TRACK_DESIGN_NOT_POSSIBLE = 3116,
STR_CALLING_MECHANIC = 3117,
STR_MEHCANIC_IS_HEADING_FOR_THE_RIDE = 3118,
STR_MEHCANIC_IS_FIXING_THE_RIDE = 3119,
STR_LOCATE_NEAREST_AVAILABLE_MECHANIC_TIP = 3120,
STR_FAVOURITE_RIDE_OF_GUEST = 3122,
STR_FAVOURITE_RIDE_OF_GUESTS = 3123,
STR_SAVE_TRACK_DESIGN_ITEM = 3128,
STR_SAVE_TRACK_DESIGN_WITH_SCENERY_ITEM = 3129,
STR_DESIGN_SAVE = 3130,
STR_DESIGN_CANCEL = 3131,
STR_CLICK_ITEMS_OF_SCENERY_TO_SELECT = 3132,
STR_DESIGN_INCLUDES_SCENERY_WHICH_IS_UNAVAILABLE = 3134,
STR_VEHICLE_DESIGN_UNAVAILABLE = 3135,
STR_THIS_DESIGN_WILL_BE_BUILT_WITH_AN_ALTERNATIVE_VEHICLE_TYPE = 3136,
STR_SELECT_NEARBY_SCENERY = 3137,
STR_RESET_SELECTION = 3138,
STR_SCROLL_LEFT_TIP = 3145,
STR_SCROLL_RIGHT_TIP = STR_SCROLL_LEFT_TIP + 1,
@@ -874,6 +1146,10 @@ enum {
STR_ROLLER_COASTER_DESIGNER = 3344,
STR_TRACK_DESIGNS_MANAGER = 3345,
STR_NO_TRACK_DESIGNS_OF_THIS_TYPE = 3359,
STR_WARNING = 3360,
STR_TOO_MANY_TRACK_DESIGNS_OF_THIS_TYPE = 3361,
STR_SOUND_FORCED_SOFTWARE_BUFFER_MIXING = 3362,
STR_SOUND_FORCED_SOFTWARE_BUFFER_MIXING_TIP = 3363,

View File

@@ -18,14 +18,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "addresses.h"
#include "../addresses.h"
#include "../interface/window.h"
#include "../localisation/localisation.h"
#include "../peep/peep.h"
#include "../ride/ride.h"
#include "../scenario.h"
#include "../world/sprite.h"
#include "award.h"
#include "news_item.h"
#include "peep.h"
#include "ride.h"
#include "scenario.h"
#include "sprite.h"
#include "window.h"
#define NEGATIVE 0
#define POSITIVE 1
@@ -474,10 +475,12 @@ static int award_is_deserved_best_custom_designed_rides(int awardType, int activ
}
/** At least 5 colourful rides and more than half of the rides are colourful. */
const uint8 dazzling_ride_colours[] = { 5, 14, 20, 30 };
static int award_is_deserved_most_dazzling_ride_colours(int awardType, int activeAwardTypes)
{
int i, countedRides, colourfulRides;
int i, j, countedRides, colourfulRides;
rct_ride *ride;
uint8 mainTrackColour;
if (activeAwardTypes & (1 << PARK_AWARD_MOST_DISAPPOINTING))
return 0;
@@ -489,8 +492,14 @@ static int award_is_deserved_most_dazzling_ride_colours(int awardType, int activ
continue;
countedRides++;
if (ride->var_1BC == 5 || ride->var_1BC == 14 || ride->var_1BC == 20 || ride->var_1BC == 30)
colourfulRides++;
mainTrackColour = ride->track_colour_main[0];
for (j = 0; j < countof(dazzling_ride_colours); j++) {
if (mainTrackColour == dazzling_ride_colours[j]) {
colourfulRides++;
break;
}
}
}
return (colourfulRides >= 5 && colourfulRides >= countedRides - colourfulRides);

View File

@@ -21,7 +21,7 @@
#ifndef _AWARD_H_
#define _AWARD_H_
#include "rct2.h"
#include "../common.h"
typedef struct {
uint16 time;

View File

@@ -18,13 +18,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "addresses.h"
#include "../addresses.h"
#include "../interface/window.h"
#include "../peep/peep.h"
#include "../ride/ride.h"
#include "../world/park.h"
#include "../world/sprite.h"
#include "finance.h"
#include "sprite.h"
#include "park.h"
#include "peep.h"
#include "ride.h"
#include "window.h"
// Monthly staff wages
const money32 wage_table[4] = {
@@ -130,7 +130,7 @@ void finance_pay_ride_upkeep()
if (ride->status != RIDE_STATUS_CLOSED && !(RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_NO_MONEY)) {
sint16 upkeep = ride->upkeep_cost;
if (upkeep != -1) {
ride->var_154 -= upkeep;
ride->total_profit -= upkeep;
ride->var_14D |= 2;
finance_payment(upkeep, RCT2_EXPENDITURE_TYPE_RIDE_UPKEEP);
}

View File

@@ -21,7 +21,7 @@
#ifndef _FINANCE_H_
#define _FINANCE_H_
#include "rct2.h"
#include "../common.h"
typedef int rct_expenditure_type;

View File

@@ -18,13 +18,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "addresses.h"
#include "../addresses.h"
#include "../interface/window.h"
#include "../localisation/localisation.h"
#include "../ride/ride.h"
#include "marketing.h"
#include "news_item.h"
#include "rct2.h"
#include "ride.h"
#include "string_ids.h"
#include "window.h"
const money16 AdvertisingCampaignPricePerWeek[] = {
MONEY(50,00), // PARK_ENTRY_FREE,
@@ -107,28 +106,28 @@ void marketing_set_guest_campaign(rct_peep *peep, int campaign)
switch (campaign) {
case ADVERTISING_CAMPAIGN_PARK_ENTRY_FREE:
peep->item_standard_flags |= PEEP_ITEM_VOUCHER;
peep->var_F0 = 0;
peep->voucher_type = VOUCHER_TYPE_PARK_ENTRY_FREE;
break;
case ADVERTISING_CAMPAIGN_RIDE_FREE:
peep->item_standard_flags |= PEEP_ITEM_VOUCHER;
peep->var_F0 = 1;
peep->var_F1 = RCT2_ADDRESS(0x01358116, uint8)[campaign];
peep->var_C5 = RCT2_ADDRESS(0x01358116, uint8)[campaign];
peep->voucher_type = VOUCHER_TYPE_RIDE_FREE;
peep->voucher_arguments = RCT2_ADDRESS(0x01358116, uint8)[campaign];
peep->guest_heading_to_ride_id = RCT2_ADDRESS(0x01358116, uint8)[campaign];
peep->var_C6 = 240;
break;
case ADVERTISING_CAMPAIGN_PARK_ENTRY_HALF_PRICE:
peep->item_standard_flags |= PEEP_ITEM_VOUCHER;
peep->var_F0 = 2;
peep->voucher_type = VOUCHER_TYPE_PARK_ENTRY_HALF_PRICE;
break;
case ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE:
peep->item_standard_flags |= PEEP_ITEM_VOUCHER;
peep->var_F0 = 3;
peep->var_F1 = RCT2_ADDRESS(0x01358116, uint8)[campaign];
peep->voucher_type = VOUCHER_TYPE_FOOD_OR_DRINK_FREE;
peep->voucher_arguments = RCT2_ADDRESS(0x01358116, uint8)[campaign];
break;
case ADVERTISING_CAMPAIGN_PARK:
break;
case ADVERTISING_CAMPAIGN_RIDE:
peep->var_C5 = RCT2_ADDRESS(0x01358116, uint8)[campaign];
peep->guest_heading_to_ride_id = RCT2_ADDRESS(0x01358116, uint8)[campaign];
peep->var_C6 = 240;
break;
}

View File

@@ -21,7 +21,8 @@
#ifndef _MARKETING_H_
#define _MARKETING_H_
#include "peep.h"
#include "../common.h"
#include "../peep/peep.h"
enum {
ADVERTISING_CAMPAIGN_PARK_ENTRY_FREE,
@@ -33,6 +34,13 @@ enum {
ADVERTISING_CAMPAIGN_COUNT
};
enum{
VOUCHER_TYPE_PARK_ENTRY_FREE,
VOUCHER_TYPE_RIDE_FREE,
VOUCHER_TYPE_PARK_ENTRY_HALF_PRICE,
VOUCHER_TYPE_FOOD_OR_DRINK_FREE,
};
extern const money16 AdvertisingCampaignPricePerWeek[6];
int marketing_get_campaign_guest_generation_probability(int campaign);

View File

@@ -19,15 +19,14 @@
*****************************************************************************/
#include <string.h>
#include "addresses.h"
#include "audio.h"
#include "date.h"
#include "../addresses.h"
#include "../audio/audio.h"
#include "../interface/window.h"
#include "../localisation/date.h"
#include "../localisation/localisation.h"
#include "../ride/ride.h"
#include "../world/sprite.h"
#include "news_item.h"
#include "rct2.h"
#include "ride.h"
#include "string_ids.h"
#include "sprite.h"
#include "window.h"
void window_game_bottom_toolbar_invalidate_news_item();
static int news_item_get_new_history_slot();
@@ -97,7 +96,7 @@ void news_item_update_current()
newsItems[0].ticks++;
if (newsItems[0].ticks == 1 && !(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 1)) {
// Play sound
sound_play_panned(SOUND_NEWS_ITEM, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16) / 2);
sound_play_panned(SOUND_NEWS_ITEM, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16) / 2, 0, 0, 0);
}
// Removal of current news item
@@ -212,7 +211,7 @@ void news_item_get_subject_location(int type, int subject, int *x, int *y, int *
}
// Find the first car of the train peep is on
vehicle = &(g_sprite_list[ride->train_car_map[peep->current_train]]).vehicle;
vehicle = &(g_sprite_list[ride->vehicles[peep->current_train]]).vehicle;
// Find the actual car peep is on
for (i = 0; i < peep->current_car; i++)
vehicle = &(g_sprite_list[vehicle->next_vehicle_on_train]).vehicle;
@@ -252,7 +251,7 @@ void news_item_add_to_queue(uint8 type, rct_string_id string_id, uint32 assoc)
// find first open slot
while (newsItem->type != NEWS_ITEM_NULL) {
if (newsItem + sizeof(newsItem) >= (rct_news_item*)0x13CB1CC)
if (newsItem + 1 >= (rct_news_item*)0x13CB1CC)
news_item_close_current();
else
newsItem++;
@@ -283,15 +282,14 @@ void news_item_add_to_queue(uint8 type, rct_string_id string_id, uint32 assoc)
* rct2: 0x0066EBE6
*
**/
void news_item_open_subject(int type, int subject) {
int eax;
void news_item_open_subject(int type, int subject)
{
rct_peep* peep;
rct_window* window;
switch (type) {
case NEWS_ITEM_RIDE:
RCT2_CALLPROC_X(0x006ACC28, subject, 0, 0, 0, 0, 0, 0);
window_ride_main_open(subject);
break;
case NEWS_ITEM_PEEP_ON_RIDE:
case NEWS_ITEM_PEEP:
@@ -302,14 +300,15 @@ void news_item_open_subject(int type, int subject) {
window_finances_open();
break;
case NEWS_ITEM_RESEARCH:
if (subject >= 0x10000) {
// Open ride list window
RCT2_CALLPROC_EBPSAFE(0x006B3CFF);
eax = (subject & 0xFF00) >> 8;
eax += (subject & 0xFF) << 8;
window_new_ride_open();
// Switch to right tab and scroll to ride location
RCT2_CALLPROC_X(0x006B3EBA, eax, 0, subject, 0, 0, 0, 0);
ride_list_item rideItem;
rideItem.type = subject >> 8;
rideItem.entry_index = subject & 0xFF;
window_new_ride_focus(rideItem);
break;
}
@@ -323,13 +322,12 @@ void news_item_open_subject(int type, int subject) {
RCT2_CALLPROC_X(0x006E1172, (subject & 0xFFFF), 0, subject, 0, 0, 0, 0);
}
RCT2_GLOBAL(0x009DE518, uint32) |= (1 << 6);
// Open scenery window
RCT2_CALLPROC_EBPSAFE(0x006E0FEF);
window_scenery_open();
}
}
// Switch to new scenery tab
RCT2_CALLPROC_X(0x006E1172, (subject & 0xFFFF), 0, subject, 0, 0, 0, 0);
break;
case NEWS_ITEM_PEEPS:
// Open guest list to right tab

View File

@@ -21,10 +21,7 @@
#ifndef _NEWS_ITEM_H_
#define _NEWS_ITEM_H_
#include "rct2.h"
#include "map.h"
#include "string_ids.h"
#include "../common.h"
enum {
NEWS_ITEM_NULL,

262
src/management/research.c Normal file
View File

@@ -0,0 +1,262 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#include "../addresses.h"
#include "../interface/window.h"
#include "../localisation/date.h"
#include "../world/scenery.h"
#include "news_item.h"
#include "research.h"
const int _researchRate[] = { 0, 160, 250, 400 };
// 0x01358844[500]
extern rct_research_item *gResearchItems = (rct_research_item*)RCT2_RESEARCH_ITEMS;
// 0x00EE787C
uint8 gResearchUncompletedCategories;
/**
*
* rct2: 0x006671AD, part of 0x00667132
*/
void research_reset_items()
{
gResearchItems[0].entryIndex = RESEARCHED_ITEMS_SEPERATOR;
gResearchItems[1].entryIndex = RESEARCHED_ITEMS_END;
gResearchItems[2].entryIndex = -3;
}
/**
*
* rct2: 0x00684BAE
*/
void research_update_uncompleted_types()
{
int uncompletedResearchTypes = 0;
rct_research_item *researchItem = gResearchItems;
while (researchItem->entryIndex != -1)
researchItem++;
researchItem++;
for (; researchItem->entryIndex != -2; researchItem++)
uncompletedResearchTypes |= (1 << researchItem->category);
gResearchUncompletedCategories = uncompletedResearchTypes;
}
/**
*
* rct2: 0x00684D2A
*/
static void research_calculate_expected_date()
{
int progress = RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS, uint16);
int progressStage = RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS_STAGE, uint8);
int researchLevel = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_RESEARCH_LEVEL, uint8);
int currentDay = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_TICKS, uint16);
int currentMonth = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint16);
int expectedDay, expectedMonth, dayQuotient, dayRemainder, progressRemaining, daysRemaining;
if (progressStage == RESEARCH_STAGE_INITIAL_RESEARCH || researchLevel == RESEARCH_FUNDING_NONE) {
RCT2_GLOBAL(RCT2_ADDRESS_NEXT_RESEARCH_EXPECTED_DAY, uint8) = 255;
} else {
progressRemaining = progressStage == RESEARCH_STAGE_COMPLETING_DESIGN ? 0x10000 : 0x20000;
progressRemaining -= progress;
daysRemaining = (progressRemaining / _researchRate[researchLevel]) * 128;
expectedDay = currentDay + (daysRemaining & 0xFFFF);
dayQuotient = expectedDay / 0x10000;
dayRemainder = expectedDay % 0x10000;
expectedMonth = date_get_month(currentMonth + dayQuotient + (daysRemaining >> 16));
expectedDay = (dayRemainder * days_in_month[expectedMonth]) >> 16;
RCT2_GLOBAL(RCT2_ADDRESS_NEXT_RESEARCH_EXPECTED_DAY, uint8) = expectedDay;
RCT2_GLOBAL(RCT2_ADDRESS_NEXT_RESEARCH_EXPECTED_MONTH, uint8) = expectedMonth;
}
}
static void research_invalidate_related_windows()
{
window_invalidate_by_id(WC_CONSTRUCT_RIDE, 0);
window_invalidate_by_id(WC_RESEARCH, 0);
}
/**
*
* rct2: 0x00684BE5
*/
static void research_next_design()
{
rct_research_item *firstUnresearchedItem, *researchItem, tmp;
int ignoreActiveResearchTypes;
int activeResearchTypes = RCT2_GLOBAL(RCT2_ADDRESS_ACTIVE_RESEARCH_TYPES, uint16);
// Skip already researched items
firstUnresearchedItem = gResearchItems;
while (firstUnresearchedItem->entryIndex != RESEARCHED_ITEMS_SEPERATOR)
firstUnresearchedItem++;
ignoreActiveResearchTypes = 0;
researchItem = firstUnresearchedItem;
for (;;) {
researchItem++;
if (researchItem->entryIndex == RESEARCHED_ITEMS_END) {
if (!ignoreActiveResearchTypes) {
ignoreActiveResearchTypes = 1;
researchItem = firstUnresearchedItem;
continue;
} else {
RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS, uint16) = 0;
RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS_STAGE, uint8) = RESEARCH_STAGE_INITIAL_RESEARCH;
research_invalidate_related_windows();
return;
}
} else if (ignoreActiveResearchTypes || (activeResearchTypes & (1 << researchItem->category))) {
break;
}
}
RCT2_GLOBAL(RCT2_ADDRESS_NEXT_RESEARCH_ITEM, uint32) = researchItem->entryIndex;
RCT2_GLOBAL(RCT2_ADDRESS_NEXT_RESEARCH_CATEGORY, uint8) = researchItem->category;
RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS, uint16) = 0;
RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS_STAGE, uint8) = RESEARCH_STAGE_DESIGNING;
// Bubble research item up until it is above the researched items seperator
do {
tmp = *researchItem;
*researchItem = *(researchItem - 1);
*(researchItem - 1) = tmp;
researchItem--;
} while ((researchItem + 1)->entryIndex != RESEARCHED_ITEMS_SEPERATOR);
research_invalidate_related_windows();
}
/**
*
* rct2: 0x006848D4
*/
void research_finish_item(sint32 entryIndex)
{
int i, ebx, ecx, rideEntryIndex, subSceneryEntryIndex;
rct_ride_type *rideEntry, *rideEntry2;
rct_scenery_set_entry *scenerySetEntry;
RCT2_GLOBAL(RCT2_ADDRESS_LAST_RESEARCHED_ITEM_SUBJECT, sint32) = entryIndex;
research_invalidate_related_windows();
if (entryIndex >= 0x10000) {
// Ride
ecx = (entryIndex >> 8) & 0xFF;
rideEntryIndex = entryIndex & 0xFF;
rideEntry = GET_RIDE_ENTRY(rideEntryIndex);
RCT2_ADDRESS(0x01357404, uint32)[ecx >> 5] |= (1 << (ecx & 0x1F));
RCT2_ADDRESS(0x01357444, uint32)[ecx] = RCT2_ADDRESS(0x0097C468, uint32)[ecx];
RCT2_ADDRESS(0x01357644, uint32)[ecx] = RCT2_ADDRESS(0x0097C5D4, uint32)[ecx];
if (RCT2_GLOBAL(0x0097D4F2 + (ecx * 8), uint16) & 8) {
ebx = RCT2_GLOBAL(0x0097D4F5 + (ecx * 8), uint8);
RCT2_ADDRESS(0x01357444, uint32)[ebx] = RCT2_ADDRESS(0x0097C468, uint32)[ebx];
RCT2_ADDRESS(0x01357644, uint32)[ebx] = RCT2_ADDRESS(0x0097C5D4, uint32)[ebx];
}
RCT2_ADDRESS(0x001357424, uint32)[rideEntryIndex >> 5] |= 1 << (rideEntryIndex & 0x1F);
if (!(rideEntry->var_008 & 0x2000)) {
for (i = 0; i < 128; i++) {
rideEntry2 = GET_RIDE_ENTRY(i);
if (rideEntry2 == (rct_ride_type*)-1)
continue;
if (rideEntry2->var_008 & 0x2000)
continue;
if (rideEntry2->var_00C == ecx || rideEntry2->var_00D == ecx || rideEntry2->var_00E == ecx)
RCT2_ADDRESS(0x001357424, uint32)[i >> 5] |= 1 << (i & 0x1F);
}
}
// I don't think 0x009AC06C is ever not 0, so probably redundant
if (RCT2_GLOBAL(0x009AC06C, uint8) == 0) {
RCT2_GLOBAL(0x013CE952, rct_string_id) = rideEntry->var_008 & 0x1000 ?
rideEntry->name : ecx + 2;
news_item_add_to_queue(NEWS_ITEM_RESEARCH, 2249, entryIndex);
}
research_invalidate_related_windows();
} else {
// Scenery
scenerySetEntry = g_scenerySetEntries[entryIndex & 0xFFFF];
for (i = 0; i < scenerySetEntry->entry_count; i++) {
subSceneryEntryIndex = scenerySetEntry->scenery_entries[i];
RCT2_ADDRESS(0x01357BD0, sint32)[subSceneryEntryIndex >> 5] |= 1 << (subSceneryEntryIndex & 0x1F);
}
// I don't think 0x009AC06C is ever not 0, so probably redundant
if (RCT2_GLOBAL(0x009AC06C, uint8) == 0) {
RCT2_GLOBAL(0x013CE952, rct_string_id) = scenerySetEntry->name;
news_item_add_to_queue(NEWS_ITEM_RESEARCH, 2250, entryIndex);
}
research_invalidate_related_windows();
init_scenery();
}
}
/**
*
* rct2: 0x00684C7A
*/
void research_update()
{
int editorScreenFlags, researchLevel, currentResearchProgress;
editorScreenFlags = SCREEN_FLAGS_SCENARIO_EDITOR | SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER;
if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & editorScreenFlags)
return;
if (RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, uint32) % 32 != 0)
return;
researchLevel = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_RESEARCH_LEVEL, uint8);
currentResearchProgress = RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS, uint16);
currentResearchProgress += _researchRate[researchLevel];
if (currentResearchProgress <= 0xFFFF) {
RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS, uint16) = currentResearchProgress;
} else {
switch (RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS_STAGE, uint8)) {
case RESEARCH_STAGE_INITIAL_RESEARCH:
research_next_design();
research_calculate_expected_date();
break;
case RESEARCH_STAGE_DESIGNING:
RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS, uint16) = 0;
RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS_STAGE, uint8) = RESEARCH_STAGE_COMPLETING_DESIGN;
research_calculate_expected_date();
research_invalidate_related_windows();
break;
case RESEARCH_STAGE_COMPLETING_DESIGN:
research_finish_item(RCT2_GLOBAL(RCT2_ADDRESS_NEXT_RESEARCH_ITEM, sint32));
RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS, uint16) = 0;
RCT2_GLOBAL(RCT2_ADDRESS_RESEARH_PROGRESS_STAGE, uint8) = 0;
research_calculate_expected_date();
research_update_uncompleted_types();
research_invalidate_related_windows();
break;
}
}
}

56
src/management/research.h Normal file
View File

@@ -0,0 +1,56 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#ifndef _RESEARCH_H_
#define _RESEARCH_H_
#include "../common.h"
typedef struct {
// Bit 16 (0: scenery entry, 1: ride entry)
sint32 entryIndex;
uint8 category;
} rct_research_item;
#define RESEARCHED_ITEMS_SEPERATOR -1
#define RESEARCHED_ITEMS_END -2
enum {
RESEARCH_FUNDING_NONE,
RESEARCH_FUNDING_MINIMUM,
RESEARCH_FUNDING_NORMAL,
RESEARCH_FUNDING_MAXIMUM
};
enum {
RESEARCH_STAGE_INITIAL_RESEARCH,
RESEARCH_STAGE_DESIGNING,
RESEARCH_STAGE_COMPLETING_DESIGN,
RESEARCH_STAGE_UNKNOWN
};
extern rct_research_item *gResearchItems;
extern uint8 gResearchUncompletedCategories;
void research_reset_items();
void research_update_uncompleted_types();
void research_update();
#endif

View File

@@ -20,25 +20,276 @@
#include <memory.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "addresses.h"
#include "localisation/localisation.h"
#include "object.h"
#include "sawyercoding.h"
#include "platform/osinterface.h"
#include "util/sawyercoding.h"
int object_entry_compare(rct_object_entry *a, rct_object_entry *b);
int object_calculate_checksum(rct_object_entry *entry, char *data, int dataLength);
int object_paint(int type, int eax, int ebx, int ecx, int edx, int esi, int edi, int ebp);
rct_object_entry *object_get_next(rct_object_entry *entry);
/**
*
* rct2: 0x006A985D
*/
int object_load(int groupIndex, rct_object_entry *entry)
{
RCT2_CALLPROC_X(0x006A985D, 0, 0, groupIndex, 0, 0, 0, (int)entry);
#ifdef _MSC_VER
__asm jb fail
#else
__asm__ goto ( "jb %l0" : : : : fail );
#endif
return 1;
fail:
RCT2_GLOBAL(0xF42B64, uint32) = groupIndex;
//part of 6a9866
rct_object_entry *installedObject = RCT2_GLOBAL(RCT2_ADDRESS_INSTALLED_OBJECT_LIST, rct_object_entry*);
if (!(RCT2_GLOBAL(0xF42B6C, uint32))){
RCT2_GLOBAL(0xF42BD9, uint8) = 0;
return 1;
}
for (int i = 0; i < RCT2_GLOBAL(0x00F42B6C, sint32); i++) {
if (object_entry_compare(installedObject, entry)){
char path[260];
char *objectPath = (char*)installedObject + 16;
subsitute_path(path, RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char), objectPath);
rct_object_entry openedEntry;
FILE *file = fopen(path, "rb");
if (file != NULL) {
fread(&openedEntry, sizeof(rct_object_entry), 1, file);
if (object_entry_compare(&openedEntry, entry)) {
// Get chunk size
char *pos = (char*)installedObject + 16;
do {
pos++;
} while (*(pos - 1) != 0);
// Read chunk
int chunkSize = *((uint32*)pos);
char *chunk;
if (chunkSize == 0xFFFFFFFF) {
chunk = rct2_malloc(0x600000);
chunkSize = sawyercoding_read_chunk(file, chunk);
chunk = rct2_realloc(chunk, chunkSize);
}
else {
chunk = rct2_malloc(chunkSize);
chunkSize = sawyercoding_read_chunk(file, chunk);
}
fclose(file);
// Calculate and check checksum
if (object_calculate_checksum(&openedEntry, chunk, chunkSize) != openedEntry.checksum) {
RCT2_GLOBAL(0x00F42BD9, uint8) = 2;
rct2_free(chunk);
return 0;
}
if (object_paint(openedEntry.flags & 0x0F, 2, 0, openedEntry.flags & 0x0F, 0, (int)chunk, 0, 0)) {
RCT2_GLOBAL(0x00F42BD9, uint8) = 3;
rct2_free(chunk);
return 0;
}
int yyy = RCT2_GLOBAL(0x009ADAF0, uint32);
if (yyy >= 0x4726E){
RCT2_GLOBAL(0x00F42BD9, uint8) = 4;
rct2_free(chunk);
return 0;
}
//B84 is openedEntry
int ebp = openedEntry.flags & 0x0F;
int esi = RCT2_ADDRESS(0x98D97C, uint32)[ebp * 2];
int ecx = groupIndex;
if (ecx == -1){
for (int ecx = 0; ((sint32*)esi)[ecx] != -1; ecx++){
if ((ecx + 1) >= object_entry_group_counts[ebp]){
RCT2_GLOBAL(0x00F42BD9, uint8) = 5;
rct2_free(chunk);
return 0;
}
}
}
((char**)esi)[ecx] = chunk;
int* edx = (int*)( ecx * 20 + RCT2_ADDRESS(0x98D980, uint32)[ebp * 2]);
memcpy(edx, (int*)&openedEntry, 20);
if (RCT2_GLOBAL(0x9ADAFD, uint8) == 0)return 1;
object_paint(ecx, 0, ecx, ebp, 0, (int)chunk, 0, 0);
return 1;
}
fclose(file);
}
}
installedObject = object_get_next(installedObject);
}
//6a991f
// Installed Object can not be found.
return 0;
//return !(RCT2_CALLPROC_X(0x006A985D, 0, 0, groupIndex, 0, 0, 0, (int)entry) & 0x400);
}
/** rct2: 0x006a9f42
* ebx : file
* ebp : entry
*/
int sub_6A9F42(FILE *file, rct_object_entry* entry){
int eax = 0, entryGroupIndex = 0, type = 0, edx = 0, edi = 0, ebp = (int)entry, chunk = 0;
RCT2_CALLFUNC_X(0x6A9DA2, &eax, &entryGroupIndex, &type, &edx, &chunk, &edi, &ebp);
if (eax == 0) return 0;
object_paint(type, 1, entryGroupIndex, type, edx, chunk, edi, ebp);
rct_object_entry* installed_entry = (rct_object_entry*)(entryGroupIndex * 20 + RCT2_ADDRESS(0x98D980, uint32)[type * 2]);
uint8* dst_buffer = malloc(0x600000);
memcpy(dst_buffer, (void*)installed_entry, 16);
uint32 size_dst = 16;
sawyercoding_chunk_header chunkHeader;
// Encoding type (not used anymore)
RCT2_GLOBAL(0x9E3CBD, uint8) = object_entry_group_encoding[type];
chunkHeader.encoding = object_entry_group_encoding[type];
chunkHeader.length = *(uint32*)(((uint8*)installed_entry + 16));
size_dst += sawyercoding_write_chunk_buffer(dst_buffer+16, (uint8*)chunk, chunkHeader);
fwrite(dst_buffer, 1, size_dst, file);
free(dst_buffer);
}
/**
*
* rct2: 0x006AA2B7
*/
int object_load_packed(FILE *file)
{
object_unload_all();
rct_object_entry* entry = RCT2_ADDRESS(0xF42B84, rct_object_entry);
fread((void*)entry, 16, 1, file);
uint8* chunk = rct2_malloc(0x600000);
uint32 chunkSize = sawyercoding_read_chunk(file, chunk);
chunk = rct2_realloc(chunk, chunkSize);
if (chunk == NULL){
return 0;
}
if (object_calculate_checksum(entry, chunk, chunkSize) != entry->checksum){
rct2_free(chunk);
return 0;
}
if (object_paint(entry->flags & 0x0F, 2, 0, entry->flags & 0x0F, 0, (int)chunk, 0, 0)) {
rct2_free(chunk);
return 0;
}
int yyy = RCT2_GLOBAL(0x009ADAF0, uint32);
if (yyy >= 0x4726E){
rct2_free(chunk);
return 0;
}
int type = entry->flags & 0x0F;
// ecx
int entryGroupIndex = 0;
for (; entryGroupIndex < object_entry_group_counts[type]; entryGroupIndex++){
if (RCT2_ADDRESS(0x98D97C, uint32*)[type * 2][entryGroupIndex] == -1){
break;
}
}
if (entryGroupIndex == object_entry_group_counts[type]){
rct2_free(chunk);
return 0;
}
RCT2_ADDRESS(0x98D97C, uint8**)[type * 2][entryGroupIndex] = chunk;
int* edx = (int*)(entryGroupIndex * 20 + RCT2_ADDRESS(0x98D980, uint32)[type * 2]);
memcpy(edx, (int*)entry, 16);
*(edx + 4) = chunkSize;
//esi
rct_object_entry *installedObject = RCT2_GLOBAL(RCT2_ADDRESS_INSTALLED_OBJECT_LIST, rct_object_entry*);
if (RCT2_GLOBAL(0xF42B6C, uint32)){
for (uint32 i = 0; i < RCT2_GLOBAL(0xF42B6C, uint32); ++i){
if (object_entry_compare(entry, installedObject)){
object_unload_all();
return 0;
}
installedObject = object_get_next(installedObject);
}
}
//Installing new data
//format_string(0x141ED68, 3163, 0);
//Code for updating progress bar removed.
char path[260];
char objectPath[13] = { 0 };
for (int i = 0; i < 8; ++i){
if (entry->name[i] != ' ')
objectPath[i] = toupper(entry->name[i]);
else
objectPath[i] = '\0';
}
subsitute_path(path, RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char), objectPath);
char* last_char = path + strlen(path);
strcat(path, ".DAT");
//
for (; osinterface_file_exists(path);){
for (char* curr_char = last_char - 1;; --curr_char){
if (*curr_char == '\\'){
subsitute_path(path, RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char), "00000000.DAT");
char* last_char = path + strlen(path);
break;
}
if (*curr_char < '0') *curr_char = '0';
else if (*curr_char == '9') *curr_char = 'A';
else if (*curr_char == 'Z') *curr_char = '0';
else (*curr_char)++;
if (*curr_char != '0') break;
}
}
// Removed progress bar code
// The following section cannot be finished until 6A9F42 is finished
// Run the game once with vanila rct2 to not reach this part of code.
RCT2_ERROR("Function not finished. Please run this save once with vanila rct2.");
FILE* obj_file = fopen(path, "wb");
if (obj_file){
// Removed progress bar code
sub_6A9F42(obj_file, entry);
fclose(obj_file);
// Removed progress bar code
object_unload_all();
// Removed progress bar code
return 1;
}
else{
object_unload_all();
return 0;
}
//create file
//6aa48C
int eax = 1;//, ebx = 0, ecx = 0, edx = 0, esi = 0, edi = 0, ebp = 0;
//RCT2_CALLFUNC_X(0x006AA2B7, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp);
return eax;
}
/**
@@ -50,7 +301,7 @@ void object_unload(int groupIndex, rct_object_entry_extended *entry)
RCT2_CALLPROC_X(0x006A9CAF, 0, groupIndex, 0, 0, 0, 0, (int)entry);
}
static int object_entry_compare(rct_object_entry *a, rct_object_entry *b)
int object_entry_compare(rct_object_entry *a, rct_object_entry *b)
{
if (a->flags & 0xF0) {
if ((a->flags & 0x0F) != (b->flags & 0x0F))
@@ -73,7 +324,7 @@ static int object_entry_compare(rct_object_entry *a, rct_object_entry *b)
return 1;
}
static int object_calculate_checksum(rct_object_entry *entry, char *data, int dataLength)
int object_calculate_checksum(rct_object_entry *entry, char *data, int dataLength)
{
int i;
char *eee = (char*)entry;
@@ -122,15 +373,7 @@ int object_paint(int type, int eax, int ebx, int ecx, int edx, int esi, int edi,
if (type == 10){
if (eax == 0) return object_scenario_load_custom_text((char*)esi);
}
RCT2_CALLPROC_X(RCT2_ADDRESS(0x0098D9D4, uint32)[type], eax, ebx, ecx, edx, esi, edi, ebp);
#ifdef _MSC_VER
__asm jb success
#else
__asm__ goto ( "jb %l0" : : : : success );
#endif
return 0;
success:
return 1;
return RCT2_CALLPROC_X(RCT2_ADDRESS(0x0098D9D4, uint32)[type], eax, ebx, ecx, edx, esi, edi, ebp) & 0x100;
}
/**
@@ -231,7 +474,7 @@ rct_object_entry *object_get_next(rct_object_entry *entry)
{
char *pos = (char*)entry;
// Skip
// Skip sizeof(rct_object_entry)
pos += 16;
// Skip filename
@@ -247,7 +490,7 @@ rct_object_entry *object_get_next(rct_object_entry *entry)
pos++;
} while (*(pos - 1) != 0);
// Skip
// Skip size of chunk
pos += 4;
// Skip
@@ -260,4 +503,4 @@ rct_object_entry *object_get_next(rct_object_entry *entry)
pos += 4;
return (rct_object_entry*)pos;
}
}

View File

@@ -45,9 +45,12 @@ typedef struct {
uint32 extended;
} rct_object_entry_extended;
extern int object_entry_group_counts[];
extern int object_entry_group_encoding[];
void object_list_load();
void object_read_and_load_entries(FILE *file);
int object_load_packed();
int object_read_and_load_entries(FILE *file);
int object_load_packed(FILE *file);
void object_unload_all();
int object_load(int groupIndex, rct_object_entry *entry);
@@ -56,5 +59,7 @@ int object_get_scenario_text(rct_object_entry *entry);
void object_free_scenario_text();
int object_get_length(rct_object_entry *entry);
rct_object_entry *object_get_next(rct_object_entry *entry);
int object_calculate_checksum(rct_object_entry *entry, char *data, int dataLength);
int object_paint(int type, int eax, int ebx, int ecx, int edx, int esi, int edi, int ebp);
#endif

View File

@@ -21,7 +21,7 @@
#include <windows.h>
#include "addresses.h"
#include "object.h"
#include "sawyercoding.h"
#include "util/sawyercoding.h"
#define OBJECT_ENTRY_GROUP_COUNT 11
#define OBJECT_ENTRY_COUNT 721
@@ -34,6 +34,7 @@ typedef struct {
uint32 var_10;
} rct_plugin_header;
// 98DA00
int object_entry_group_counts[] = {
128, // rides
252, // small scenery
@@ -48,6 +49,21 @@ int object_entry_group_counts[] = {
1 // scenario text
};
// 98DA2C
int object_entry_group_encoding[] = {
CHUNK_ENCODING_RLE,
CHUNK_ENCODING_RLE,
CHUNK_ENCODING_RLE,
CHUNK_ENCODING_RLE,
CHUNK_ENCODING_RLE,
CHUNK_ENCODING_RLE,
CHUNK_ENCODING_RLE,
CHUNK_ENCODING_RLE,
CHUNK_ENCODING_RLE,
CHUNK_ENCODING_RLE,
CHUNK_ENCODING_ROTATE
};
struct { void **data; rct_object_entry_extended *entries; } object_entry_groups[] = {
(void**)(0x009ACFA4 ), (rct_object_entry_extended*)(0x00F3F03C ), // rides
(void**)(0x009ACFA4 + (128 * 4)), (rct_object_entry_extended*)(0x00F3F03C + (128 * 20)), // small scenery
@@ -157,7 +173,7 @@ static int check_object_entry(rct_object_entry *entry)
*
* rct2: 0x006AA0C6
*/
void object_read_and_load_entries(FILE *file)
int object_read_and_load_entries(FILE *file)
{
object_unload_all();
@@ -184,26 +200,21 @@ void object_read_and_load_entries(FILE *file)
// Load the obect
if (!object_load(entryGroupIndex, &entries[i])) {
// Failed to load the object
free(entries);
//Destroy progress bar
memcpy((char*)0x13CE952, entries[i].name, 8);
free(entries);
object_unload_all();
return;
RCT2_GLOBAL(0x14241BC, uint32) = 0;
return 0;
}
}
free(entries);
return 1;
}
/**
*
* rct2: 0x006AA2B7
*/
int object_load_packed()
{
int eax, ebx, ecx, edx, esi, edi, ebp;
RCT2_CALLFUNC_X(0x006AA2B7, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp);
return eax;
}
/**
*
@@ -217,4 +228,4 @@ void object_unload_all()
for (j = 0; j < object_entry_group_counts[i]; j++)
if (object_entry_groups[i].data[j] != (void**)0xFFFFFFFF)
object_unload(j, &object_entry_groups[i].entries[j]);
}
}

View File

@@ -19,17 +19,49 @@
*****************************************************************************/
#include <windows.h>
#include "addresses.h"
#include "audio.h"
#include "news_item.h"
#include "../addresses.h"
#include "../audio/audio.h"
#include "../audio/mixer.h"
#include "../interface/window.h"
#include "../localisation/localisation.h"
#include "../management/news_item.h"
#include "../ride/ride.h"
#include "../sprites.h"
#include "../world/sprite.h"
#include "peep.h"
#include "rct2.h"
#include "ride.h"
#include "sprite.h"
#include "window.h"
#include "staff.h"
static void peep_update(rct_peep *peep);
const char *gPeepEasterEggNames[] = {
"MICHAEL SCHUMACHER",
"JACQUES VILLENEUVE",
"DAMON HILL",
"MR BEAN",
"CHRIS SAWYER",
"KATIE BRAYSHAW",
"MELANIE WARN",
"SIMON FOSTER",
"JOHN WARDLEY",
"LISA STIRLING",
"DONALD MACRAE",
"KATHERINE MCGOWAN",
"FRANCES MCGOWAN",
"CORINA MASSOURA",
"CAROL YOUNG",
"MIA SHERIDAN",
"KATIE RODGER",
"EMMA GARRELL",
"JOANNE BARTON",
"FELICITY ANDERSON",
"KATIE SMITH",
"EILIDH BELL",
"NANCY STILLWAGON",
"ANDY HINE",
"ELISSA WHITE",
"DAVID ELLIS"
};
int peep_get_staff_count()
{
uint16 spriteIndex;
@@ -177,31 +209,31 @@ void peep_problem_warnings_update()
break;
case PEEP_THOUGHT_TYPE_HUNGRY: // 0x14
if (peep->var_C5 == -1){
if (peep->guest_heading_to_ride_id == -1){
hunger_counter++;
break;
}
ride = &g_ride_list[peep->var_C5];
ride = &g_ride_list[peep->guest_heading_to_ride_id];
if (!(RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + ride->type * 8, uint32) & 0x80000))
hunger_counter++;
break;
case PEEP_THOUGHT_TYPE_THIRSTY:
if (peep->var_C5 == -1){
if (peep->guest_heading_to_ride_id == -1){
thirst_counter++;
break;
}
ride = &g_ride_list[peep->var_C5];
ride = &g_ride_list[peep->guest_heading_to_ride_id];
if (!(RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + ride->type * 8, uint32) & 0x1000000))
thirst_counter++;
break;
case PEEP_THOUGHT_TYPE_BATHROOM:
if (peep->var_C5 == -1){
if (peep->guest_heading_to_ride_id == -1){
bathroom_counter++;
break;
}
ride = &g_ride_list[peep->var_C5];
ride = &g_ride_list[peep->guest_heading_to_ride_id];
if (!(RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + ride->type * 8, uint32) & 0x2000000))
bathroom_counter++;
break;
@@ -307,7 +339,7 @@ void peep_update_crowd_noise()
visiblePeeps = 0;
FOR_ALL_GUESTS(spriteIndex, peep) {
if (peep->var_16 == 0x8000)
if (peep->var_16 == (sint16)0x8000)
continue;
if (viewport->view_x > peep->var_1A)
continue;
@@ -330,7 +362,12 @@ void peep_update_crowd_noise()
if (visiblePeeps < 0) {
// Mute crowd noise
if (RCT2_GLOBAL(0x009AF5FC, uint32) != 1) {
#ifdef USE_MIXER
Mixer_Stop_Channel(gMusicChannels[2]);
gMusicChannels[2] = 0;
#else
sound_channel_stop(2); //RCT2_CALLPROC_1(0x00401A05, int, 2);
#endif
RCT2_GLOBAL(0x009AF5FC, uint32) = 1;
}
} else {
@@ -345,14 +382,26 @@ void peep_update_crowd_noise()
// Check if crowd noise is already playing
if (RCT2_GLOBAL(0x009AF5FC, uint32) == 1) {
// Load and play crowd noise
#ifdef USE_MIXER
gMusicChannels[2] = Mixer_Play_Music(PATH_ID_CSS2);
if (gMusicChannels[2]) {
Mixer_Channel_Volume(gMusicChannels[2], DStoMixerVolume(volume));
RCT2_GLOBAL(0x009AF5FC, uint32) = volume;
}
#else
if (sound_channel_load_file2(2, (char*)get_file_path(PATH_ID_CSS2), 0)) {
sound_channel_play(2, 1, volume, 0, 0);
RCT2_GLOBAL(0x009AF5FC, uint32) = volume;
}
#endif
} else {
// Alter crowd noise volume
if (RCT2_GLOBAL(0x009AF5FC, uint32) != volume) {
#ifdef USE_MIXER
Mixer_Channel_Volume(gMusicChannels[2], DStoMixerVolume(volume));
#else
sound_channel_set_volume(2, volume);//RCT2_CALLPROC_2(0x00401AD3, int, int, 2, volume);
#endif
RCT2_GLOBAL(0x009AF5FC, uint32) = volume;
}
}
@@ -393,7 +442,7 @@ void peep_applause()
}
// Play applause noise
sound_play_panned(SOUND_APPLAUSE, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16) / 2);
sound_play_panned(SOUND_APPLAUSE, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16) / 2, 0, 0, 0);
}
/**
@@ -447,8 +496,8 @@ void get_arguments_from_action(rct_peep* peep, uint32 *argument_1, uint32* argum
break;
case PEEP_STATE_WALKING:
case 0x14:
if (peep->var_C5 != 0xFF){
ride = g_ride_list[peep->var_C5];
if (peep->guest_heading_to_ride_id != 0xFF){
ride = g_ride_list[peep->guest_heading_to_ride_id];
*argument_1 = STR_HEADING_FOR | (ride.name << 16);
*argument_2 = ride.name_arguments;
}
@@ -586,4 +635,137 @@ void get_arguments_from_thought(rct_peep_thought thought, uint32* argument_1, ui
*/
int peep_can_be_picked_up(rct_peep* peep){
return RCT2_ADDRESS(0x982004, uint8)[peep->state] & 1;
}
}
enum{
PEEP_FACE_OFFSET_ANGRY = 0,
PEEP_FACE_OFFSET_VERY_VERY_SICK,
PEEP_FACE_OFFSET_VERY_SICK,
PEEP_FACE_OFFSET_SICK,
PEEP_FACE_OFFSET_VERY_TIRED,
PEEP_FACE_OFFSET_TIRED,
PEEP_FACE_OFFSET_VERY_VERY_UNHAPPY,
PEEP_FACE_OFFSET_VERY_UNHAPPY,
PEEP_FACE_OFFSET_UNHAPPY,
PEEP_FACE_OFFSET_NORMAL,
PEEP_FACE_OFFSET_HAPPY,
PEEP_FACE_OFFSET_VERY_HAPPY,
PEEP_FACE_OFFSET_VERY_VERY_HAPPY,
};
const int face_sprite_small[] = {
SPR_PEEP_SMALL_FACE_ANGRY,
SPR_PEEP_SMALL_FACE_VERY_VERY_SICK,
SPR_PEEP_SMALL_FACE_VERY_SICK,
SPR_PEEP_SMALL_FACE_SICK,
SPR_PEEP_SMALL_FACE_VERY_TIRED,
SPR_PEEP_SMALL_FACE_TIRED,
SPR_PEEP_SMALL_FACE_VERY_VERY_UNHAPPY,
SPR_PEEP_SMALL_FACE_VERY_UNHAPPY,
SPR_PEEP_SMALL_FACE_UNHAPPY,
SPR_PEEP_SMALL_FACE_NORMAL,
SPR_PEEP_SMALL_FACE_HAPPY,
SPR_PEEP_SMALL_FACE_VERY_HAPPY,
SPR_PEEP_SMALL_FACE_VERY_VERY_HAPPY,
};
const int face_sprite_large[] = {
SPR_PEEP_LARGE_FACE_ANGRY,
SPR_PEEP_LARGE_FACE_VERY_VERY_SICK,
SPR_PEEP_LARGE_FACE_VERY_SICK,
SPR_PEEP_LARGE_FACE_SICK,
SPR_PEEP_LARGE_FACE_VERY_TIRED,
SPR_PEEP_LARGE_FACE_TIRED,
SPR_PEEP_LARGE_FACE_VERY_VERY_UNHAPPY,
SPR_PEEP_LARGE_FACE_VERY_UNHAPPY,
SPR_PEEP_LARGE_FACE_UNHAPPY,
SPR_PEEP_LARGE_FACE_NORMAL,
SPR_PEEP_LARGE_FACE_HAPPY,
SPR_PEEP_LARGE_FACE_VERY_HAPPY,
SPR_PEEP_LARGE_FACE_VERY_VERY_HAPPY,
};
int get_face_sprite_offset(rct_peep *peep){
// ANGRY
if (peep->var_F3) return PEEP_FACE_OFFSET_ANGRY;
// VERY_VERY_SICK
if (peep->nausea > 200) return PEEP_FACE_OFFSET_VERY_VERY_SICK;
// VERY_SICK
if (peep->nausea > 170) return PEEP_FACE_OFFSET_VERY_SICK;
// SICK
if (peep->nausea > 140) return PEEP_FACE_OFFSET_SICK;
// VERY_TIRED
if (peep->energy < 46) return PEEP_FACE_OFFSET_VERY_TIRED;
// TIRED
if (peep->energy < 70) return PEEP_FACE_OFFSET_TIRED;
int offset = PEEP_FACE_OFFSET_VERY_VERY_UNHAPPY;
//There are 7 different happiness based faces
for (int i = 37; peep->happiness >= i; i += 37)
{
offset++;
}
return offset;
}
/**
* Function split into large and small sprite
* rct2: 0x00698721
*/
int get_peep_face_sprite_small(rct_peep *peep){
return face_sprite_small[get_face_sprite_offset(peep)];
}
/**
* Function split into large and small sprite
* rct2: 0x00698721
*/
int get_peep_face_sprite_large(rct_peep *peep){
return face_sprite_large[get_face_sprite_offset(peep)];
}
/**
*
* rct2: 0x0069A5A0
* tests if a peep's name matches a cheat code, normally returns using a register flag
* @param index (eax)
* @param ride (esi)
*/
int peep_check_easteregg_name(int index, rct_peep *peep)
{
char buffer[256];
format_string(buffer, peep->name_string_idx, &peep->id);
return _stricmp(buffer, gPeepEasterEggNames[index]) == 0;
}
int peep_get_easteregg_name_id(rct_peep *peep)
{
char buffer[256];
int i;
format_string(buffer, peep->name_string_idx, &peep->id);
for (i = 0; i < countof(gPeepEasterEggNames); i++)
if (_stricmp(buffer, gPeepEasterEggNames[i]) == 0)
return i;
return -1;
}
int peep_is_mechanic(rct_peep *peep)
{
return (
peep->sprite_identifier == SPRITE_IDENTIFIER_PEEP &&
peep->type == PEEP_TYPE_STAFF &&
peep->staff_type == STAFF_TYPE_MECHANIC
);
}

View File

@@ -21,7 +21,7 @@
#ifndef _PEEP_H_
#define _PEEP_H_
#include "rct2.h"
#include "../common.h"
#define PEEP_MAX_THOUGHTS 5
@@ -296,10 +296,10 @@ enum PEEP_ITEM {
};
typedef struct {
uint8 type;
uint8 item;
uint8 var_2;
uint8 var_3;
uint8 type; //0
uint8 item; //1
uint8 var_2; //2
uint8 var_3; //3
} rct_peep_thought;
typedef struct {
@@ -332,7 +332,10 @@ typedef struct {
uint8 pad_2C;
uint8 sprite_type; // 0x2D
uint8 type; // 0x2E
uint8 staff_type; // 0x2F
union{ // 0x2F
uint8 staff_type;
uint8 no_of_rides;
};
uint8 tshirt_colour; // 0x30
uint8 trousers_colour; // 0x31
uint16 var_32;
@@ -342,7 +345,7 @@ typedef struct {
uint8 energy; // 0x38
uint8 energy_growth_rate; // 0x39
uint8 happiness; // 0x3A
uint8 happiness_growth_rate; // 0x3B
sint8 happiness_growth_rate; // 0x3B
uint8 nausea; // 0x3C
uint8 nausea_growth_rate; // 0x3D
uint8 hunger; // 0x3E
@@ -351,7 +354,7 @@ typedef struct {
uint8 pad_41[0x2];
uint8 intensity; // 0x43
uint8 nausea_tolerance; // 0x44
uint8 var_45;
uint8 var_45; // Some sort of flags?
money16 paid_on_drink; // 0x46
uint8 pad_48[0x10];
uint32 item_extra_flags; // 0x58
@@ -376,17 +379,23 @@ typedef struct {
uint8 pad_77;
uint8 var_78;
uint8 pad_79[0x03];
uint8 rides_been_on[32]; // 0x7C
uint8 rides_been_on[32]; // 0x7C
// 255 bit bitmap of every ride the peep has been on see
// window_peep_rides_update for how to use.
uint32 id; // 0x9C
money32 cash_in_pocket; // 0xA0
money32 cash_spent; // 0xA4
uint8 var_A8; // 0xA8
sint32 time_in_park; // 0xA9
sint32 time_in_park; // 0xA8
uint8 var_AC; // 0xAC
uint8 var_AD; // creation/hire time?
uint16 var_AE;
rct_peep_thought thoughts[PEEP_MAX_THOUGHTS]; // 0xB0
uint8 var_C4; // 0xC4
uint8 var_C5;
union // 0xC5
{
uint8 staff_id;
uint8 guest_heading_to_ride_id;
};
uint8 var_C6;
uint8 photo1_ride_ref; // 0xC7
uint32 flags; // 0xC8
@@ -404,8 +413,8 @@ typedef struct {
uint8 no_of_drinks; // 0xED
uint8 no_of_souvenirs; // 0xEE
uint8 pad_EF;
uint8 var_F0;
uint8 var_F1;
uint8 voucher_type; // 0xF0
uint8 voucher_arguments; // 0xF1 ride_id or string_offset_id
uint8 pad_F2;
uint8 var_F3;
uint8 pad_F4[0x02];
@@ -417,6 +426,35 @@ typedef struct {
uint32 item_standard_flags; // 0xFC
} rct_peep;
enum {
EASTEREGG_PEEP_NAME_MICHAEL_SCHUMACHER,
EASTEREGG_PEEP_NAME_JACQUES_VILLENEUVE,
EASTEREGG_PEEP_NAME_DAMON_HILL,
EASTEREGG_PEEP_NAME_MR_BEAN,
EASTEREGG_PEEP_NAME_CHRIS_SAWYER,
EASTEREGG_PEEP_NAME_KATIE_BRAYSHAW,
EASTEREGG_PEEP_NAME_MELANIE_WARN,
EASTEREGG_PEEP_NAME_SIMON_FOSTER,
EASTEREGG_PEEP_NAME_JOHN_WARDLEY,
EASTEREGG_PEEP_NAME_LISA_STIRLING,
EASTEREGG_PEEP_NAME_DONALD_MACRAE,
EASTEREGG_PEEP_NAME_KATHERINE_MCGOWAN,
EASTEREGG_PEEP_NAME_FRANCES_MCGOWAN,
EASTEREGG_PEEP_NAME_CORINA_MASSOURA,
EASTEREGG_PEEP_NAME_CAROL_YOUNG,
EASTEREGG_PEEP_NAME_MIA_SHERIDAN,
EASTEREGG_PEEP_NAME_KATIE_RODGER,
EASTEREGG_PEEP_NAME_EMMA_GARRELL,
EASTEREGG_PEEP_NAME_JOANNE_BARTON,
EASTEREGG_PEEP_NAME_FELICITY_ANDERSON,
EASTEREGG_PEEP_NAME_KATIE_SMITH,
EASTEREGG_PEEP_NAME_EILIDH_BELL,
EASTEREGG_PEEP_NAME_NANCY_STILLWAGON,
EASTEREGG_PEEP_NAME_ANDY_HINE,
EASTEREGG_PEEP_NAME_ELISSA_WHITE,
EASTEREGG_PEEP_NAME_DAVID_ELLIS
};
/** Helper macro until rides are stored in this module. */
#define GET_PEEP(sprite_index) &(g_sprite_list[sprite_index].peep)
@@ -445,5 +483,10 @@ void peep_applause();
rct_peep *peep_generate(int x, int y, int z);
void get_arguments_from_action(rct_peep* peep, uint32 *argument_1, uint32* argument_2);
void get_arguments_from_thought(rct_peep_thought thought, uint32* argument_1, uint32* argument_2);
int get_peep_face_sprite_small(rct_peep *peep);
int get_peep_face_sprite_large(rct_peep *peep);
int peep_check_easteregg_name(int index, rct_peep *peep);
int peep_get_easteregg_name_id(rct_peep *peep);
int peep_is_mechanic(rct_peep *peep);
#endif

View File

@@ -18,15 +18,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "staff.h"
#include "addresses.h"
#include "rct2.h"
#include "game.h"
#include "finance.h"
#include "../addresses.h"
#include "../game.h"
#include "../interface/viewport.h"
#include "../localisation/string_ids.h"
#include "../management/finance.h"
#include "../world/sprite.h"
#include "peep.h"
#include "sprite.h"
#include "string_ids.h"
#include "viewport.h"
#include "staff.h"
/**
*
@@ -99,7 +98,7 @@ void game_command_hire_new_staff_member(int* eax, int* ebx, int* ecx, int* edx,
int i;
for (i = 0; i < STAFF_MAX_COUNT; i++) {
if (!(RCT2_ADDRESS(0x013CA672, uint8)[i] & 1))
if (!(RCT2_ADDRESS(RCT2_ADDRESS_STAFF_MODE_ARRAY, uint8)[i] & 1))
break;
}
@@ -220,9 +219,9 @@ void game_command_hire_new_staff_member(int* eax, int* ebx, int* ecx, int* edx,
RCT2_CALLPROC_X(0x00699115, (uint32)ebp & 0xFFFFFF3F, 0, 0, 0, (int)newPeep, 0,
(*ebp << 25) | (*ebp >> 6));
newPeep->var_C5 = newStaffId;
newPeep->staff_id = newStaffId;
RCT2_ADDRESS(0x013CA672, uint8)[newStaffId] = 1;
RCT2_ADDRESS(RCT2_ADDRESS_STAFF_MODE_ARRAY, uint8)[newStaffId] = STAFF_MODE_WALK;
for (int edi = 0; edi < 0x80; edi++) {
int addr = 0x013B0E72 + (newStaffId << 9) + edi * 4;
@@ -267,4 +266,27 @@ uint16 hire_new_staff_member(uint8 staff_type)
return 0xFFFF;
return edi;
}
}
void sub_6C0C3F()
{
register rct_peep* peep;
for (register uint8 staff_type = 0; staff_type < STAFF_TYPE_COUNT; ++staff_type)
{
for (register uint8 i = 0; i < 128; ++i)
RCT2_ADDRESS(0x13B0E72 + (staff_type + STAFF_MAX_COUNT) * 512, uint32)[i] = 0;
for (register uint16 sprite_index = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_PEEP, uint16); sprite_index != SPRITE_INDEX_NULL; sprite_index = peep->next)
{
peep = GET_PEEP(sprite_index);
if (peep->type == PEEP_TYPE_STAFF && staff_type == peep->staff_type)
{
for (register uint8 i = 0; i < 128; ++i)
RCT2_ADDRESS(0x13B0E72 + (staff_type + STAFF_MAX_COUNT) * 512, uint32)[i] |= RCT2_ADDRESS(0x13B0E72 + (peep->staff_id * 512) * 512, uint32)[i];
}
}
}
}

View File

@@ -21,9 +21,16 @@
#ifndef _STAFF_H_
#define _STAFF_H_
#include "rct2.h"
#include "../common.h"
#define STAFF_MAX_COUNT 0xC8
#define STAFF_TYPE_COUNT 0x04
enum STAFF_MODE {
STAFF_MODE_NONE,
STAFF_MODE_WALK,
STAFF_MODE_PATROL = 3
};
enum STAFF_TYPE {
STAFF_TYPE_HANDYMAN,
@@ -37,5 +44,6 @@ void game_command_hire_new_staff_member(int* eax, int* ebx, int* ecx, int* edx,
void update_staff_colour(uint8 staff_type, uint16 color);
uint16 hire_new_staff_member(uint8 staff_type);
void sub_6C0C3F();
#endif

View File

@@ -25,15 +25,14 @@
#include <SDL_syswm.h>
#include <windows.h>
#include "addresses.h"
#include "config.h"
#include "gfx.h"
#include "input.h"
#include "../addresses.h"
#include "../config.h"
#include "../cursors.h"
#include "../drawing/drawing.h"
#include "../input.h"
#include "../interface/screenshot.h"
#include "../interface/window.h"
#include "osinterface.h"
#include "screenshot.h"
#include "window.h"
#include "rct2.h"
#include "cursors.h"
typedef void(*update_palette_func)(char*, int, int);
@@ -57,6 +56,9 @@ static SDL_Cursor* _cursors[NO_CURSORS];
static const int _fullscreen_modes[] = { 0, SDL_WINDOW_FULLSCREEN, SDL_WINDOW_FULLSCREEN_DESKTOP };
static unsigned int _lastGestureTimestamp;
static float _gestureRadius;
void osinterface_init()
{
osinterface_create_window();
@@ -67,6 +69,17 @@ void osinterface_init()
// RCT2_CALLPROC(0x00404584); // dinput_init()
}
int osinterface_scancode_to_rct_keycode(int sdl_key){
char keycode = (char)SDL_GetKeyFromScancode((SDL_Scancode)sdl_key);
// Until we reshufle the text files to use the new positions
// this will suffice to move the majority to the correct positions.
// Note any special buttons PgUp PgDwn are mapped wrong.
if (keycode >= 'a' && keycode <= 'z')keycode = toupper(keycode);
return keycode;
}
/**
* This is not quite the same as the below function as we don't want to
* derfererence the cursor before the function.
@@ -395,6 +408,25 @@ void osinterface_process_messages()
screenshot_check();
}
break;
case SDL_MULTIGESTURE:
if (e.mgesture.numFingers == 2) {
if (e.mgesture.timestamp > _lastGestureTimestamp + 1000)
_gestureRadius = 0;
_lastGestureTimestamp = e.mgesture.timestamp;
_gestureRadius += e.mgesture.dDist;
// Zoom gesture
const int tolerance = 128;
int gesturePixels = (int)(_gestureRadius * RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16));
if (gesturePixels > tolerance) {
_gestureRadius = 0;
handle_shortcut_command(SHORTCUT_ZOOM_VIEW_IN);
} else if (gesturePixels < -tolerance) {
_gestureRadius = 0;
handle_shortcut_command(SHORTCUT_ZOOM_VIEW_OUT);
}
}
break;
default:
break;
}
@@ -654,7 +686,7 @@ int osinterface_file_close(HANDLE handle)
*
* rct2: 0x00408060
*/
HANDLE osinterface_file_open(char* filename)
HANDLE osinterface_file_open(const char* filename)
{
return CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS | FILE_ATTRIBUTE_NORMAL, 0);
}
@@ -663,7 +695,7 @@ HANDLE osinterface_file_open(char* filename)
*
* rct2: 0x0040807D
*/
HANDLE osinterface_file_create(char* filename)
HANDLE osinterface_file_create(const char* filename)
{
return CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
}
@@ -672,7 +704,7 @@ HANDLE osinterface_file_create(char* filename)
*
* rct2: 0x00408099
*/
int osinterface_file_move(char* srcfilename, char* dstfilename)
int osinterface_file_move(const char* srcfilename, const char* dstfilename)
{
return (MoveFileA(srcfilename, dstfilename) != 0) - 1;
}
@@ -681,7 +713,7 @@ int osinterface_file_move(char* srcfilename, char* dstfilename)
*
* rct2: 0x004080AF
*/
int osinterface_file_delete(char* filename)
int osinterface_file_delete(const char* filename)
{
return (DeleteFileA(filename) != 0) - 1;
}

View File

@@ -21,7 +21,8 @@
#ifndef _SDL_INTERFACE_H_
#define _SDL_INTERFACE_H_
#include "rct2.h"
#include <windows.h>
#include "../common.h"
enum {
CURSOR_UP = 0,
@@ -74,7 +75,7 @@ typedef struct {
char path[260];
uint32 var_20C;
uint8 pad_210[0x100];
char addon[15][0x80];
char addon[16][0x80];
uint32 addons; //0xB10
} rct2_install_info;
@@ -96,6 +97,10 @@ void osinterface_progressbar_setpos(int pos);
void osinterface_set_cursor(char cursor);
HANDLE osinterface_file_open(const char* filename);
int osinterface_file_read(HANDLE handle, void* data, int size);
int osinterface_file_close(HANDLE handle);
int osinterface_open_common_file_dialog(int type, char *title, char *filename, char *filterPattern, char *filterName);
void osinterface_show_messagebox(char* message);
char* osinterface_open_directory_browser(char *title);
@@ -107,5 +112,6 @@ int osinterface_directory_exists(const char *path);
int osinterface_ensure_directory_exists(const char *path);
char osinterface_get_path_separator();
int osinterface_scancode_to_rct_keycode(int sdl_key);
#endif

36
src/platform/osx.c Normal file
View File

@@ -0,0 +1,36 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#ifdef __APPLE__
/**
* OSX entry point to OpenRCT2.
*/
// int main(char *argv[], int argc)
// {
// return 0;
// }
char platform_get_path_separator()
{
return '/';
}
#endif

30
src/platform/platform.h Normal file
View File

@@ -0,0 +1,30 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#ifndef _PLATFORM_H_
#define _PLATFORM_H_
// Platform specific definitions
char platform_get_path_separator();
int platform_directory_exists(const char *path);
int platform_ensure_directory_exists(const char *path);
#endif

20
src/platform/shared.c Normal file
View File

@@ -0,0 +1,20 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/

38
src/platform/unix.c Normal file
View File

@@ -0,0 +1,38 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#ifndef _WIN32
#ifndef __APPLE__
/**
* Unix, linux and fallback entry point to OpenRCT2.
*/
// int main(char *argv[], int argc)
// {
// return 0;
// }
char platform_get_path_separator()
{
return '/';
}
#endif
#endif

76
src/platform/windows.c Normal file
View File

@@ -0,0 +1,76 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#ifdef _WIN32
#include <windows.h>
/**
* Windows entry point to OpenRCT2 without a console window.
*/
// int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
// {
// return 0;
// }
/**
* Windows entry point to OpenRCT2 with a console window using a traditional C main function.
*/
// int main(char *argv[], int argc)
// {
// return 0;
// }
/**
* Entry point for when the DLL is loaded. This will be removed when OpenRCT2 can be built as a stand alone application.
*/
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
return TRUE;
}
/**
* The function that is called directly from the host application (rct2.exe)'s WinMain. This will be removed when OpenRCT2 can
* be built as a stand alone application.
*/
// __declspec(dllexport) int StartOpenRCT(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
// {
// }
char platform_get_path_separator()
{
return '\\';
}
int platform_directory_exists(const char *path)
{
DWORD dwAttrib = GetFileAttributes(path);
return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
}
int platform_ensure_directory_exists(const char *path)
{
if (platform_directory_exists(path))
return 1;
return CreateDirectory(path, NULL);
}
#endif

View File

@@ -29,28 +29,27 @@
#include <shlobj.h>
#include <SDL.h>
#include "addresses.h"
#include "audio.h"
#include "climate.h"
#include "audio/audio.h"
#include "audio/mixer.h"
#include "config.h"
#include "date.h"
#include "drawing/drawing.h"
#include "editor.h"
#include "game.h"
#include "gfx.h"
#include "interface/viewport.h"
#include "intro.h"
#include "language.h"
#include "map.h"
#include "news_item.h"
#include "localisation/date.h"
#include "localisation/localisation.h"
#include "management/news_item.h"
#include "object.h"
#include "osinterface.h"
#include "park.h"
#include "rct2.h"
#include "ride.h"
#include "platform/osinterface.h"
#include "ride/ride.h"
#include "ride/track.h"
#include "scenario.h"
#include "title.h"
#include "track.h"
#include "viewport.h"
#include "sprite.h"
#include "string_ids.h"
#include "world/map.h"
#include "world/park.h"
#include "world/climate.h"
#include "world/sprite.h"
typedef struct tm tm_t;
@@ -70,11 +69,6 @@ PCHAR *CommandLineToArgvA(PCHAR CmdLine, int *_argc);
static int _finished;
static jmp_buf _end_update_jump;
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
return TRUE;
}
__declspec(dllexport) int StartOpenRCT(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
print_launch_information();
@@ -90,6 +84,7 @@ __declspec(dllexport) int StartOpenRCT(HINSTANCE hInstance, HINSTANCE hPrevInsta
config_init();
language_open(gGeneral_config.language);
rct2_init();
Mixer_Init(NULL);
rct2_loop();
osinterface_free();
exit(0);
@@ -161,13 +156,16 @@ void rct2_init()
// RCT2_CALLPROC_EBPSAFE(0x00674B81); // pointless expansion pack crap
object_list_load();
scenario_load_list();
track_load_list(253);
ride_list_item item = { 253, 0 };
track_load_list(item);
gfx_load_g1();
//RCT2_CALLPROC_EBPSAFE(0x006C19AC); //Load character widths
gfx_load_character_widths();
osinterface_init();
RCT2_CALLPROC_EBPSAFE(0x006BA8E0); // init_audio();
audio_init1();//RCT2_CALLPROC_EBPSAFE(0x006BA8E0); // init_audio();
viewport_init_all();
news_item_init_queue();
get_local_time();
@@ -185,7 +183,7 @@ void rct2_init()
RCT2_CALLPROC_EBPSAFE(0x006DFEE4);
window_new_ride_init_vars();
window_guest_list_init_vars_b();
window_staff_init_vars();
window_staff_list_init_vars();
title_load();
@@ -683,4 +681,4 @@ PCHAR *CommandLineToArgvA(PCHAR CmdLine, int *_argc)
(*_argc) = argc;
return argv;
}
}

View File

@@ -84,13 +84,15 @@ typedef fixed32_1dp money32;
// would write FIXED_2DP(3,65)
#define FIXED_XDP(x, whole, fraction) ((whole) * (10 * x) + (fraction))
#define FIXED_1DP(whole, fraction) FIXED_XDP(1, whole, fraction)
#define FIXED_2DP(whole, fraction) FIXED_XDP(2, whole, fraction)
#define FIXED_2DP(whole, fraction) FIXED_XDP(10, whole, fraction)
// Construct a money value in the format MONEY(10,70) to represent 10.70. Fractional part must be two digits.
#define MONEY(whole, fraction) ((whole) * 10 + ((fraction) / 10))
#define MONEY32_UNDEFINED ((money32)0x80000000)
typedef unsigned short rct_string_id;
void rct2_finish();
enum {

45
src/readme.md Normal file
View File

@@ -0,0 +1,45 @@
# Source directory structure
- **audio**
Contains files for mixing and playing music and sound.
- **drawing**
Low level drawing logic and palette tables.
- **interface**
Window and widget logic, includes high level drawing and input.
- **localisation**
String IDs, currency and date logic.
- **network**
Network and multiplayer logic, includes management of network games and downloading / uploading of content.
- **management**
Park management logic such as finance, marketing and research.
- **platform**
Compiler and operating system specific code such as type definitions, message handling and file input / output.
- **ride**
Data and logic for rides, vehicles and track.
- **util**
Utility and helper functions.
- **windows**
Definitions and logic for all the windows in the game.
- **world**
World objects and mechanics such as the climate, landscape, sprites and park.

View File

@@ -19,15 +19,18 @@
*****************************************************************************/
#include <windows.h>
#include "addresses.h"
#include "game.h"
#include "map.h"
#include "news_item.h"
#include "sprite.h"
#include "../addresses.h"
#include "../game.h"
#include "../interface/window.h"
#include "../localisation/localisation.h"
#include "../management/news_item.h"
#include "../peep/peep.h"
#include "../peep/staff.h"
#include "../scenario.h"
#include "../world/map.h"
#include "../world/sprite.h"
#include "ride.h"
#include "sprite.h"
#include "peep.h"
#include "window.h"
#include "ride_data.h"
#pragma region Ride classification table
@@ -152,7 +155,7 @@ void ride_init_all()
for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) {
ride_measurement = GET_RIDE_MEASUREMENT(i);
ride_measurement->var_00 = 0xFF;
ride_measurement->ride_index = 255;
}
}
@@ -427,10 +430,10 @@ rct_map_element *ride_find_track_gap(rct_map_element *startTrackElement, int *ou
*
* rct2: 0x006B4800
*/
void ride_construct_new(int list_item)
void ride_construct_new(ride_list_item listItem)
{
int eax, ebx, ecx, edx, esi, edi, ebp;
edx = list_item;
edx = *((uint16*)&listItem);
eax = 0;
ecx = 0;
ebx = 1;
@@ -474,3 +477,370 @@ int ride_try_construct(rct_map_element *trackMapElement)
RCT2_CALLPROC_X(0x006CC056, 0, 0, 0, (int)trackMapElement, 0, 0, 0);
return 1;
}
/**
*
* rct2: 0x006AF561
*/
void ride_get_status(int rideIndex, int *formatSecondary, int *argument)
{
rct_ride *ride = &g_ride_list[rideIndex];
if (ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED) {
*formatSecondary = STR_CRASHED;
return;
}
if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) {
*formatSecondary = STR_BROKEN_DOWN;
return;
}
if (ride->status == RIDE_STATUS_CLOSED) {
*formatSecondary = STR_CLOSED;
return;
}
if (ride->status == RIDE_STATUS_TESTING) {
*formatSecondary = STR_TEST_RUN;
return;
}
rct_peep *peep = GET_PEEP(ride->race_winner);
if (ride->mode == RIDE_MODE_RACE && !(ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) && ride->race_winner != 0xFFFF && peep->sprite_identifier == SPRITE_IDENTIFIER_PEEP) {
if (peep->name_string_idx == STR_GUEST) {
*argument = peep->id;
*formatSecondary = STR_RACE_WON_BY_GUEST;
} else {
*argument = peep->name_string_idx;
*formatSecondary = STR_RACE_WON_BY;
}
} else {
if (!(RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + ride->type * 8, uint32) & 0x20000)) {
*argument = ride->num_riders;
*formatSecondary = STR_PERSON_ON_RIDE;
if(*argument != 1)
*formatSecondary = STR_PEOPLE_ON_RIDE;
} else {
*formatSecondary = STR_OPEN;
}
}
}
rct_peep *ride_get_assigned_mechanic(rct_ride *ride)
{
rct_peep *peep;
if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) {
if (
ride->mechanic_status == RIDE_MECHANIC_STATUS_HEADING ||
ride->mechanic_status == 3 ||
ride->mechanic_status == 4
) {
peep = &(g_sprite_list[ride->mechanic].peep);
if (peep_is_mechanic(peep))
return peep;
}
}
return NULL;
}
int ride_get_total_length(rct_ride *ride)
{
int i, totalLength = 0;
for (i = 0; i < ride->num_stations; i++)
totalLength += ride->length[i];
return totalLength;
}
int ride_can_have_multiple_circuits(rct_ride *ride)
{
if (!(RCT2_GLOBAL(0x0097D4F2 + (ride->type * 8), uint16) & 0x200))
return 0;
// Only allow circuit or launch modes
if (
ride->mode != RIDE_MODE_CONTINUOUS_CIRCUIT &&
ride->mode != RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE &&
ride->mode != RIDE_MODE_POWERED_LAUNCH
) {
return 0;
}
// Must have no more than one vehicle and one station
if (ride->num_vehicles > 1 || ride->num_stations > 1)
return 0;
return 1;
}
track_colour ride_get_track_colour(rct_ride *ride, int colourScheme)
{
track_colour result;
result.main = ride->track_colour_main[colourScheme];
result.additional = ride->track_colour_additional[colourScheme];
result.supports = ride->track_colour_supports[colourScheme];
return result;
}
vehicle_colour ride_get_vehicle_colour(rct_ride *ride, int vehicleIndex)
{
vehicle_colour result;
result.main = ride->vehicle_colours[vehicleIndex] & 0xFF;
result.additional_1 = ride->vehicle_colours[vehicleIndex] >> 8;
result.additional_2 = ride->vehicle_colours_extended[vehicleIndex];
return result;
}
/**
*
* rct2: 0x006AC988
* set the speed of the go kart type vehicle at the start to a random value or alter if peep name is an easter egg
* @param ride (esi)
*/
void ride_init_vehicle_speed(rct_ride *ride)
{
rct_ride_type *rideEntry;
rct_vehicle *vehicle;
uint8 *unk;
int i;
for (i = 0; i < ride->num_vehicles; i++) {
vehicle = &g_sprite_list[ride->vehicles[i]].vehicle;
vehicle->var_48 &= ~(1 << 6);
rideEntry = GET_RIDE_ENTRY(vehicle->var_D6);
unk = (uint8*)((int)rideEntry + (vehicle->var_31 * 0x65));
vehicle->speed = (scenario_rand() & 16) - 8 + RCT2_GLOBAL(unk + 0x76, uint8);
if (vehicle->var_B3) {
rct_peep *peep = &g_sprite_list[vehicle->peep].peep;
switch (peep_get_easteregg_name_id(peep)) {
case EASTEREGG_PEEP_NAME_MICHAEL_SCHUMACHER:
vehicle->speed += 35;
break;
case EASTEREGG_PEEP_NAME_JACQUES_VILLENEUVE:
vehicle->speed += 25;
break;
case EASTEREGG_PEEP_NAME_DAMON_HILL:
vehicle->speed += 55;
break;
case EASTEREGG_PEEP_NAME_CHRIS_SAWYER:
vehicle->speed += 14;
break;
case EASTEREGG_PEEP_NAME_MR_BEAN:
vehicle->speed = 9;
break;
}
}
}
}
rct_ride_type *ride_get_entry(rct_ride *ride)
{
return GET_RIDE_ENTRY(ride->subtype);
}
uint8 *get_ride_entry_indices_for_ride_type(uint8 rideType)
{
uint8 *typeToRideEntryIndexMap = (uint8*)0x009E32F8;
uint8 *entryIndexList = typeToRideEntryIndexMap;
while (rideType > 0) {
do {
entryIndexList++;
} while (*(entryIndexList - 1) != 255);
rideType--;
}
return entryIndexList;
}
/**
* rct2: 0x006B64F2
*/
void ride_measurement_update(rct_ride_measurement *measurement)
{
uint16 spriteIndex;
rct_ride *ride;
rct_vehicle *vehicle;
int unk, velocity, altitude, verticalG, lateralG;
ride = GET_RIDE(measurement->ride_index);
spriteIndex = ride->vehicles[measurement->vehicle_index];
if (spriteIndex == SPRITE_INDEX_NULL)
return;
vehicle = &(g_sprite_list[spriteIndex].vehicle);
if (measurement->flags & RIDE_MEASUREMENT_FLAG_UNLOADING) {
if (vehicle->status != VEHICLE_STATUS_DEPARTING && vehicle->status != VEHICLE_STATUS_STOPPING)
return;
measurement->flags &= ~RIDE_MEASUREMENT_FLAG_UNLOADING;
if (measurement->var_0B == vehicle->var_4B)
measurement->current_item = 0;
}
if (vehicle->status == VEHICLE_STATUS_UNLOADING_PASSENGERS) {
measurement->flags |= RIDE_MEASUREMENT_FLAG_UNLOADING;
return;
}
unk = (vehicle->var_36 / 4) & 0xFF;
if (unk == 216 || unk == 123 || unk == 9 || unk == 63 || unk == 147 || unk == 155)
if (vehicle->velocity == 0)
return;
if (measurement->current_item >= RIDE_MEASUREMENT_MAX_ITEMS)
return;
if (measurement->flags & RIDE_MEASUREMENT_FLAG_G_FORCES) {
vehicle_get_g_forces(vehicle, &verticalG, &lateralG);
verticalG = clamp(-127, verticalG / 8, 127);
lateralG = clamp(-127, lateralG / 8, 127);
if (RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, uint32) & 1) {
verticalG = (verticalG + measurement->vertical[measurement->current_item]) / 2;
lateralG = (lateralG + measurement->lateral[measurement->current_item]) / 2;
}
measurement->vertical[measurement->current_item] = verticalG & 0xFF;
measurement->lateral[measurement->current_item] = lateralG & 0xFF;
}
velocity = min(abs((vehicle->velocity * 5) >> 16), 255);
altitude = min(vehicle->z / 8, 255);
if (RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, uint32) & 1) {
velocity = (velocity + measurement->velocity[measurement->current_item]) / 2;
altitude = (altitude + measurement->altitude[measurement->current_item]) / 2;
}
measurement->velocity[measurement->current_item] = velocity & 0xFF;
measurement->altitude[measurement->current_item] = altitude & 0xFF;
if (RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, uint32) & 1) {
measurement->current_item++;
measurement->num_items = max(measurement->num_items, measurement->current_item);
}
}
/**
* rct2: 0x006B6456
*/
void ride_measurements_update()
{
rct_ride *ride;
rct_ride_measurement *measurement;
rct_vehicle *vehicle;
int i, j;
uint16 spriteIndex;
if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR)
return;
// For each ride measurement
for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) {
measurement = GET_RIDE_MEASUREMENT(i);
if (measurement->ride_index == 255)
continue;
ride = GET_RIDE(measurement->ride_index);
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK))
continue;
if (measurement->flags & RIDE_MEASUREMENT_FLAG_RUNNING) {
ride_measurement_update(measurement);
} else {
// For each vehicle
for (j = 0; j < ride->num_vehicles; j++) {
spriteIndex = ride->vehicles[j];
if (spriteIndex == SPRITE_INDEX_NULL)
continue;
vehicle = &(g_sprite_list[spriteIndex].vehicle);
if (vehicle->status == VEHICLE_STATUS_DEPARTING || vehicle->status == VEHICLE_STATUS_STOPPING) {
measurement->vehicle_index = j;
measurement->var_0B = vehicle->var_4B;
measurement->flags |= RIDE_MEASUREMENT_FLAG_RUNNING;
measurement->flags &= ~RIDE_MEASUREMENT_FLAG_UNLOADING;
ride_measurement_update(measurement);
break;
}
}
}
}
}
/**
*
* rct2: 0x006B66D9
*/
rct_ride_measurement *ride_get_measurement(int rideIndex, rct_string_id *message)
{
rct_ride *ride;
rct_ride_measurement *measurement;
uint32 lruTicks;
int i, lruIndex;
ride = GET_RIDE(rideIndex);
// Check if ride type supports data logging
if (!(RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + (ride->type * 8), uint32) & 0x200)) {
if (message != NULL) *message = STR_DATA_LOGGING_NOT_AVAILABLE_FOR_THIS_TYPE_OF_RIDE;
return NULL;
}
// Check if a measurement already exists for this ride
for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) {
measurement = GET_RIDE_MEASUREMENT(i);
if (measurement->ride_index == i)
goto use_measurement;
}
// Find a free measurement
for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) {
measurement = GET_RIDE_MEASUREMENT(i);
if (measurement->ride_index == 255)
goto new_measurement;
}
// Use last recently used measurement for some other ride
lruIndex = 0;
lruTicks = 0xFFFFFFFF;
for (i = 0; i < MAX_RIDE_MEASUREMENTS; i++) {
measurement = GET_RIDE_MEASUREMENT(i);
if (measurement->last_use_tick <= lruTicks) {
lruTicks = measurement->last_use_tick;
lruIndex = i;
}
}
i = lruIndex;
measurement = GET_RIDE_MEASUREMENT(i);
ride->measurement_index = 255;
new_measurement:
measurement->ride_index = rideIndex;
ride->measurement_index = i;
measurement->flags = 0;
if (RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + (ride->type * 8), uint32) & 0x80)
measurement->flags |= RIDE_MEASUREMENT_FLAG_G_FORCES;
measurement->num_items = 0;
measurement->current_item = 0;
use_measurement:
measurement->last_use_tick = RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, uint32);
if (measurement->flags & 1) {
if (message != NULL) *message = 0;
return measurement;
} else {
RCT2_GLOBAL(0x013CE952, uint16) = RideNameConvention[ride->type].vehicle_name;
RCT2_GLOBAL(0x013CE952 + 2, uint16) = RideNameConvention[ride->type].station_name;
if (message != NULL) *message = STR_DATA_LOGGING_WILL_START_WHEN_NEXT_LEAVES;
return NULL;
}
}

View File

@@ -21,9 +21,9 @@
#ifndef _RIDE_H_
#define _RIDE_H_
#include "map.h"
#include "rct2.h"
#include "string_ids.h"
#include "../common.h"
#include "../peep/peep.h"
#include "../world/map.h"
typedef fixed16_2dp ride_rating;
@@ -38,6 +38,14 @@ typedef struct {
ride_rating nausea;
} rating_tuple;
/**
* Couples a ride type and subtype together.
*/
typedef struct {
uint8 type;
uint8 entry_index;
} ride_list_item;
/**
* Ride type structure.
* size: unknown
@@ -49,15 +57,22 @@ typedef struct {
uint32 var_008;
uint8 var_00C;
uint8 var_00D;
uint8 pad_00E[0x5];
uint8 var_00E;
uint8 var_00F;
uint8 var_010;
uint8 var_011;
uint8 var_012;
uint8 var_013;
uint8 pad_014[0x19E];
sint8 excitement_multipler; // 0x1B2
sint8 intensity_multipler; // 0x1B3
sint8 nausea_multipler; // 0x1B4
uint8 pad_1B5[0x09];
uint8 pad_1B5;
uint32 var_1B6;
uint8 pad_1BA[0x04];
uint8 category[2]; // 0x1BE
uint8 shop_item; // 0x1C0
uint8 shop_item_secondary; // 0x1C1
} rct_ride_type;
/**
@@ -72,7 +87,7 @@ typedef struct {
uint16 pad_002;
uint8 mode; // 0x004
uint8 colour_scheme_type; // 0x005
uint16 car_colours[32]; // 0x006
uint16 vehicle_colours[32]; // 0x006
uint8 pad_046[0x03];
// 0 = closed, 1 = open, 2 = test
uint8 status; // 0x049
@@ -81,29 +96,43 @@ typedef struct {
uint16 overall_view; // 0x050 00XX = X, XX00 = Y (* 32 + 16)
uint16 station_starts[4]; // 0x052
uint8 station_heights[4]; // 0x05A
uint8 pad_05E[0xC];
uint8 pad_05E[0x4];
uint8 var_062[4];
uint8 pad_066[0x4];
uint16 entrances[4]; // 0x06A
uint16 exits[4]; // 0x072
uint8 pad_07A[0x0C];
uint16 train_car_map[1]; // 0x086 Points to the first car in the train
uint8 pad_088[0x3F];
uint16 vehicles[32]; // 0x086 Points to the first car in the train
uint8 depart_flags; // 0x0C6
// Not sure if these should be uint or sint.
uint8 var_0C7;
uint8 var_0C8;
uint8 var_0C9;
uint8 pad_0CA[0x1A];
sint32 var_0E4;
sint32 var_0E8;
sint32 var_0EC;
sint32 var_0F0;
uint8 pad_0F4[0x20];
uint8 var_114;
// Track length? Number of track segments?
uint8 var_115;
uint8 pad_116[0x0E];
uint8 num_stations; // 0x0C7
uint8 num_vehicles; // 0x0C8
uint8 num_cars_per_train; // 0x0C9
uint8 pad_0CA[0x2];
uint8 var_0CC;
uint8 var_0CD;
uint8 min_waiting_time; // 0x0CE
uint8 max_waiting_time; // 0x0CF
uint8 var_0D0;
uint8 pad_0D1[0x3];
uint8 measurement_index; // 0x0D4
uint8 pad_0D5[0x3];
sint32 max_speed; // 0x0D8
sint32 average_speed; // 0x0DC
uint8 pad_0E0[0x4];
sint32 length[4]; // 0x0E4
uint16 time[4]; // 0x0F4
fixed16_2dp max_positive_vertical_g; // 0x0FC
fixed16_2dp max_negative_vertical_g; // 0x0FE
fixed16_2dp max_lateral_g; // 0x100
uint8 pad_102[0x12];
uint8 inversions; // 0x114 (???X XXXX) holes for mini golf
uint8 drops; // 0x115 (??XX XXXX)
uint8 pad_116;
uint8 highest_drop_height; // 0x117
uint32 var_118;
uint8 pad_11C[0x08];
sint16 var_124;
sint16 var_126;
sint16 var_128;
@@ -116,59 +145,82 @@ typedef struct {
sint16 var_136;
money16 price; // 0x138
uint8 pad_13A[0x06];
sint16 excitement; // 0x140
sint16 intensity; // 0x142
uint16 nausea; // 0x144
ride_rating excitement; // 0x140
ride_rating intensity; // 0x142
ride_rating nausea; // 0x144
uint16 reliability; // 0x146
uint16 pad_148;
uint16 var_14A;
uint8 pad_14C;
uint8 var_14D;
uint8 pad_14E[0x06];
uint32 var_154;
uint8 pad_14E[0x02];
uint32 total_customers; // 0x150
money32 total_profit; // 0x154
uint16 var_158;
uint8 pad_15A;
uint8 num_riders; // 0x15B
uint8 pad_15C[0x24];
uint16 build_date;
sint16 upkeep_cost; // 0x182
sint16 build_date; // 0x180
money16 upkeep_cost; // 0x182
uint16 race_winner; // 0x184
uint8 pad_186[0x06];
uint8 var_18C;
uint8 pad_18D[0x09];
uint8 mechanic_status; // 0x18D
uint16 mechanic; // 0x18E
uint8 pad_190[0x03];
uint8 breakdown_reason; // 0x193
money16 price_secondary; // 0x194
uint16 var_196;
// used in computing excitement, nausea, etc
uint8 var_198;
uint8 var_199;
uint8 pad_19A[0x14];
uint8 inspection_interval; // 0x19A
uint8 last_inspection; // 0x19B
uint8 pad_19C[0x8];
uint32 var_1A4;
uint8 pad_1A8[6];
uint8 var_1AE;
uint8 connected_message_throttle;
uint32 pad_1B0;
sint32 profit; // 0x1B4
uint8 connected_message_throttle; // 0x1AF
money32 income_per_hour; // 0x1B0
money32 profit; // 0x1B4
uint8 queue_time[4]; // 0x1B8
uint8 var_1BC;
uint8 pad_1BD[0x10];
uint8 var_1CD;
uint8 track_colour_main[4]; // 0x1BC
uint8 track_colour_additional[4]; // 0x1C0
uint8 track_colour_supports[4]; // 0x1C4
uint8 music; // 0x1C8
uint8 entrance_style; // 0x1C9
uint16 var_1CA;
uint8 num_block_brakes; // 0x1CC
uint8 lift_hill_speed; // 0x1CD
uint16 guests_favourite; // 0x1CE
uint32 lifecycle_flags; // 0x1D0
uint8 var_1D4;
uint8 pad_1D5[0x1F];
// Example value for wild mouse ride is d5 (before it's been constructed)
// I tried searching the IDA file for "1F4" but couldn't find places where
// this is written to.
uint16 var_1F4;
uint8 pad_1F6[0x0a];
uint8 vehicle_colours_extended[32]; // 0x1D4
uint16 total_air_time; // 0x1F4
uint8 pad_1F6;
uint8 num_circuits; // 0x1F7
uint8 pad_1F8[0x8];
uint16 queue_length[4]; // 0x200
uint8 pad_208[0x58];
} rct_ride;
#define RIDE_MEASUREMENT_MAX_ITEMS 4800
/**
* Ride measurement structure.
* size: 0x04B0C
*/
typedef struct {
uint8 var_00;
uint8 pad_01[0x4B0B];
uint8 ride_index; // 0x0000
uint8 flags; // 0x0001
uint32 last_use_tick; // 0x0002
uint16 num_items; // 0x0006
uint16 current_item; // 0x0008
uint8 vehicle_index; // 0x000A
uint8 var_0B;
sint8 vertical[RIDE_MEASUREMENT_MAX_ITEMS]; // 0x000C
sint8 lateral[RIDE_MEASUREMENT_MAX_ITEMS]; // 0x12CC
uint8 velocity[RIDE_MEASUREMENT_MAX_ITEMS]; // 0x258C
uint8 altitude[RIDE_MEASUREMENT_MAX_ITEMS]; // 0x384C
} rct_ride_measurement;
enum {
@@ -179,7 +231,7 @@ enum {
// Constants used by the lifecycle_flags property at 0x1D0
enum {
RIDE_LIFECYCLE_ON_TRACK = 1,
RIDE_LIFECYCLE_ON_TRACK = 1 << 0,
RIDE_LIFECYCLE_TESTED = 1 << 1,
RIDE_LIFECYCLE_TEST_IN_PROGRESS = 1 << 2,
RIDE_LIFECYCLE_NO_RAW_STATS = 1 << 3,
@@ -189,13 +241,14 @@ enum {
RIDE_LIFECYCLE_BROKEN_DOWN = 1 << 7,
RIDE_LIFECYCLE_CRASHED = 1 << 10,
RIDE_LIFECYCLE_11 = 1 << 11,
RIDE_LIFECYCLE_EVER_BEEN_OPENED = 1 << 12,
RIDE_LIFECYCLE_MUSIC = 1 << 13,
RIDE_LIFECYCLE_INDESTRUCTIBLE = 1 << 14,
RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK = 1 << 15,
RIDE_LIFECYCLE_CABLE_LIFT = 1 << 17
RIDE_LIFECYCLE_CABLE_LIFT = 1 << 17,
RIDE_LIFECYCLE_19 = 1 << 19
};
enum {
@@ -300,9 +353,9 @@ enum {
};
enum {
RIDE_MODE_NORMAL = 0,
RIDE_MODE_NORMAL,
RIDE_MODE_CONTINUOUS_CIRCUIT,
RIDE_MODE_REVERSE_INCLINED_SHUTTLE,
RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE,
RIDE_MODE_POWERED_LAUNCH, // RCT1 style?
RIDE_MODE_SHUTTLE,
RIDE_MODE_BOAT_HIRE,
@@ -323,7 +376,7 @@ enum {
RIDE_MODE_3D_FILM_MOUSE_TAILS,
RIDE_MODE_SPACE_RINGS,
RIDE_MODE_BEGINNERS,
RIDE_MODE_LIM_POWERED_LAUNCH, // 0x17
RIDE_MODE_LIM_POWERED_LAUNCH,
RIDE_MODE_FILM_THRILL_RIDERS,
RIDE_MODE_3D_FILM_STORM_CHASERS,
RIDE_MODE_3D_FILM_SPACE_RAIDERS,
@@ -335,7 +388,7 @@ enum {
RIDE_MODE_CROOKED_HOUSE,
RIDE_MODE_FREEFALL_DROP,
RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED,
RIDE_MODE_POWERED_LAUNCH2, // 0x23. RCT2 style?
RIDE_MODE_POWERED_LAUNCH_35, // RCT2 style?
RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED
};
@@ -354,6 +407,115 @@ enum {
RIDE_GROUP_SHOP
};
enum {
MUSIC_STYLE_DODGEMS_BEAT,
MUSIC_STYLE_FAIRGROUND_ORGAN,
MUSIC_STYLE_ROMAN_FANFARE,
MUSIC_STYLE_ORIENTAL,
MUSIC_STYLE_MARTIAN,
MUSIC_STYLE_JUNGLE_DRUMS,
MUSIC_STYLE_EGYPTIAN,
MUSIC_STYLE_TOYLAND,
MUSIC_STYLE_8,
MUSIC_STYLE_SPACE,
MUSIC_STYLE_HORROR,
MUSIC_STYLE_TECHNO,
MUSIC_STYLE_GENTLE,
MUSIC_STYLE_SUMMER,
MUSIC_STYLE_WATER,
MUSIC_STYLE_WILD_WEST,
MUSIC_STYLE_JURASSIC,
MUSIC_STYLE_ROCK,
MUSIC_STYLE_RAGTIME,
MUSIC_STYLE_FANTASY,
MUSIC_STYLE_ROCK_STYLE_2,
MUSIC_STYLE_ICE,
MUSIC_STYLE_SNOW,
MUSIC_STYLE_CUSTOM_MUSIC_1,
MUSIC_STYLE_CUSTOM_MUSIC_2,
MUSIC_STYLE_MEDIEVAL,
MUSIC_STYLE_URBAN,
MUSIC_STYLE_ORGAN,
MUSIC_STYLE_MECHANICAL,
MUSIC_STYLE_MODERN,
MUSIC_STYLE_PIRATES,
MUSIC_STYLE_ROCK_STYLE_3,
MUSIC_STYLE_CANDY_STYLE
};
enum {
BREAKDOWN_NONE = 255,
BREAKDOWN_SAFETY_CUT_OUT = 0,
BREAKDOWN_RESTRAINTS_STUCK_CLOSED,
BREAKDOWN_RESTRAINTS_STUCK_OPEN,
BREAKDOWN_DOORS_STUCK_CLOSED,
BREAKDOWN_DOORS_STUCK_OPEN,
BREAKDOWN_VEHICLE_MALFUNCTION,
BREAKDOWN_BRAKES_FAILURE,
BREAKDOWN_CONTROL_FAILURE
};
enum {
RIDE_MECHANIC_STATUS_CALLING = 1,
RIDE_MECHANIC_STATUS_HEADING = 2,
RIDE_MECHANIC_STATUS_FIXING = 3,
};
enum {
RIDE_DEPART_WAIT_FOR_LOAD_MASK = 7,
RIDE_DEPART_WAIT_FOR_LOAD = 1 << 3,
RIDE_DEPART_LEAVE_WHEN_ANOTHER_ARRIVES = 1 << 4,
RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS = 1 << 5,
RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH = 1 << 6,
RIDE_DEPART_WAIT_FOR_MAXIMUM_LENGTH = 1 << 7
};
enum {
RIDE_COLOUR_SCHEME_MAIN,
RIDE_COLOUR_SCHEME_ADDITIONAL_1,
RIDE_COLOUR_SCHEME_ADDITIONAL_2,
RIDE_COLOUR_SCHEME_ADDITIONAL_3
};
enum {
VEHICLE_COLOUR_SCHEME_SAME,
VEHICLE_COLOUR_SCHEME_PER_TRAIN,
VEHICLE_COLOUR_SCHEME_PER_VEHICLE
};
enum {
RIDE_ENTRANCE_STYLE_PLAIN,
RIDE_ENTRANCE_STYLE_WOODEN,
RIDE_ENTRANCE_STYLE_CANVAS_TENT,
RIDE_ENTRANCE_STYLE_CASTLE_GREY,
RIDE_ENTRANCE_STYLE_CASTLE_BROWN,
RIDE_ENTRANCE_STYLE_JUNGLE,
RIDE_ENTRANCE_STYLE_LOG_CABIN,
RIDE_ENTRANCE_STYLE_CLASSICAL_ROMAN,
RIDE_ENTRANCE_STYLE_ABSTRACT,
RIDE_ENTRANCE_STYLE_SNOW_ICE,
RIDE_ENTRANCE_STYLE_PAGODA,
RIDE_ENTRANCE_STYLE_SPACE
};
typedef struct {
uint8 main;
uint8 additional;
uint8 supports;
} track_colour;
typedef struct {
uint8 main;
uint8 additional_1;
uint8 additional_2;
} vehicle_colour;
enum {
RIDE_MEASUREMENT_FLAG_RUNNING = 1 << 0,
RIDE_MEASUREMENT_FLAG_UNLOADING = 1 << 1,
RIDE_MEASUREMENT_FLAG_G_FORCES = 1 << 2
};
#define MAX_RIDES 255
#define MAX_RIDE_MEASUREMENTS 8
@@ -368,6 +530,7 @@ extern rct_ride* g_ride_list;
/** Helper macros until rides are stored in this module. */
#define GET_RIDE(x) (&g_ride_list[x])
#define GET_RIDE_MEASUREMENT(x) (&(RCT2_ADDRESS(RCT2_ADDRESS_RIDE_MEASUREMENTS, rct_ride_measurement)[x]))
#define GET_RIDE_ENTRY(x) RCT2_ADDRESS(RCT2_ADDRESS_RIDE_ENTRIES, rct_ride_type*)[x]
/**
* Helper macro loop for enumerating through all the non null rides.
@@ -387,7 +550,17 @@ void ride_update_favourited_stat();
void ride_check_all_reachable();
rct_map_element *sub_6CAF80(int rideIndex, int *outX, int *outY);
rct_map_element *ride_find_track_gap(rct_map_element *startTrackElement, int *outX, int *outY);
void ride_construct_new(int list_item);
void ride_construct_new(ride_list_item listItem);
int ride_try_construct(rct_map_element *trackMapElement);
void ride_get_status(int rideIndex, int *formatSecondary, int *argument);
rct_peep *ride_get_assigned_mechanic(rct_ride *ride);
int ride_get_total_length(rct_ride *ride);
int ride_can_have_multiple_circuits(rct_ride *ride);
track_colour ride_get_track_colour(rct_ride *ride, int colourScheme);
vehicle_colour ride_get_vehicle_colour(rct_ride *ride, int vehicleIndex);
rct_ride_type *ride_get_entry(rct_ride *ride);
uint8 *get_ride_entry_indices_for_ride_type(uint8 rideType);
void ride_measurements_update();
rct_ride_measurement *ride_get_measurement(int rideIndex, rct_string_id *message);
#endif

View File

@@ -9,7 +9,7 @@
*/
#include <stdbool.h>
#include "rct2.h"
#include "ride.h"
#include "ride_data.h"
const bool hasRunningTrack[0x60] = {
@@ -491,6 +491,7 @@ const bool rideUnknownData2[0x60] = {
true, // 59 LIM Launched Roller Coaster
};
/* Data at 0x0097E3B8 */
const uint8 rideUnknownData3[0x60] = {
10, // 00 Spiral Roller coaster
10, // 01 Stand Up Coaster
@@ -582,4 +583,192 @@ const uint8 rideUnknownData3[0x60] = {
10, // 57 Mini Roller Coaster
10, // 58 Mine Ride
10, // 59 LIM Launched Roller Coaster
};
const rct_ride_name_convention RideNameConvention[96] = {
{ 1229, 1243, 1257, 0 }, // 00 Spiral Roller coaster
{ 1229, 1243, 1257, 0 }, // 01 Stand Up Coaster
{ 1229, 1243, 1257, 0 }, // 02 Suspended Swinging
{ 1229, 1243, 1257, 0 }, // 03 Inverted
{ 1229, 1243, 1257, 0 }, // 04 Steel Mini Coaster
{ 1229, 1243, 1257, 0 }, // 05 Mini Railroad
{ 1229, 1243, 1257, 0 }, // 06 Monorail
{ 1264, 1243, 1257, 0 }, // 07 Mini Suspended Coaster
{ 1236, 1250, 1250, 0 }, // 08 Bumper Boats
{ 1264, 1243, 1257, 0 }, // 09 Wooden Wild Mine/Mouse
{ 1264, 1243, 1257, 0 }, // 0a Steeplechase/Motorbike/Soap Box Derby
{ 1264, 1243, 1257, 0 }, // 0b Car Ride
{ 1264, 1243, 1257, 0 }, // 0c Launched Freefall
{ 1229, 1243, 1257, 0 }, // 0d Bobsleigh Coaster
{ 1292, 1243, 1257, 0 }, // 0e Observation Tower
{ 1229, 1243, 1257, 0 }, // 0f Looping Roller Coaster
{ 1236, 1243, 1257, 0 }, // 10 Dinghy Slide
{ 1229, 1243, 1257, 0 }, // 11 Mine Train Coaster
{ 1264, 1243, 1257, 0 }, // 12 Chairlift
{ 1229, 1243, 1257, 0 }, // 13 Corkscrew Roller Coaster
{ 1229, 1243, 1257, 0 }, // 14 Maze
{ 1229, 1271, 1257, 0 }, // 15 Spiral Slide
{ 1264, 1243, 1257, 0 }, // 16 Go Karts
{ 1236, 1243, 1257, 0 }, // 17 Log Flume
{ 1236, 1243, 1257, 0 }, // 18 River Rapids
{ 1264, 1271, 1257, 0 }, // 19 Bumper Cars
{ 1285, 1278, 1257, 0 }, // 1a Pirate Ship
{ 1285, 1278, 1257, 0 }, // 1b Swinging Inverter Ship
{ 1264, 1271, 1257, 0 }, // 1c Food Stall
{ 1264, 1271, 1257, 0 }, // 1d (none)
{ 1264, 1271, 1257, 0 }, // 1e Drink Stall
{ 1264, 1271, 1257, 0 }, // 1f (none)
{ 1264, 1271, 1257, 0 }, // 20 Shop (all types)
{ 1264, 1278, 1257, 0 }, // 21 Merry Go Round
{ 1264, 1271, 1257, 0 }, // 22 Balloon Stall (maybe)
{ 1264, 1271, 1257, 0 }, // 23 Information Kiosk
{ 1264, 1271, 1257, 0 }, // 24 Bathroom
{ 1299, 1278, 1257, 0 }, // 25 Ferris Wheel
{ 1264, 1278, 1257, 0 }, // 26 Motion Simulator
{ 1271, 1278, 1257, 0 }, // 27 3D Cinema
{ 1264, 1278, 1257, 0 }, // 28 Gravitron
{ 1306, 1278, 1257, 0 }, // 29 Space Rings
{ 1264, 1243, 1257, 0 }, // 2a Reverse Freefall Coaster
{ 1292, 1243, 1257, 0 }, // 2b Elevator
{ 1229, 1243, 1257, 0 }, // 2c Vertical Drop Roller Coaster
{ 1264, 1271, 1257, 0 }, // 2d ATM
{ 1278, 1278, 1257, 0 }, // 2e Twist
{ 1271, 1278, 1257, 0 }, // 2f Haunted House
{ 1264, 1271, 1257, 0 }, // 30 First Aid
{ 1271, 1278, 1257, 0 }, // 31 Circus Show
{ 1264, 1243, 1257, 0 }, // 32 Ghost Train
{ 1229, 1243, 1257, 0 }, // 33 Twister Roller Coaster
{ 1229, 1243, 1257, 0 }, // 34 Wooden Roller Coaster
{ 1229, 1243, 1257, 0 }, // 35 Side-Friction Roller Coaster
{ 1264, 1243, 1257, 0 }, // 36 Wild Mouse
{ 1229, 1243, 1257, 0 }, // 37 Multi Dimension Coaster
{ 1229, 1243, 1257, 0 }, // 38 (none)
{ 1229, 1243, 1257, 0 }, // 39 Flying Roller Coaster
{ 1229, 1243, 1257, 0 }, // 3a (none)
{ 1264, 1243, 1257, 0 }, // 3b Virginia Reel
{ 1236, 1243, 1257, 0 }, // 3c Splash Boats
{ 1264, 1243, 1257, 0 }, // 3d Mini Helicopters
{ 1229, 1243, 1257, 0 }, // 3e Lay-down Roller Coaster
{ 1229, 1243, 1257, 0 }, // 3f Suspended Monorail
{ 1229, 1243, 1257, 0 }, // 40 (none)
{ 1264, 1243, 1257, 0 }, // 41 Reverser Roller Coaster
{ 1264, 1243, 1257, 0 }, // 42 Heartline Twister Roller Coaster
{ 1313, 1320, 1257, 0 }, // 43 Mini Golf
{ 1229, 1243, 1257, 0 }, // 44 Giga Coaster
{ 1264, 1243, 1257, 0 }, // 45 Roto-Drop
{ 1264, 1271, 1257, 0 }, // 46 Flying Saucers
{ 1271, 1278, 1257, 0 }, // 47 Crooked House
{ 1264, 1243, 1257, 0 }, // 48 Monorail Cycles
{ 1229, 1243, 1257, 0 }, // 49 Compact Inverted Coaster
{ 1236, 1243, 1257, 0 }, // 4a Water Coaster
{ 1229, 1243, 1257, 0 }, // 4b Air Powered Vertical Coaster
{ 1264, 1243, 1257, 0 }, // 4c Inverted Hairpin Coaster
{ 1264, 1278, 1257, 0 }, // 4d Magic Carpet
{ 1236, 1243, 1250, 0 }, // 4e Submarine Ride
{ 1236, 1243, 1257, 0 }, // 4f River Rafts
{ 1264, 1271, 1257, 0 }, // 50 (none)
{ 1299, 1278, 1257, 0 }, // 51 Enterprise
{ 1264, 1271, 1257, 0 }, // 52 (none)
{ 1264, 1271, 1257, 0 }, // 53 (none)
{ 1264, 1271, 1257, 0 }, // 54 (none)
{ 1229, 1243, 1257, 0 }, // 55 (none)
{ 1229, 1243, 1257, 0 }, // 56 Inverted Impulse Coaster
{ 1264, 1243, 1257, 0 }, // 57 Mini Roller Coaster
{ 1229, 1243, 1257, 0 }, // 58 Mine Ride
{ 1264, 1243, 1257, 0 }, // 59 LIM Launched Roller Coaster
{ 1229, 1243, 1257, 0 }
};
const uint8 RideAvailableModes[] = {
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 00 Spiral Roller coaster
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 01 Stand Up Coaster
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 02 Suspended Swinging
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 03 Inverted
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 04 Steel Mini Coaster
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_SHUTTLE, 0xFF, // 05 Mini Railroad
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_SHUTTLE, 0xFF, // 06 Monorail
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 07 Mini Suspended Coaster
RIDE_MODE_BOAT_HIRE, 0xFF, // 08 Bumper Boats
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 09 Wooden Wild Mine/Mouse
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 0A Steeplechase/Motorbike/Soap Box Derby
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 0B Car Ride
RIDE_MODE_UPWARD_LAUNCH, RIDE_MODE_DOWNWARD_LAUNCH, 0xFF, // 0C Launched Freefall
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 0D Bobsleigh Coaster
RIDE_MODE_ROTATING_LIFT, 0xFF, // 0E Observation Tower
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE, RIDE_MODE_POWERED_LAUNCH, 0xFF, // 0F Looping Roller Coaster
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 10 Dinghy Slide
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 11 Mine Train Coaster
RIDE_MODE_STATION_TO_STATION, 0xFF, // 12 Chairlift
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0x23, 2, 0xFF, // 13 Corkscrew Roller Coaster
RIDE_MODE_MAZE, 0xFF, // 14 Maze
RIDE_MODE_SINGLE_RIDE_PER_ADMISSION, RIDE_MODE_UNLIMITED_RIDES_PER_ADMISSION, 0xFF, // 15 Spiral Slide
RIDE_MODE_RACE, RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 16 Go Karts
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 17 Log Flume
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 18 River Rapids
RIDE_MODE_BUMPERCAR, 0xFF, // 19 Bumper Cars
RIDE_MODE_SWING, 0xFF, // 1A Pirate Ship
RIDE_MODE_SWING, 0xFF, // 1B Swinging Inverter Ship
RIDE_MODE_SHOP_STALL, 0xFF, // 1C Food Stall
RIDE_MODE_SHOP_STALL, 0xFF, // 1D (none)
RIDE_MODE_SHOP_STALL, 0xFF, // 1E Drink Stall
RIDE_MODE_SHOP_STALL, 0xFF, // 1F (none)
RIDE_MODE_SHOP_STALL, 0xFF, // 20 Shop (all types)
RIDE_MODE_ROTATION, 0xFF, // 21 Merry Go Round
RIDE_MODE_SHOP_STALL, 0xFF, // 22 Balloon Stall (maybe)
RIDE_MODE_SHOP_STALL, 0xFF, // 23 Information Kiosk
RIDE_MODE_SHOP_STALL, 0xFF, // 24 Bathroom
RIDE_MODE_FORWARD_ROTATION, RIDE_MODE_BACKWARD_ROTATION, 0xFF, // 25 Ferris Wheel
RIDE_MODE_FILM_AVENGING_AVIATORS, RIDE_MODE_FILM_THRILL_RIDERS, 0xFF, // 26 Motion Simulator
RIDE_MODE_3D_FILM_MOUSE_TAILS, RIDE_MODE_3D_FILM_STORM_CHASERS, RIDE_MODE_3D_FILM_SPACE_RAIDERS, 0xFF, // 27 3D Cinema
RIDE_MODE_BEGINNERS, RIDE_MODE_INTENSE, RIDE_MODE_BERSERK, 0xFF, // 28 Gravitron
RIDE_MODE_SPACE_RINGS, 0xFF, // 29 Space Rings
RIDE_MODE_LIM_POWERED_LAUNCH, 0xFF, // 2A Reverse Freefall Coaster
RIDE_MODE_SHUTTLE, 0xFF, // 2B Elevator
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 2C Vertical Drop Roller Coaster
RIDE_MODE_SHOP_STALL, 0xFF, // 2D ATM
RIDE_MODE_ROTATION, 0xFF, // 2E Twist
RIDE_MODE_HAUNTED_HOUSE, 0xFF, // 2F Haunted House
RIDE_MODE_SHOP_STALL, 0xFF, // 30 First Aid
RIDE_MODE_CIRCUS_SHOW, 0xFF, // 31 Circus Show
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 32 Ghost Train
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 33 Twister Roller Coaster
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 34 Wooden Roller Coaster
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 35 Side-Friction Roller Coaster
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 36 Wild Mouse
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 37 Multi Dimension Coaster
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 38 (none)
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 39 Flying Roller Coaster
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 3A (none)
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 3B Virginia Reel
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 3C Splash Boats
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 3D Mini Helicopters
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 3E Lay-down Roller Coaster
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_SHUTTLE, 0xFF, // 3F Suspended Monorail
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 40 (none)
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 41 Reverser Roller Coaster
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 42 Heartline Twister Roller Coaster
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 43 Mini Golf
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 44 Giga Coaster
RIDE_MODE_FREEFALL_DROP, 0xFF, // 45 Roto-Drop
RIDE_MODE_BUMPERCAR, 0xFF, // 46 Flying Saucers
RIDE_MODE_CROOKED_HOUSE, 0xFF, // 47 Crooked House
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 48 Monorail Cycles
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 2, 0xFF, // 49 Compact Inverted Coaster
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 4A Water Coaster
RIDE_MODE_POWERED_LAUNCH, 0xFF, // 4B Air Powered Vertical Coaster
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 4C Inverted Hairpin Coaster
RIDE_MODE_SWING, 0xFF, // 4D Magic Carpet
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 4E Submarine Ride
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 4F River Rafts
RIDE_MODE_SHOP_STALL, 0xFF, // 50 (none)
RIDE_MODE_ROTATION, 0xFF, // 51 Enterprise
RIDE_MODE_SHOP_STALL, 0xFF, // 52 (none)
RIDE_MODE_SHOP_STALL, 0xFF, // 53 (none)
RIDE_MODE_SHOP_STALL, 0xFF, // 54 (none)
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 55 (none)
RIDE_MODE_POWERED_LAUNCH, 0xFF, // 56 Inverted Impulse Coaster
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 57 Mini Roller Coaster
RIDE_MODE_CONTINUOUS_CIRCUIT, 0xFF, // 58 Mine Ride
RIDE_MODE_CONTINUOUS_CIRCUIT, RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED, 0xFF, // 59 LIM Launched Roller Coaster
RIDE_MODE_POWERED_LAUNCH_35, RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED, 0xFF // 60 (none)
};

View File

@@ -22,7 +22,14 @@
#define _RIDE_DATA_H_
#include <stdbool.h>
#include "rct2.h"
#include "../common.h"
typedef struct {
rct_string_id vehicle_name;
rct_string_id structure_name;
rct_string_id station_name;
rct_string_id unk_name;
} rct_ride_name_convention;
extern const bool hasRunningTrack[0x60];
extern const uint8 initialUpkeepCosts[0x60];
@@ -32,4 +39,7 @@ extern const uint8 rideUnknownData1[0x60];
extern const bool rideUnknownData2[0x60];
extern const uint8 rideUnknownData3[0x60];
extern const rct_ride_name_convention RideNameConvention[96];
extern const uint8 RideAvailableModes[];
#endif

View File

@@ -18,7 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "addresses.h"
#include "../addresses.h"
#include "ride.h"
#include "ride_data.h"
#include "ride_ratings.h"
@@ -56,9 +56,9 @@ void crooked_house_excitement(rct_ride *ride)
ride->var_14D |= 2;
// clear all bits except lowest 5
ride->var_114 &= 0x1f;
ride->inversions &= 0x1F;
// set 6th,7th,8th bits
ride->var_114 |= 0xE0;
ride->inversions |= 0xE0;
}
/**
@@ -75,27 +75,22 @@ uint16 compute_upkeep(rct_ride *ride)
uint16 upkeep = initialUpkeepCosts[ride->type];
uint16 trackCost = costPerTrackPiece[ride->type];
uint8 dl = ride->var_115;
uint8 dl = ride->drops;
dl = dl >> 6;
dl = dl & 3;
upkeep += trackCost * dl;
uint32 cuml = ride->var_0E4;
cuml += ride->var_0E8;
cuml += ride->var_0EC;
cuml += ride->var_0F0;
cuml = cuml >> 0x10;
uint32 totalLength = (ride->length[0] + ride->length[1] + ride->length[2] + ride->length[3]) >> 16;
// The data originally here was 20's and 0's. The 20's all represented
// rides that had tracks. The 0's were fixed rides like crooked house or
// bumper cars.
// Data source is 0x0097E3AC
if (hasRunningTrack[ride->type]) {
cuml = cuml * 20;
totalLength *= 20;
}
cuml = cuml >> 0x0A;
upkeep += (uint16)cuml;
upkeep += (uint16)(totalLength >> 10);
if (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO) {
// The original code read from a table starting at 0x0097E3AE and
@@ -130,24 +125,24 @@ uint16 compute_upkeep(rct_ride *ride)
// various variables set on the ride itself.
// https://gist.github.com/kevinburke/e19b803cd2769d96c540
upkeep += rideUnknownData1[ride->type] * ride->var_0C8;
upkeep += rideUnknownData1[ride->type] * ride->num_vehicles;
// either set to 3 or 0, extra boosts for some rides including mini golf
if (rideUnknownData2[ride->type]) {
upkeep += 3 * ride->var_0C9;
upkeep += 3 * ride->num_cars_per_train;
}
// slight upkeep boosts for some rides - 5 for mini railroad, 10 for log
// flume/rapids, 10 for roller coaster, 28 for giga coaster
upkeep += rideUnknownData3[ride->type] * ride->var_0C7;
upkeep += rideUnknownData3[ride->type] * ride->num_stations;
if (ride->mode == RIDE_MODE_REVERSE_INCLINED_SHUTTLE) {
if (ride->mode == RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE) {
upkeep += 30;
} else if (ride->mode == RIDE_MODE_POWERED_LAUNCH) {
upkeep += 160;
} else if (ride->mode == RIDE_MODE_LIM_POWERED_LAUNCH) {
upkeep += 320;
} else if (ride->mode == RIDE_MODE_POWERED_LAUNCH2 ||
} else if (ride->mode == RIDE_MODE_POWERED_LAUNCH_35 ||
ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED) {
upkeep += 220;
}
@@ -184,7 +179,7 @@ rating_tuple per_ride_rating_adjustments(rct_ride *ride, ride_rating excitement,
// more detail: https://gist.github.com/kevinburke/d951e74e678b235eef3e
uint16 ridetype_var = RCT2_GLOBAL(0x0097D4F2 + ride->type * 8, uint16);
if (ridetype_var & 0x80) {
uint16 ax = ride->var_1F4;
uint16 ax = ride->total_air_time;
if (rideType->var_008 & 0x800) {
// 65e86e
ax = ax - 96;
@@ -244,7 +239,7 @@ ride_rating apply_intensity_penalty(ride_rating excitement, ride_rating intensit
*/
void sub_655FD6(rct_ride *ride)
{
uint8 al = ride->var_1CD;
uint8 al = ride->lift_hill_speed;
// No idea what this address is; maybe like compensation of some kind? The
// maximum possible value?
// List of ride names/values is here:

View File

@@ -21,7 +21,7 @@
#ifndef _RIDE_RATINGS_H_
#define _RIDE_RATINGS_H_
#include "rct2.h"
#include "../common.h"
#include "ride.h"
void crooked_house_excitement(rct_ride *ride);

View File

@@ -18,7 +18,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "addresses.h"
#include <string.h>
#include <windows.h>
#include "../addresses.h"
#include "../platform/osinterface.h"
#include "ride.h"
#include "track.h"
/**
@@ -220,7 +224,207 @@ const rct_trackdefinition gTrackDefinitions[] = {
*
* rct2: 0x006CED50
*/
void track_load_list(int edx)
void track_load_list(ride_list_item item)
{
RCT2_CALLPROC_X(0x006CED50, 0, 0, 0, edx, 0, 0, 0);
RCT2_CALLPROC_X(0x006CED50, 0, 0, 0, *((uint16*)&item), 0, 0, 0);
}
/**
*
* rct2: 0x00676EBA
*/
static uint8 sub_676EBA()
{
int eax, ebx, ecx, edx, esi, edi, ebp;
RCT2_CALLFUNC_X(0x00676EBA, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp);
return eax & 0xFF;
}
/**
*
* rct2: 0x00676EAF
*/
static void sub_676EAF(uint8 *esi, int ecx)
{
do {
*esi++ = sub_676EBA();
} while (--ecx != 0);
}
/**
*
* rct2: 0x0067726A
* path: 0x0141EF68
*/
int sub_67726A(const char *path)
{
HANDLE *hFile;
const char *ch;
char trackFilename[MAX_PATH], *dst;
int i;
uint8* edi;
RCT2_GLOBAL(0x009AAC54, uint8) = 1;
// Get filename
ch = strrchr(path, '\\');
ch = ch == NULL ? path : ch + 1;
dst = trackFilename;
while (*ch != 0 && *ch != '.') {
*dst++ = *ch++;
}
*dst = 0;
hFile = osinterface_file_open(path);
if (hFile == INVALID_HANDLE_VALUE)
return 0;
RCT2_GLOBAL(0x009E382C, HANDLE) = hFile;
if (!RCT2_CALLPROC_X(0x006770C1, 0, 0, 0, 0, 0, 0, 0)) {
CloseHandle(hFile);
return 0;
}
RCT2_CALLPROC_EBPSAFE(0x00676E7A);
memset((void*)0x009D81D8, 0, 67);
sub_676EAF((void*)0x009D8178, 32);
uint8 al = RCT2_GLOBAL(0x009D817F, uint8) >> 2;
if (al >= 2)
sub_676EAF((void*)0x009D8198, 40);
sub_676EAF((void*)0x009D81C0, 24);
al = RCT2_GLOBAL(0x009D817F, uint8) >> 2;
if (al != 0)
sub_676EAF((void*)0x009D81D8, al == 1 ? 140 : 67);
sub_676EAF((void*)0x009D821B, 24572);
al = RCT2_GLOBAL(0x009D817F, uint8) >> 2;
if (al < 2) {
if (RCT2_GLOBAL(0x009D8178, uint8) == 20) {
edi = (uint8*)0x009D821B;
while (*edi != 0) {
edi += 4;
}
edi += 4;
memset(edi, 255, (uint8*)0x009DE217 - edi);
} else {
edi = (uint8*)0x009D821B;
while (*edi != 255) {
edi += 2;
}
edi++;
memset(edi, 255, (uint8*)0x009DE217 - edi);
}
}
CloseHandle(hFile);
al = RCT2_GLOBAL(0x009D817F, uint8) >> 2;
if (al > 2)
return 0;
if (al <= 1) {
edi = (uint8*)0x009D8180;
for (i = 0; i < 67; i++)
*edi++ = RCT2_ADDRESS(0x0097F0BC, uint8)[*edi];
edi = (uint8*)0x009D81D8;
for (i = 0; i < 12; i++)
*edi++ = RCT2_ADDRESS(0x0097F0BC, uint8)[*edi];
RCT2_GLOBAL(0x009D81D2, uint8) >>= 1;
if (!RCT2_CALLPROC_X(0x00677530, 0, 0, 0, 0, 0, 0, 0))
RCT2_GLOBAL(0x009D8178, uint8) = 255;
if (RCT2_GLOBAL(0x009D8178, uint8) == 4)
RCT2_GLOBAL(0x009D8178, uint8) = 255;
if (RCT2_GLOBAL(0x009D8178, uint8) == 0)
RCT2_GLOBAL(0x009D8178, uint8) = 52;
if (RCT2_GLOBAL(0x009D8178, uint8) == 19) {
if (RCT2_GLOBAL(0x009D817E, uint8) == 3)
RCT2_GLOBAL(0x009D817E, uint8) = 35;
if (RCT2_GLOBAL(0x009D8179, uint8) == 79) {
if (RCT2_GLOBAL(0x009D817E, uint8) == 2)
RCT2_GLOBAL(0x009D817E, uint8) = 1;
}
}
int unk1 = RCT2_GLOBAL(0x009D8179, uint8);
if (RCT2_GLOBAL(0x009D8178, uint8) == 20) {
unk1 = 0x0097F66C;
} else {
if (unk1 == 3 && RCT2_GLOBAL(0x009D8178, uint8) == 3)
unk1 = 80;
unk1 = 0x0097F0DC + (unk1 * 16);
}
memcpy((void*)0x009D81E8, (void*)unk1, 16);
for (i = 0; i < 32; i++)
RCT2_ADDRESS(0x009D81FA, uint8)[i] = RCT2_ADDRESS(0x009D8181, uint8)[i * 2];
RCT2_GLOBAL(0x009D81F8, uint8) = 255;
RCT2_GLOBAL(0x009D81F9, uint8) = 255;
RCT2_GLOBAL(0x009D821A, uint8) = 5;
}
RCT2_GLOBAL(0x009D81C8, uint8) = min(
RCT2_GLOBAL(0x009D81C8, uint8),
RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + 5 + (RCT2_GLOBAL(0x009D8178, uint8) * 8), uint8)
);
return 1;
}
/**
*
* I don't think preview is a necessary output argument. It can be obtained easily using the track design structure.
* rct2: 0x006D1DEC
*/
rct_track_design *track_get_info(int index, uint8** preview)
{
rct_track_design *trackDesign;
uint8 *trackDesignList = (uint8*)0x00F441EC;
int i;
trackDesign = NULL;
// Check if track design has already been loaded
for (i = 0; i < 4; i++) {
if (index == RCT2_ADDRESS(0x00F44109, uint32)[i]) {
trackDesign = &RCT2_GLOBAL(0x00F44105, rct_track_design*)[i];
break;
}
}
if (trackDesign == NULL) {
// Load track design
i = RCT2_GLOBAL(0x00F44119, uint32);
RCT2_GLOBAL(0x00F44119, uint32)++;
if (RCT2_GLOBAL(0x00F44119, uint32) >= 4)
RCT2_GLOBAL(0x00F44119, uint32) = 0;
RCT2_ADDRESS(0x00F44109, uint32)[i] = index;
subsitute_path((char*)0x0141EF68, (char*)RCT2_ADDRESS_TRACKS_PATH, trackDesignList + (index * 128));
if (!sub_67726A((char*)0x0141EF68)) {
if (preview != NULL) *preview = NULL;
return NULL;
}
trackDesign = &RCT2_GLOBAL(0x00F44105, rct_track_design*)[i];
memcpy(trackDesign, (void*)0x009D8178, 163);
RCT2_CALLPROC_EBPSAFE(0x006D1EF0);
trackDesign->cost = RCT2_GLOBAL(0x00F4411D, money32);
trackDesign->var_06 = RCT2_GLOBAL(0x00F44151, uint8) & 7;
}
// Set preview to correct preview image based on rotation
if (preview != NULL)
*preview = trackDesign->preview[RCT2_GLOBAL(0x00F440AE, uint8)];
return trackDesign;
}

View File

@@ -21,7 +21,8 @@
#ifndef _TRACK_H_
#define _TRACK_H_
#include "rct2.h"
#include "../common.h"
#include "ride.h"
typedef struct {
uint8 type;
@@ -33,6 +34,45 @@ typedef struct {
uint8 pad[2];
} rct_trackdefinition;
#define TRACK_PREVIEW_IMAGE_SIZE (370 * 217)
/**
* Track design structure.
* size: 0x4E72B
*/
typedef struct {
uint8 type; // 0x00
uint8 pad_01;
money32 cost; // 0x02
uint8 var_06;
uint8 var_07;
uint8 pad_08[0x42];
uint8 total_air_time; // 0x4A
uint8 pad_4B[0x06];
uint8 max_speed; // 0x51
uint8 average_speed; // 0x52
uint16 ride_length; // 0x53
uint8 max_positive_vertical_g; // 0x55
uint8 max_negitive_vertical_g; // 0x56
uint8 max_lateral_g; // 0x57
union {
uint8 inversions; // 0x58
uint8 holes; // 0x58
};
uint8 drops; // 0x59
uint8 highest_drop_height; // 0x5A
uint8 excitement; // 0x5B
uint8 intensity; // 0x5C
uint8 nausea; // 0x5D
uint8 pad_5E[0x0E];
uint32 var_6C;
uint8 pad_70[0x10];
uint8 space_required_x; // 0x80
uint8 space_required_y; // 0x81
uint8 pad_82[0x21];
uint8 preview[4][TRACK_PREVIEW_IMAGE_SIZE]; // 0xA3
} rct_track_design;
enum {
TRACK_NONE = 0,
@@ -90,6 +130,8 @@ enum {
TRACK_CORKSCREW_DOWN = 224
};
void track_load_list(int edx);
void track_load_list(ride_list_item item);
int sub_67726A(const char *path);
rct_track_design *track_get_info(int index, uint8** preview);
#endif

589
src/ride/vehicle.c Normal file
View File

@@ -0,0 +1,589 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#include "../addresses.h"
#include "../audio/audio.h"
#include "../audio/mixer.h"
#include "../interface/viewport.h"
#include "../world/sprite.h"
#include "ride.h"
#include "vehicle.h"
static void vehicle_update(rct_vehicle *vehicle);
/**
*
* rct2: 0x006BB9FF
*/
void vehicle_update_sound_params(rct_vehicle* vehicle)
{
if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 2) && (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 4) || RCT2_GLOBAL(0x0141F570, uint8) == 6)) {
if (vehicle->sound1_id != (uint8)-1 || vehicle->sound2_id != (uint8)-1) {
if (vehicle->var_16.width != 0x8000) {
RCT2_GLOBAL(0x009AF5A0, rct_widthheight) = vehicle->var_16;
RCT2_GLOBAL(0x009AF5A4, rct_widthheight) = vehicle->view;
sint16 v4 = RCT2_GLOBAL(0x00F438A4, rct_viewport*)->view_x;
sint16 v5 = RCT2_GLOBAL(0x00F438A4, rct_viewport*)->view_y;
sint16 v6 = RCT2_GLOBAL(0x00F438A4, rct_viewport*)->view_width / 4;
sint16 v7 = RCT2_GLOBAL(0x00F438A4, rct_viewport*)->view_height / 4;
if (!RCT2_GLOBAL(0x00F438A8, rct_window*)->classification) {
v4 -= v6;
v5 -= v7;
}
if (v4 < RCT2_GLOBAL(0x009AF5A4, rct_widthheight).width && v5 < RCT2_GLOBAL(0x009AF5A4, rct_widthheight).height) {
sint16 t8 = RCT2_GLOBAL(0x00F438A4, rct_viewport*)->view_width + v4;
sint16 t9 = RCT2_GLOBAL(0x00F438A4, rct_viewport*)->view_height + v5;
if (!RCT2_GLOBAL(0x00F438A8, rct_window*)->classification) {
t8 += v6 + v6;
t9 += v7 + v7;
}
if (t8 >= RCT2_GLOBAL(0x009AF5A0, rct_widthheight).width && t9 >= RCT2_GLOBAL(0x009AF5A0, rct_widthheight).height) {
uint16 v9 = sub_6BC2F3(vehicle);
rct_vehicle_sound_params* i;
//for (i = RCT2_ADDRESS(0x00F438B4, rct_vehicle_sound_params); i < RCT2_GLOBAL(0x00F438B0, rct_vehicle_sound_params*) && v9 <= i->var_A; i++);
for (i = &gVehicleSoundParamsList[0]; i < gVehicleSoundParamsListEnd && v9 <= i->var_A; i++);
//if (i < RCT2_ADDRESS(0x00F43908, rct_vehicle_sound_params)) { // 0x00F43908 is end of rct_vehicle_sound_params list, which has 7 elements, not to be confused with variable at 0x00F43908
if (i < &gVehicleSoundParamsList[countof(gVehicleSoundParamsList)]) {
//if (RCT2_GLOBAL(0x00F438B0, rct_vehicle_sound_params*) < RCT2_ADDRESS(0x00F43908, rct_vehicle_sound_params)) {
// RCT2_GLOBAL(0x00F438B0, rct_vehicle_sound_params*)++;
//}
if (gVehicleSoundParamsListEnd < &gVehicleSoundParamsList[countof(gVehicleSoundParamsList)]) {
gVehicleSoundParamsListEnd++;
}
//rct_vehicle_sound_params* j = RCT2_GLOBAL(0x00F438B0, rct_vehicle_sound_params*) - 1;
rct_vehicle_sound_params* j = gVehicleSoundParamsListEnd - 1;
while (j >= i) {
j--;
*(j + 1) = *j;
}
i->var_A = v9;
rct_widthheight v12;
v12.height = vehicle->var_16.height;
v12.width = ((uint16)RCT2_GLOBAL(0x009AF5A0, rct_widthheight).width / 2) + ((uint16)RCT2_GLOBAL(0x009AF5A4, rct_widthheight).width / 2) - RCT2_GLOBAL(0x00F438A4, rct_viewport*)->view_x;
v12.width >>= RCT2_GLOBAL(0x00F438A4, rct_viewport*)->zoom;
v12.width += RCT2_GLOBAL(0x00F438A4, rct_viewport*)->x;
uint16 v14 = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16);
if (v14 < 64) {
v14 = 64;
}
rct_widthheight v15;
i->pan = (((((uint32)v12.width << 16) / v14) - 0x8000) >> 4);
v15.width = 0;
v15.height = (RCT2_GLOBAL(0x009AF5A0, rct_widthheight).height / 2) + (RCT2_GLOBAL(0x009AF5A4, rct_widthheight).height / 2) - RCT2_GLOBAL(0x00F438A4, rct_viewport*)->view_y;
v15.height >>= RCT2_GLOBAL(0x00F438A4, rct_viewport*)->zoom;
v15.height += RCT2_GLOBAL(0x00F438A4, rct_viewport*)->y;
uint16 v18 = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, uint16);
if (v18 < 64) {
v18 = 64;
}
i->var_4 = (sint16)(((v15.both / v18) - 0x8000) >> 4);
sint32 v19 = vehicle->velocity;
int testaddr = (vehicle->var_31 * 0x65);
testaddr += (int)RCT2_ADDRESS(0x009ACFA4, rct_ride_type*)[vehicle->var_D6];
uint8 test = ((uint8*)testaddr)[0x74];
if (test & 1) {
v19 *= 2;
}
if (v19 < 0) {
v19 = -v19;
}
v19 >>= 5;
v19 *= 5512;
v19 >>= 14;
v19 += 11025;
v19 += 16 * vehicle->var_BF;
i->frequency = (uint16)v19;
i->id = vehicle->sprite_index;
i->var_8 = 0;
if (vehicle->x != 0x8000) {
uint16 v22 = (vehicle->y & 0xFFE0) << 8;
v22 |= (vehicle->x & 0xFFE0 | v22) & 0xFFFF;
rct_map_element* map_element;
for (map_element = RCT2_ADDRESS(RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS, rct_map_element*)[v22 >> 3]; map_element->type & MAP_ELEMENT_TYPE_MASK; map_element++);
if (map_element->base_height * 8 > vehicle->z) {
i->var_8 = 0x30;
}
}
}
}
}
}
}
}
}
/**
*
* rct2: 0x006BC2F3
*/
int sub_6BC2F3(rct_vehicle* vehicle)
{
int result = 0;
rct_vehicle* vehicle_temp = vehicle;
do {
result += vehicle_temp->var_46;
} while (vehicle_temp->next_vehicle_on_train != (uint16)-1 && (vehicle_temp = GET_VEHICLE(vehicle_temp->next_vehicle_on_train)));
sint32 v4 = vehicle->velocity;
if (v4 < 0) {
v4 = -v4;
}
result += ((uint16)v4) >> 13;
rct_vehicle_sound* vehicle_sound = &gVehicleSoundList[0];//RCT2_ADDRESS(RCT2_ADDRESS_VEHICLE_SOUND_LIST, rct_vehicle_sound);
while (vehicle_sound->id != vehicle->sprite_index) {
vehicle_sound++;
//if (vehicle_sound >= RCT2_GLOBAL(0x009AF42C, rct_vehicle_sound*)) {
if (vehicle_sound >= &gVehicleSoundList[countof(gVehicleSoundList)]) {
return result;
}
}
return result + 300;
}
/**
*
* rct2: 0x006BBC6B
*/
void vehicle_sounds_update()
{
if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_SOUND_DEVICE, uint32) != -1 && !RCT2_GLOBAL(0x009AF59C, uint8) && RCT2_GLOBAL(0x009AF59D, uint8) & 1) {
RCT2_GLOBAL(0x00F438A4, rct_viewport*) = (rct_viewport*)-1;
rct_window* window = RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*);
rct_viewport* viewport = (rct_viewport*)-1;
for (window = RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*); window >= RCT2_ADDRESS(RCT2_ADDRESS_WINDOW_LIST, rct_window); window--) {
viewport = window->viewport;
if (viewport && viewport->flags & VIEWPORT_FLAG_SOUND_ON) {
break;
}
}
RCT2_GLOBAL(0x00F438A4, rct_viewport*) = viewport;
if (viewport != (rct_viewport*)-1) {
if (window) {
RCT2_GLOBAL(0x00F438A8, rct_window*) = window;
RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 0;
if (viewport->zoom) {
RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 35;
if (viewport->zoom != 1) {
RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8) = 70;
}
}
}
//label12:
//RCT2_GLOBAL(0x00F438B0, rct_vehicle_sound_params**) = &RCT2_GLOBAL(0x00F438B4, rct_vehicle_sound_params*);
gVehicleSoundParamsListEnd = &gVehicleSoundParamsList[0];
for (uint16 i = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_VEHICLE, uint16); i != SPRITE_INDEX_NULL; i = g_sprite_list[i].vehicle.next) {
vehicle_update_sound_params(&g_sprite_list[i].vehicle);
}
//printf("vehicle sounds: %d\n", (gVehicleSoundParamsListEnd - &gVehicleSoundParamsList[0]));
//RCT2_ADDRESS_VEHICLE_SOUND_LIST;
//for (rct_vehicle_sound* vehicle_sound = &RCT2_GLOBAL(RCT2_ADDRESS_VEHICLE_SOUND_LIST, rct_vehicle_sound); vehicle_sound != &RCT2_GLOBAL(0x009AF42C, rct_vehicle_sound); vehicle_sound++) {
for(int i = 0; i < countof(gVehicleSoundList); i++){
rct_vehicle_sound* vehicle_sound = &gVehicleSoundList[i];
if (vehicle_sound->id != (uint16)-1) {
//for (rct_vehicle_sound_params* vehicle_sound_params = &RCT2_GLOBAL(0x00F438B4, rct_vehicle_sound_params); vehicle_sound_params != RCT2_GLOBAL(0x00F438B0, rct_vehicle_sound_params*); vehicle_sound_params++) {
for (rct_vehicle_sound_params* vehicle_sound_params = &gVehicleSoundParamsList[0]; vehicle_sound_params != gVehicleSoundParamsListEnd; vehicle_sound_params++) {
if (vehicle_sound->id == vehicle_sound_params->id) {
goto label26;
}
}
if (vehicle_sound->sound1_id != (uint16)-1) {
#ifdef USE_MIXER
Mixer_Stop_Channel(vehicle_sound->sound1_channel);
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_stop(&vehicle_sound->sound1);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
}
if (vehicle_sound->sound2_id != (uint16)-1) {
#ifdef USE_MIXER
Mixer_Stop_Channel(vehicle_sound->sound2_channel);
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_stop(&vehicle_sound->sound2);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
}
vehicle_sound->id = (uint16)-1;
}
label26:
1;
}
//for (rct_vehicle_sound_params* vehicle_sound_params = &RCT2_GLOBAL(0x00F438B4, rct_vehicle_sound_params); ; vehicle_sound_params++) {
for (rct_vehicle_sound_params* vehicle_sound_params = &gVehicleSoundParamsList[0]; ; vehicle_sound_params++) {
label28:
//if (vehicle_sound_params >= RCT2_GLOBAL(0x00F438B0, rct_vehicle_sound_params*)) {
if (vehicle_sound_params >= gVehicleSoundParamsListEnd) {
return;
}
uint8 vol1 = 0xFF;
uint8 vol2 = 0xFF;
sint16 v = vehicle_sound_params->var_4;
if (v < 0) {
v = -v;
}
if (v > 0xFFF) {
v = 0xFFF;
}
v -= 0x800;
if (v > 0) {
v -= 0x400;
v = -v;
v = (uint16)v / 4;
vol1 = LOBYTE(v);
if (HIBYTE(v) != 0) {
vol1 = 0xFF;
if (HIBYTE(v) < 0) {
vol1 = 0;
}
}
}
sint16 w = vehicle_sound_params->pan;
if (w < 0) {
w = -w;
}
if (w > 0xFFF) {
w = 0xFFF;
}
w -= 0x800;
if (w > 0) {
w -= 0x400;
w = -w;
w = (uint16)w / 4;
vol2 = LOBYTE(w);
if (HIBYTE(w) != 0) {
vol2 = 0xFF;
if (HIBYTE(w) < 0) {
vol2 = 0;
}
}
}
if (vol1 >= vol2) {
vol1 = vol2;
}
if (vol1 < RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8)) {
vol1 = 0;
} else {
vol1 = vol1 - RCT2_GLOBAL(RCT2_ADDRESS_VOLUME_ADJUST_ZOOM, uint8);
}
rct_vehicle_sound* vehicle_sound = &gVehicleSoundList[0];//&RCT2_GLOBAL(RCT2_ADDRESS_VEHICLE_SOUND_LIST, rct_vehicle_sound);
while (vehicle_sound_params->id != vehicle_sound->id) {
vehicle_sound++;
//if (vehicle_sound >= &RCT2_GLOBAL(0x009AF42C, rct_vehicle_sound)) {
if (vehicle_sound >= &gVehicleSoundList[countof(gVehicleSoundList)]) {
vehicle_sound = &gVehicleSoundList[0];//&RCT2_GLOBAL(RCT2_ADDRESS_VEHICLE_SOUND_LIST, rct_vehicle_sound);
int i = 0;
while (vehicle_sound->id != (uint16)-1) {
vehicle_sound++;
i++;
if (i >= countof(gVehicleSoundList)/*RCT2_GLOBAL(0x009AAC75, uint8)*/) {
vehicle_sound_params = (rct_vehicle_sound_params*)((int)vehicle_sound_params + 10);
goto label28;
}
}
vehicle_sound->id = vehicle_sound_params->id;
vehicle_sound->sound1_id = (uint16)-1;
vehicle_sound->sound2_id = (uint16)-1;
vehicle_sound->var_2 = 0x30;
break;
}
}
uint8 v21 = vehicle_sound_params->var_8 & 0xFF;
uint8 v22 = vehicle_sound->var_2 & 0xFF;
if (v22 != v21) {
if (v22 < v21) {
v22 += 4;
} else {
v22 -= 4;
}
}
vehicle_sound->var_2 = v22;
if (vol1 < v22) {
vol1 = 0;
} else {
vol1 = vol1 - v22;
}
// do sound1 stuff, track noise
rct_sprite* sprite = &g_sprite_list[vehicle_sound_params->id];
sint16 volume = sprite->vehicle.sound1_volume;
volume *= vol1;
volume = (uint16)volume / 8;
volume -= 0x1FFF;
if (volume < -10000) {
volume = -10000;
}
if (sprite->vehicle.sound1_id == (uint8)-1) {
if (vehicle_sound->sound1_id != (uint16)-1) {
vehicle_sound->sound1_id = -1;
#ifdef USE_MIXER
Mixer_Stop_Channel(vehicle_sound->sound1_channel);
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_stop(&vehicle_sound->sound1);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
}
} else {
if (vehicle_sound->sound1_id == (uint16)-1) {
goto label69;
}
if (sprite->vehicle.sound1_id != vehicle_sound->sound1_id) {
#ifdef USE_MIXER
Mixer_Stop_Channel(vehicle_sound->sound1_channel);
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_stop(&vehicle_sound->sound1);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
label69:
vehicle_sound->sound1_id = sprite->vehicle.sound1_id;
#ifndef USE_MIXER
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_prepare(sprite->vehicle.sound1_id, &vehicle_sound->sound1, 1, RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_SOUND_SW_BUFFER, uint32));
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
vehicle_sound->sound1_pan = vehicle_sound_params->pan;
vehicle_sound->sound1_volume = volume;
vehicle_sound->sound1_freq = vehicle_sound_params->frequency;
uint16 frequency = vehicle_sound_params->frequency;
if (RCT2_ADDRESS(0x009AF51F, uint8)[2 * sprite->vehicle.sound1_id] & 2) {
frequency = (frequency / 2) + 4000;
}
uint8 looping = RCT2_ADDRESS(0x009AF51E, uint8)[2 * sprite->vehicle.sound1_id];
int pan = vehicle_sound_params->pan;
if (!RCT2_GLOBAL(0x009AAC6D, uint8)) {
pan = 0;
}
#ifdef USE_MIXER
vehicle_sound->sound1_channel = Mixer_Play_Effect(sprite->vehicle.sound1_id, looping ? MIXER_LOOP_INFINITE : MIXER_LOOP_NONE, DStoMixerVolume(volume), DStoMixerPan(pan), DStoMixerRate(frequency), 0);
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_play(&vehicle_sound->sound1, looping, volume, pan, frequency);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
goto label87;
}
if (volume != vehicle_sound->sound1_volume) {
vehicle_sound->sound1_volume = volume;
#ifdef USE_MIXER
Mixer_Channel_Volume(vehicle_sound->sound1_channel, DStoMixerVolume(volume));
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_set_volume(&vehicle_sound->sound1, volume);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
}
if (vehicle_sound_params->pan != vehicle_sound->sound1_pan) {
vehicle_sound->sound1_pan = vehicle_sound_params->pan;
if (RCT2_GLOBAL(0x009AAC6D, uint8)) {
#ifdef USE_MIXER
Mixer_Channel_Pan(vehicle_sound->sound1_channel, DStoMixerPan(vehicle_sound_params->pan));
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_set_pan(&vehicle_sound->sound1, vehicle_sound_params->pan);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
}
}
if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 3) && vehicle_sound_params->frequency != vehicle_sound->sound1_freq) {
vehicle_sound->sound1_freq = vehicle_sound_params->frequency;
uint16 frequency = vehicle_sound_params->frequency;
if (RCT2_GLOBAL(0x009AF51F, uint8*)[2 * sprite->vehicle.sound1_id] & 2) {
frequency = (frequency / 2) + 4000;
}
#ifdef USE_MIXER
Mixer_Channel_Rate(vehicle_sound->sound1_channel, DStoMixerRate(frequency));
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_set_frequency(&vehicle_sound->sound1, frequency);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
}
}
label87: // do sound2 stuff, screams
sprite = &g_sprite_list[vehicle_sound_params->id];
volume = sprite->vehicle.sound2_volume;
volume *= vol1;
volume = (uint16)volume / 8;
volume -= 0x1FFF;
if (volume < -10000) {
volume = -10000;
}
if (sprite->vehicle.sound2_id == (uint8)-1) {
if (vehicle_sound->sound2_id != (uint16)-1) {
vehicle_sound->sound2_id = -1;
#ifdef USE_MIXER
Mixer_Stop_Channel(vehicle_sound->sound2_channel);
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_stop(&vehicle_sound->sound2);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
}
} else {
if (vehicle_sound->sound2_id == (uint16)-1) {
goto label93;
}
if (sprite->vehicle.sound2_id != vehicle_sound->sound2_id) {
#ifdef USE_MIXER
Mixer_Stop_Channel(vehicle_sound->sound2_channel);
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_stop(&vehicle_sound->sound2);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
label93:
vehicle_sound->sound2_id = sprite->vehicle.sound2_id;
#ifndef USE_MIXER
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_prepare(sprite->vehicle.sound2_id, &vehicle_sound->sound2, 1, RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_SOUND_SW_BUFFER, uint32));
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
vehicle_sound->sound2_pan = vehicle_sound_params->pan;
vehicle_sound->sound2_volume = volume;
vehicle_sound->sound2_freq = vehicle_sound_params->frequency;
uint16 frequency = vehicle_sound_params->frequency;
if (RCT2_ADDRESS(0x009AF51F, uint8)[2 * sprite->vehicle.sound2_id] & 1) {
frequency = 12649;
}
frequency = (frequency * 2) - 3248;
if (frequency > 25700) {
frequency = 25700;
}
uint8 looping = RCT2_ADDRESS(0x009AF51E, uint8)[2 * sprite->vehicle.sound2_id];
int pan = vehicle_sound_params->pan;
if (!RCT2_GLOBAL(0x009AAC6D, uint8)) {
pan = 0;
}
#ifdef USE_MIXER
vehicle_sound->sound2_channel = Mixer_Play_Effect(sprite->vehicle.sound2_id, looping ? MIXER_LOOP_INFINITE : MIXER_LOOP_NONE, DStoMixerVolume(volume), DStoMixerPan(pan), DStoMixerRate(frequency), 0);
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_play(&vehicle_sound->sound2, looping, volume, pan, frequency);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
goto label114;
}
if (volume != vehicle_sound->sound2_volume) {
#ifdef USE_MIXER
Mixer_Channel_Volume(vehicle_sound->sound2_channel, DStoMixerVolume(volume));
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_set_volume(&vehicle_sound->sound2, volume);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
vehicle_sound->sound2_volume = volume;
}
if (vehicle_sound_params->pan != vehicle_sound->sound2_pan) {
vehicle_sound->sound2_pan = vehicle_sound_params->pan;
if (RCT2_GLOBAL(0x009AAC6D, uint8)) {
#ifdef USE_MIXER
Mixer_Channel_Pan(vehicle_sound->sound2_channel, DStoMixerPan(vehicle_sound_params->pan));
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_set_pan(&vehicle_sound->sound2, vehicle_sound_params->pan);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
}
}
if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 3) && vehicle_sound_params->frequency != vehicle_sound->sound2_freq) {
vehicle_sound->sound2_freq = vehicle_sound_params->frequency;
if (!(RCT2_ADDRESS(0x009AF51F, uint8)[2 * sprite->vehicle.sound2_id] & 1)) {
uint16 frequency = (vehicle_sound_params->frequency * 2) - 3248;
if (frequency > 25700) {
frequency = 25700;
}
#ifdef USE_MIXER
Mixer_Channel_Rate(vehicle_sound->sound2_channel, DStoMixerRate(frequency));
#else
RCT2_GLOBAL(0x014241BC, uint32) = 1;
sound_set_frequency(&vehicle_sound->sound2, frequency);
RCT2_GLOBAL(0x014241BC, uint32) = 0;
#endif
}
}
}
label114:
1;
}
}
}
}
/**
*
* rct2: 0x006D4204
*/
void vehicle_update_all()
{
uint16 sprite_index;
rct_vehicle *vehicle;
if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 2)
return;
if ((RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 4) && RCT2_GLOBAL(0x0141F570, uint8) != 6)
return;
sprite_index = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_VEHICLE, uint16);
while (sprite_index != SPRITE_INDEX_NULL) {
vehicle = &(g_sprite_list[sprite_index].vehicle);
sprite_index = vehicle->next;
vehicle_update(vehicle);
}
}
/**
*
* rct2: 0x006D77F2
*/
static void vehicle_update(rct_vehicle *vehicle)
{
RCT2_CALLPROC_X(0x006D77F2, 0, 0, 0, 0, (int)vehicle, 0, 0);
}
/**
*
* rct2: 0x006D73D0
* ax: verticalG
* dx: lateralG
* esi: vehicle
*/
void vehicle_get_g_forces(rct_vehicle *vehicle, int *verticalG, int *lateralG)
{
int eax, ebx, ecx, edx, esi, edi, ebp;
esi = (int)vehicle;
RCT2_CALLFUNC_X(0x006D73D0, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp);
if (verticalG != NULL) *verticalG = (sint16)(eax & 0xFFFF);
if (lateralG != NULL) *lateralG = (sint16)(edx & 0xFFFF);
}

133
src/ride/vehicle.h Normal file
View File

@@ -0,0 +1,133 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#ifndef _VEHICLE_H_
#define _VEHICLE_H_
#include "../common.h"
typedef union {
struct {
sint16 width;
sint16 height;
};
sint32 both;
} rct_widthheight;
typedef struct {
uint8 sprite_identifier; // 0x00
uint8 pad_01[0x03];
uint16 next; // 0x04
uint16 previous; // 0x06
uint8 linked_list_type_offset; // 0x08 Valid values are SPRITE_LINKEDLIST_OFFSET_...
uint8 pad_09;
uint16 sprite_index; // 0x0A
uint8 pad_0C[2];
sint16 x; // 0x0E
sint16 y; // 0x10
sint16 z; // 0x12
uint8 pad_14[0x02];
rct_widthheight var_16;
rct_widthheight view; // 0x1A
uint16 var_1E;
uint8 pad_20[0x08];
sint32 velocity; // 0x28
uint8 pad_2C[0x04];
uint8 ride; // 0x30
uint8 var_31;
uint8 pad_32[0x02];
uint16 var_34;
sint16 var_36;
uint8 pad_38[0x06];
uint16 next_vehicle_on_train; // 0x3E
uint32 var_40;
uint16 var_44;
uint16 var_46;
uint16 var_48;
uint8 pad_4A;
uint8 var_4B;
uint8 pad_4C[0x4];
uint8 status; // 0x50
uint8 var_51;
uint16 peep; // 0x52
uint8 pad_54[0x2C];
uint16 var_80;
uint8 pad_82[0x31];
uint8 var_B3;
uint8 pad_B4[0x07];
uint8 sound1_id; // 0xBB
uint8 sound1_volume; // 0xBC
uint8 sound2_id; // 0xBD
uint8 sound2_volume; // 0xBE
sint8 var_BF;
uint8 pad_C0[0x02];
uint8 speed; // 0xC2
uint8 pad_C3[0x09];
uint8 var_CC;
uint8 var_CD;
uint8 var_CE;
uint8 pad_CF[0x07];
uint8 var_D6;
} rct_vehicle;
enum {
VEHICLE_STATUS_MOVING_TO_END_OF_STATION,
VEHICLE_STATUS_WAITING_FOR_PASSENGERS,
VEHICLE_STATUS_WAITING_TO_DEPART,
VEHICLE_STATUS_DEPARTING,
VEHICLE_STATUS_TRAVELLING,
VEHICLE_STATUS_ARRIVING,
VEHICLE_STATUS_UNLOADING_PASSENGERS,
VEHICLE_STATUS_TRAVELLING_07,
VEHICLE_STATUS_CRASHING,
VEHICLE_STATUS_CRASHED,
VEHICLE_STATUS_TRAVELLING_0A,
VEHICLE_STATUS_SWINGING,
VEHICLE_STATUS_ROTATING,
VEHICLE_STATUS_ROTATING_0D,
VEHICLE_STATUS_OPERATING,
VEHICLE_STATUS_SHOWING_FILM,
VEHICLE_STATUS_ROTATING_10,
VEHICLE_STATUS_OPERATING_11,
VEHICLE_STATUS_OPERATING_12,
VEHICLE_STATUS_DOING_CIRCUS_SHOW,
VEHICLE_STATUS_OPERATING_13,
VEHICLE_STATUS_WAITING_FOR_CABLE_LIFT,
VEHICLE_STATUS_TRAVELLING_15,
VEHICLE_STATUS_STOPPING,
VEHICLE_STATUS_WAITING_FOR_PASSENGERS_17,
VEHICLE_STATUS_WAITING_TO_START,
VEHICLE_STATUS_STARTING,
VEHICLE_STATUS_OPERATING_1A,
VEHICLE_STATUS_STOPPING_1B,
VEHICLE_STATUS_UNLOADING_PASSENGERS_1C,
VEHICLE_STATUS_STOPPED_BY_BLOCK_BRAKES
};
void vehicle_update_all();
int sub_6BC2F3(rct_vehicle* vehicle);
void sub_6BB9FF(rct_vehicle* vehicle);
void vehicle_sounds_update();
void vehicle_get_g_forces(rct_vehicle *vehicle, int *verticalG, int *lateralG);
/** Helper macro until rides are stored in this module. */
#define GET_VEHICLE(sprite_index) &(g_sprite_list[sprite_index].vehicle)
#endif

View File

@@ -1,188 +0,0 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#include <windows.h>
#include <string.h>
#include "addresses.h"
#include "rct2.h"
#include "sawyercoding.h"
static int decode_chunk_rle(char *buffer, int length);
static int decode_chunk_repeat(char *buffer, int length);
static void decode_chunk_rotate(char *buffer, int length);
/**
*
* rct2: 0x00676FD2
*/
int sawyercoding_validate_checksum(FILE *file)
{
uint32 i, checksum, fileChecksum, dataSize, bufferSize;
uint8 buffer[1024];
// Get data size
fseek(file, 0, SEEK_END);
dataSize = ftell(file);
if (dataSize < 8)
return 0;
dataSize -= 4;
// Calculate checksum
fseek(file, 0, SEEK_SET);
checksum = 0;
do {
bufferSize = min(dataSize, 1024);
if (fread(buffer, bufferSize, 1, file) != 1)
return 0;
for (i = 0; i < bufferSize; i++)
checksum += buffer[i];
dataSize -= bufferSize;
} while (dataSize != 0);
// Read file checksum
if (fread(&fileChecksum, sizeof(fileChecksum), 1, file) != 1)
return 0;
// Reset file position
fseek(file, 0, SEEK_SET);
// Validate
return checksum == fileChecksum;
}
/**
*
* rct2: 0x0067685F
* buffer (esi)
*/
int sawyercoding_read_chunk(FILE *file, uint8 *buffer)
{
sawyercoding_chunk_header chunkHeader;
// Read chunk header
if (fread(&chunkHeader, sizeof(sawyercoding_chunk_header), 1, file) != 1) {
RCT2_ERROR("Unable to read chunk header!");
return -1;
}
// Read chunk data
if (fread(buffer, chunkHeader.length, 1, file) != 1) {
RCT2_ERROR("Unable to read chunk data!");
return -1;
}
// Decode chunk data
switch (chunkHeader.encoding) {
case CHUNK_ENCODING_RLE:
chunkHeader.length = decode_chunk_rle(buffer, chunkHeader.length);
break;
case CHUNK_ENCODING_RLECOMPRESSED:
chunkHeader.length = decode_chunk_rle(buffer, chunkHeader.length);
chunkHeader.length = decode_chunk_repeat(buffer, chunkHeader.length);
break;
case CHUNK_ENCODING_ROTATE:
decode_chunk_rotate(buffer, chunkHeader.length);
break;
}
// Set length
RCT2_GLOBAL(0x009E3828, uint32) = chunkHeader.length;
return chunkHeader.length;
}
/**
*
* rct2: 0x0067693A
*/
static int decode_chunk_rle(char *buffer, int length)
{
int i, j, count;
unsigned char *src, *dst, rleCodeByte;
// Backup buffer
src = malloc(length);
memcpy(src, buffer, length);
dst = buffer;
for (i = 0; i < length; i++) {
rleCodeByte = src[i];
if (rleCodeByte & 128) {
i++;
count = 257 - rleCodeByte;
for (j = 0; j < count; j++)
*dst++ = src[i];
} else {
for (j = 0; j <= rleCodeByte; j++)
*dst++ = src[++i];
}
}
// Free backup buffer
free(src);
// Return final size
return (char*)dst - buffer;
}
/**
*
* rct2: 0x006769F1
*/
static int decode_chunk_repeat(char *buffer, int length)
{
int i, j, count;
unsigned char *src, *dst, *copyOffset;
// Backup buffer
src = malloc(length);
memcpy(src, buffer, length);
dst = buffer;
for (i = 0; i < length; i++) {
if (src[i] == 0xFF) {
*dst++ = src[++i];
} else {
count = (src[i] & 7) + 1;
copyOffset = dst + (int)(src[i] >> 3) - 32;
for (j = 0; j < count; j++)
*dst++ = *copyOffset++;
}
}
// Free backup buffer
free(src);
// Return final size
return (char*)dst - buffer;
}
/**
*
* rct2: 0x006768F4
*/
static void decode_chunk_rotate(char *buffer, int length)
{
int i, code = 1;
for (i = 0; i < length; i++) {
buffer[i] = ror8(buffer[i], code);
code = (code + 2) % 8;
}
}

View File

@@ -21,22 +21,21 @@
#include <windows.h>
#include <string.h>
#include "addresses.h"
#include "award.h"
#include "date.h"
#include "finance.h"
#include "game.h"
#include "map.h"
#include "marketing.h"
#include "news_item.h"
#include "interface/viewport.h"
#include "localisation/date.h"
#include "localisation/localisation.h"
#include "management/award.h"
#include "management/finance.h"
#include "management/marketing.h"
#include "management/news_item.h"
#include "object.h"
#include "park.h"
#include "rct2.h"
#include "ride.h"
#include "sawyercoding.h"
#include "ride/ride.h"
#include "scenario.h"
#include "string_ids.h"
#include "sprite.h"
#include "viewport.h"
#include "util/sawyercoding.h"
#include "world/map.h"
#include "world/park.h"
#include "world/sprite.h"
/**
* Loads only the basic information from a scenario.
@@ -109,7 +108,7 @@ int scenario_load(const char *path)
if (s6Header->num_packed_objects > 0) {
j = 0;
for (i = 0; i < s6Header->num_packed_objects; i++)
j += object_load_packed();
j += object_load_packed(file);
if (j > 0)
object_list_load();
}
@@ -192,7 +191,7 @@ int scenario_load_and_play_from_path(const char *path)
srand0 = RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_SRAND_0, uint32) ^ timeGetTime();
srand1 = RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_SRAND_1, uint32) ^ timeGetTime();
RCT2_CALLPROC_EBPSAFE(0x006CBCC3);
window_close_construction_windows();
if (!scenario_load(path))
return 0;
@@ -425,13 +424,7 @@ void scenario_objective8_check()
ride->status == RIDE_STATUS_OPEN &&
ride->excitement >= RIDE_RATING(7,00) && type_already_counted[subtype_id] == 0){
// this calculates the length, no idea why it's done so complicated though.
uint8 limit = ride->pad_088[63];
uint32 sum = 0;
for (int j = 0; j < limit; ++j) {
sum += ((uint32*)&ride->pad_088[92])[j];
}
if ((sum >> 16) > (uint32)objective_length) {
if ((ride_get_total_length(ride) >> 16) > objective_length) {
type_already_counted[subtype_id]++;
rcs++;
}

View File

@@ -176,14 +176,15 @@ typedef struct {
uint8 guests_in_park_history[32];
// SC6[10]
uint16 word_01357CF2;
uint32 word_01357CF4;
uint8 active_research_types;
uint8 research_progress_stage;
uint32 dword_01357CF4;
uint8 byte_01357CF8[1000];
uint32 dword_013580E0[32];
uint16 word_013580E4[16];
uint8 byte_013580E6;
uint8 byte_013580E7;
uint8 byte_013580E8;
uint32 dword_013580E0;
uint16 research_progress;
uint8 next_research_category;
uint8 next_research_expected_day;
uint8 next_research_expected_month;
uint8 byte_013580E9;
uint16 park_size;
uint16 guest_generation_probability;
@@ -330,7 +331,7 @@ typedef struct {
uint8 ride_measurements[0x25860];
uint32 dword_13B0E6C;
uint16 word_13B0E70;
uint32 dword_13B0E72[0x6600];
uint32 dword_13B0E72[0x6600]; // 512 bytes per staff peep
uint8 byte_13CA672[116];
uint8 byte_13CA6E6[84];
uint8 byte_13CA73A[4];

View File

@@ -44,6 +44,8 @@ enum {
SPR_RESIZE = 5058,
SPR_CONSTRUCTION = 5164,
SPR_DEMOLISH = 5165,
SPR_HEARING_VIEWPORT = 5166,
SPR_LOCATE = 5167,
SPR_RENAME = 5168,

View File

@@ -22,23 +22,22 @@
#include <string.h>
#include <time.h>
#include "addresses.h"
#include "audio.h"
#include "audio/audio.h"
#include "config.h"
#include "climate.h"
#include "date.h"
#include "game.h"
#include "gfx.h"
#include "intro.h"
#include "map.h"
#include "news_item.h"
#include "park.h"
#include "rct2.h"
#include "ride.h"
#include "scenario.h"
#include "sprite.h"
#include "string_ids.h"
#include "viewport.h"
#include "drawing/drawing.h"
#include "editor.h"
#include "localisation/date.h"
#include "localisation/localisation.h"
#include "game.h"
#include "interface/viewport.h"
#include "intro.h"
#include "management/news_item.h"
#include "ride/ride.h"
#include "scenario.h"
#include "world/climate.h"
#include "world/map.h"
#include "world/park.h"
#include "world/sprite.h"
static const int gOldMusic = 0;
static const int gRandomShowcase = 0;
@@ -109,7 +108,7 @@ void title_load()
RCT2_CALLPROC_EBPSAFE(0x006DFEE4);
window_new_ride_init_vars();
window_guest_list_init_vars_b();
window_staff_init_vars();
window_staff_list_init_vars();
map_update_tile_pointers(); //RCT2_CALLPROC_EBPSAFE(0x0068AFFD);
reset_0x69EBE4();// RCT2_CALLPROC_EBPSAFE(0x0069EBE4);
viewport_init_all();
@@ -290,7 +289,7 @@ void title_update()
// RCT2_CALLPROC_EBPSAFE(0x006EA627); // window_manager_handle_input();
game_handle_input();
update_water_animation();
update_palette_effects();
update_rain_animation();
if (RCT2_GLOBAL(0x009AAC73, uint8) != 255) {

View File

@@ -20,8 +20,9 @@
#include <string.h>
#include "addresses.h"
#include "localisation/localisation.h"
#include "tutorial.h"
#include "window_error.h"
#include "windows/error.h"
/**
*
@@ -30,7 +31,7 @@
void tutorial_start(int type)
{
strcpy((char*)0x009BC677, "Tutorial not implemented.");
window_error_open(3165, -1);
window_error_open(3165, STR_NONE);
// RCT2_CALLPROC_X(0x0066ECC1, type, 0, 0, 0, 0, 0, 0);
}

305
src/util/sawyercoding.c Normal file
View File

@@ -0,0 +1,305 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#include <windows.h>
#include <string.h>
#include "../addresses.h"
#include "sawyercoding.h"
static int decode_chunk_rle(uint8* src_buffer, uint8* dst_buffer, int length);
static int decode_chunk_repeat(char *buffer, int length);
static void decode_chunk_rotate(char *buffer, int length);
int encode_chunk_rle(char *src_buffer, char *dst_buffer, int length);
void encode_chunk_rotate(char *buffer, int length);
int sawyercoding_calculate_checksum(uint8* buffer, uint32 length){
int checksum = 0;
do {
int bufferSize = min(length , 1024);
for (int i = 0; i < bufferSize; i++)
checksum += buffer[i];
length -= bufferSize;
} while (length != 0);
return checksum;
}
/**
*
* rct2: 0x00676FD2
*/
int sawyercoding_validate_checksum(FILE *file)
{
uint32 i, checksum, fileChecksum, dataSize, bufferSize;
uint8 buffer[1024];
// Get data size
fseek(file, 0, SEEK_END);
dataSize = ftell(file);
if (dataSize < 8)
return 0;
dataSize -= 4;
// Calculate checksum
fseek(file, 0, SEEK_SET);
checksum = 0;
do {
bufferSize = min(dataSize, 1024);
if (fread(buffer, bufferSize, 1, file) != 1)
return 0;
for (i = 0; i < bufferSize; i++)
checksum += buffer[i];
dataSize -= bufferSize;
} while (dataSize != 0);
// Read file checksum
if (fread(&fileChecksum, sizeof(fileChecksum), 1, file) != 1)
return 0;
// Reset file position
fseek(file, 0, SEEK_SET);
// Validate
return checksum == fileChecksum;
}
/**
*
* rct2: 0x0067685F
* buffer (esi)
*/
int sawyercoding_read_chunk(FILE *file, uint8 *buffer)
{
sawyercoding_chunk_header chunkHeader;
// Read chunk header
if (fread(&chunkHeader, sizeof(sawyercoding_chunk_header), 1, file) != 1) {
RCT2_ERROR("Unable to read chunk header!");
return -1;
}
uint8* src_buffer = malloc(chunkHeader.length);
// Read chunk data
if (fread(src_buffer, chunkHeader.length, 1, file) != 1) {
free(src_buffer);
RCT2_ERROR("Unable to read chunk data!");
return -1;
}
// Decode chunk data
switch (chunkHeader.encoding) {
case CHUNK_ENCODING_NONE:
memcpy(buffer, src_buffer, chunkHeader.length);
break;
case CHUNK_ENCODING_RLE:
chunkHeader.length = decode_chunk_rle(src_buffer, buffer, chunkHeader.length);
break;
case CHUNK_ENCODING_RLECOMPRESSED:
chunkHeader.length = decode_chunk_rle(src_buffer, buffer, chunkHeader.length);
chunkHeader.length = decode_chunk_repeat(buffer, chunkHeader.length);
break;
case CHUNK_ENCODING_ROTATE:
memcpy(buffer, src_buffer, chunkHeader.length);
decode_chunk_rotate(buffer, chunkHeader.length);
break;
}
free(src_buffer);
// Set length
RCT2_GLOBAL(0x009E3828, uint32) = chunkHeader.length;
return chunkHeader.length;
}
/**
*
* rct2: 0x0067693A
*/
static int decode_chunk_rle(uint8* src_buffer, uint8* dst_buffer, int length)
{
int i, j, count;
uint8 *dst, rleCodeByte;
dst = dst_buffer;
for (i = 0; i < length; i++) {
rleCodeByte = src_buffer[i];
if (rleCodeByte & 128) {
i++;
count = 257 - rleCodeByte;
for (j = 0; j < count; j++)
*dst++ = src_buffer[i];
} else {
for (j = 0; j <= rleCodeByte; j++)
*dst++ = src_buffer[++i];
}
}
// Return final size
return dst - dst_buffer;
}
/**
*
* rct2: 0x006769F1
*/
static int decode_chunk_repeat(char *buffer, int length)
{
int i, j, count;
unsigned char *src, *dst, *copyOffset;
// Backup buffer
src = malloc(length);
memcpy(src, buffer, length);
dst = buffer;
for (i = 0; i < length; i++) {
if (src[i] == 0xFF) {
*dst++ = src[++i];
} else {
count = (src[i] & 7) + 1;
copyOffset = dst + (int)(src[i] >> 3) - 32;
for (j = 0; j < count; j++)
*dst++ = *copyOffset++;
}
}
// Free backup buffer
free(src);
// Return final size
return (char*)dst - buffer;
}
/**
*
* rct2: 0x006768F4
*/
static void decode_chunk_rotate(char *buffer, int length)
{
int i, code = 1;
for (i = 0; i < length; i++) {
buffer[i] = ror8(buffer[i], code);
code = (code + 2) % 8;
}
}
/**
*
* rct2: 0x006762E1
*
*/
int sawyercoding_write_chunk_buffer(uint8 *dst_file, uint8* buffer, sawyercoding_chunk_header chunkHeader){
uint8* encode_buffer;
switch (chunkHeader.encoding){
case CHUNK_ENCODING_NONE:
memcpy(dst_file, &chunkHeader, sizeof(sawyercoding_chunk_header));
dst_file += sizeof(sawyercoding_chunk_header);
memcpy(dst_file, buffer, chunkHeader.length);
//fwrite(&chunkHeader, sizeof(sawyercoding_chunk_header), 1, file);
//fwrite(buffer, 1, chunkHeader.length, file);
break;
case CHUNK_ENCODING_RLE:
encode_buffer = malloc(0x600000);
chunkHeader.length = encode_chunk_rle(buffer, encode_buffer, chunkHeader.length);
memcpy(dst_file, &chunkHeader, sizeof(sawyercoding_chunk_header));
dst_file += sizeof(sawyercoding_chunk_header);
memcpy(dst_file, encode_buffer, chunkHeader.length);
free(encode_buffer);
break;
case CHUNK_ENCODING_RLECOMPRESSED:
RCT2_ERROR("This has not been implemented");
return -1;
//chunkHeader.length = decode_chunk_rle(src_buffer, buffer, chunkHeader.length);
//chunkHeader.length = decode_chunk_repeat(buffer, chunkHeader.length);
break;
case CHUNK_ENCODING_ROTATE:
encode_chunk_rotate(buffer, chunkHeader.length);
memcpy(dst_file, &chunkHeader, sizeof(sawyercoding_chunk_header));
dst_file += sizeof(sawyercoding_chunk_header);
memcpy(dst_file, buffer, chunkHeader.length);
break;
}
return chunkHeader.length + sizeof(sawyercoding_chunk_header);
}
/**
* Ensure dst_buffer is bigger than src_buffer then resize afterwards
* returns length of dst_buffer
*/
int encode_chunk_rle(char *src_buffer, char *dst_buffer, int length)
{
char* src = src_buffer;
char* dst = dst_buffer;
char* end_src = src + length;
uint8 count = 0;
char* src_norm_start = src;
while (src < end_src - 1){
if ((count && *src == src[1]) || count > 120){
*dst++ = count - 1;
for (; count != 0; --count){
*dst++ = *src_norm_start++;
}
}
if (*src == src[1]){
for (; (count < 120) && ((src + count) < end_src); count++){
if (*src != src[count]) break;
}
*dst++ = 257 - count;
*dst++ = *src;
src += count;
src_norm_start = src;
count = 0;
}
else{
count++;
src++;
}
}
if (src == end_src - 1)count++;
if (count){
*dst++ = count - 1;
for (; count != 0; --count){
*dst++ = *src_norm_start++;
}
}
return dst - dst_buffer;
}
/**
*
*
*/
void encode_chunk_rotate(char *buffer, int length)
{
int i, code = 1;
for (i = 0; i < length; i++) {
buffer[i] = rol8(buffer[i], code);
code = (code + 2) % 8;
}
}

View File

@@ -22,7 +22,7 @@
#define _SAWYERCODING_H_
#include <stdio.h>
#include "rct2.h"
#include "../common.h"
typedef struct {
uint8 encoding;
@@ -37,6 +37,8 @@ enum {
};
int sawyercoding_validate_checksum(FILE *file);
int sawyercoding_calculate_checksum(uint8* buffer, uint32 length);
int sawyercoding_read_chunk(FILE *file, uint8 *buffer);
int sawyercoding_write_chunk_buffer(uint8 *dst_file, uint8* buffer, sawyercoding_chunk_header chunkHeader);
#endif

View File

@@ -1,59 +0,0 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#include "addresses.h"
#include "sprite.h"
#include "vehicle.h"
static void vehicle_update(rct_vehicle *vehicle);
/**
*
* rct2: 0x006D4204
*/
void vehicle_update_all()
{
uint16 sprite_index;
rct_vehicle *vehicle;
if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 2)
return;
if ((RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 4) && RCT2_GLOBAL(0x0141F570, uint8) != 6)
return;
sprite_index = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_VEHICLE, uint16);
while (sprite_index != SPRITE_INDEX_NULL) {
vehicle = &(g_sprite_list[sprite_index].vehicle);
sprite_index = vehicle->next;
vehicle_update(vehicle);
}
}
/**
*
* rct2: 0x006D77F2
*/
static void vehicle_update(rct_vehicle *vehicle)
{
RCT2_CALLPROC_X(0x006D77F2, 0, 0, 0, 0, (int)vehicle, 0, 0);
}

View File

@@ -1,77 +0,0 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#ifndef _VEHICLE_H_
#define _VEHICLE_H_
#include "rct2.h"
typedef union {
struct {
uint16 width;
uint16 height;
};
uint32 both;
} rct_widthheight;
typedef struct {
uint8 sprite_identifier; // 0x00
uint8 pad_01[0x03];
uint16 next; // 0x04
uint16 previous; // 0x06
uint8 linked_list_type_offset; // 0x08 Valid values are SPRITE_LINKEDLIST_OFFSET_...
uint8 pad_09;
uint16 sprite_index; // 0x0A
uint8 pad_0C[2];
sint16 x; // 0x0E
sint16 y; // 0x10
sint16 z; // 0x12
uint8 pad_14[0x02];
rct_widthheight var_16;
rct_widthheight view; // 0x1A
uint16 var_1E;
uint8 pad_20[0x08];
uint32 var_28;
uint8 pad_2C[0x04];
uint8 ride; // 0x30
uint8 var_31;
uint8 pad_32[0x0C];
uint16 next_vehicle_on_train; // 0x3E
uint8 pad_40[0x08];
uint16 var_48;
uint8 pad_4A[0x06];
uint8 var_50;
uint8 var_51;
uint8 pad_52[0x2E];
uint8 var_BB;
uint8 var_BC;
uint8 var_BD;
uint8 pad_BE[0x0E];
uint8 var_CC;
uint8 pad_CD[0x09];
uint8 var_D6;
} rct_vehicle;
void vehicle_update_all();
/** Helper macro until rides are stored in this module. */
#define GET_VEHICLE(sprite_index) &(g_sprite_list[sprite_index].vehicle)
#endif

View File

@@ -1,924 +0,0 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John, Duncan Frost
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#include "addresses.h"
#include "game.h"
#include "map.h"
#include "ride.h"
#include "peep.h"
#include "string_ids.h"
#include "sprite.h"
#include "sprites.h"
#include "viewport.h"
#include "widget.h"
#include "window.h"
#include "window_dropdown.h"
enum WINDOW_PEEP_PAGE {
WINDOW_PEEP_OVERVIEW,
WINDOW_PEEP_STATS,
WINDOW_PEEP_RIDES,
WINDOW_PEEP_FINANCE,
WINDOW_PEEP_THOUGHTS,
WINDOW_PEEP_INVENTORY
};
enum WINDOW_PEEP_WIDGET_IDX {
WIDX_BACKGROUND,
WIDX_TITLE,
WIDX_CLOSE,
WIDX_PAGE_BACKGROUND,
WIDX_TAB_1,
WIDX_TAB_2,
WIDX_TAB_3,
WIDX_TAB_4,
WIDX_TAB_5,
WIDX_TAB_6,
WIDX_MARQUEE = 10,
WIDX_VIEWPORT,
WIDX_ACTION_LBL,
WIDX_PICKUP,
WIDX_RENAME,
WIDX_LOCATE,
WIDX_TRACK,
WIDX_RIDE_SCROLL = 10
};
void window_peep_emptysub(){};
rct_widget window_peep_overview_widgets[] = {
{ WWT_FRAME, 0, 0, 191, 0, 156, 0x0FFFFFFFF, STR_NONE }, // Panel / Background
{ WWT_CAPTION, 0, 1, 190, 1, 14, 865, STR_WINDOW_TITLE_TIP }, // Title
{ WWT_CLOSEBOX, 0, 179, 189, 2, 13, 824, STR_CLOSE_WINDOW_TIP }, // Close x button
{ WWT_RESIZE, 1, 1, 191, 43, 156, 0x0FFFFFFFF, STR_NONE }, // Resize
{ WWT_TAB, 1, 3, 33, 17, 43, 0x2000144E, 1938 }, // Tab 1
{ WWT_TAB, 1, 73, 64, 17, 43, 0x2000144E, 1940}, // Tab 2
{ WWT_TAB, 1, 65, 95, 17, 43, 0x2000144E, 1941}, // Tab 3
{ WWT_TAB, 1, 96, 126, 17, 43, 0x2000144E, 1942}, // Tab 4
{ WWT_TAB, 1, 127, 157, 17, 43, 0x2000144E, 1943}, // Tab 5
{ WWT_TAB, 1, 158, 188, 17, 43, 0x2000144E, 1944}, // Tab 6
{ WWT_12, 1, 3, 166, 45, 56, 0x0FFFFFFFF, STR_NONE}, // Label Thought marquee
{ WWT_VIEWPORT, 1, 3, 166, 57, 143, 0x0FFFFFFFF, STR_NONE }, // Viewport
{ WWT_12, 1, 3, 166, 144, 154, 0x0FFFFFFFF, STR_NONE}, // Label Action
{ WWT_FLATBTN, 1, 167, 190, 45, 68, 0x1436, 1706}, // Pickup Button
{ WWT_FLATBTN, 1, 167, 190, 69, 92, SPR_RENAME, 1055}, // Rename Button
{ WWT_FLATBTN, 1, 167, 190, 93, 116, SPR_LOCATE, STR_LOCATE_SUBJECT_TIP},// Locate Button
{ WWT_FLATBTN, 1, 167, 190, 117, 140, SPR_TRACK_PEEP, 1930}, // Track Button
{ WIDGETS_END },
};
rct_widget window_peep_stats_widgets[] = {
{WWT_FRAME, 0, 0, 191, 0, 156, -1, STR_NONE},
{WWT_CAPTION, 0, 1, 190, 1, 14, 865, STR_WINDOW_TITLE_TIP},
{WWT_CLOSEBOX, 0, 179, 189, 2, 13, 824, STR_CLOSE_WINDOW_TIP},
{WWT_RESIZE, 1, 0, 191, 43, 156, -1, STR_NONE},
{WWT_TAB, 1, 3, 33, 17, 43, 0x2000144E, 1938},
{WWT_TAB, 1, 34, 64, 17, 43, 0x2000144E, 1940},
{WWT_TAB, 1, 65, 95, 17, 43, 0x2000144E, 1941},
{WWT_TAB, 1, 96, 126, 17, 43, 0x2000144E, 1942},
{WWT_TAB, 1, 127, 157, 17, 43, 0x2000144E, 1943},
{WWT_TAB, 1, 158, 188, 17, 43, 0x2000144E, 1944},
{WIDGETS_END},
};
rct_widget window_peep_rides_widgets[] = {
{WWT_FRAME, 0, 0, 191, 0, 156, -1, STR_NONE},
{WWT_CAPTION, 0, 1, 190, 1, 14, 865, STR_WINDOW_TITLE_TIP},
{WWT_CLOSEBOX, 0, 179, 189, 2, 13, 824, STR_CLOSE_WINDOW_TIP},
{WWT_RESIZE, 1, 0, 191, 43, 156, -1, STR_NONE},
{WWT_TAB, 1, 3, 33, 17, 43, 0x2000144E, 1938},
{WWT_TAB, 1, 34, 64, 17, 43, 0x2000144E, 1940},
{WWT_TAB, 1, 65, 95, 17, 43, 0x2000144E, 1941},
{WWT_TAB, 1, 96, 126, 17, 43, 0x2000144E, 1942},
{WWT_TAB, 1, 127, 157, 17, 43, 0x2000144E, 1943},
{WWT_TAB, 1, 158, 188, 17, 43, 0x2000144E, 1944},
{WWT_SCROLL, 1, 3, 188, 57, 143, 2, STR_NONE},
{WIDGETS_END},
};
rct_widget window_peep_finance_widgets[] = {
{WWT_FRAME, 0, 0, 191, 0, 156, -1, STR_NONE},
{WWT_CAPTION, 0, 1, 190, 1, 14, 865, STR_WINDOW_TITLE_TIP},
{WWT_CLOSEBOX, 0, 179, 189, 2, 13, 824, STR_CLOSE_WINDOW_TIP},
{WWT_RESIZE, 1, 0, 191, 43, 156, -1, STR_NONE},
{WWT_TAB, 1, 3, 33, 17, 43, 0x2000144E, 1938},
{WWT_TAB, 1, 34, 64, 17, 43, 0x2000144E, 1940},
{WWT_TAB, 1, 65, 95, 17, 43, 0x2000144E, 1941},
{WWT_TAB, 1, 96, 126, 17, 43, 0x2000144E, 1942},
{WWT_TAB, 1, 127, 157, 17, 43, 0x2000144E, 1943},
{WWT_TAB, 1, 158, 188, 17, 43, 0x2000144E, 1944},
{WIDGETS_END},
};
rct_widget window_peep_thoughts_widgets[] = {
{WWT_FRAME, 0, 0, 191, 0, 156, -1, STR_NONE},
{WWT_CAPTION, 0, 1, 190, 1, 14, 865, STR_WINDOW_TITLE_TIP},
{WWT_CLOSEBOX, 0, 179, 189, 2, 13, 824, STR_CLOSE_WINDOW_TIP},
{WWT_RESIZE, 1, 0, 191, 43, 156, -1, STR_NONE},
{WWT_TAB, 1, 3, 33, 17, 43, 0x2000144E, 1938},
{WWT_TAB, 1, 34, 64, 17, 43, 0x2000144E, 1940},
{WWT_TAB, 1, 65, 95, 17, 43, 0x2000144E, 1941},
{WWT_TAB, 1, 96, 126, 17, 43, 0x2000144E, 1942},
{WWT_TAB, 1, 127, 157, 17, 43, 0x2000144E, 1943},
{WWT_TAB, 1, 158, 188, 17, 43, 0x2000144E, 1944},
{WIDGETS_END},
};
rct_widget window_peep_inventory_widgets[] = {
{WWT_FRAME, 0, 0, 191, 0, 156, -1, STR_NONE},
{WWT_CAPTION, 0, 1, 190, 1, 14, 865, STR_WINDOW_TITLE_TIP},
{WWT_CLOSEBOX, 0, 179, 189, 2, 13, 824, STR_CLOSE_WINDOW_TIP},
{WWT_RESIZE, 1, 0, 191, 43, 156, -1, STR_NONE},
{WWT_TAB, 1, 3, 33, 17, 43, 0x2000144E, 1938},
{WWT_TAB, 1, 34, 64, 17, 43, 0x2000144E, 1940},
{WWT_TAB, 1, 65, 95, 17, 43, 0x2000144E, 1941},
{WWT_TAB, 1, 96, 126, 17, 43, 0x2000144E, 1942},
{WWT_TAB, 1, 127, 157, 17, 43, 0x2000144E, 1943},
{WWT_TAB, 1, 158, 188, 17, 43, 0x2000144E, 1944},
{WIDGETS_END},
};
//0x981D0C
rct_widget *window_peep_page_widgets[] = {
window_peep_overview_widgets,
window_peep_stats_widgets,
window_peep_rides_widgets,
window_peep_finance_widgets,
window_peep_thoughts_widgets,
window_peep_inventory_widgets
};
void window_peep_set_page(rct_window* w, int page);
void window_peep_disable_widgets(rct_window* w);
void window_peep_viewport_init(rct_window* w);
void window_peep_close();
void window_peep_resize();
void window_peep_overview_mouse_up();
void window_peep_overview_paint();
void window_peep_overview_invalidate();
void window_peep_overview_viewport_init_wrapper();
static void* window_peep_overview_events[] = {
window_peep_close,
window_peep_overview_mouse_up,
window_peep_resize,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
(void*)0x696F45,
window_peep_emptysub,
window_peep_emptysub,
(void*)0x696A5F,
(void*)0x696A54,
window_peep_emptysub,
window_peep_emptysub,
(void*)0x696A49,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
(void*)0x696A6A,
window_peep_overview_viewport_init_wrapper,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_overview_invalidate, //Invalidate
window_peep_overview_paint, //Paint
window_peep_emptysub
};
static void* window_peep_stats_events[] = {
window_peep_emptysub,
(void*) 0x0069744F, //mouse_up
(void*) 0x00697488, //resize
window_peep_emptysub,
window_peep_emptysub,
(void*) 0x006974ED,
(void*) 0x0069746A,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
(void*) 0x0069707D, //invalidate
(void*) 0x0069711D, //paint
window_peep_emptysub
};
static void* window_peep_rides_events[] = {
window_peep_emptysub,
(void*) 0x00697795, //mouse_up
(void*) 0x006978F4, //resize
window_peep_emptysub,
window_peep_emptysub,
(void*) 0x00697959,
(void*) 0x006977B0,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
(void*) 0x0069784E,
(void*) 0x006978CC,
window_peep_emptysub,
(void*) 0x0069789C,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
(void*) 0x00697844,
window_peep_emptysub,
window_peep_emptysub,
(void*) 0x0069757A, //invalidate
(void*) 0x00697637, //paint
(void*) 0x006976FC
};
static void* window_peep_finance_events[] = {
window_peep_emptysub,
(void*) 0x00697BDD, //mouse_up
(void*) 0x00697C16, //resize
window_peep_emptysub,
window_peep_emptysub,
(void*) 0x00697C7B,
(void*) 0x00697BF8,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
(void*) 0x00697968, //invalidate
(void*) 0x00697A08, //paint
window_peep_emptysub
};
static void* window_peep_thoughts_events[] = {
window_peep_emptysub,
(void*) 0x00697E18, //mouse_up
(void*) 0x00697E33, //resize
window_peep_emptysub,
window_peep_emptysub,
(void*) 0x00697ED2,
(void*) 0x00697EB4,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
(void*) 0x00697C8A, //invalidate
(void*) 0x00697D2A, //paint
window_peep_emptysub
};
static void* window_peep_inventory_events[] = {
window_peep_emptysub,
(void*) 0x00698279, //mouse_up
(void*) 0x00698294, //resize
window_peep_emptysub,
window_peep_emptysub,
(void*) 0x00698333,
(void*) 0x00698315,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
window_peep_emptysub,
(void*) 0x00697EE1, //invalidate
(void*) 0x00697F81, //paint
window_peep_emptysub
};
//0x981D24
void* window_peep_page_events[] = {
window_peep_overview_events,
window_peep_stats_events,
window_peep_rides_events,
window_peep_finance_events,
window_peep_thoughts_events,
window_peep_inventory_events
};
//0x981D3C
uint32 window_peep_page_enabled_widgets[] = {
(1 << WIDX_CLOSE) |
(1 << WIDX_TAB_1) |
(1 << WIDX_TAB_2) |
(1 << WIDX_TAB_3) |
(1 << WIDX_TAB_4) |
(1 << WIDX_TAB_5) |
(1 << WIDX_TAB_6) |
(1 << WIDX_RENAME)|
(1 << WIDX_PICKUP)|
(1 << WIDX_LOCATE)|
(1 << WIDX_TRACK),
(1 << WIDX_CLOSE) |
(1 << WIDX_TAB_1) |
(1 << WIDX_TAB_2) |
(1 << WIDX_TAB_3) |
(1 << WIDX_TAB_4) |
(1 << WIDX_TAB_5) |
(1 << WIDX_TAB_6),
(1 << WIDX_CLOSE) |
(1 << WIDX_TAB_1) |
(1 << WIDX_TAB_2) |
(1 << WIDX_TAB_3) |
(1 << WIDX_TAB_4) |
(1 << WIDX_TAB_5) |
(1 << WIDX_TAB_6) |
(1 << WIDX_RIDE_SCROLL),
(1 << WIDX_CLOSE) |
(1 << WIDX_TAB_1) |
(1 << WIDX_TAB_2) |
(1 << WIDX_TAB_3) |
(1 << WIDX_TAB_4) |
(1 << WIDX_TAB_5) |
(1 << WIDX_TAB_6),
(1 << WIDX_CLOSE) |
(1 << WIDX_TAB_1) |
(1 << WIDX_TAB_2) |
(1 << WIDX_TAB_3) |
(1 << WIDX_TAB_4) |
(1 << WIDX_TAB_5) |
(1 << WIDX_TAB_6),
(1 << WIDX_CLOSE) |
(1 << WIDX_TAB_1) |
(1 << WIDX_TAB_2) |
(1 << WIDX_TAB_3) |
(1 << WIDX_TAB_4) |
(1 << WIDX_TAB_5) |
(1 << WIDX_TAB_6)
};
/**
* rct2: 0x006989E9
*
*/
void window_peep_open(rct_peep* peep){
if (peep->type == PEEP_TYPE_STAFF){
window_staff_peep_open(peep);
return;
}
rct_window* window;
window = window_bring_to_front_by_id(WC_PEEP, peep->sprite_index);
if (window == NULL){
window = window_create_auto_pos(192, 157, (uint32*)window_peep_overview_events, WC_PEEP, 0);
window->widgets = window_peep_overview_widgets;
window->enabled_widgets = window_peep_page_enabled_widgets[0];
window->number = peep->sprite_index;
window->page = 0;
window->viewport_focus_coordinates.y = 0;
window->frame_no = 0;
window->list_information_type = 0;
window->var_492 = 0;
window->var_494 = 0;
window_peep_disable_widgets(window);
window->min_width = 192;
window->min_height = 157;
window->max_width = 500;
window->max_height = 450;
window->flags = 1 << 8;
window->no_list_items = 0;
window->selected_list_item = -1;
window->colours[0] = 1;
window->colours[1] = 15;
window->colours[2] = 15;
window->viewport_focus_coordinates.y = -1;
}
window->page = 0;
window_invalidate(window);
window->widgets = window_peep_page_widgets[WINDOW_PEEP_OVERVIEW];
window->enabled_widgets = window_peep_page_enabled_widgets[WINDOW_PEEP_OVERVIEW];
window->var_020 = RCT2_GLOBAL(0x981D54,uint32);
window->event_handlers = window_peep_page_events[WINDOW_PEEP_OVERVIEW];
window->pressed_widgets = 0;
window_peep_disable_widgets(window);
window_init_scroll_widgets(window);
window_peep_viewport_init(window);
}
/* rct2: 0x006987A6
* Disables the finance tab when no money.
* Disables peep pickup when in certain no pickup states.
*/
void window_peep_disable_widgets(rct_window* w){
rct_peep* peep = &g_sprite_list[w->number].peep;
uint64 disabled_widgets = 0;
if (peep_can_be_picked_up(peep)){
if (w->disabled_widgets & (1 << WIDX_PICKUP))
window_invalidate(w);
}
else{
disabled_widgets = (1 << WIDX_PICKUP);
if (!(w->disabled_widgets & (1 << WIDX_PICKUP)))
window_invalidate(w);
}
if (RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_NO_MONEY){
disabled_widgets |= (1 << WIDX_TAB_4); //Disable finance tab if no money
}
w->disabled_widgets = disabled_widgets;
}
/* rct2: 0x00696A75 */
void window_peep_close(){
rct_window* w;
window_get_register(w);
if (RCT2_GLOBAL(0x9DE518,uint32) & (1<<3)){
if (w->classification == RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS,rct_windowclass) &&
w->number == RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER,rct_windownumber))
tool_cancel();
}
}
/* rct2: 0x00696FBE */
void window_peep_resize(){
rct_window* w;
window_get_register(w);
window_peep_disable_widgets(w);
RCT2_CALLPROC_EBPSAFE(w->event_handlers[WE_INVALIDATE]);
window_invalidate_by_id(0xA97, w->number);
window_set_resize(w, 192, 159, 500, 450);
rct_viewport* view = w->viewport;
if (view){
if ((w->width - 30) == view->width){
if ((w->height - 72) == view->height){
window_peep_viewport_init(w);
return;
}
}
uint8 zoom_amount = 1 << view->zoom;
view->width = w->width - 30;
view->height = w->height - 72;
view->view_width = view->width / zoom_amount;
view->view_height = view->height / zoom_amount;
}
window_peep_viewport_init(w);
}
/* rct2: 0x00696A06 */
void window_peep_overview_mouse_up(){
short widgetIndex;
rct_window* w;
window_widget_get_registers(w, widgetIndex);
rct_peep* peep = GET_PEEP(w->number);
switch(widgetIndex){
case WIDX_CLOSE:
window_close(w);
break;
case WIDX_TAB_1:
case WIDX_TAB_2:
case WIDX_TAB_3:
case WIDX_TAB_4:
case WIDX_TAB_5:
case WIDX_TAB_6:
window_peep_set_page(w, widgetIndex - WIDX_TAB_1);
break;
case WIDX_PICKUP:
if (!peep_can_be_picked_up(peep)) {
return;
}
if (tool_set(w, widgetIndex, 7)) {
return;
}
w->var_48C = peep->sprite_identifier;
RCT2_CALLPROC_X(0x0069A512, 0, 0, 0, 0, (int)peep, 0, 0);
RCT2_CALLPROC_X(0x006EC473, 0, 0, 0, 0, (int)peep, 0, 0);
RCT2_CALLPROC_X(0x0069E9D3, 0x8000, 0, peep->y, peep->z, (int)peep, 0, 0);
RCT2_CALLPROC_X(0x0069A409, 0, 0, 0, 0, (int)peep, 0, 0);
peep->state = 9;
peep->pad_2C = 0;
RCT2_CALLPROC_X(0x0069A42F, 0, 0, 0, 0, (int)peep, 0, 0);
break;
case WIDX_RENAME:
window_show_textinput(w, (int)widgetIndex, 0x5AC, 0x5AD, peep->name_string_idx);
break;
case WIDX_LOCATE:
window_scroll_to_viewport(w);
break;
case WIDX_TRACK:
g_sprite_list[w->number].peep.flags ^= PEEP_FLAGS_TRACKING;
break;
}
}
void window_peep_set_page(rct_window* w, int page){
if (RCT2_GLOBAL(0x9DE518,uint32) & (1 << 3))
{
if(w->number == RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER, rct_windownumber) &&
w->classification == RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, rct_windowclass))
tool_cancel();
}
int listen = 0;
if ( page == WINDOW_PEEP_OVERVIEW && w->page==WINDOW_PEEP_OVERVIEW && w->viewport){
if(!(w->viewport->flags & VIEWPORT_FLAG_SOUND_ON))
listen = 1;
}
w->page = page;
w->frame_no = 0;
w->no_list_items = 0;
w->selected_list_item = -1;
rct_viewport* viewport = w->viewport;
w->viewport = 0;
if (viewport){
viewport->width = 0;
}
w->enabled_widgets = window_peep_page_enabled_widgets[page];
w->var_020 = RCT2_ADDRESS(0x981D54,uint32)[page];
w->event_handlers = window_peep_page_events[page];
w->pressed_widgets = 0;
w->widgets = window_peep_page_widgets[page];
window_peep_disable_widgets(w);
window_invalidate(w);
RCT2_CALLPROC_X(w->event_handlers[WE_RESIZE], 0, 0, 0, 0, (int)w, 0, 0);
RCT2_CALLPROC_X(w->event_handlers[WE_INVALIDATE], 0, 0, 0, 0, (int)w, 0, 0);
window_init_scroll_widgets(w);
window_invalidate(w);
if (listen && w->viewport) w->viewport->flags |= VIEWPORT_FLAG_SOUND_ON;
}
void window_peep_overview_viewport_init_wrapper(){
rct_window* w;
window_get_register(w);
window_peep_viewport_init(w);
}
/* rct2: 0x0069883C */
void window_peep_viewport_init(rct_window* w){
if (w->page != WINDOW_PEEP_OVERVIEW) return;
union{
sprite_focus sprite;
coordinate_focus coordinate;
} focus; //The focus will be either a sprite or a coordinate.
focus.sprite.sprite_id = w->number;
rct_peep* peep = GET_PEEP(w->number);
if (peep->state == PEEP_STATE_PICKED){
focus.sprite.sprite_id = -1;
}
else{
uint8 final_check = 1;
if (peep->state == PEEP_STATE_ON_RIDE
|| peep->state == PEEP_STATE_ENTERING_RIDE
|| (peep->state == PEEP_STATE_LEAVING_RIDE && peep->x == SPRITE_LOCATION_NULL)){
rct_ride* ride = &(RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, rct_ride)[peep->current_ride]);
if (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK){
rct_vehicle* train = GET_VEHICLE(ride->train_car_map[peep->current_train]);
int car = peep->current_car;
for (; car != 0; car--){
train = GET_VEHICLE(train->next_vehicle_on_train);
}
focus.sprite.sprite_id = train->sprite_index;
final_check = 0;
}
}
if (peep->x == SPRITE_LOCATION_NULL && final_check){
rct_ride* ride = &(RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, rct_ride)[peep->current_ride]);
int x = ride->overall_view & 0xFF * 32 + 16;
int y = (ride->overall_view >> 8) * 32 + 16;
int height = map_element_height(x, y);
height += 32;
focus.coordinate.x = x;
focus.coordinate.y = y;
focus.coordinate.z = height;
focus.sprite.type |= VIEWPORT_FOCUS_TYPE_COORDINATE;
}
else{
focus.sprite.type |= VIEWPORT_FOCUS_TYPE_SPRITE | VIEWPORT_FOCUS_TYPE_COORDINATE;
focus.sprite.pad_486 &= 0xFFFF;
}
focus.coordinate.rotation = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8);
}
uint16 viewport_flags;
if (w->viewport){
//Check all combos, for now skipping y and rot
if (focus.coordinate.x == w->viewport_focus_coordinates.x &&
focus.coordinate.y == w->viewport_focus_coordinates.y &&
focus.coordinate.z == w->viewport_focus_coordinates.z &&
focus.coordinate.rotation == w->viewport_focus_coordinates.rotation)
return;
viewport_flags = w->viewport->flags;
w->viewport->width = 0;
w->viewport = 0;
viewport_update_pointers();
}
else{
viewport_flags = 0;
if (RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) & 0x1)
viewport_flags |= VIEWPORT_FLAG_GRIDLINES;
}
RCT2_CALLPROC_X(w->event_handlers[WE_INVALIDATE], 0, 0, 0, 0, (int)w, 0, 0);
w->viewport_focus_coordinates.x = focus.coordinate.x;
w->viewport_focus_coordinates.y = focus.coordinate.y;
w->viewport_focus_coordinates.z = focus.coordinate.z;
w->viewport_focus_coordinates.rotation = focus.coordinate.rotation;
if (peep->state != PEEP_STATE_PICKED){
if (!(w->viewport)){
rct_widget* view_widget = &w->widgets[WIDX_VIEWPORT];
int x = view_widget->left + 1 + w->x;
int y = view_widget->top + 1 + w->y;
int width = view_widget->right - view_widget->left - 1;
int height = view_widget->bottom - view_widget->top - 1;
viewport_create(w, x, y, width, height, 0, focus.coordinate.x, focus.coordinate.y, focus.coordinate.z, focus.sprite.type & VIEWPORT_FOCUS_TYPE_MASK, focus.sprite.sprite_id);
w->flags |= WF_2;
window_invalidate(w);
}
}
if (w->viewport)
w->viewport->flags = viewport_flags;
window_invalidate(w);
}
/* rct2: 0x696887 */
void window_peep_overview_paint(){
rct_window *w;
rct_drawpixelinfo *dpi;
//rct_widget *labelWidget;
window_paint_get_registers(w, dpi);
RCT2_CALLPROC_X(0x696887, 0, 0, 0, 0, (int)w, (int)dpi, 0);
return;
window_draw_widgets(w, dpi);
//6983dd
//698597
//6985d8
//69861f
//69869b
//698661
// Draw the viewport no sound sprite
if (w->viewport){
window_draw_viewport(dpi, w);
rct_viewport* viewport = w->viewport;
if (viewport->flags & VIEWPORT_FLAG_SOUND_ON){
gfx_draw_sprite(dpi, SPR_HEARING_VIEWPORT, w->x + 2, w->y + 2, 0);
}
}
// Draw the centered label
uint32 argument1, argument2;
rct_peep* peep = GET_PEEP(w->number);
get_arguments_from_action(peep, &argument1, &argument2);
RCT2_GLOBAL(0x13CE952, uint32) = argument1;
RCT2_GLOBAL(0x13CE952 + 4, uint32) = argument2;
rct_widget* widget = &w->widgets[WIDX_ACTION_LBL];
int x = (widget->left + widget->right) / 2 + w->x;
int y = w->y + widget->top - 1;
int width = widget->right - widget->left;
gfx_draw_string_centred_clipped(dpi, 1191, (void*)0x13CE952, 0, x, y, width);
// Draw the marquee thought
widget = &w->widgets[WIDX_MARQUEE];
width = widget->right - widget->left - 3;
int left = widget->left + 2 + w->x;
int top = widget->top + w->y;
int height = widget->bottom - widget->top;
rct_drawpixelinfo* dpi_marquee = clip_drawpixelinfo(dpi, left, width, top, height);
if (!dpi_marquee)return;
int i = 0;
for (; i < PEEP_MAX_THOUGHTS; ++i){
if (peep->thoughts[i].type == PEEP_THOUGHT_TYPE_NONE){
w->list_information_type = 0;
return;
}
if (peep->thoughts[i].var_2 == 1){ // If a fresh thought
break;
}
}
if (i == PEEP_MAX_THOUGHTS){
w->list_information_type = 0;
return;
}
get_arguments_from_thought(peep->thoughts[i], &argument1, &argument2);
RCT2_GLOBAL(0x13CE952, uint32) = argument1;
RCT2_GLOBAL(0x13CE952 + 4, uint32) = argument2;
RCT2_GLOBAL(0x13CE952 + 8, uint16) = 0;
x = widget->right - widget->left - w->list_information_type;
gfx_draw_string_left(dpi_marquee, 1193, (void*)0x13CE952, 0, x, 0);
}
/* rct2: 0x696749*/
void window_peep_overview_invalidate(){
rct_window* w;
window_get_register(w);
if (window_peep_page_widgets[w->page] != w->widgets){
w->widgets = window_peep_page_widgets[w->page];
window_init_scroll_widgets(w);
}
w->pressed_widgets &= ~(WIDX_TAB_1 | WIDX_TAB_2 |WIDX_TAB_3 |WIDX_TAB_4 |WIDX_TAB_5 |WIDX_TAB_6);
w->pressed_widgets |= 1ULL << (w->page + WIDX_TAB_1);
rct_peep* peep = GET_PEEP(w->number);
RCT2_GLOBAL(0x13CE952,uint16) = peep->name_string_idx;
RCT2_GLOBAL(0x13CE954,uint32) = peep->id;
w->pressed_widgets &= ~(1<<WIDX_TRACK);
if (peep->flags & 0x8){
w->pressed_widgets |= (1<<WIDX_TRACK);
}
window_peep_overview_widgets[WIDX_BACKGROUND].right = w->width - 1;
window_peep_overview_widgets[WIDX_BACKGROUND].bottom = w->height - 1;
window_peep_overview_widgets[WIDX_PAGE_BACKGROUND].right =w->width - 1;
window_peep_overview_widgets[WIDX_PAGE_BACKGROUND].bottom = w->height - 1;
window_peep_overview_widgets[WIDX_TITLE].right = w->width - 2;
window_peep_overview_widgets[WIDX_CLOSE].left = w->width - 13;
window_peep_overview_widgets[WIDX_CLOSE].right = w->width - 3;
window_peep_overview_widgets[WIDX_VIEWPORT].right = w->width - 26;
window_peep_overview_widgets[WIDX_VIEWPORT].bottom = w->height - 14;
window_peep_overview_widgets[WIDX_ACTION_LBL].top = w->height - 12;
window_peep_overview_widgets[WIDX_ACTION_LBL].bottom = w->height - 3;
window_peep_overview_widgets[WIDX_ACTION_LBL].right = w->width - 24;
window_peep_overview_widgets[WIDX_MARQUEE].right = w->width - 24;
window_peep_overview_widgets[WIDX_PICKUP].right = w->width - 2;
window_peep_overview_widgets[WIDX_RENAME].right = w->width - 2;
window_peep_overview_widgets[WIDX_LOCATE].right = w->width - 2;
window_peep_overview_widgets[WIDX_TRACK].right = w->width - 2;
window_peep_overview_widgets[WIDX_PICKUP].left = w->width - 25;
window_peep_overview_widgets[WIDX_RENAME].left = w->width - 25;
window_peep_overview_widgets[WIDX_LOCATE].left = w->width - 25;
window_peep_overview_widgets[WIDX_TRACK].left = w->width - 25;
window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_6);
}
void window_peep_overview_tab_paint( rct_window* w, rct_drawpixelinfo* dpi){
if ( w->disabled_widgets & (1ULL<<WIDX_TAB_1) )return;
//ax
int x = w->widgets[WIDX_TAB_1].left + 1 + w->x;
//cx
int y = w->widgets[WIDX_TAB_1].top + 1 + w->y;
//bx
int width = w->widgets[WIDX_TAB_1].right - 1 - w->widgets[WIDX_TAB_1].left;
//dx
int height = w->widgets[WIDX_TAB_1].bottom - 1 - w->widgets[WIDX_TAB_1].top;
if (w->page == WINDOW_PEEP_OVERVIEW){
height++;
}
rct_drawpixelinfo* cliped_dpi = clip_drawpixelinfo( dpi, x, width, y, height );
if (!cliped_dpi) return;
int cx = 14;
int dx = 20;
//ebp
rct_peep* peep = GET_PEEP(w->number);
if (peep->type == 1 && peep->staff_type == 3)
dx++;
int eax = RCT2_GLOBAL(peep->sprite_type*8 + 0x982708, uint32);
int ebx = *(uint32*)eax;
ebx++;
eax = 0;
if (w->page == WINDOW_PEEP_OVERVIEW){
int ax = *((uint16*)w + 496 / 2);
ax &= ~((1<<0)|(1<<1));
}
ebx += eax;
//698474
}

View File

@@ -1,154 +0,0 @@
/*****************************************************************************
* Copyright (c) 2014 Ted John
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2.
*
* OpenRCT2 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/>.
*****************************************************************************/
#include <string.h>
#include "addresses.h"
#include "game.h"
#include "ride.h"
#include "string_ids.h"
#include "sprite.h"
#include "sprites.h"
#include "widget.h"
#include "window.h"
#include "window_dropdown.h"
#pragma region Widgets
static rct_widget *window_ride_page_widgets[] = {
(rct_widget*)0x009ADC34,
(rct_widget*)0x009ADDA8,
(rct_widget*)0x009ADEFC,
(rct_widget*)0x009AE190,
(rct_widget*)0x009AE2A4,
(rct_widget*)0x009AE4C8,
(rct_widget*)0x009AE5DC,
(rct_widget*)0x009AE710,
(rct_widget*)0x009AE844,
(rct_widget*)0x009AE9C8
};
#pragma endregion
#pragma region Events
static uint32* window_ride_page_events[] = {
(uint32*)0x0098DFD4,
(uint32*)0x0098E204,
(uint32*)0x0098E0B4,
(uint32*)0x0098E124,
(uint32*)0x0098E044,
(uint32*)0x0098E194,
(uint32*)0x0098DE14,
(uint32*)0x0098DF64,
(uint32*)0x0098DEF4,
(uint32*)0x0098DE84
};
#pragma endregion
/**
*
* rct2: 0x006AEAB4
*/
rct_window *window_ride_open(int rideIndex)
{
rct_window *w;
w = window_create_auto_pos(316, 180, window_ride_page_events[0], WC_RIDE, 0x400);
w->widgets = window_ride_page_widgets[0];
w->enabled_widgets = 0x007DBFF4;
w->number = rideIndex;
w->page = 0;
w->var_48C = 0;
w->frame_no = 0;
w->list_information_type = 0;
w->var_492 = 0;
w->var_494 = 0;
RCT2_CALLPROC_X(0x006AEB9F, 0, 0, 0, 0, (int)w, 0, 0);
w->min_width = 316;
w->min_height = 180;
w->max_width = 500;
w->max_height = 450;
w->flags |= WF_RESIZABLE;
w->colours[0] = 1;
w->colours[1] = 26;
w->colours[2] = 11;
rct_ride *ride = &g_ride_list[rideIndex];
uint8 *edx = (uint8*)0x009E32F8;
if (ride->type != RIDE_TYPE_NULL) {
int rideType = ride->type;
do {
edx++;
if (*(edx - 1) != 0xFF)
continue;
} while (rideType-- != 0);
}
int eax, ebx = 0, ecx;
while (*edx != 0xFF) {
eax = *edx++;
ecx = eax >> 5;
eax &= 0x1F;
if (!(RCT2_ADDRESS(0x001357424, uint32)[ecx] & (1 << eax)))
continue;
ebx++;
}
RCT2_GLOBAL((int)w + 496, uint16) = ebx;
return w;
}
/**
*
* rct2: 0x006ACC28
*/
void window_ride_main_open(int rideIndex)
{
rct_window *w;
w = window_bring_to_front_by_id(WC_RIDE, rideIndex);
if (w == NULL) {
w = window_ride_open(rideIndex);
w->ride.var_482 = -1;
}
if (RCT2_GLOBAL(0x009DE518, uint32) & (1 << 3)) {
if (w->classification == RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, rct_windowclass) &&
w->number == RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER, rct_windownumber)
) {
tool_cancel();
}
}
w->page = 0;
w->width = 316;
w->height = 180;
window_invalidate(w);
w->widgets = window_ride_page_widgets[0];
w->enabled_widgets = 0x007DBFF4;
w->var_020 = 0;
w->event_handlers = window_ride_page_events[0];
w->pressed_widgets = 0;
RCT2_CALLPROC_X(0x006AEB9F, 0, 0, 0, 0, (int)w, 0, 0);
window_init_scroll_widgets(w);
w->ride.var_480 = 0;
RCT2_CALLPROC_X(0x006AF994, 0, 0, 0, 0, (int)w, 0, 0);
}

View File

@@ -20,11 +20,11 @@
#include <windows.h>
#include <string.h>
#include "addresses.h"
#include "string_ids.h"
#include "sprites.h"
#include "widget.h"
#include "window.h"
#include "../addresses.h"
#include "../localisation/localisation.h"
#include "../sprites.h"
#include "../interface/widget.h"
#include "../interface/window.h"
enum WINDOW_ABOUT_WIDGET_IDX {
WIDX_BACKGROUND,
@@ -125,10 +125,10 @@ static void window_about_mouseup()
window_close(w);
break;
case WIDX_MUSIC_CREDITS:
RCT2_CALLPROC_EBPSAFE(0x0066D55B);
window_music_credits_open();
break;
case WIDX_PUBLISHER_CREDITS:
RCT2_CALLPROC_EBPSAFE(0x0066D4EC);
window_publisher_credits_open();
break;
}
}

View File

@@ -19,12 +19,12 @@
*****************************************************************************/
#include <string.h>
#include "addresses.h"
#include "config.h"
#include "string_ids.h"
#include "viewport.h"
#include "widget.h"
#include "window.h"
#include "../addresses.h"
#include "../config.h"
#include "../localisation/localisation.h"
#include "../interface/viewport.h"
#include "../interface/widget.h"
#include "../interface/window.h"
enum WINDOW_BANNER_WIDGET_IDX {
WIDX_BACKGROUND,

View File

@@ -21,18 +21,18 @@
#include <windows.h>
#include <string.h>
#include <limits.h>
#include "addresses.h"
#include "park.h"
#include "peep.h"
#include "string_ids.h"
#include "sprite.h"
#include "sprites.h"
#include "widget.h"
#include "window.h"
#include "climate.h"
#include "ride.h"
#include "scenario.h"
#include "game.h"
#include "../addresses.h"
#include "../game.h"
#include "../interface/widget.h"
#include "../interface/window.h"
#include "../localisation/localisation.h"
#include "../peep/peep.h"
#include "../ride/ride.h"
#include "../scenario.h"
#include "../sprites.h"
#include "../world/climate.h"
#include "../world/park.h"
#include "../world/sprite.h"
//#define WW 200
//#define WH 128
@@ -238,9 +238,9 @@ static void* window_cheats_page_events[] = {
};
static uint32 window_cheats_page_enabled_widgets[] = {
(1 << WIDX_CLOSE) | (1 << WIDX_TAB_1) | (1 << WIDX_TAB_2) | (1 << WIDX_HIGH_MONEY) | (1 << WIDX_PARK_ENTRANCE_FEE),
(1 << WIDX_CLOSE) | (1 << WIDX_TAB_1) | (1 << WIDX_TAB_2) | (1 << WIDX_HAPPY_GUESTS) | (1 << WIDX_TRAM_GUESTS),
(1 << WIDX_CLOSE) | (1 << WIDX_TAB_1) | (1 << WIDX_TAB_2) | (1 << WIDX_FREEZE_CLIMATE) | (1 << WIDX_OPEN_CLOSE_PARK) | (1 << WIDX_DECREASE_GAME_SPEED) | (1 << WIDX_INCREASE_GAME_SPEED),
(1 << WIDX_CLOSE) | (1 << WIDX_TAB_1) | (1 << WIDX_TAB_2) | (1 << WIDX_TAB_3) | (1 << WIDX_HIGH_MONEY) | (1 << WIDX_PARK_ENTRANCE_FEE),
(1 << WIDX_CLOSE) | (1 << WIDX_TAB_1) | (1 << WIDX_TAB_2) | (1 << WIDX_TAB_3) | (1 << WIDX_HAPPY_GUESTS) | (1 << WIDX_TRAM_GUESTS),
(1 << WIDX_CLOSE) | (1 << WIDX_TAB_1) | (1 << WIDX_TAB_2) | (1 << WIDX_TAB_3) | (1 << WIDX_FREEZE_CLIMATE) | (1 << WIDX_OPEN_CLOSE_PARK) | (1 << WIDX_DECREASE_GAME_SPEED) | (1 << WIDX_INCREASE_GAME_SPEED),
};
static void window_cheats_draw_tab_images(rct_drawpixelinfo *dpi, rct_window *w);

Some files were not shown because too many files have changed in this diff Show More