diff --git a/.gitignore b/.gitignore index b7d9f54188..c159c2b889 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,12 @@ sdl +# Compiled dll +openrct2.dll + +# Build artifacts +.cache + ################# ## Eclipse ################# diff --git a/.travis.yml b/.travis.yml index fae0c47d0f..067ef67593 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,33 +1,17 @@ language: c before_install: - - sudo apt-get update -qq - - sudo apt-get install -y --force-yes binutils-mingw-w64-i686 gcc-mingw-w64-i686 g++-mingw-w64-i686 - - # fetch precompiled SDL2 + headers for MinGW and push it into the expected directory - - export SDL2_PV=2.0.3 - - wget http://libsdl.org/release/SDL2-devel-${SDL2_PV}-mingw.tar.gz - - tar -xzvf SDL2-devel-${SDL2_PV}-mingw.tar.gz - - # but first fix SDL2 bug - - wget "https://github.com/anyc/anyc-overlay/raw/master/media-libs/libsdl2-mingw/files/libsdl2-mingw-2.0.3-fix-platform-detection-for-mingw.patch" - - pushd SDL2-${SDL2_PV}/i686-w64-mingw32/include/SDL2/ && patch -p2 < ../../../../libsdl2-mingw-2.0.3-fix-platform-detection-for-mingw.patch && popd - - - sudo mkdir -p /usr/local/cross-tools/ - - sudo mv SDL2-${SDL2_PV}/i686-w64-mingw32 /usr/local/cross-tools/ - - # build a wrapper that looks for the sdl2.pc file in the new directory - - echo -e "#! /bin/sh\\nexport PKG_CONFIG_LIBDIR=/usr/local/cross-tools/i686-w64-mingw32/lib/pkgconfig\\npkg-config \$@" > i686-w64-mingw32-pkg-config - - chmod +x i686-w64-mingw32-pkg-config - - sudo mv i686-w64-mingw32-pkg-config /usr/local/bin/ + - bash install.sh script: - - mkdir build - - pushd build - - cmake -DCMAKE_TOOLCHAIN_FILE=../CMakeLists_mingw.txt -DCMAKE_BUILD_TYPE=Debug .. - - make - #- make VERBOSE=1 - - popd + - bash build.sh -#notifications: - #irc: "irc.freenode.net#openrct2" \ No newline at end of file +notifications: + irc: "irc.freenode.net#openrct2-dev" + on_failure: always + on_success: change + +cache: + directories: + - cache + apt: true diff --git a/CMakeLists_mingw.txt b/CMakeLists_mingw.txt index a891576ae6..7fb25965c1 100644 --- a/CMakeLists_mingw.txt +++ b/CMakeLists_mingw.txt @@ -1,16 +1,25 @@ SET(CMAKE_SYSTEM_NAME Windows) -SET(COMPILER_PREFIX i686-w64-mingw32) -SET(CMAKE_C_COMPILER ${COMPILER_PREFIX}-gcc) -SET(CMAKE_CXX_COMPILER ${COMPILER_PREFIX}-c++) -SET(CMAKE_RC_COMPILER ${COMPILER_PREFIX}-windres) -SET(CMAKE_PKGCONFIG_EXECUTABLE ${COMPILER_PREFIX}-pkg-config) -SET(PKG_CONFIG_EXECUTABLE ${COMPILER_PREFIX}-pkg-config) +if (APPLE) + SET(COMPILER_PREFIX i586-mingw32) + SET(CMAKE_C_COMPILER ${COMPILER_PREFIX}-gcc) + SET(CMAKE_CXX_COMPILER ${COMPILER_PREFIX}-c++) + SET(CMAKE_RC_COMPILER ${COMPILER_PREFIX}-windres) + SET(CMAKE_PKGCONFIG_EXECUTABLE i686-w64-mingw32-pkg-config) + SET(PKG_CONFIG_EXECUTABLE i686-w64-mingw32-pkg-config) +else() + SET(COMPILER_PREFIX i686-w64-mingw32) + SET(CMAKE_C_COMPILER ${COMPILER_PREFIX}-gcc) + SET(CMAKE_CXX_COMPILER ${COMPILER_PREFIX}-c++) + SET(CMAKE_RC_COMPILER ${COMPILER_PREFIX}-windres) + SET(CMAKE_PKGCONFIG_EXECUTABLE ${COMPILER_PREFIX}-pkg-config) + SET(PKG_CONFIG_EXECUTABLE ${COMPILER_PREFIX}-pkg-config) +endif (APPLE) # potential flags to make code more similar to MSVC: -# -fshort-wchar -fshort-enums -mms-bitfields -fpack-struct=1 +# -fshort-wchar -fshort-enums -mms-bitfields # -set(CMAKE_C_FLAGS "-masm=intel -std=gnu99" CACHE STRING "" FORCE) +set(CMAKE_C_FLAGS "-masm=intel -std=gnu99 -fpack-struct=2" CACHE STRING "" FORCE) set(CMAKE_SHARED_LINKER_FLAGS "-static-libgcc" CACHE STRING "" FORCE) include_directories("/usr/include/wine/windows/") diff --git a/build.sh b/build.sh new file mode 100755 index 0000000000..965c72cbbf --- /dev/null +++ b/build.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -e + +if [[ ! -d build ]]; then + mkdir -p build +fi + +pushd build + cmake -DCMAKE_TOOLCHAIN_FILE=../CMakeLists_mingw.txt -DCMAKE_BUILD_TYPE=Debug .. + make +popd + +if [[ -t 1 ]]; then + echo -e "\nDone! Run OpenRCT2 by typing:\n\n\033[95mwine openrct2.exe\n\033[0m" +else + echo -e "\nDone! Run OpenRCT2 by typing:\n\nwine openrct2.exe\n" +fi + diff --git a/clean.sh b/clean.sh new file mode 100644 index 0000000000..03b524eee0 --- /dev/null +++ b/clean.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -ev + +sudo rm -rf /usr/local/cross-tools/i686-w64-mingw32 +#rm -rf .cache +rm -rf .cache/*.patch +rm -rf .cache/SDL2-2.0.3 +rm -rf .cache/i686-w64-mingw32-pkg-config +rm -rf build diff --git a/install.sh b/install.sh new file mode 100755 index 0000000000..f74e9bcf92 --- /dev/null +++ b/install.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +set -e + +SDL2_PV=2.0.3 + +cachedir=.cache +mkdir -p $cachedir + +echo `uname` + +if [[ `uname` == "Darwin" ]]; then + echo "Installation of OpenRCT2 assumes you have homebrew and use it to install packages." + # Very possible I'm missing some dependencies here. + brew install cmake wine + + if [[ ! -d /usr/include/wine ]]; then + # This will almost certainly break as brew changes. Better ideas + # welcome. + sudo ln -s /usr/local/Cellar/wine/1.6.2/include/wine /usr/include + fi + + mingw_dmg=gcc-4.8.0-qt-4.8.4-for-mingw32.dmg + mingw_path=/usr/local/gcc-4.8.0-qt-4.8.4-for-mingw32/win32-gcc/bin + if [[ ! -f $cachedir/$mingw_dmg ]]; then + wget http://crossgcc.rts-software.org/download/gcc-4.8.0-qt-4.8.4-win32/$mingw_dmg --output-document $cachedir/$mingw_dmg + fi + + if [[ ! -d $mingw_path ]]; then + echo "Open the DMG file and install its contents" + open $cachedir/$mingw_dmg + fi + + echo "You will need to add $mingw_path to your \$PATH" +elif [[ `uname` == "Linux" ]]; then + sudo apt-get install -y --force-yes binutils-mingw-w64-i686 gcc-mingw-w64-i686 g++-mingw-w64-i686 +fi + +if [[ ! -f $cachedir/SDL2-devel-${SDL2_PV}-mingw.tar.gz ]]; then + wget http://libsdl.org/release/SDL2-devel-${SDL2_PV}-mingw.tar.gz --output-document $cachedir/SDL2-devel-${SDL2_PV}-mingw.tar.gz; +fi +if [[ ! -d $cachedir/SDL2-${SDL2_PV} ]]; then + pushd $cachedir + tar -xzf SDL2-devel-${SDL2_PV}-mingw.tar.gz + popd +fi + +# Apply platform patch +mingw_patch=libsdl2-mingw-2.0.3-fix-platform-detection-for-mingw.patch +if [[ ! -f $cachedir/$mingw_patch ]]; then + wget "https://github.com/anyc/anyc-overlay/raw/master/media-libs/libsdl2-mingw/files/$mingw_patch" --output-document $cachedir/$mingw_patch; + + # XXX not sure how to make this idempotent. + pushd $cachedir/SDL2-${SDL2_PV}/i686-w64-mingw32/include/SDL2/ + echo "Applying patch." + patch -p2 < ../../../../$mingw_patch + popd +fi + +if [[ ! -d /usr/local/cross-tools ]]; then + sudo mkdir -p /usr/local/cross-tools +fi +if [[ ! -d /usr/local/cross-tools/i686-w64-mingw32 ]]; then + sudo cp -r $cachedir/SDL2-${SDL2_PV}/i686-w64-mingw32 /usr/local/cross-tools/ +fi + +if [[ ! -f $cachedir/i686-w64-mingw32-pkg-config ]]; then + # If this fails to work because of newlines, be sure you are running this + # script with Bash, and not sh. We should really move this to a separate + # file. + echo -e "#! /bin/sh\nexport PKG_CONFIG_LIBDIR=/usr/local/cross-tools/i686-w64-mingw32/lib/pkgconfig\npkg-config \$@" > $cachedir/i686-w64-mingw32-pkg-config; +fi + +chmod +x $cachedir/i686-w64-mingw32-pkg-config +sudo cp $cachedir/i686-w64-mingw32-pkg-config /usr/local/bin/ + +ls -al /usr/local/bin | grep pkg-config +cat /usr/local/bin/i686-w64-mingw32-pkg-config diff --git a/licence.txt b/licence.txt index 20d40b6bce..94a9ed024d 100644 --- a/licence.txt +++ b/licence.txt @@ -671,4 +671,4 @@ into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. \ No newline at end of file +. diff --git a/projects/openrct2.vcxproj b/projects/openrct2.vcxproj index fef57276cd..00362abcc4 100644 --- a/projects/openrct2.vcxproj +++ b/projects/openrct2.vcxproj @@ -17,6 +17,7 @@ + @@ -26,6 +27,7 @@ + @@ -38,7 +40,6 @@ - @@ -46,6 +47,7 @@ + @@ -57,6 +59,7 @@ + @@ -66,6 +69,7 @@ + @@ -80,11 +84,13 @@ + + @@ -199,4 +205,4 @@ - + \ No newline at end of file diff --git a/projects/openrct2.vcxproj.filters b/projects/openrct2.vcxproj.filters index f6568613c3..dd606605a9 100644 --- a/projects/openrct2.vcxproj.filters +++ b/projects/openrct2.vcxproj.filters @@ -126,10 +126,16 @@ Header Files - + Header Files - + + Header Files + + + Header Files + + Header Files @@ -314,10 +320,22 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + Resource Files - + \ No newline at end of file diff --git a/src/addresses.h b/src/addresses.h index ddea02037e..c2e6708f69 100644 --- a/src/addresses.h +++ b/src/addresses.h @@ -84,6 +84,8 @@ #define RCT2_ADDRESS_DIRTY_BLOCK_COLUMNS 0x009ABDE8 #define RCT2_ADDRESS_DIRTY_BLOCK_ROWS 0x009ABDEC +#define RCT2_ADDRESS_LIGHTNING_ACTIVE 0x009AC068 + #define RCT2_ADDRESS_RUN_INTRO_TICK_PART 0x009AC319 #define RCT2_ADDRESS_INSTALLED_OBJECT_LIST 0x009ADAE8 @@ -111,6 +113,10 @@ #define RCT2_ADDRESS_CURRENT_TOOL 0x009DE545 #define RCT2_ADDRESS_TOOL_WIDGETINDEX 0x009DE546 +#define RCT2_ADDRESS_PICKEDUP_PEEP_SPRITE 0x009DE550 +#define RCT2_ADDRESS_PICKEDUP_PEEP_X 0x009DE554 +#define RCT2_ADDRESS_PICKEDUP_PEEP_Y 0x009DE556 + #define RCT2_ADDRESS_CURSOR_OVER_WINDOWCLASS 0x009DE55C #define RCT2_ADDRESS_CURSOR_OVER_WINDOWNUMBER 0x009DE55E #define RCT2_ADDRESS_CURSOR_OVER_WIDGETINDEX 0x009DE560 @@ -176,11 +182,11 @@ #define RCT2_ADDRESS_PARK_FLAGS 0x013573E4 #define RCT2_ADDRESS_PARK_ENTRANCE_FEE 0x013573E8 #define RCT2_ADDRESS_GUESTS_IN_PARK 0x01357844 +#define RCT2_ADDRESS_GUESTS_HEADING_FOR_PARK 0x01357846 #define RCT2_ADDRESS_MONTHLY_RIDE_INCOME 0x01357894 #define RCT2_ADDRESS_CURRENT_PARK_RATING 0x01357CB0 #define RCT2_ADDRESS_PARK_RATING_HISTORY 0x01357CB2 #define RCT2_ADDRESS_GUESTS_IN_PARK_HISTORY 0x01357CD2 -#define RCT2_ADDRESS_GUEST_GENERATION_PROBABILITY 0x013580EC #define RCT2_ADDRESS_OBJECTIVE_TYPE 0x013580F8 #define RCT2_ADDRESS_OBJECTIVE_YEAR 0x013580F9 #define RCT2_ADDRESS_OBJECTIVE_CURRENCY 0x013580FC @@ -218,6 +224,8 @@ #define RCT2_ADDRESS_MAP_SIZE 0x01358834 #define RCT2_ADDRESS_PARK_SIZE 0x013580EA +#define RCT2_TOTAL_RIDE_VALUE 0x013580EE + #define RCT2_ADDRESS_SCENARIO_NAME 0x0135920A #define RCT2_ADDRESS_SCENARIO_DETAILS 0x0135924A @@ -249,6 +257,7 @@ #define RCT2_ADDRESS_NEWS_ITEM_LIST 0x013CA754 #define RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE 0x013CE950 +#define RCT2_ADDRESS_CURRENT_FONT_FLAGS 0x013CE9A2 #define RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS 0x013CE9A4 #define RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT 0x0141E9AC @@ -257,6 +266,10 @@ #define RCT2_ADDRESS_GAME_COMMAND_ERROR_STRING_ID 0x0141E9AE #define RCT2_ADDRESS_CURRENT_ROTATION 0x0141E9E0 +#define RCT2_ADDRESS_FONT_CHAR_WIDTH 0x0141E9E8 + +#define RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER 0x0141ED68 + #define RCT2_ADDRESS_WATER_RAISE_COST 0x0141F738 #define RCT2_ADDRESS_WATER_LOWER_COST 0x0141F73C @@ -357,6 +370,47 @@ static void RCT2_CALLPROC_X(int address, int _eax, int _ebx, int _ecx, int _edx, #endif } +static void RCT2_CALLPROC_X_EBPSAFE(int address, int _eax, int _ebx, int _ecx, int _edx, int _esi, int _edi, int _ebp) +{ + #ifdef _MSC_VER + __asm { + push ebp + push address + mov eax, _eax + mov ebx, _ebx + mov ecx, _ecx + mov edx, _edx + mov esi, _esi + mov edi, _edi + mov ebp, _ebp + call[esp] + add esp, 4 + pop ebp + } + #else + __asm__ ( "\ + \n\ + push ebx \n\ + push ebp \n\ + push %[address] \n\ + mov eax, %[_eax] \n\ + mov ebx, %[_ebx] \n\ + mov ecx, %[_ecx] \n\ + mov edx, %[_edx] \n\ + mov esi, %[_esi] \n\ + mov edi, %[_edi] \n\ + mov ebp, %[_ebp] \n\ + call [esp] \n\ + add esp, 4 \n\ + pop ebp \n\ + pop ebx \n\ + " : [address] "+m" (address), [_eax] "+m" (_eax), [_ebx] "+m" (_ebx), [_ecx] "+m" (_ecx), [_edx] "+m" (_edx), [_esi] "+m" (_esi), [_edi] "+m" (_edi), [_ebp] "+m" (_ebp) + : + : "eax","ecx","edx","esi","edi" + ); + #endif +} + static void RCT2_CALLFUNC_X(int address, int *_eax, int *_ebx, int *_ecx, int *_edx, int *_esi, int *_edi, int *_ebp) { #ifdef _MSC_VER diff --git a/src/audio.h b/src/audio.h index 52a51c2cee..ed6165bdcd 100644 --- a/src/audio.h +++ b/src/audio.h @@ -64,9 +64,69 @@ void pause_sounds(); void unpause_sounds(); typedef enum { - RCT2_SOUND_SCREAM = 11, - RCT2_SOUND_CHAINLIFT = 56, - RCT2_SOUND_TRACKFRICTION = 57, + SOUND_LIFT_1 = 0, + SOUND_TRACK_FRICTION_1 = 1, + SOUND_LIFT_2 = 2, + SOUND_SCREAM_1 = 3, + SOUND_CLICK_1 = 4, + SOUND_CLICK_2 = 5, + SOUND_PLACE_ITEM = 6, + SOUND_SCREAM_2 = 7, + SOUND_SCREAM_3 = 8, + SOUND_SCREAM_4 = 9, + SOUND_SCREAM_5 = 10, + SOUND_SCREAM_6 = 11, + SOUND_LIFT_3 = 12, + SOUND_PURCHASE = 13, + SOUND_CRASH = 14, + SOUND_LAYING_OUT_WATER = 15, + SOUND_WATER_1 = 16, + SOUND_WATER_2 = 17, + SOUND_TRAIN_WHISTLE = 18, + SOUND_TRAIN_CHUGGING = 19, + SOUND_WATER_SPLASH = 20, + SOUND_HAMMERING = 21, + SOUND_RIDE_LAUNCH_1 = 22, + SOUND_RIDE_LAUNCH_2 = 23, + SOUND_COUGH_1 = 24, + SOUND_COUGH_2 = 25, + SOUND_COUGH_3 = 26, + SOUND_COUGH_4 = 27, + SOUND_RAIN_1 = 28, + SOUND_THUNDER_1 = 29, + SOUND_THUNDER_2 = 30, + SOUND_RAIN_2 = 31, + SOUND_RAIN_3 = 32, + SOUND_BALLOON_POP = 33, + SOUND_MECHANIC_FIX = 34, + SOUND_SCREAM_7 = 35, + SOUND_TOILET_FLUSH = 36, + SOUND_CLICK_3 = 37, + SOUND_QUACK = 38, + SOUND_NEWS_ITEM = 39, + SOUND_WINDOW_OPEN = 40, + SOUND_LAUGH_1 = 41, + SOUND_LAUGH_2 = 42, + SOUND_LAUGH_3 = 43, + SOUND_APPLAUSE = 44, + SOUND_HAUNTED_HOUSE_SCARE = 45, + SOUND_HAUNTED_HOUSE_SCREAM_1 = 46, + SOUND_HAUNTED_HOUSE_SCREAM_2 = 47, + SOUND_48 = 48, + SOUND_49 = 49, + SOUND_ERROR = 50, + SOUND_51 = 51, + SOUND_LIFT_4 = 52, + SOUND_LIFT_5 = 53, + SOUND_TRACK_FRICTION_2 = 54, + SOUND_LIFT_6 = 55, + SOUND_LIFT_7 = 56, + SOUND_TRACK_FRICTION_3 = 57, + SOUND_SCREAM_8 = 58, + SOUND_TRAM = 59, + SOUND_DOOR_OPEN = 60, + SOUND_DOOR_CLOSE = 61, + SOUND_62 = 62 } RCT2_SOUND; #endif diff --git a/src/award.c b/src/award.c new file mode 100644 index 0000000000..3f76984ebf --- /dev/null +++ b/src/award.c @@ -0,0 +1,625 @@ +/***************************************************************************** + * 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 . + *****************************************************************************/ + +#include "addresses.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 + +int _awardPositiveMap[] = { + NEGATIVE, // PARK_AWARD_MOST_UNTIDY + POSITIVE, // PARK_AWARD_MOST_TIDY + POSITIVE, // PARK_AWARD_BEST_ROLLERCOASTERS + POSITIVE, // PARK_AWARD_BEST_VALUE + POSITIVE, // PARK_AWARD_MOST_BEAUTIFUL + NEGATIVE, // PARK_AWARD_WORST_VALUE + POSITIVE, // PARK_AWARD_SAFEST + POSITIVE, // PARK_AWARD_BEST_STAFF + POSITIVE, // PARK_AWARD_BEST_FOOD + NEGATIVE, // PARK_AWARD_WORST_FOOD + POSITIVE, // PARK_AWARD_BEST_RESTROOMS + NEGATIVE, // PARK_AWARD_MOST_DISAPPOINTING + POSITIVE, // PARK_AWARD_BEST_WATER_RIDES + POSITIVE, // PARK_AWARD_BEST_CUSTOM_DESIGNED_RIDES + POSITIVE, // PARK_AWARD_MOST_DAZZLING_RIDE_COLOURS + NEGATIVE, // PARK_AWARD_MOST_CONFUSING_LAYOUT + POSITIVE, // PARK_AWARD_BEST_GENTLE_RIDES +}; + +int award_is_positive(int type) +{ + return _awardPositiveMap[type]; +} + +#pragma region Award checks + +/** More than 1/16 of the total guests must be thinking untidy thoughts. */ +static int award_is_deserved_most_untidy(int awardType, int activeAwardTypes) +{ + uint16 spriteIndex; + rct_peep *peep; + int negativeCount; + + if (activeAwardTypes & (1 << PARK_AWARD_MOST_BEAUTIFUL)) + return 0; + if (activeAwardTypes & (1 << PARK_AWARD_BEST_STAFF)) + return 0; + if (activeAwardTypes & (1 << PARK_AWARD_MOST_TIDY)) + return 0; + + negativeCount = 0; + FOR_ALL_GUESTS(spriteIndex, peep) { + if (peep->var_2A != 0) + continue; + + if (peep->thoughts[0].var_2 > 5) + continue; + + if (peep->thoughts[0].type == PEEP_THOUGHT_TYPE_BAD_LITTER || + peep->thoughts[0].type == PEEP_THOUGHT_TYPE_PATH_DISGUSTING || + peep->thoughts[0].type == PEEP_THOUGHT_TYPE_VANDALISM + ) { + negativeCount++; + } + } + + return (negativeCount > RCT2_GLOBAL(RCT2_ADDRESS_GUESTS_IN_PARK, uint16) / 16); +} + +/** More than 1/64 of the total guests must be thinking tidy thoughts and less than 6 guests thinking untidy thoughts. */ +static int award_is_deserved_most_tidy(int awardType, int activeAwardTypes) +{ + uint16 spriteIndex; + rct_peep *peep; + int positiveCount; + int negativeCount; + + if (activeAwardTypes & (1 << PARK_AWARD_MOST_UNTIDY)) + return 0; + if (activeAwardTypes & (1 << PARK_AWARD_MOST_DISAPPOINTING)) + return 0; + + positiveCount = 0; + negativeCount = 0; + FOR_ALL_GUESTS(spriteIndex, peep) { + if (peep->var_2A != 0) + continue; + + if (peep->thoughts[0].var_2 > 5) + continue; + + if (peep->thoughts[0].type == PEEP_THOUGHT_VERY_CLEAN) + positiveCount++; + + if (peep->thoughts[0].type == PEEP_THOUGHT_TYPE_BAD_LITTER || + peep->thoughts[0].type == PEEP_THOUGHT_TYPE_PATH_DISGUSTING || + peep->thoughts[0].type == PEEP_THOUGHT_TYPE_VANDALISM + ) { + negativeCount++; + } + } + + return (negativeCount <= 5 && positiveCount > RCT2_GLOBAL(RCT2_ADDRESS_GUESTS_IN_PARK, uint16) / 64); +} + +/** At least 6 open roller coasters. */ +static int award_is_deserved_best_rollercoasters(int awardType, int activeAwardTypes) +{ + int i, rollerCoasters; + rct_ride *ride; + char *object; + + rollerCoasters = 0; + FOR_ALL_RIDES(i, ride) { + object = RCT2_ADDRESS(0x009ACFA4, char*)[ride->subtype]; + if (RCT2_GLOBAL(object + 0x1BE, uint8) != RIDE_GROUP_ROLLERCOASTER && RCT2_GLOBAL(object + 0x1BF, uint8) != RIDE_GROUP_ROLLERCOASTER) + continue; + + if (ride->status != RIDE_STATUS_OPEN || (ride->lifecycle_flags & 0x400)) + continue; + + rollerCoasters++; + } + + return (rollerCoasters >= 6); +} + +/** Entrance fee is 0.10 less than half of the total ride value. */ +static int award_is_deserved_best_value(int awardType, int activeAwardTypes) +{ + if (activeAwardTypes & (1 << PARK_AWARD_WORST_VALUE)) + return 0; + if (activeAwardTypes & (1 << PARK_AWARD_MOST_DISAPPOINTING)) + return 0; + if (RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & (PARK_FLAGS_11 | PARK_FLAGS_PARK_FREE_ENTRY)) + return 0; + if (RCT2_GLOBAL(RCT2_TOTAL_RIDE_VALUE, money16) < MONEY(10, 00)) + return 0; + if (RCT2_GLOBAL(RCT2_ADDRESS_PARK_ENTRANCE_FEE, money16) + MONEY(0, 10) >= RCT2_GLOBAL(RCT2_TOTAL_RIDE_VALUE, money16) / 2) + return 0; + return 1; +} + +/** More than 1/128 of the total guests must be thinking scenic thoughts and less than 16 untidy thoughts. */ +static int award_is_deserved_most_beautiful(int awardType, int activeAwardTypes) +{ + uint16 spriteIndex; + rct_peep *peep; + int positiveCount; + int negativeCount; + + if (activeAwardTypes & (1 << PARK_AWARD_MOST_UNTIDY)) + return 0; + if (activeAwardTypes & (1 << PARK_AWARD_MOST_DISAPPOINTING)) + return 0; + + positiveCount = 0; + negativeCount = 0; + FOR_ALL_GUESTS(spriteIndex, peep) { + if (peep->var_2A != 0) + continue; + + if (peep->thoughts[0].var_2 > 5) + continue; + + if (peep->thoughts[0].type == PEEP_THOUGHT_TYPE_SCENERY) + positiveCount++; + + if (peep->thoughts[0].type == PEEP_THOUGHT_TYPE_BAD_LITTER || + peep->thoughts[0].type == PEEP_THOUGHT_TYPE_PATH_DISGUSTING || + peep->thoughts[0].type == PEEP_THOUGHT_TYPE_VANDALISM + ) { + negativeCount++; + } + } + + return (negativeCount <= 15 && positiveCount > RCT2_GLOBAL(RCT2_ADDRESS_GUESTS_IN_PARK, uint16) / 128); +} + +/** Entrance fee is more than total ride value. */ +static int award_is_deserved_worse_value(int awardType, int activeAwardTypes) +{ + if (activeAwardTypes & (1 << PARK_AWARD_BEST_VALUE)) + return 0; + if (RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_11) + return 0; + if (RCT2_GLOBAL(RCT2_ADDRESS_PARK_ENTRANCE_FEE, money16) == MONEY(0, 00)) + return 0; + if (RCT2_GLOBAL(RCT2_TOTAL_RIDE_VALUE, money16) >= RCT2_GLOBAL(RCT2_ADDRESS_PARK_ENTRANCE_FEE, money16)) + return 0; + return 1; +} + +/** No more than 2 people who think the vandalism is bad and no crashes. */ +static int award_is_deserved_safest(int awardType, int activeAwardTypes) +{ + int i, peepsWhoDislikeVandalism; + uint16 spriteIndex; + rct_peep *peep; + rct_ride *ride; + + peepsWhoDislikeVandalism = 0; + FOR_ALL_GUESTS(spriteIndex, peep) { + if (peep->var_2A != 0) + continue; + if (peep->thoughts[0].var_2 <= 5 && peep->thoughts[0].type == PEEP_THOUGHT_TYPE_VANDALISM) + peepsWhoDislikeVandalism++; + } + + if (peepsWhoDislikeVandalism > 2) + return 0; + + // Check for rides that have crashed maybe? + FOR_ALL_RIDES(i, ride) + if (ride->var_1AE != 0) + return 0; + + return 1; +} + +/** All staff types, at least 20 staff, one staff per 32 peeps. */ +static int award_is_deserved_best_staff(int awardType, int activeAwardTypes) +{ + uint16 spriteIndex; + rct_peep *peep; + int peepCount, staffCount; + int staffTypeFlags; + + if (activeAwardTypes & (1 << PARK_AWARD_MOST_UNTIDY)) + return 0; + + peepCount = 0; + staffCount = 0; + staffTypeFlags = 0; + FOR_ALL_PEEPS(spriteIndex, peep) { + if (peep->type == PEEP_TYPE_STAFF) { + staffCount++; + staffTypeFlags |= (1 << peep->staff_type); + } else { + peepCount++; + } + } + + return ((staffTypeFlags & 0xF) && staffCount >= 20 && staffCount >= peepCount / 32); + +} + +/** At least 7 shops, 4 unique, one shop per 128 guests and no more than 12 hungry guests. */ +static int award_is_deserved_best_food(int awardType, int activeAwardTypes) +{ + int i, hungryPeeps, shops, uniqueShops; + uint64 shopTypes; + rct_ride *ride; + char *object; + uint16 spriteIndex; + rct_peep *peep; + + if (activeAwardTypes & (1 << PARK_AWARD_WORST_FOOD)) + return 0; + + shops = 0; + uniqueShops = 0; + shopTypes = 0; + FOR_ALL_RIDES(i, ride) { + if (ride->status != RIDE_STATUS_OPEN) + continue; + if (!(RCT2_GLOBAL(0x0097CF40 + (ride->type * 8), uint32) & 0x800000)) + continue; + + shops++; + object = RCT2_ADDRESS(0x009ACFA4, char*)[ride->subtype]; + if (!(shopTypes & (1ULL << RCT2_GLOBAL(object + 0x1C0, uint8)))) { + shopTypes |= (1ULL << RCT2_GLOBAL(object + 0x1C0, uint8)); + uniqueShops++; + } + } + + if (shops < 7 || uniqueShops < 4 || shops < RCT2_GLOBAL(RCT2_ADDRESS_GUESTS_IN_PARK, uint16) / 128) + return 0; + + // Count hungry peeps + hungryPeeps = 0; + FOR_ALL_GUESTS(spriteIndex, peep) { + if (peep->var_2A != 0) + continue; + + if (peep->thoughts[0].var_2 <= 5 && peep->thoughts[0].type == PEEP_THOUGHT_TYPE_HUNGRY) + hungryPeeps++; + } + + return (hungryPeeps <= 12); +} + +/** No more than 2 unique shops, less than one shop per 256 guests and more than 15 hungry guests. */ +static int award_is_deserved_worst_food(int awardType, int activeAwardTypes) +{ + int i, hungryPeeps, shops, uniqueShops; + uint64 shopTypes; + rct_ride *ride; + char *object; + uint16 spriteIndex; + rct_peep *peep; + + if (activeAwardTypes & (1 << PARK_AWARD_BEST_FOOD)) + return 0; + + shops = 0; + uniqueShops = 0; + shopTypes = 0; + FOR_ALL_RIDES(i, ride) { + if (ride->status != RIDE_STATUS_OPEN) + continue; + if (!(RCT2_GLOBAL(0x0097CF40 + (ride->type * 8), uint32) & 0x800000)) + continue; + + shops++; + object = RCT2_ADDRESS(0x009ACFA4, char*)[ride->subtype]; + if (!(shopTypes & (1ULL << RCT2_GLOBAL(object + 0x1C0, uint8)))) { + shopTypes |= (1ULL << RCT2_GLOBAL(object + 0x1C0, uint8)); + uniqueShops++; + } + } + + if (uniqueShops > 2 || shops > RCT2_GLOBAL(RCT2_ADDRESS_GUESTS_IN_PARK, uint16) / 256) + return 0; + + // Count hungry peeps + hungryPeeps = 0; + FOR_ALL_GUESTS(spriteIndex, peep) { + if (peep->var_2A != 0) + continue; + + if (peep->thoughts[0].var_2 <= 5 && peep->thoughts[0].type == PEEP_THOUGHT_TYPE_HUNGRY) + hungryPeeps++; + } + + return (hungryPeeps > 15); +} + +/** At least 4 restrooms, 1 restroom per 128 guests and no more than 16 guests who think they need the restroom. */ +static int award_is_deserved_best_restrooms(int awardType, int activeAwardTypes) +{ + unsigned int i, numRestrooms, guestsWhoNeedRestroom; + rct_ride *ride; + uint16 spriteIndex; + rct_peep *peep; + + // Count open restrooms + numRestrooms = 0; + FOR_ALL_RIDES(i, ride) + if (ride->type == RIDE_TYPE_BATHROOM && ride->status == RIDE_STATUS_OPEN) + numRestrooms++; + + // At least 4 open restrooms + if (numRestrooms < 4) + return 0; + + // At least one open restroom for every 128 guests + if (numRestrooms < RCT2_GLOBAL(RCT2_ADDRESS_GUESTS_IN_PARK, uint16) / 128U) + return 0; + + // Count number of guests who are thinking they need the restroom + guestsWhoNeedRestroom = 0; + FOR_ALL_GUESTS(spriteIndex, peep) { + if (peep->var_2A != 0) + continue; + + if (peep->thoughts[0].var_2 <= 5 && peep->thoughts[0].type == PEEP_THOUGHT_TYPE_BATHROOM) + guestsWhoNeedRestroom++; + } + + return (guestsWhoNeedRestroom <= 16); +} + +/** More than half of the rides have satisfication <= 6 and park rating <= 650. */ +static int award_is_deserved_most_disappointing(int awardType, int activeAwardTypes) +{ + unsigned int i, countedRides, disappointingRides; + rct_ride *ride; + + if (activeAwardTypes & (1 << PARK_AWARD_BEST_VALUE)) + return 0; + if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_PARK_RATING, uint16) > 650) + return 0; + + // Count the number of disappointing rides + countedRides = 0; + disappointingRides = 0; + + FOR_ALL_RIDES(i, ride) { + if (ride->excitement == 0xFFFF || ride->var_158 == 0xFF) + continue; + + countedRides++; + + // Satification maybe? + if (ride->var_158 <= 6) + disappointingRides++; + } + + // Half of the rides are disappointing + return (disappointingRides >= countedRides / 2); +} + +/** At least 6 open water rides. */ +static int award_is_deserved_best_water_rides(int awardType, int activeAwardTypes) +{ + int i, waterRides; + rct_ride *ride; + char *object; + + waterRides = 0; + FOR_ALL_RIDES(i, ride) { + object = RCT2_ADDRESS(0x009ACFA4, char*)[ride->subtype]; + if (RCT2_GLOBAL(object + 0x1BE, uint8) != RIDE_GROUP_WATER && RCT2_GLOBAL(object + 0x1BF, uint8) != RIDE_GROUP_WATER) + continue; + + if (ride->status != RIDE_STATUS_OPEN || (ride->lifecycle_flags & 0x400)) + continue; + + waterRides++; + } + + return (waterRides >= 6); +} + +/** At least 6 custom designed rides. */ +static int award_is_deserved_best_custom_designed_rides(int awardType, int activeAwardTypes) +{ + int i, customDesignedRides; + rct_ride *ride; + + if (activeAwardTypes & (1 << PARK_AWARD_MOST_DISAPPOINTING)) + return 0; + + customDesignedRides = 0; + FOR_ALL_RIDES(i, ride) { + if (!(RCT2_GLOBAL(0x0097CF40 + (ride->type * 8), uint32) & 0x10000000)) + continue; + if (ride->lifecycle_flags & 0x40000) + continue; + if (ride->excitement < RIDE_RATING(5, 50)) + continue; + if (ride->status != RIDE_STATUS_OPEN || (ride->lifecycle_flags & 0x400)) + continue; + + customDesignedRides++; + } + + return (customDesignedRides >= 6); +} + +/** At least 5 colourful rides and more than half of the rides are colourful. */ +static int award_is_deserved_most_dazzling_ride_colours(int awardType, int activeAwardTypes) +{ + int i, countedRides, colourfulRides; + rct_ride *ride; + + if (activeAwardTypes & (1 << PARK_AWARD_MOST_DISAPPOINTING)) + return 0; + + countedRides = 0; + colourfulRides = 0; + FOR_ALL_RIDES(i, ride) { + if (!(RCT2_GLOBAL(0x0097CF40 + (ride->type * 8), uint32) & 0x10000000)) + continue; + + countedRides++; + if (ride->var_1BC == 5 || ride->var_1BC == 14 || ride->var_1BC == 20 || ride->var_1BC == 30) + colourfulRides++; + } + + return (colourfulRides >= 5 && colourfulRides >= countedRides - colourfulRides); +} + +/** At least 10 peeps and more than 1/64 of total guests are lost or can't find something. */ +static int award_is_deserved_most_confusing_layout(int awardType, int activeAwardTypes) +{ + unsigned int peepsCounted, peepsLost; + uint16 spriteIndex; + rct_peep *peep; + + peepsCounted = 0; + peepsLost = 0; + FOR_ALL_GUESTS(spriteIndex, peep) { + if (peep->var_2A != 0) + continue; + + peepsCounted++; + if (peep->thoughts[0].var_2 <= 5 && peep->thoughts[0].type == PEEP_THOUGHT_TYPE_LOST || peep->thoughts[0].type == PEEP_THOUGHT_TYPE_CANT_FIND) + peepsLost++; + } + + return (peepsLost >= 10 && peepsLost >= peepsCounted / 64); +} + +/** At least 10 open gentle rides. */ +static int award_is_deserved_best_gentle_rides(int awardType, int activeAwardTypes) +{ + int i, gentleRides; + rct_ride *ride; + char *object; + + gentleRides = 0; + FOR_ALL_RIDES(i, ride) { + object = RCT2_ADDRESS(0x009ACFA4, char*)[ride->subtype]; + if (RCT2_GLOBAL(object + 0x1BE, uint8) != RIDE_GROUP_GENTLE && RCT2_GLOBAL(object + 0x1BF, uint8) != RIDE_GROUP_GENTLE) + continue; + + if (ride->status != RIDE_STATUS_OPEN || (ride->lifecycle_flags & 0x400)) + continue; + + gentleRides++; + } + + return (gentleRides >= 10); +} + +typedef int (*award_deserved_check)(int, int); + +award_deserved_check _awardChecks[] = { + award_is_deserved_most_untidy, + award_is_deserved_most_tidy, + award_is_deserved_best_rollercoasters, + award_is_deserved_best_value, + award_is_deserved_most_beautiful, + award_is_deserved_worse_value, + award_is_deserved_safest, + award_is_deserved_best_staff, + award_is_deserved_best_food, + award_is_deserved_worst_food, + award_is_deserved_best_restrooms, + award_is_deserved_most_disappointing, + award_is_deserved_best_water_rides, + award_is_deserved_best_custom_designed_rides, + award_is_deserved_most_dazzling_ride_colours, + award_is_deserved_most_confusing_layout, + award_is_deserved_best_gentle_rides +}; + +static int award_is_deserved(int awardType, int activeAwardTypes) +{ + return _awardChecks[awardType](awardType, activeAwardTypes); +} + +#pragma endregion + +void award_reset() +{ + int i; + for (i = 0; i < MAX_AWARDS; i++) + RCT2_ADDRESS(RCT2_ADDRESS_AWARD_LIST, rct_award)[i].time = 0; +} + +/** + * + * rct2: 0x0066A86C + */ +void award_update_all() +{ + int i, activeAwardTypes, freeAwardEntryIndex; + rct_award *awards; + + awards = RCT2_ADDRESS(RCT2_ADDRESS_AWARD_LIST, rct_award); + + // Only add new awards if park is open + if (RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_PARK_OPEN) { + // Set active award types as flags + activeAwardTypes = 0; + freeAwardEntryIndex = -1; + for (i = 0; i < MAX_AWARDS; i++) { + if (awards[i].time != 0) + activeAwardTypes |= (1 << awards[i].type); + else if (freeAwardEntryIndex == -1) + freeAwardEntryIndex = i; + } + + // Check if there was a free award entry + if (freeAwardEntryIndex != -1) { + // Get a random award type not already active + int awardType; + do { + awardType = (((scenario_rand() & 0xFF) * 17) >> 8) & 0xFF; + } while (activeAwardTypes & (1 << awardType)); + + // Check if award is deserved + if (award_is_deserved(awardType, activeAwardTypes)) { + // Add award + awards[freeAwardEntryIndex].type = awardType; + awards[freeAwardEntryIndex].time = 5; + news_item_add_to_queue(NEWS_ITEM_AWARD, STR_NEWS_ITEM_AWARD_MOST_UNTIDY + awardType, 0); + window_invalidate_by_id(WC_PARK_INFORMATION, 0); + } + } + } + + // Decrease award times + for (i = 0; i < MAX_AWARDS; i++) + if (awards[i].time != 0) + if (--awards[i].time == 0) + window_invalidate_by_id(WC_PARK_INFORMATION, 0); +} \ No newline at end of file diff --git a/src/award.h b/src/award.h new file mode 100644 index 0000000000..aba8cb9456 --- /dev/null +++ b/src/award.h @@ -0,0 +1,58 @@ +/***************************************************************************** + * 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 . + *****************************************************************************/ + +#ifndef _AWARD_H_ +#define _AWARD_H_ + +#include "rct2.h" + +typedef struct { + uint16 time; + uint16 type; +} rct_award; + +enum { + PARK_AWARD_MOST_UNTIDY, + PARK_AWARD_MOST_TIDY, + PARK_AWARD_BEST_ROLLERCOASTERS, + PARK_AWARD_BEST_VALUE, + PARK_AWARD_MOST_BEAUTIFUL, + PARK_AWARD_WORST_VALUE, + PARK_AWARD_SAFEST, + PARK_AWARD_BEST_STAFF, + PARK_AWARD_BEST_FOOD, + PARK_AWARD_WORST_FOOD, + PARK_AWARD_BEST_RESTROOMS, + PARK_AWARD_MOST_DISAPPOINTING, + PARK_AWARD_BEST_WATER_RIDES, + PARK_AWARD_BEST_CUSTOM_DESIGNED_RIDES, + PARK_AWARD_MOST_DAZZLING_RIDE_COLOURS, + PARK_AWARD_MOST_CONFUSING_LAYOUT, + PARK_AWARD_BEST_GENTLE_RIDES, + PARK_AWARD_COUNT +}; + +#define MAX_AWARDS 4 + +int award_is_positive(int type); +void award_reset(); +void award_update_all(); + +#endif \ No newline at end of file diff --git a/src/climate.c b/src/climate.c index 6c1d4a1fd7..37bae75fda 100644 --- a/src/climate.c +++ b/src/climate.c @@ -19,10 +19,19 @@ *****************************************************************************/ #include "addresses.h" +#include "audio.h" #include "climate.h" #include "date.h" #include "gfx.h" #include "rct2.h" +#include "scenario.h" + +enum { + THUNDER_STATUS_NULL = 0, + THUNDER_STATUS_PLAYING = 1, + + MAX_THUNDER_INSTANCES = 2 +}; typedef struct { sint8 base_temperature; @@ -32,6 +41,8 @@ typedef struct { int gClimateNextWeather; +static int _climateCurrentWeatherEffect; + static int _climateNextTemperature; static int _climateNextWeatherEffect; static int _climateNextWeatherGloom; @@ -39,8 +50,24 @@ static int _climateNextRainLevel; static const rct_weather_transition* climate_transitions[4]; +// Sound data +static int _rainVolume = 1; +static rct_sound _rainSoundInstance; +static unsigned int _lightningTimer, _thunderTimer; +static rct_sound _thunderSoundInstance[MAX_THUNDER_INSTANCES]; +static int _thunderStatus[MAX_THUNDER_INSTANCES] = { THUNDER_STATUS_NULL, THUNDER_STATUS_NULL }; +static unsigned int _thunderSoundId; +static int _thunderVolume; +static int _thunderStereoEcho = 0; + static void climate_determine_future_weather(); +static void climate_update_rain_sound(); +static void climate_update_thunder_sound(); +static void climate_update_lightning(); +static void climate_update_thunder(); +static int climate_play_thunder(int instanceIndex, int soundId, int volume, int pan); + int climate_celsius_to_fahrenheit(int celsius) { return (celsius * 29) / 16 + 32; @@ -88,7 +115,7 @@ void climate_update() if (temperature == target_temperature) { if (cur_gloom == next_gloom) { - RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_WEATHER_EFFECT, sint8) = _climateNextWeatherEffect; + _climateCurrentWeatherEffect = _climateNextWeatherEffect; if (cur_rain == next_rain) { RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_WEATHER, sint8) = gClimateNextWeather; @@ -152,7 +179,127 @@ static void climate_determine_future_weather() RCT2_GLOBAL(RCT2_ADDRESS_CLIMATE_UPDATE_TIMER, sint16) = 1920; } +/** + * + * rct2: 0x006BCB91 + */ +void climate_update_sound() +{ + if (RCT2_GLOBAL(0x009AF280, uint32) == 0xFFFFFFFF) + return; + if (RCT2_GLOBAL(0x009AF59C, uint8) != 0) + return; + if (!(RCT2_GLOBAL(0x009AF59D, uint8) & 1)) + return; + if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 1) + return; + climate_update_rain_sound(); + climate_update_thunder_sound(); +} + +static void climate_update_rain_sound() +{ + if (_climateCurrentWeatherEffect == 1 || _climateCurrentWeatherEffect == 2) { + if (_rainVolume == 1) { + // Start playing the rain sound + if (sound_prepare(SOUND_RAIN_1, &_rainSoundInstance, 1, RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_SOUND_SW_BUFFER, uint32))) + sound_play(&_rainSoundInstance, 1, -4000, 0, 0); + _rainVolume = -4000; + } else { + // Increase rain sound + _rainVolume = min(-1400, _rainVolume + 80); + RCT2_CALLPROC_2(0x00404F0D, rct_sound*, int, &_rainSoundInstance, _rainVolume); + } + } else if (_rainVolume != 1) { + // Decrease rain sound + _rainVolume -= 80; + if (_rainVolume > -4000) { + RCT2_CALLPROC_2(0x00404F0D, rct_sound*, int, &_rainSoundInstance, _rainVolume); + } else { + sound_stop(&_rainSoundInstance); + _rainVolume = 1; + } + } +} + +static void climate_update_thunder_sound() +{ + if (_thunderStereoEcho) { + // Play thunder on right side + _thunderStereoEcho = 0; + climate_play_thunder(1, _thunderSoundId, _thunderVolume, 10000); + } else if (_thunderTimer != 0) { + climate_update_lightning(); + climate_update_thunder(); + } else if (_climateCurrentWeatherEffect == 2) { + // Create new thunder and lightning + unsigned int randomNumber = scenario_rand(); + if ((randomNumber & 0xFFFF) <= 0x1B4) { + randomNumber >>= 16; + _thunderTimer = 43 + (randomNumber % 64); + _lightningTimer = randomNumber % 32; + } + } + + // Stop thunder sounds if they have finished + for (int i = 0; i < MAX_THUNDER_INSTANCES; i++) { + if (_thunderStatus[i] == THUNDER_STATUS_NULL) + continue; + + if (!RCT2_CALLFUNC_1(0x00404E53, int, rct_sound*, &_thunderSoundInstance[i])) { + sound_stop(&_thunderSoundInstance[i]); + _thunderStatus[i] = THUNDER_STATUS_NULL; + } + } +} + +static void climate_update_lightning() +{ + if (_lightningTimer == 0) + return; + + _lightningTimer--; + if (RCT2_GLOBAL(RCT2_ADDRESS_LIGHTNING_ACTIVE, uint16) == 0) + if ((scenario_rand() & 0xFFFF) <= 0x2000) + RCT2_GLOBAL(RCT2_ADDRESS_LIGHTNING_ACTIVE, uint16) = 1; +} + +static void climate_update_thunder() +{ + _thunderTimer--; + if (_thunderTimer != 0) + return; + + unsigned int randomNumber = scenario_rand(); + if (randomNumber & 0x10000) { + if (_thunderStatus[0] == THUNDER_STATUS_NULL && _thunderStatus[1] == THUNDER_STATUS_NULL) { + // Play thunder on left side + _thunderSoundId = (randomNumber & 0x20000) ? SOUND_THUNDER_1 : SOUND_THUNDER_2; + _thunderVolume = (-((int)((randomNumber >> 18) & 0xFF))) << 3; + climate_play_thunder(0, _thunderSoundId, _thunderVolume, -10000); + + // Let thunder play on right side + _thunderStereoEcho = 1; + } + } else { + _thunderSoundId = (randomNumber & 0x20000) ? SOUND_THUNDER_1 : SOUND_THUNDER_2; + int pan = (((randomNumber >> 18) & 0xFF) - 128) * 16; + climate_play_thunder(0, _thunderSoundId, 0, pan); + } +} + +static int climate_play_thunder(int instanceIndex, int soundId, int volume, int pan) +{ + if (sound_prepare(soundId, &_thunderSoundInstance[instanceIndex], 1, RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_SOUND_SW_BUFFER, uint32))) { + sound_play(&_thunderSoundInstance[instanceIndex], 0, volume, pan, 0); + + _thunderStatus[instanceIndex] = THUNDER_STATUS_PLAYING; + return 1; + } + + return 0; +} #pragma region Climate / Weather data tables diff --git a/src/climate.h b/src/climate.h index 814039f10f..376323d907 100644 --- a/src/climate.h +++ b/src/climate.h @@ -44,5 +44,6 @@ extern const rct_weather climate_weather_data[6]; int climate_celsius_to_fahrenheit(int celsius); void climate_reset(int climate); void climate_update(); +void climate_update_sound(); #endif diff --git a/src/config.c b/src/config.c index 4415705b28..41551d94a5 100644 --- a/src/config.c +++ b/src/config.c @@ -19,14 +19,12 @@ *****************************************************************************/ #include -#include -#include -#include #include +#include #include "addresses.h" #include "config.h" #include "rct2.h" -#include + #include "osinterface.h" @@ -78,15 +76,19 @@ static const uint16 _defaultShortcutKeys[SHORTCUT_COUNT] = { general_configuration_t gGeneral_config; general_configuration_t gGeneral_config_default = { - 1, - 1, - SCREENSHOT_FORMAT_PNG, - "", - MEASUREMENT_FORMAT_IMPERIAL, - TEMPERATURE_FORMAT_F, - 0, - 0, - 1, + 0, // play_intro + 1, // confirmation_prompt + SCREENSHOT_FORMAT_PNG, // screenshot_format + "", // game_path + MEASUREMENT_FORMAT_IMPERIAL, // measurement_format + TEMPERATURE_FORMAT_F, // temperature_format + CURRENCY_POUNDS, // currency_format + 0, // construction_marker_colour + 1, // edge_scrolling + 0, // always_show_gridlines + 1, // landscape_smoothing + 0, // show_height_as_units + 1, // save_plugin_data }; sound_configuration_t gSound_config; @@ -102,6 +104,10 @@ static void config_create_default(char *path); static int config_parse_currency(char* currency); static void config_error(char *msg); +void config_save_ini(char *path); +void config_write_ini_general(FILE *fp); +void config_write_ini_sound(FILE *fp); + /** * * rct2: 0x006E3604 @@ -117,20 +123,20 @@ void config_reset_shortcut_keys() */ void config_load() { - HANDLE hFile; - DWORD bytesRead; + FILE *fp=NULL; char* path = get_file_path(PATH_ID_GAMECFG); - hFile = CreateFile(get_file_path(PATH_ID_GAMECFG), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, - FILE_FLAG_RANDOM_ACCESS | FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile != INVALID_HANDLE_VALUE) { + + fp = fopen(path, "rb"); + + if (fp != NULL) { // Read and check magic number - ReadFile(hFile, RCT2_ADDRESS(0x013CE928, void), 4, &bytesRead, NULL); + fread(RCT2_ADDRESS(0x013CE928, void), 1, 4, fp); + if (RCT2_GLOBAL(0x013CE928, int) == MagicNumber) { // Read options - ReadFile(hFile, (void*)0x009AAC5C, 2155, &bytesRead, NULL); - CloseHandle(hFile); - + fread((void*)0x009AAC5C, 1, 2155, fp); + fclose(fp); //general configuration RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_EDGE_SCROLLING, sint8) = gGeneral_config.edge_scrolling; @@ -138,6 +144,37 @@ void config_load() RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_METRIC, sint8) = gGeneral_config.measurement_format; RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_TEMPERATURE, sint8) = gGeneral_config.temperature_format; + // always show gridlines + if (gGeneral_config.always_show_gridlines){ + RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) |= CONFIG_FLAG_ALWAYS_SHOW_GRIDLINES; + } + else { + RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) &= !CONFIG_FLAG_ALWAYS_SHOW_GRIDLINES; + } + + // landscape smoothing + if (!gGeneral_config.landscape_smoothing){ + RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) |= CONFIG_FLAG_DISABLE_SMOOTH_LANDSCAPE; + } + else { + RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) &= !CONFIG_FLAG_DISABLE_SMOOTH_LANDSCAPE; + } + + // show height as units + if (gGeneral_config.show_height_as_units){ + RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) |= CONFIG_FLAG_SHOW_HEIGHT_AS_UNITS; + } + else { + RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) &= !CONFIG_FLAG_SHOW_HEIGHT_AS_UNITS; + } + + // save plugin data + if (gGeneral_config.save_plugin_data){ + RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) |= CONFIG_FLAG_SAVE_PLUGIN_DATA; + } + else { + RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) &= !CONFIG_FLAG_SAVE_PLUGIN_DATA; + } //sound configuration RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_SOUND_QUALITY, sint8) = gSound_config.sound_quality; @@ -191,23 +228,152 @@ void config_load() RCT2_GLOBAL(0x009AA00D, sint8) = 1; } + /** * Save configuration to the data/config.cfg file * rct2: 0x00675487 */ void config_save() { - HANDLE hFile; - DWORD bytesWritten; + FILE *fp=NULL; + char *configIniPath = osinterface_get_orct2_homefolder();; - hFile = CreateFile(get_file_path(PATH_ID_GAMECFG), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile != INVALID_HANDLE_VALUE) { - WriteFile(hFile, &MagicNumber, 4, &bytesWritten, NULL); - WriteFile(hFile, (LPCVOID)0x009AAC5C, 2155, &bytesWritten, NULL); - CloseHandle(hFile); + fp = fopen(get_file_path(PATH_ID_GAMECFG), "wb"); + if (fp != NULL){ + fwrite(&MagicNumber, 4, 1, fp); + fwrite((void*)0x009AAC5C, 2155, 1, fp); + fclose(fp); + } + + sprintf(configIniPath, "%s%c%s", configIniPath, osinterface_get_path_separator(), "config.ini"); + config_save_ini(configIniPath); +} + +void config_save_ini(char *path) +{ + FILE *fp = NULL; + + + fp = fopen(path, "wt+"); + + config_write_ini_general(fp); + config_write_ini_sound(fp); + + fclose(fp); +} + +void config_write_ini_sound(FILE *fp) +{ + fprintf(fp, "[sound]\n"); + if (gSound_config.sound_quality == SOUND_QUALITY_LOW) { + fprintf(fp, "sound_quality = low\n"); + } + else if (gSound_config.sound_quality == SOUND_QUALITY_MEDIUM) { + fprintf(fp, "sound_quality = medium\n"); + } + else{ + fprintf(fp, "sound_quality = high\n"); + } + + if (gSound_config.forced_software_buffering){ + fprintf(fp, "forced_software_buffering = true\n"); + } + else { + fprintf(fp, "forced_software_buffering = false\n"); } } +void config_write_ini_general(FILE *fp) +{ + int currencyIterator = 0; + + fprintf(fp, "[general]\n"); + fprintf(fp, "game_path = %s\n", gGeneral_config.game_path); + + switch (gGeneral_config.screenshot_format) + { + case SCREENSHOT_FORMAT_BMP: + fprintf(fp, "screenshot_format = BMP\n"); + break; + case SCREENSHOT_FORMAT_PNG: + fprintf(fp, "screenshot_format = PNG\n"); + break; + default: + config_error("error saving config.ini: wrong screenshot_format"); + break; + } + + if (gGeneral_config.play_intro){ + fprintf(fp, "play_intro = true\n"); + } + else { + fprintf(fp, "play_intro = false\n"); + } + + if (gGeneral_config.confirmation_prompt){ + fprintf(fp, "confirmation_prompt = true\n"); + } + else { + fprintf(fp, "confirmation_prompt = false\n"); + } + + if (gGeneral_config.edge_scrolling){ + fprintf(fp, "edge_scrolling = true\n"); + } + else { + fprintf(fp, "edge_scrolling = false\n"); + } + + for (currencyIterator = 0; currencyIterator < countof(_currencyLookupTable); currencyIterator++) { + if (_currencyLookupTable[currencyIterator].value == gGeneral_config.currency_format) { + gGeneral_config.currency_format = _currencyLookupTable[currencyIterator].value; + fprintf(fp, "currency = %s\n", _currencyLookupTable[currencyIterator].key); + break; // There are more than one valid item for Pound, Euro and Dollar ... + } + } + + if (gGeneral_config.measurement_format == MEASUREMENT_FORMAT_IMPERIAL) { + fprintf(fp, "measurement_format = imperial\n"); + } + else { + fprintf(fp, "measurement_format = metric\n"); + } + + if (gGeneral_config.temperature_format == TEMPERATURE_FORMAT_F) { + fprintf(fp, "temperature_format = fahrenheit\n"); + } + else { + fprintf(fp, "temperature_format = celsius\n"); + } + + if (gGeneral_config.always_show_gridlines){ + fprintf(fp, "always_show_gridlines = true\n"); + } + else { + fprintf(fp, "always_show_gridlines = false\n"); + } + + if (gGeneral_config.landscape_smoothing){ + fprintf(fp, "landscape_smoothing = true\n"); + } + else { + fprintf(fp, "landscape_smoothing = false\n"); + } + + if (gGeneral_config.show_height_as_units){ + fprintf(fp, "show_height_as_units = true\n"); + } + else { + fprintf(fp, "show_height_as_units = false\n"); + } + + if (gGeneral_config.save_plugin_data){ + fprintf(fp, "save_plugin_data = true\n"); + } + else { + fprintf(fp, "save_plugin_data = false\n"); + } +} /** * Initilise the settings. @@ -216,20 +382,19 @@ void config_save() */ void config_init() { - TCHAR path[MAX_PATH]; + char *path = osinterface_get_orct2_homefolder(); FILE* fp; memcpy(&gGeneral_config, &gGeneral_config_default, sizeof(general_configuration_t)); - if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, 0, path))) { // find home folder - strcat(path, "\\OpenRCT2"); - DWORD dwAttrib = GetFileAttributes(path); - if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) { // folder does not exist - if (!CreateDirectory(path, NULL)) { - config_error("Could not create config file (do you have write access to your documents folder?)"); - } + if (strcmp(path, "") != 0){ + if (!osinterface_ensure_directory_exists(path)) { + config_error("Could not create config file (do you have write access to your documents folder?)"); + return; } - strcat(path, "\\config.ini"); + + sprintf(path, "%s%c%s", path, osinterface_get_path_separator(), "config.ini"); + fp = fopen(path, "r"); if (!fp) { config_create_default(path); @@ -242,6 +407,8 @@ void config_init() fclose(fp); } + + free(path); } /** @@ -253,7 +420,7 @@ void config_init() static int config_find_rct2_path(char *resultPath) { int i; - DWORD dwAttrib; + const char *searchLocations[] = { "C:\\Program Files\\Infogrames\\RollerCoaster Tycoon 2", "C:\\Program Files (x86)\\Infogrames\\RollerCoaster Tycoon 2", @@ -265,8 +432,7 @@ static int config_find_rct2_path(char *resultPath) }; for (i = 0; i < countof(searchLocations); i++) { - dwAttrib = GetFileAttributes(searchLocations[i]); - if (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) { + if ( osinterface_directory_exists(searchLocations[i]) ) { strcpy(resultPath, searchLocations[i]); return 1; } @@ -282,8 +448,7 @@ static int config_find_rct2_path(char *resultPath) */ static void config_create_default(char *path) { - FILE* fp; - + gGeneral_config = gGeneral_config_default; if (!config_find_rct2_path(gGeneral_config.game_path)) { osinterface_show_messagebox("Unable to find RCT2 installation directory. Please select the directory where you installed RCT2!"); @@ -291,20 +456,7 @@ static void config_create_default(char *path) strcpy(gGeneral_config.game_path, res); } - fp = fopen(path, "w"); - fprintf(fp, "[general]\n"); - fprintf(fp, "game_path = %s\n", gGeneral_config.game_path); - fprintf(fp, "screenshot_format = PNG\n"); - fprintf(fp, "play_intro = false\n"); - fprintf(fp, "confirmation_prompt = true\n"); - fprintf(fp, "edge_scrolling = true\n"); - fprintf(fp, "currency = GBP\n"); - fprintf(fp, "measurement_format = imperial\n"); - fprintf(fp, "temperature_format = fahrenheit\n"); - fprintf(fp, "[sound]\n"); - fprintf(fp, "sound_quality = high\n"); - fprintf(fp, "forced_software_buffering = false\n"); - fclose(fp); + config_save_ini(path); } @@ -419,7 +571,38 @@ static void config_general(char *setting, char *value){ else if (strcmp(setting, "currency") == 0){ config_parse_currency(value); } - + else if (strcmp(setting, "always_show_gridlines") == 0){ + if (strcmp(value, "true") == 0){ + gGeneral_config.always_show_gridlines = 1; + } + else { + gGeneral_config.always_show_gridlines = 0; + } + } + else if (strcmp(setting, "landscape_smoothing") == 0){ + if (strcmp(value, "true") == 0){ + gGeneral_config.landscape_smoothing = 1; + } + else { + gGeneral_config.landscape_smoothing = 0; + } + } + else if (strcmp(setting, "show_height_as_units") == 0){ + if (strcmp(value, "true") == 0){ + gGeneral_config.show_height_as_units = 1; + } + else { + gGeneral_config.show_height_as_units = 0; + } + } + else if (strcmp(setting, "save_plugin_data") == 0){ + if (strcmp(value, "true") == 0){ + gGeneral_config.save_plugin_data = 1; + } + else { + gGeneral_config.save_plugin_data = 0; + } + } } /** @@ -592,25 +775,6 @@ static int config_parse_section(FILE *fp, char *setting, char *value){ return 1; } -static const struct { char *key; int value; } _currencyLookupTable[] = { - { "GBP", CURRENCY_POUNDS }, - { "USD", CURRENCY_DOLLARS }, - { "FRF", CURRENCY_FRANC }, - { "DEM", CURRENCY_DEUTSCHMARK }, - { "YEN", CURRENCY_YEN }, - { "ESP", CURRENCY_PESETA }, - { "ITL", CURRENCY_LIRA }, - { "NLG", CURRENCY_GUILDERS }, - { "NOK", CURRENCY_KRONA }, - { "SEK", CURRENCY_KRONA }, - { "DEK", CURRENCY_KRONA }, - { "EUR", CURRENCY_EUROS }, - - { "£", CURRENCY_POUNDS }, - { "$", CURRENCY_DOLLARS }, - { "€", CURRENCY_EUROS } -}; - static int config_parse_currency(char *currency) { int i; diff --git a/src/config.h b/src/config.h index ef04a12386..6e43a04b4e 100644 --- a/src/config.h +++ b/src/config.h @@ -21,8 +21,8 @@ #ifndef _CONFIG_H_ #define _CONFIG_H_ -#include #include "rct2.h" +#include // for MAX_PATH enum { CONFIG_FLAG_ALWAYS_SHOW_GRIDLINES = (1 << 0), @@ -136,9 +136,31 @@ typedef struct general_configuration { sint8 currency_format; sint8 construction_marker_colour; sint8 edge_scrolling; - + sint8 always_show_gridlines; + sint8 landscape_smoothing; + sint8 show_height_as_units; + sint8 save_plugin_data; } general_configuration_t; +static const struct { char *key; int value; } _currencyLookupTable[] = { + { "GBP", CURRENCY_POUNDS }, + { "USD", CURRENCY_DOLLARS }, + { "FRF", CURRENCY_FRANC }, + { "DEM", CURRENCY_DEUTSCHMARK }, + { "YEN", CURRENCY_YEN }, + { "ESP", CURRENCY_PESETA }, + { "ITL", CURRENCY_LIRA }, + { "NLG", CURRENCY_GUILDERS }, + { "NOK", CURRENCY_KRONA }, + { "SEK", CURRENCY_KRONA }, + { "DEK", CURRENCY_KRONA }, + { "EUR", CURRENCY_EUROS }, + + { "£", CURRENCY_POUNDS }, + { "$", CURRENCY_DOLLARS }, + { "€", CURRENCY_EUROS } +}; + //typedef struct hotkey_configuration{ //}; diff --git a/src/finance.c b/src/finance.c index cceea4ee5d..a900c9ec82 100644 --- a/src/finance.c +++ b/src/finance.c @@ -71,16 +71,13 @@ void finance_payment(money32 amount, rct_expenditure_type type) void finance_pay_wages() { rct_peep* peep; - uint16 sprite_idx; + uint16 spriteIndex; - if (RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & 0x800) + if (RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_11) return; - for (sprite_idx = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_PEEP, uint16); sprite_idx != SPRITE_INDEX_NULL; sprite_idx = peep->next) { - peep = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[sprite_idx].peep); - if (peep->type == PEEP_TYPE_STAFF) - finance_payment(wage_table[peep->staff_type] / 4, RCT_EXPENDITURE_TYPE_WAGES); - } + FOR_ALL_STAFF(spriteIndex, peep) + finance_payment(wage_table[peep->staff_type] / 4, RCT_EXPENDITURE_TYPE_WAGES); } /** @@ -120,12 +117,10 @@ void finance_pay_interest() */ void finance_pay_ride_upkeep() { + int i; rct_ride* ride; - for (int i = 0; i < MAX_RIDES; i++) { - ride = &(RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, rct_ride)[i]); - if (ride->type == RIDE_TYPE_NULL) - continue; + FOR_ALL_RIDES(i, ride) { if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_EVER_BEEN_OPENED)) { ride->build_date = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint16); ride->var_196 = 25855; // durability? @@ -142,6 +137,15 @@ void finance_pay_ride_upkeep() } } +void finance_reset_history() +{ + int i; + for (i = 0; i < 128; i++) { + RCT2_ADDRESS(RCT2_ADDRESS_BALANCE_HISTORY, money32)[i] = MONEY32_UNDEFINED; + RCT2_ADDRESS(RCT2_ADDRESS_WEEKLY_PROFIT_HISTORY, money32)[i] = MONEY32_UNDEFINED; + RCT2_ADDRESS(RCT2_ADDRESS_PARK_VALUE_HISTORY, money32)[i] = MONEY32_UNDEFINED; + } +} /** * diff --git a/src/finance.h b/src/finance.h index fb63ae127f..ace3b157ae 100644 --- a/src/finance.h +++ b/src/finance.h @@ -38,6 +38,7 @@ void finance_pay_wages(); void finance_pay_research(); void finance_pay_interest(); void finance_pay_ride_upkeep(); +void finance_reset_history(); void finance_init(); void sub_69E869(); diff --git a/src/game.c b/src/game.c index 2c0475910d..a5327e18c8 100644 --- a/src/game.c +++ b/src/game.c @@ -35,6 +35,7 @@ #include "string_ids.h" #include "title.h" #include "tutorial.h" +#include "vehicle.h" #include "viewport.h" #include "widget.h" #include "window.h" @@ -58,10 +59,47 @@ void game_create_windows() RCT2_CALLPROC_EBPSAFE(0x0066B905); } +/** + * + * rct2: 0x006838BD + */ +void update_water_animation() +{ + RCT2_CALLPROC_EBPSAFE(0x006838BD); +} + +/** + * + * rct2: 0x00684218 + */ +void update_rain_animation() +{ + if (RCT2_GLOBAL(0x009ABDF2, uint8) == 0) + return; + + // Draw picked-up peep + if (RCT2_GLOBAL(0x009DE550, 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) + ); + } + + // Get rain draw function and draw rain + uint32 eax = RCT2_ADDRESS(0x009AC058, uint32)[RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_RAIN_LEVEL, uint8)]; + if (eax != 0xFFFFFFFF && !(RCT2_GLOBAL(0x009DEA6F, uint8) & 1)) + RCT2_CALLPROC_X(0x00684266, eax, 0, 0, 0, 0, 0, 0); +} + void game_update() { int eax, tmp; + // Handles picked-up peep and rain redraw + RCT2_CALLPROC_EBPSAFE(0x006843DC); + // 0x006E3AEC // screen_game_process_mouse_input(); // RCT2_CALLPROC_EBPSAFE(0x006E3AEC); // screen_game_process_keyboard_input(); screenshot_check(); @@ -123,8 +161,8 @@ void game_update() RCT2_GLOBAL(0x0141F568, uint8) = RCT2_GLOBAL(0x0013CA740, uint8); game_handle_input(); - RCT2_CALLPROC_EBPSAFE(0x006838BD); - RCT2_CALLPROC_EBPSAFE(0x00684218); + update_water_animation(); + update_rain_animation(); if (RCT2_GLOBAL(0x009AAC73, uint8) != 255) { RCT2_GLOBAL(0x009AAC73, uint8)++; @@ -149,7 +187,7 @@ void game_logic_update() RCT2_CALLPROC_EBPSAFE(0x006646E1); RCT2_CALLPROC_EBPSAFE(0x006A876D); peep_update_all(); - RCT2_CALLPROC_EBPSAFE(0x006D4204); // update vehicles + vehicle_update_all(); RCT2_CALLPROC_EBPSAFE(0x00672AA4); // update text effects RCT2_CALLPROC_EBPSAFE(0x006ABE4C); // update rides park_update(); @@ -159,7 +197,7 @@ void game_logic_update() RCT2_CALLPROC_EBPSAFE(0x0068AFAD); RCT2_CALLPROC_EBPSAFE(0x006BBC6B); // vehicle and scream sounds peep_update_crowd_noise(); - RCT2_CALLPROC_EBPSAFE(0x006BCB91); // weather sound effects + climate_update_sound(); news_item_update_current(); RCT2_CALLPROC_EBPSAFE(0x0067009A); // scenario editor opening of windows for a phase @@ -524,17 +562,11 @@ static void input_mouseover(int x, int y, rct_window *w, int widgetIndex) input_mouseover_widget_check(windowClass, windowNumber, widgetIndex); - if (w != NULL && widgetIndex != -1 && widget->type == WWT_SCROLL) { - int eax, ebx, ecx, edx, esi, edi, ebp; - eax = x; - ebx = y; - esi = (int)w; - edi = (int)widget; - RCT2_CALLFUNC_X(0x006E9F92, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); // widget_scoll_get_part - eax &= 0xFFFF; - ebx &= 0xFFFF; - ecx &= 0xFFFF; - edx &= 0xFFFF; + if (w != NULL && widgetIndex != -1 && widget->type == WWT_SCROLL) + { + int eax, ebx, ecx, edx; + widget_scroll_get_part(w, widget, x, y, &eax, &ebx, &ecx, &edx); + if (ecx < 0) goto showTooltip; if (ecx == 0) { @@ -616,6 +648,37 @@ static void input_mouseover_widget_flatbutton_invalidate() widget_invalidate(RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_OVER_WINDOWCLASS, rct_windowclass), RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_OVER_WINDOWNUMBER, rct_windownumber), RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_OVER_WIDGETINDEX, rct_windownumber)); } +static void RCT2_CALLPROC_WE_MOUSE_DOWN(int address, int widgetIndex, rct_window*w, rct_widget* widget ) +{ +#ifdef _MSC_VER + __asm { + push address + push widget + push w + push widgetIndex + mov edi, widget + mov edx, widgetIndex + mov esi, w + call[esp + 12] + add esp, 16 + } +#else + __asm__("\ + push %[address]\n\ + mov edi, %[widget] \n\ + mov eax, %[w] \n\ + mov edx, %[widgetIndex] \n\ + push edi \n\ + push eax \n\ + push edx \n\ + mov esi, %[w] \n\ + call [esp+12] \n\ + add esp, 16 \n\ + " :[address] "+m" (address), [w] "+m" (w), [widget] "+m" (widget), [widgetIndex] "+m" (widgetIndex): : "eax", "esi", "edx", "edi" + ); +#endif +} + /** * * rct2: 0x006E95F9 @@ -695,16 +758,8 @@ static void input_leftmousedown(int x, int y, rct_window *w, int widgetIndex) RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_CURSOR_X, uint16) = x; RCT2_GLOBAL(RCT2_ADDRESS_TOOLTIP_CURSOR_Y, uint16) = y; - int eax, ebx, ecx, edx, esi, edi, ebp; - eax = x; - ebx = y; - esi = (int)w; - edi = (int)widget; - RCT2_CALLFUNC_X(0x006E9F92, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); // widget_scoll_get_part - eax &= 0xFFFF; - ebx &= 0xFFFF; - ecx &= 0xFFFF; - edx &= 0xFFFF; + int eax, ebx, ecx, edx; + widget_scroll_get_part(w, widget, x, y, &eax, &ebx, &ecx, &edx); RCT2_GLOBAL(0x009DE548, uint16) = ecx; RCT2_GLOBAL(0x009DE54C, uint32) = edx; @@ -754,7 +809,7 @@ static void input_leftmousedown(int x, int y, rct_window *w, int widgetIndex) if (widget_is_disabled(w, widgetIndex)) break; - sound_play_panned(4, w->x + (widget->left + widget->right) / 2); + sound_play_panned(SOUND_CLICK_1, w->x + (widget->left + widget->right) / 2); // Set new cursor down widget RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WINDOWCLASS, rct_windowclass) = windowClass; @@ -765,7 +820,7 @@ static void input_leftmousedown(int x, int y, rct_window *w, int widgetIndex) RCT2_GLOBAL(0x009DE528, uint16) = 1; widget_invalidate(windowClass, windowNumber, widgetIndex); - RCT2_CALLPROC_X(w->event_handlers[WE_MOUSE_DOWN], 0, 0, 0, widgetIndex, (int)w, (int)widget, 0); + RCT2_CALLPROC_WE_MOUSE_DOWN(w->event_handlers[WE_MOUSE_DOWN], widgetIndex, w, widget); break; } } @@ -1021,7 +1076,7 @@ void handle_shortcut_command(int shortcutIndex) case SHORTCUT_SHOW_FINANCIAL_INFORMATION: if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 0x0C)) if (!(RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & 0x800)) - RCT2_CALLPROC_EBPSAFE(0x0069DDF1); + window_finances_open(); break; case SHORTCUT_SHOW_RESEARCH_INFORMATION: if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 0x0E)) { @@ -1029,7 +1084,7 @@ void handle_shortcut_command(int shortcutIndex) RCT2_CALLPROC_EBPSAFE(0x006B3CFF); window = window_find_by_id(WC_CONSTRUCT_RIDE, 0); if (window != NULL) - window_event_helper(window, 10, WE_MOUSE_DOWN); + RCT2_CALLPROC_WE_MOUSE_DOWN(window->event_handlers[WE_MOUSE_DOWN], 10, window, NULL); } break; case SHORTCUT_SHOW_RIDES_LIST: @@ -1596,6 +1651,35 @@ static void load_game() } } +char save_game() +{ + int eax, ebx, ecx, edx, esi, edi, ebp; + RCT2_CALLFUNC_X(0x006750E9, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + if (eax == 0) { + // user pressed "cancel" + gfx_invalidate_screen(); + return 0; + } + + char *src = (char*)0x0141EF67; + do { + src++; + } while (*src != '.' && *src != '\0'); + strcpy(src, ".SV6"); + strcpy((char*) RCT2_ADDRESS_SAVED_GAMES_PATH_2, (char*) 0x0141EF68); + + eax = 0; + if (RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) & 8) + eax |= 1; + RCT2_CALLPROC_X(0x006754F5, eax, 0, 0, 0, 0, 0, 0); + // check success? + + game_do_command(0, 1047, 0, -1, 0, 0, 0); + gfx_invalidate_screen(); + + return 1; +} + /** * * rct2: 0x006E3879 diff --git a/src/game.h b/src/game.h index 8619d04526..4b1bd42d4c 100644 --- a/src/game.h +++ b/src/game.h @@ -29,5 +29,6 @@ int game_do_command(int eax, int ebx, int ecx, int edx, int esi, int edi, int eb void game_load_or_quit_no_save_prompt(); int game_load_save(); +char save_game(); #endif diff --git a/src/gfx.c b/src/gfx.c index 8c14b8c932..74a38d433d 100644 --- a/src/gfx.c +++ b/src/gfx.c @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014 Ted John + * Copyright (c) 2014 Ted John, Peter Hill * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. * * This file is part of OpenRCT2. @@ -28,6 +28,7 @@ #include "rct2.h" #include "string_ids.h" #include "window.h" +#include "osinterface.h" typedef struct { uint32 num_entries; @@ -42,6 +43,64 @@ int gLastDrawStringY; uint8 _screenDirtyBlocks[5120]; +enum{ + //The next byte specifies the X coordinate + CHAR_CONTROL_CODE_MOVE_X = 1, + + //The next byte specifies the palette + CHAR_CONTROL_CODE_ADJUST_PALETTE_1 = 2, + + //Jumps a fixed amount of pixels down and + //starts a new line + CHAR_CONTROL_CODE_NEW_LINE_FIXED = 5, + //Jumps less than the above + CHAR_CONTROL_CODE_NEW_LINE_FIXED_SMALLER = 6, + + CHAR_CONTROL_CODE_FONT_2 = 7, + CHAR_CONTROL_CODE_FONT_3 = 8, + CHAR_CONTROL_CODE_FONT_1 = 9, + CHAR_CONTROL_CODE_FONT_0 = 0xA, + + CHAR_CONTROL_CODE_FONT_FLAG_SET_BIT_1 = 0xB, + CHAR_CONTROL_CODE_FONT_FLAG_CLEAR_BIT_1 = 0xC, + + //Adjusts the palette depending on a global var + CHAR_CONTROL_CODE_ADJUST_PALETTE_2 = 0xD, + CHAR_CONTROL_CODE_ADJUST_PALETTE_3 = 0xE, + CHAR_CONTROL_CODE_ADJUST_PALETTE_4 = 0xF, + + //The next 2 bytes specify the X and Y coordinates + CHAR_CONTROL_CODE_NEW_LINE_X_Y = 0x11, + + //The next 4 bytes specify the sprite + CHAR_CONTROL_CODE_SPRITE = 0x17 +}; + +//Originally 0x9ABE0C, 12 elements from 0xF3 are the peep top colour, 12 elements from 0xCA are peep trouser colour +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 +}; + static void gfx_draw_dirty_blocks(int x, int y, int columns, int rows); /** @@ -87,6 +146,67 @@ int gfx_load_g1() return 0; } +/* +* 0x6C19AC +*/ +void gfx_load_character_widths(){ + + uint8* char_width_pointer = RCT2_ADDRESS(RCT2_ADDRESS_FONT_CHAR_WIDTH, uint8); + for (int char_set_offset = 0; char_set_offset < 4*0xE0; char_set_offset+=0xE0){ + for (uint8 c = 0; c < 0xE0; c++, char_width_pointer++){ + rct_g1_element g1 = RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[c + 0xF15 + char_set_offset]; + int width; + + if (char_set_offset == 0xE0*3) width = g1.width + 1; + else width = g1.width - 1; + + if (c >= 0x5B && c < 0x7F){ + width = 0; + } + *char_width_pointer = (uint8)width; + } + + } + + uint8 drawing_surface[0x40]; + rct_drawpixelinfo dpi = { + .bits = (char*)&drawing_surface, + .width = 8, + .height = 8, + .x = 0, + .y = 0, + .pitch = 0, + .zoom_level = 0}; + + + for (int i = 0; i < 0xE0; ++i){ + memset(drawing_surface, 0, sizeof(drawing_surface)); + gfx_draw_sprite(&dpi, i + 0x10D5, -1, 0); + + for (int x = 0; x < 8; ++x){ + uint8 val = 0; + for (int y = 0; y < 8; ++y){ + val >>= 1; + if (dpi.bits[x + y * 8]==1){ + val |= 0x80; + } + } + RCT2_ADDRESS(0xF4393C, uint8)[i * 8 + x] = val; + } + + } + + for (int i = 0; i < 0x20; ++i){ + rct_g1_element* g1 = &(RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[0x606 + i]); + uint8* unknown_pointer = RCT2_ADDRESS(0x9C3852, uint8) + 0xa12 * i; + g1->offset = unknown_pointer; + g1->width = 0x40; + g1->height = 0x28; + *((uint16*)unknown_pointer) = 0xFFFF; + *((uint32*)(unknown_pointer + 0x0E)) = 0; + } +} + /** * Clears the screen with the specified colour. * rct2: 0x00678A9F @@ -254,11 +374,198 @@ void gfx_draw_line(rct_drawpixelinfo *dpi, int x1, int y1, int x2, int y2, int c */ void gfx_fill_rect(rct_drawpixelinfo *dpi, int left, int top, int right, int bottom, int colour) { - RCT2_CALLPROC_X(0x00678AD4, left, right, top, bottom, 0, (int)dpi, 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); + + if (!(colour & 0x1000000)) { + if (!(colour & 0x8000000)) { + left_ = left - dpi->x; + if (left_ < 0) + left_ = 0; + + right_ = right - dpi->x; + right_++; + if (right_ > dpi->width) + right_ = dpi->width; + + right_ -= left_; + + top_ = top - dpi->y; + if (top_ < 0) + top_ = 0; + + bottom_ = bottom - dpi->y; + bottom_++; + + if (bottom_ > dpi->height) + bottom_ = dpi->height; + + bottom_ -= top_; + + if (!(colour & 0x2000000)) { + if (!(colour & 0x4000000)) { + uint8* pixel = (top_ * (dpi->width + dpi->pitch)) + left_ + dpi->bits; + + int length = dpi->width + dpi->pitch - right_; + + for (int i = 0; i < bottom_; ++i) { + memset(pixel, (colour & 0xFF), right_); + pixel += length + right_; + } + } else { + // 00678B8A 00678E38 + char* esi; + esi = (top_ * (dpi->width + dpi->pitch)) + left_ + dpi->bits;; + + int eax, ebp; + eax = colour; + ebp = dpi->width + dpi->pitch - right_; + + RCT2_GLOBAL(0x00EDF810, uint32) = ebp; + RCT2_GLOBAL(0x009ABDB2, uint16) = bottom_; + RCT2_GLOBAL(0x00EDF814, uint32) = right_; + + top_ = (top + dpi->y) & 0xf; + right_ = (right + dpi_->x) &0xf; + + dpi_ = (rct_drawpixelinfo*)esi; + + esi = (char*)(eax >> 0x1C); + esi = (char*)RCT2_GLOBAL(0x0097FEFC,uint32)[esi]; // or possibly uint8)[esi*4] ? + + for (; RCT2_GLOBAL(0x009ABDB2, uint16) > 0; RCT2_GLOBAL(0x009ABDB2, uint16)--) { + // push ebx + // push ecx + ebp = *(esi + top_*2); + + // mov bp, [esi+top_*2]; + int ecx; + ecx = RCT2_GLOBAL(0x00EDF814, uint32); + + for (int i = ecx; i >=0; --i) { + if (!(ebp & (1 << right_))) + dpi_->bits = (char*)(left_ & 0xFF); + + right_++; + right_ = right_ & 0xF; + dpi_++; + } + // pop ecx + // pop ebx + top_++; + top_ = top_ &0xf; + dpi_ += RCT2_GLOBAL(0x00EDF810, uint32); + } + return; + } + + } else { + // 00678B7E 00678C83 + if (dpi->zoom_level < 1) { + // Location in screen buffer? + uint8* pixel = top_ * (dpi->width + dpi->pitch) + left_ + dpi->bits; + + // Find colour in colour table? + uint32 eax = RCT2_ADDRESS(0x0097FCBC, uint32)[(colour & 0xFF)]; + rct_g1_element* g1_element = &(RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[eax]); + + int length = (dpi->width + dpi->pitch) - right_; + + // Fill the rectangle with the colours from the colour table + for (int i = 0; i < bottom_; ++i) { + for (int j = 0; j < right_; ++j) { + *pixel = *((uint8*)(&g1_element->offset[*pixel])); + pixel++; + } + pixel += length; + } + } else if (dpi->zoom_level > 1) { + // 00678C8A 00678D57 + right_ = right; + } else if (dpi->zoom_level == 1) { + // 00678C88 00678CEE + right = right; + } + + } + } else { + // 00678B3A 00678EC9 + right_ = right; + } + } else { + // 00678B2E 00678BE5 + // Cross hatching + uint16 pattern = 0; + + left_ = left_ - dpi->x; + if (left_ < 0) { + pattern = pattern ^ left_; + left_ = 0; + } + + right_ = right_ - dpi->x; + right_++; + + if (right_ > dpi->width) + right_ = dpi-> width; + + right_ = right_ - left_; + + top_ = top - dpi->y; + if (top_ < 0) { + pattern = pattern ^ top_; + top_ = 0; + } + + bottom_ = bottom - dpi->y; + bottom_++; + + if (bottom_ > dpi->height) + bottom_ = dpi->height; + + bottom_ -= top_; + + uint8* pixel = (top_ * (dpi->width + dpi->pitch)) + left_ + dpi->bits; + + int length = dpi->width + dpi->pitch - right_; + + uint32 ecx; + for (int i = 0; i < bottom_; ++i) { + ecx = pattern; + // Rotate right + ecx = (ecx >> 1) | (ecx << (sizeof(ecx) * CHAR_BIT - 1)); + ecx = (ecx & 0xFFFF0000) | right_; + // Fill every other pixel with the colour + for (; (ecx & 0xFFFF) > 0; ecx--) { + ecx = ecx ^ 0x80000000; + if ((int)ecx < 0) { + *pixel = colour & 0xFF; + } + pixel++; + } + pattern = pattern ^ 1; + pixel += length; + + } + } + + // 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) @@ -266,13 +573,339 @@ void gfx_fill_rect(rct_drawpixelinfo *dpi, int left, int top, int right, int bot * right (bx) * bottom (dx) * colour (ebp) - * _si (si) + * flags (si) */ -void gfx_fill_rect_inset(rct_drawpixelinfo* dpi, short left, short top, short right, short bottom, int colour, short _si) +void gfx_fill_rect_inset(rct_drawpixelinfo* dpi, short left, short top, short right, short bottom, int colour, short flags) { - RCT2_CALLPROC_X(0x006E6F81, left, right, top, bottom, _si, (int)dpi, colour); + 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); + } + } + } } +#define RCT2_Y_RELATED_GLOBAL_1 0x9E3D12 //uint16 +#define RCT2_Y_END_POINT_GLOBAL 0x9ABDAC //sint16 +#define RCT2_Y_START_POINT_GLOBAL 0xEDF808 //sint16 +#define RCT2_X_RELATED_GLOBAL_1 0x9E3D10 //uint16 +#define RCT2_X_END_POINT_GLOBAL 0x9ABDA8 //sint16 +#define RCT2_X_START_POINT_GLOBAL 0xEDF80C //sint16 +#define RCT2_DPI_LINE_LENGTH_GLOBAL 0x9ABDB0 //uint16 width+pitch + +/* +* rct2: 0x67A690 +* copies a sprite onto the buffer. There is no compression used on the sprite +* image. +*/ +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 + 1; + //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_level){ + uint8* next_source_pointer = source_pointer + source_image->width*zoom_level; + uint8* next_unknown_pointer = unknown_pointer + source_image->width*zoom_level; + uint8* next_dest_pointer = dest_pointer + dest_dpi->width + dest_dpi->pitch; + + for (int no_pixels = width; no_pixels > 0; no_pixels -= zoom_level, source_pointer+=zoom_level, unknown_pointer+=zoom_level, 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_level){ + uint8* next_source_pointer = source_pointer + source_image->width*zoom_level; + uint8* next_dest_pointer = dest_pointer + dest_dpi->width + dest_dpi->pitch; + for (int no_pixels = width; no_pixels > 0; no_pixels -= zoom_level, source_pointer+= zoom_level, 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_level){ + uint8* next_source_pointer = source_pointer + source_image->width*zoom_level; + uint8* next_dest_pointer = dest_pointer + dest_dpi->width + dest_dpi->pitch; + + for (int no_pixels = width; no_pixels > 0; no_pixels -= zoom_level, source_pointer += zoom_level, 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_level){ + uint8* next_source_pointer = source_pointer + source_image->width*zoom_level; + uint8* next_dest_pointer = dest_pointer + dest_dpi->width + dest_dpi->pitch; + + for (int no_pixels = width; no_pixels > 0; no_pixels -= zoom_level, dest_pointer++, source_pointer += zoom_level){ + *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_level){ + uint8* next_source_pointer = source_pointer + source_image->width*zoom_level; + uint8* next_unknown_pointer = unknown_pointer + source_image->width*zoom_level; + uint8* next_dest_pointer = dest_pointer + dest_dpi->width + dest_dpi->pitch; + + for (int no_pixels = width; no_pixels > 0; no_pixels -= zoom_level, dest_pointer++, source_pointer += zoom_level, unknown_pointer += zoom_level){ + 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_level){ + uint8* next_source_pointer = source_pointer + source_image->width*zoom_level; + uint8* next_dest_pointer = dest_pointer + dest_dpi->width + dest_dpi->pitch; + + for (int no_pixels = width; no_pixels > 0; no_pixels -= zoom_level, dest_pointer++, source_pointer += zoom_level){ + uint8 pixel = *source_pointer; + if (pixel){ + *dest_pointer = pixel; + } + } + dest_pointer = next_dest_pointer; + source_pointer = next_source_pointer; + } + return; +} + + +/* +* rct2: 0x67AA18 transfers readied images onto buffers +* This function copies the sprite data onto the screen +* I think its only used for bitmaps onto buttons but i am not sure. +* There is still a small bug with this code when it is in the choose park view. +*/ +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 + 1; + + 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_level){ + + //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_level; + } + 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_level, source_pointer += zoom_level, dest_pointer++){ + uint8 al = *source_pointer; + uint8 ah = *dest_pointer; + if (image_type & IMAGE_TYPE_MIX_BACKGROUND)//Mix with background and image Not Tested + al = palette_pointer[(al | ((int)ah) << 8) - 0x100]; + else //Adjust colours? + 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_level, dest_pointer++){ + uint8 pixel = *dest_pointer; + pixel = palette_pointer[pixel]; + *dest_pointer = pixel; + } + } + else + { + for (; no_pixels > 0; no_pixels -= zoom_level, source_pointer += zoom_level, dest_pointer++){ + *dest_pointer = *source_pointer; + } + } + } + + //Add a line to the drawing surface pointer + next_dest_pointer += (int)dpi->width + (int)dpi->pitch; + } +} + + +void gfx_draw_sprite_palette_set(rct_drawpixelinfo *dpi, int image_id, int x, int y, uint8* palette_pointer, uint8* unknown_pointer); /** * * rct2: 0x0067A28E @@ -282,7 +915,264 @@ void gfx_fill_rect_inset(rct_drawpixelinfo* dpi, short left, short top, short ri */ void gfx_draw_sprite(rct_drawpixelinfo *dpi, int image_id, int x, int y) { - RCT2_CALLPROC_X(0x0067A28E, 0, image_id, x, y, 0, (int)dpi, 0); + + int eax = 0, ebx = image_id, ecx = x, edx = y, esi = 0, edi = (int)dpi, ebp = 0; + 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; + eax = (image_id >> 26) & 0x7; + + uint8* unknown_pointer = (uint8*)(RCT2_ADDRESS(0x9E3CE4, uint32*)[image_sub_type]); + RCT2_GLOBAL(0x009E3CDC, uint32) = RCT2_GLOBAL(0x009E3CE4 + eax * 4, uint32); + + if (image_type && !(image_type & IMAGE_TYPE_UNKNOWN)) { + + if (!(image_type & IMAGE_TYPE_MIX_BACKGROUND)){ + eax = image_id; + eax >>= 19; + eax &= 0xFF; + unknown_pointer = NULL; + RCT2_GLOBAL(0x009E3CDC, uint32) = 0; + } + else{ + eax = image_id; + eax >>= 19; + eax &= 0x7F; + } + eax = RCT2_GLOBAL(eax * 4 + 0x97FCBC, uint32); + + palette_pointer = ((rct_g1_element*)RCT2_ADDRESS_G1_ELEMENTS)[eax].offset; + RCT2_GLOBAL(0x9ABDA4, uint32) = (uint32)palette_pointer; + } + else if (image_type && !(image_type & IMAGE_TYPE_USE_PALETTE)){ + //Has not been tested + RCT2_GLOBAL(0x9E3CDC, uint32) = 0; + unknown_pointer = NULL; + + eax = image_id; + eax >>= 19; + //push edx/y + eax &= 0x1F; + ebp = RCT2_GLOBAL(ebp * 4 + 0x97FCBC, uint32); + eax = RCT2_GLOBAL(eax * 4 + 0x97FCBC, uint32); + ebp <<= 0x4; + eax <<= 0x4; + ebp = RCT2_GLOBAL(ebp + RCT2_ADDRESS_G1_ELEMENTS, uint32); + eax = RCT2_GLOBAL(eax + RCT2_ADDRESS_G1_ELEMENTS, uint32); + edx = *((uint32*)(eax + 0xF3)); + esi = *((uint32*)(eax + 0xF7)); + RCT2_GLOBAL(0x9ABFFF, uint32) = edx; + RCT2_GLOBAL(0x9AC003, uint32) = esi; + edx = *((uint32*)(ebp + 0xF3)); + esi = *((uint32*)(ebp + 0xF7)); + esi = *((uint32*)(eax + 0xF7)); + + edx = *((uint32*)(eax + 0xFB)); + esi = *((uint32*)(ebp + 0xFB)); + + eax = image_id; + RCT2_GLOBAL(0x9AC007, uint32) = edx; + eax >>= 24; + RCT2_GLOBAL(0x9ABF42, uint32) = esi; + eax &= 0x1F; + + //image_id + RCT2_GLOBAL(0xEDF81C, uint32) |= 0x20000000; + image_id |= IMAGE_TYPE_USE_PALETTE; + + eax = RCT2_GLOBAL(eax * 4 + 0x97FCBC, uint32); + eax <<= 4; + eax = RCT2_GLOBAL(eax + RCT2_ADDRESS_G1_ELEMENTS, uint32); + edx = *((uint32*)(eax + 0xF3)); + esi = *((uint32*)(eax + 0xF7)); + RCT2_GLOBAL(0x9ABFD6, uint32) = edx; + RCT2_GLOBAL(0x9ABFDA, uint32) = esi; + edx = *((uint32*)(eax + 0xFB)); + RCT2_GLOBAL(0x9ABDA4, uint32) = 0x9ABF0C; + palette_pointer = (uint8*)0x9ABF0C; + RCT2_GLOBAL(0x9ABFDE, uint32) = edx; + edx = y; + + } + 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 = 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 = 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); +} + +/* +* 0x67A46E +*/ +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(0x0067BD81, 0, (int)g1_source, 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; + }*/ + + //We add on one so that divides will create the correct number of pixels + int zoom_level = dpi->zoom_level + 1; + //This will be the height of the drawn image + int height = g1_source->height / zoom_level; + //This is the start y coordinate on the destination + int dest_start_y = y - dpi->y + g1_source->y_offset; + //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*zoom_level; + //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; + } + + //This will be the width of the drawn image + int width = g1_source->width / zoom_level; + //This is the source start x coordinate + int source_start_x = 0; + //This is the destination start x coordinate + int dest_start_x = x - dpi->x + g1_source->x_offset; + + 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*zoom_level; + //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; + } + + + uint8* dest_pointer = (uint8*)dpi->bits; + //Move the pointer to the start point of the destination + dest_pointer += (dpi->width + dpi->pitch)*dest_start_y + dest_start_x; + + height *= zoom_level; + width *= zoom_level; + + 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; } /** @@ -309,8 +1199,7 @@ void gfx_transpose_palette(int pal, unsigned char product) esi += 3; edi += 4; } - - RCT2_CALLPROC_3(0x00405595, int, int, int, 0x01424680, 10, 236); + osinterface_update_palette((char*)0x01424680, 10, 236); } /** @@ -325,7 +1214,22 @@ void gfx_transpose_palette(int pal, unsigned char product) */ void gfx_draw_string_centred(rct_drawpixelinfo *dpi, int format, int x, int y, int colour, void *args) { - RCT2_CALLPROC_X(0x006C1D6C, colour, format, x, y, (int)args, (int)dpi, 0); + char* buffer; + short text_width; + + buffer = RCT2_ADDRESS(RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER, char); + format_string(buffer, format, args); + + RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16) = 0xE0; + + // Measure text width + text_width = gfx_get_string_width(buffer); + + // Draw the text centred + if (text_width <= 0xFFFF) { + x -= text_width / 2; + gfx_draw_string(dpi, buffer, colour, x, y); + } } /** @@ -466,20 +1370,307 @@ void gfx_redraw_screen_rect(short left, short top, short right, short bottom) } /** - * + * Return the width of the string in buffer + * * rct2: 0x006C2321 * buffer (esi) */ -int gfx_get_string_width(char *buffer) +int gfx_get_string_width(char* buffer) { - int eax, ebx, ecx, edx, esi, edi, ebp; + // Current font sprites + uint16* current_font_sprite_base; + // Width of string + int width; + rct_g1_element g1_element; + + current_font_sprite_base = RCT2_ADDRESS(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16); + width = 0; - esi = (int)buffer; - RCT2_CALLFUNC_X(0x006C2321, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + for (uint8* curr_char = (uint8*)buffer; *curr_char != (uint8)NULL; curr_char++) { - return ecx & 0xFFFF; + if (*curr_char >= 0x20) { + width += RCT2_ADDRESS(RCT2_ADDRESS_FONT_CHAR_WIDTH, uint8)[*current_font_sprite_base + (*curr_char - 0x20)]; + continue; + } + switch(*curr_char) { + case CHAR_CONTROL_CODE_MOVE_X: + curr_char++; + width = *curr_char; + break; + case CHAR_CONTROL_CODE_ADJUST_PALETTE_1: + case 3: + case 4: + curr_char++; + break; + case CHAR_CONTROL_CODE_NEW_LINE_FIXED: + case CHAR_CONTROL_CODE_NEW_LINE_FIXED_SMALLER: + continue; + case CHAR_CONTROL_CODE_FONT_2: + *current_font_sprite_base = 0x1C0; + break; + case CHAR_CONTROL_CODE_FONT_3: + *current_font_sprite_base = 0x2A0; + break; + case CHAR_CONTROL_CODE_FONT_1: + *current_font_sprite_base = 0x0E0; + break; + case CHAR_CONTROL_CODE_FONT_0: + *current_font_sprite_base = 0; + break; + case CHAR_CONTROL_CODE_FONT_FLAG_SET_BIT_1: + case CHAR_CONTROL_CODE_FONT_FLAG_CLEAR_BIT_1: + case CHAR_CONTROL_CODE_ADJUST_PALETTE_2: + case CHAR_CONTROL_CODE_ADJUST_PALETTE_3: + case CHAR_CONTROL_CODE_ADJUST_PALETTE_4: + case 0x10: + continue; + case CHAR_CONTROL_CODE_SPRITE: + g1_element = RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[*((uint32*)(curr_char+1))&0x7FFFF]; + width += g1_element.width; + curr_char += 4; + break; + default: + if (*curr_char <= 0x16) { //case 0x11? CHAR_CONTROL_CODE_NEW_LINE_X_Y + curr_char += 2; + continue; + } + curr_char += 4;//never happens? + break; + } + } + return width; } +/** + * Clip the text in buffer to width, add ellipsis and return the new width of the clipped string + * + * rct2: 0x006C2460 + * buffer (esi) + * width (edi) + */ +int gfx_clip_string(char* buffer, int width) +{ + // Location of font sprites + uint16 current_font_sprite_base; + // Width the string has to fit into + unsigned int max_width; + // Character to change to ellipsis + unsigned char* last_char; + // Width of the string, including ellipsis + + unsigned int clipped_width; + + rct_g1_element g1_element; + + if (width < 6) { + *buffer = 0; + return 0; + } + + current_font_sprite_base = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16); + max_width = width - (3 * RCT2_ADDRESS(0x141E9F6, uint8)[current_font_sprite_base]); + + clipped_width = 0; + last_char = buffer; + + for (unsigned char* curr_char = buffer; *curr_char != (uint8)NULL; curr_char++) { + if (*curr_char < 0x20) { + switch (*curr_char) { + case CHAR_CONTROL_CODE_MOVE_X: + curr_char++; + clipped_width = *curr_char; + continue; + case CHAR_CONTROL_CODE_ADJUST_PALETTE_1: + case 3: + case 4: + curr_char++; + continue; + case CHAR_CONTROL_CODE_NEW_LINE_FIXED: + case CHAR_CONTROL_CODE_NEW_LINE_FIXED_SMALLER: + continue; + case CHAR_CONTROL_CODE_FONT_2: + current_font_sprite_base = 0x1C0; + break; + case CHAR_CONTROL_CODE_FONT_3: + current_font_sprite_base = 0x2A0; + break; + case CHAR_CONTROL_CODE_FONT_1: + current_font_sprite_base = 0x0E0; + break; + case CHAR_CONTROL_CODE_FONT_0: + current_font_sprite_base = 0; + break; + case CHAR_CONTROL_CODE_FONT_FLAG_SET_BIT_1: + case CHAR_CONTROL_CODE_FONT_FLAG_CLEAR_BIT_1: + case CHAR_CONTROL_CODE_ADJUST_PALETTE_2: + case CHAR_CONTROL_CODE_ADJUST_PALETTE_3: + case CHAR_CONTROL_CODE_ADJUST_PALETTE_4: + case 0x10: + continue; + case CHAR_CONTROL_CODE_SPRITE: + g1_element = RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[*((uint32*)(curr_char+1))&0x7FFFF]; + clipped_width += g1_element.width; + curr_char += 4; + continue; + default: + if (*curr_char <= 0x16) { //case 0x11? CHAR_CONTROL_CODE_NEW_LINE_X_Y + curr_char += 2; + continue; + } + curr_char += 4;//never happens? + continue; + } + max_width = width - (3 * RCT2_ADDRESS(0x141E9F6, uint8)[current_font_sprite_base]); + } + + clipped_width += RCT2_ADDRESS(RCT2_ADDRESS_FONT_CHAR_WIDTH, uint8)[current_font_sprite_base + (*curr_char - 0x20)]; + + if ((int)clipped_width > width) { + *((uint32*)last_char) = '...'; + clipped_width = width; + return clipped_width; + } + if (clipped_width <= max_width) { + last_char = curr_char+1; + } + } + return clipped_width; +} + + +/** + * Wrap the text in buffer to width, returns width of longest line. + * + * Inserts NULL where line should break (as \n is used for something else), + * so the number of lines is returned in num_lines. font_height seems to be + * a control character for line height. + * + * rct2: 0x006C21E2 + * buffer (esi) + * width (edi) - in + * num_lines (edi) - out + * font_height (ebx) - out + */ +int gfx_wrap_string(char* buffer, int width, int* num_lines, int* font_height) +{ + unsigned int line_width = 0; + unsigned int max_width = 0; + rct_g1_element g1_element; + + uint16* current_font_sprite_base = RCT2_ADDRESS(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16); + + *num_lines = 0; + *font_height = 0; + + // Pointer to the start of the current word + unsigned char* curr_word = NULL; + // Width of line up to current word + unsigned int curr_width; + + for (unsigned char* curr_char = buffer; *curr_char != (uint8)NULL; curr_char++) { + + // Remember start of current word and line width up to this word + if (*curr_char == ' ') { + curr_word = curr_char; + curr_width = line_width; + } + + // 5 is RCT2 new line? + if (*curr_char != 5) { + if (*curr_char < ' ') { + switch(*curr_char) { + case CHAR_CONTROL_CODE_MOVE_X: + case CHAR_CONTROL_CODE_ADJUST_PALETTE_1: + case 3: + case 4: + curr_char++; + continue; + case CHAR_CONTROL_CODE_NEW_LINE_FIXED: + case CHAR_CONTROL_CODE_NEW_LINE_FIXED_SMALLER: + continue; + case CHAR_CONTROL_CODE_FONT_2: + *font_height = 0x1C0; + continue; + case CHAR_CONTROL_CODE_FONT_3: + *font_height = 0x2A0; + continue; + case CHAR_CONTROL_CODE_FONT_1: + *font_height = 0xE0; + continue; + case CHAR_CONTROL_CODE_FONT_0: + *font_height = 0; + continue; + case CHAR_CONTROL_CODE_FONT_FLAG_SET_BIT_1: + case CHAR_CONTROL_CODE_FONT_FLAG_CLEAR_BIT_1: + case CHAR_CONTROL_CODE_ADJUST_PALETTE_2: + case CHAR_CONTROL_CODE_ADJUST_PALETTE_3: + case CHAR_CONTROL_CODE_ADJUST_PALETTE_4: + case 0x10: + continue; + case CHAR_CONTROL_CODE_SPRITE: + g1_element = RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[*((uint32*)(curr_char + 1)) & 0x7FFFF]; + line_width += g1_element.width; + curr_char += 4; + break; + default: + if (*curr_char <= 0x16) { + curr_char += 2; + continue; + } + curr_char += 4; + continue; + } + } + + line_width += RCT2_ADDRESS(RCT2_ADDRESS_FONT_CHAR_WIDTH, uint8)[*current_font_sprite_base + (*curr_char - 0x20)]; + + if ((int)line_width <= width) { + continue; + } + if (curr_word == 0) { + curr_char--; + unsigned char* old_char = curr_char; + unsigned char swap_char = 0; + unsigned char temp; + // Insert NULL at current character + // Aboslutely no guarantee that this won't overrun! + do { + temp = swap_char; + swap_char = *curr_char; + *curr_char = temp; + curr_char++; + } while(swap_char != 0); + + *curr_char = swap_char; + curr_char = old_char; + curr_char++; + *num_lines += 1; + + if (line_width > max_width) { + max_width = line_width; + } + line_width = 0; + curr_word = 0; + continue; + } + curr_char = curr_word; + line_width = curr_width; + } + + *num_lines += 1; + *curr_char = 0; + + if (line_width > max_width) { + max_width = line_width; + } + line_width = 0; + curr_word = 0; + } + + return max_width; +} + + /** * Draws i formatted text string left aligned at i specified position but clips * the text with an elipsis if the text width exceeds the specified width. @@ -494,15 +1685,17 @@ int gfx_get_string_width(char *buffer) */ void gfx_draw_string_left_clipped(rct_drawpixelinfo* dpi, int format, void* args, int colour, int x, int y, int width) { - RCT2_CALLPROC_X(0x006C1B83, colour, format, x, y, (int)args, (int)dpi, width); + char* buffer; - //char* buffer; + buffer = RCT2_ADDRESS(RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER, char); + format_string(buffer, format, args); - //buffer = (char*)0x0141ED68; - //format_string(buffer, format, args); - //rctmem->current_font_sprite_base = 224; - //clip_text(buffer, width); - //gfx_draw_string(dpi, buffer, colour, x, y); + RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16) = 0xE0; + + // Clip text - return value is not needed + gfx_clip_string(buffer, width); + + gfx_draw_string(dpi, buffer, colour, x, y); } /** @@ -519,22 +1712,24 @@ void gfx_draw_string_left_clipped(rct_drawpixelinfo* dpi, int format, void* args */ void gfx_draw_string_centred_clipped(rct_drawpixelinfo *dpi, int format, void *args, int colour, int x, int y, int width) { - RCT2_CALLPROC_X(0x006C1BBA, colour, format, x, y, (int)args, (int)dpi, width); + char* buffer; + short text_width; - //char* buffer; - //short text_width; + buffer = RCT2_ADDRESS(RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER, char); + format_string(buffer, format, args); - //buffer = (char*)0x0141ED68; - //format_string(buffer, format, args); - //rctmem->current_font_sprite_base = 224; - //text_width = clip_text(buffer, width); + RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16) = 0xE0; - //// Draw the text centred - //x -= (text_width - 1) / 2; - //gfx_draw_string(dpi, buffer, colour, x, y); + // Clip text + text_width = gfx_clip_string(buffer, width); + + // Draw the text centred + if (text_width <= 0xFFFF) { + x -= (text_width - 1) / 2; + gfx_draw_string(dpi, buffer, colour, x, y); + } } - /** * Draws i formatted text string right aligned. * rct2: 0x006C1BFC @@ -550,7 +1745,7 @@ void gfx_draw_string_right(rct_drawpixelinfo* dpi, int format, void* args, int c char* buffer; short text_width; - buffer = (char*)0x0141ED68; + buffer = RCT2_ADDRESS(RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER, char); format_string(buffer, format, args); // Measure text width @@ -574,45 +1769,108 @@ void gfx_draw_string_right(rct_drawpixelinfo* dpi, int format, void* args, int c */ int gfx_draw_string_centred_wrapped(rct_drawpixelinfo *dpi, void *args, int x, int y, int width, int format, int colour) { - int eax, ebx, ecx, edx, esi, edi, ebp; + int font_height, line_height, line_width, line_y, num_lines; + // Location of font sprites + uint16* current_font_sprite_base; - eax = colour; - ebx = format; - ecx = x; - edx = y; - esi = (int)args; - edi = (int)dpi; - ebp = width; - RCT2_CALLFUNC_X(0x006C1E53, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + char* buffer = RCT2_ADDRESS(0x009C383D, char); - return (sint16)(edx & 0xFFFF) - y; + current_font_sprite_base = RCT2_ADDRESS(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16); + *current_font_sprite_base = 0xE0; + + gfx_draw_string(dpi, buffer, colour, dpi->x, dpi->y); + + buffer = RCT2_ADDRESS(RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER, char); + + format_string(buffer, format, args); + + *current_font_sprite_base = 0xE0; + + // line_width unused here + line_width = gfx_wrap_string(buffer, width, &num_lines, &font_height); + + line_height = 0x0A; + + if (font_height > 0xE0) { + line_height = 6; + if (font_height != 0x1C0) { + line_height = 0x12; + } + } + + if (*buffer == 0x0B) { + line_height = line_height + 1; + } + + font_height = (line_height / 2) * num_lines; + line_y = y - font_height; + + RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_FLAGS, uint16) = 0; + + for (int line = 0; line <= num_lines; ++line) { + int half_width = gfx_get_string_width(buffer) / 2; + gfx_draw_string(dpi, buffer, 0xFE, x - half_width, line_y); + + buffer += strlen(buffer) + 1; + line_y += line_height; + } + + return line_y - y; } /** * * rct2: 0x006C2105 * dpi (edi) - * format (esi) + * args (esi) * x (cx) * y (dx) * width (bp) - * colour (bx) - * unknown (al) + * format (bx) + * colour (al) */ -int gfx_draw_string_left_wrapped(rct_drawpixelinfo *dpi, void *format, int x, int y, int width, int colour, int unknown) +int gfx_draw_string_left_wrapped(rct_drawpixelinfo *dpi, void *args, int x, int y, int width, int format, int colour) { - int eax, ebx, ecx, edx, esi, edi, ebp; + // font height might actually be something else + int font_height, line_height, line_width, line_y, num_lines; - eax = unknown; - ebx = colour; - ecx = x; - edx = y; - esi = (int)format; - edi = (int)dpi; - ebp = width; - RCT2_CALLFUNC_X(0x006C2105, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + // Location of font sprites + uint16* current_font_sprite_base = RCT2_ADDRESS(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16); + *current_font_sprite_base = 0xE0; - return (sint16)(edx & 0xFFFF) - y; + char* buffer = RCT2_ADDRESS(0x009C383D, char); + + gfx_draw_string(dpi, buffer, colour, dpi->x, dpi->y); + + buffer = RCT2_ADDRESS(RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER, char); + + format_string(buffer, format, args); + + *current_font_sprite_base = 0xE0; + + // Line width unused here + line_width = gfx_wrap_string(buffer, width, &num_lines, &font_height); + + line_height = 0x0A; + + if (font_height > 0xE0) { + line_height = 6; + if (font_height != 0x1C0) { + line_height = 0x12; + } + } + + RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_FLAGS, uint16) = 0; + + line_y = y; + + for (int line = 0; line <= num_lines; ++line) { + gfx_draw_string(dpi, buffer, 0xFE, x, line_y); + buffer += strlen(buffer) + 1; + line_y += line_height; + } + + return line_y - y; } /** @@ -629,33 +1887,356 @@ void gfx_draw_string_left(rct_drawpixelinfo *dpi, int format, void *args, int co { char* buffer; - buffer = (char*)0x0141ED68; + buffer = RCT2_ADDRESS(RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER, char); format_string(buffer, format, args); + RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16) = 0xE0; gfx_draw_string(dpi, buffer, colour, x, y); } + +void colour_char(int al, uint16* current_font_flags) { + + int eax; + + rct_g1_element g1_element = RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[0x1332]; + eax = ((uint32*)g1_element.offset)[al & 0xFF]; + + if (!(*current_font_flags & 2)) { + eax = eax & 0x0FF0000FF; + } + // Adjust text palette. Store current colour? + text_palette[1] = eax & 0xFF; + text_palette[2] = (eax>>8) & 0xFF; + text_palette[3] = (eax >> 16) & 0xFF; + text_palette[4] = (eax >> 24) & 0xFF; + RCT2_GLOBAL(0x009ABDA4, uint32) = (uint32)text_palette; +} + + +void sub_682AC7(int ebp, uint16* current_font_flags) { + + int eax; + + eax = RCT2_ADDRESS(0x0141FD45, uint8)[ebp * 8]; + if (*current_font_flags & 2) { + eax |= 0x0A0A00; + } + //Adjust text palette. Store current colour? + text_palette[1] = eax & 0xFF; + text_palette[2] = (eax >> 8) & 0xFF; + text_palette[3] = (eax >> 16) & 0xFF; + text_palette[4] = (eax >> 24) & 0xFF; + RCT2_GLOBAL(0x009ABDA4, uint32) = (uint32)text_palette; + +} + + /** * * rct2: 0x00682702 * dpi (edi) - * format (esi) + * buffer (esi) * colour (al) * x (cx) * y (dx) */ -void gfx_draw_string(rct_drawpixelinfo *dpi, char *format, int colour, int x, int y) +void gfx_draw_string(rct_drawpixelinfo *dpi, char *buffer, int colour, int x, int y) { - int eax, ebx, ecx, edx, esi, edi, ebp; - eax = colour; - ebx = 0; - ecx = x; - edx = y; - esi = (int)format; - edi = (int)dpi; - ebp = 0; - RCT2_CALLFUNC_X(0x00682702, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + int eax, ebx, ecx, ebp; + rct_g1_element* g1_element; - gLastDrawStringX = ecx; - gLastDrawStringY = edx; + // Maximum length/height of string + int max_x = x; + int max_y = y; + + // + uint16* current_font_flags = RCT2_ADDRESS(RCT2_ADDRESS_CURRENT_FONT_FLAGS, uint16); + uint16* current_font_sprite_base = RCT2_ADDRESS(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16); + + // Flag for skipping non-printing characters + int skip_char = 0; + + if (colour != 0xFE) { + + if (x >= dpi->x + dpi->width) + return; + + if (x + 0x280 <= dpi->x) + return; + + if (y >= dpi->y + dpi->height) + return; + + if (y + 0x5A <= dpi->y) { + return; + } + + if (colour != 0xFF) { + + // switch_colour: + *current_font_flags = 0; + if (*current_font_sprite_base < 0) { + *current_font_flags |= 4; + if (*current_font_sprite_base != 0xFFFF) { + *current_font_flags |= 8; + } + *current_font_sprite_base = 0xE0; + } + if (colour & (1 << 5)) { + *current_font_flags |= 2; + } + colour &= ~(1 << 5); + + if (!(colour & 0x40)) { + ebp = colour; + if (*current_font_flags & 1) { + if ((y + 0x13 <= dpi->y) || (dpi->y + dpi->height <= y)) { + skip_char = 1; + } else { + skip_char = 0; + } + } else { + sub_682AC7(ebp, current_font_flags); + } + } else { + *current_font_flags |= 1; + colour &= 0x1F; + + if (*current_font_flags & 4) { + if (*current_font_flags & 8) { + eax = RCT2_ADDRESS(0x0141FC48, uint8)[colour * 8]; + eax = eax << 10; + eax = eax | RCT2_ADDRESS(0x0141FC46, uint8)[colour * 8]; + } else { + eax = RCT2_ADDRESS(0x0141FC49, uint8)[colour * 8]; + eax = eax << 10; + eax = eax | RCT2_ADDRESS(0x0141FC47, uint8)[colour * 8]; + } + } else { + eax = RCT2_ADDRESS(0x0141FC4A, uint8)[colour * 8]; + eax = eax << 10; + eax = eax | RCT2_ADDRESS(0x0141FC48, uint8)[colour * 8]; + } + // Adjust text palette. Store current colour? ; + text_palette[1] = eax & 0xFF; + text_palette[2] = (eax >> 8) & 0xFF; + text_palette[3] = (eax >> 16) & 0xFF; + text_palette[4] = (eax >> 24) & 0xFF; + RCT2_GLOBAL(0x009ABDA4, uint32) = (uint32)text_palette; + eax = 0; + } + } + } + + if ((y + 0x13 <= dpi->y) || (dpi->y + dpi->height <= y)) { + skip_char = 1; + } + + for (uint8 al = *buffer; al > 0; ++buffer, al = *buffer) { + + // Skip to the next printing character + if (skip_char) { + if (al < 0x20) { + // Control codes + skip_char = 0; + } else if (al >= 0x8E && al < 0x9C) { + // Colour codes + al -= 0x8E; + if (*current_font_flags == 1) { + if ((y + 0x13 <= dpi->y) || (dpi->y + dpi->height <= y)) { + skip_char = 1; + } else { + skip_char = 0; + } + continue; + } + colour_char(al, current_font_flags); + continue; + } else { + continue; + } + } + + // Control codes + switch (al) { + case CHAR_CONTROL_CODE_MOVE_X://Start New Line at start+buffer x, same y. (Overwrite?) + al = *(buffer+1); + buffer++; + max_x = x; + max_x += al; + break; + case CHAR_CONTROL_CODE_ADJUST_PALETTE_1: + al = *(buffer + 1); + buffer++; + if (*current_font_flags & 1) { + if ((y + 0x13 <= dpi->y) || (dpi->y + dpi->height <= y)) { + skip_char = 1; + break; + } + } + + eax = RCT2_ADDRESS(0x097FCBC, uint32)[al * 4]; + g1_element = &(RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[eax]); + ebx = g1_element->offset[0xF9] + (1 << 8); + if (!(*current_font_flags & 2)) { + ebx = ebx & 0xFF; + } + + //Adjust the text palette + memcpy(&(text_palette[3]), &(g1_element->offset[0xF7]), 2); + memcpy(&(text_palette[5]), &(g1_element->offset[0xFA]), 2); + //Set the palette pointer + RCT2_GLOBAL(0x009ABDA4, uint32) = (uint32)text_palette; + + + if ((y + 0x13 <= dpi->y) || (dpi->y + dpi->height <= y)) { + skip_char = 1; + } + break; + case CHAR_CONTROL_CODE_NEW_LINE_FIXED://Start New Line at set y lower + max_x = x; + max_y += 0x0A; + if (*current_font_sprite_base <= 0x0E) { + break; + } + max_y -= 4; + if (*current_font_sprite_base == 0x1C0) { + break; + } + max_y -= 0xFFF4; + break; + case CHAR_CONTROL_CODE_NEW_LINE_FIXED_SMALLER://Start New Line at set y lower + max_x = x; + max_y += 5; + if (*current_font_sprite_base <= 0x0E) { + break; + } + max_y -= 2; + if (*current_font_sprite_base == 0x1C0) { + break; + } + max_y -= 0xFFFA;//This does not look correct probably should be an add + break; + case CHAR_CONTROL_CODE_FONT_2: + *current_font_sprite_base = 0x1C0; + break; + case CHAR_CONTROL_CODE_FONT_3: + *current_font_sprite_base = 0x2A0; + break; + case CHAR_CONTROL_CODE_FONT_1: + *current_font_sprite_base = 0xE0; + break; + case CHAR_CONTROL_CODE_FONT_0: + *current_font_sprite_base = 0; + break; + case CHAR_CONTROL_CODE_FONT_FLAG_SET_BIT_1: + *current_font_flags |= 2; + break; + case CHAR_CONTROL_CODE_FONT_FLAG_CLEAR_BIT_1: + *current_font_flags &= 0x0FFFD; + break; + case CHAR_CONTROL_CODE_ADJUST_PALETTE_2: + ebp = RCT2_GLOBAL(0x0141F740, uint8); + if (*current_font_flags & 1) { + if ((y + 0x13 <= dpi->y) || (dpi->y + dpi->height <= y)) { + skip_char = 1; + } + else { + skip_char = 0; + } + break; + } + sub_682AC7(ebp, current_font_flags); + break; + case CHAR_CONTROL_CODE_ADJUST_PALETTE_3: + ebp = RCT2_GLOBAL(0x0141F741, uint8); + if (*current_font_flags & 1) { + if ((y + 0x13 <= dpi->y) || (dpi->y + dpi->height <= y)) { + skip_char = 1; + } + else { + skip_char = 0; + } + break; + } + sub_682AC7(ebp, current_font_flags); + break; + case CHAR_CONTROL_CODE_ADJUST_PALETTE_4: + ebp = RCT2_GLOBAL(0x0141F742, uint8); + if (*current_font_flags & 1) { + if ((y + 0x13 <= dpi->y) || (dpi->y + dpi->height <= y)) { + skip_char = 1; + } + else { + skip_char = 0; + } + break; + } + sub_682AC7(ebp, current_font_flags); + break; + case CHAR_CONTROL_CODE_NEW_LINE_X_Y: //Start new line at specified x,y + eax = *((uint16*)(buffer+1)); + buffer += 2; + max_x = x; + max_x += (eax & 0xFF); + max_y = y; + max_y += (eax & 0xFF00) >> 8; + break; + case CHAR_CONTROL_CODE_SPRITE: + buffer += 4; + if (max_x >= dpi->x + dpi->width) { + skip_char = 1; + break; + } + ebx = *(buffer - 3); + eax = ebx & 0x7FFFF; + g1_element = &(RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[eax]); + + gfx_draw_sprite(dpi, ebx, max_x, max_y); + + max_x = max_x + g1_element->width; + break; + default: + // Colour codes + if ((al >= 0x8E) && (al < 0x9C)){ + + if (*current_font_flags == 1) { + if ((y + 0x13 <= dpi->y) || (dpi->y + dpi->height <= y)) { + skip_char = 1; + } else { + skip_char = 0; + } + continue; + } + colour_char(al-0x8E, current_font_flags); + continue; + } + + // Normal Characters + if (max_x >= dpi->x + dpi->width) { + skip_char = 1; + } + if (max_x + 0x1A < dpi->x) { + ebx = al-0x20; + ebx += *current_font_sprite_base; + max_x = max_x + (RCT2_ADDRESS(RCT2_ADDRESS_FONT_CHAR_WIDTH, uint8)[ebx] & 0xFF); + continue; + } + ebx = al-0x20 + *current_font_sprite_base; + + ecx = max_x; + max_x += (RCT2_ADDRESS(RCT2_ADDRESS_FONT_CHAR_WIDTH, uint8)[ebx] & 0xFF); + ebx += 0xF15; + + RCT2_GLOBAL(0x00EDF81C, uint32) = 0x20000000; + + gfx_draw_sprite_palette_set(dpi, 0x20000000 | ebx, ecx, max_y, RCT2_GLOBAL(0x9ABDA4, uint8*), NULL); + + continue; + } + } + + gLastDrawStringX = max_x; + gLastDrawStringY = max_y; } diff --git a/src/gfx.h b/src/gfx.h index 8eba37d27a..6e484b74e7 100644 --- a/src/gfx.h +++ b/src/gfx.h @@ -31,7 +31,7 @@ typedef struct { short width; // 0x08 short height; // 0x0A short pitch; // 0x0C note: this is actually (pitch - width) - char pad_0E; // 0x0E + uint8 zoom_level; // 0x0E char var_0F; // 0x0F } rct_drawpixelinfo; @@ -46,10 +46,23 @@ typedef struct { sint16 unused; // 0x0E } rct_g1_element; +enum{ + G1_FLAG_BMP = (1 << 0), //No invisible sections + G1_FLAG_RLE_COMPRESSION = (1<<2), +}; + +enum{ + IMAGE_TYPE_NO_BACKGROUND = 0, + IMAGE_TYPE_USE_PALETTE= (1 << 1), + IMAGE_TYPE_MIX_BACKGROUND = (1<<2), + IMAGE_TYPE_UNKNOWN = (1<<3) +}; + extern int gLastDrawStringX; extern int gLastDrawStringY; int gfx_load_g1(); +void gfx_load_character_widths(); void gfx_clear(rct_drawpixelinfo *dpi, int colour); void gfx_draw_pixel(rct_drawpixelinfo *dpi, int x, int y, int colour); @@ -66,7 +79,7 @@ void gfx_draw_string_centred_clipped(rct_drawpixelinfo *dpi, int format, void *a 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 *format, int x, int y, int width, int colour, int unknown); +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); diff --git a/src/intro.c b/src/intro.c index aee44c43d1..2d19d04312 100644 --- a/src/intro.c +++ b/src/intro.c @@ -82,7 +82,7 @@ void intro_update() _sound_playing_flag = 0; if (RCT2_GLOBAL(0x009AF280, sint32) != -1) { // Prepare and play the sound - if (sound_prepare(RCT2_SOUND_CHAINLIFT, &_prepared_sound, 0, 1)) + if (sound_prepare(SOUND_LIFT_7, &_prepared_sound, 0, 1)) if (sound_play(&_prepared_sound, 1, 0, 0, 0)) _sound_playing_flag = 1; } @@ -159,7 +159,7 @@ void intro_update() // Play the track friction sound if (RCT2_GLOBAL(0x009AF280, sint32) != -1) { // Prepare and play the sound - if (sound_prepare(RCT2_SOUND_TRACKFRICTION, &_prepared_sound, 1, 1)) + if (sound_prepare(SOUND_TRACK_FRICTION_3, &_prepared_sound, 1, 1)) if (sound_play(&_prepared_sound, 1, -800, 0, 0x3A98)) _sound_playing_flag = 1; } @@ -189,7 +189,7 @@ void intro_update() // Play long peep scream sound if (RCT2_GLOBAL(0x009AF280, sint32) != -1) - if (sound_prepare(RCT2_SOUND_SCREAM, &_prepared_sound, 0, 1)) + if (sound_prepare(SOUND_SCREAM_1, &_prepared_sound, 0, 1)) if (sound_play(&_prepared_sound, 0, 0, 0, 0)) _sound_playing_flag = 1; diff --git a/src/map.c b/src/map.c index 935f059ee0..39fcdbef7d 100644 --- a/src/map.c +++ b/src/map.c @@ -328,4 +328,40 @@ void sub_68B089() } while (mapElement->base_height == 255); mapElement++; RCT2_GLOBAL(0x0140E9A4, rct_map_element*) = mapElement; -} \ No newline at end of file +} + + +/** + * Checks if the tile at coordinate at height counts as connected. + * @return 1 if connected, 0 otherwise + */ +int map_coord_is_connected(uint16 tile_idx, uint8 height, uint8 face_direction) +{ + rct_map_element* tile = RCT2_ADDRESS(RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS, rct_map_element*)[tile_idx]; + + do { + rct_map_element_path_properties props = tile->properties.path; + uint8 path_type = props.type >> 2, path_dir = props.type & 3; + uint8 element_type = tile->type & MAP_ELEMENT_TYPE_MASK; + + if (element_type != PATH_ROAD) + continue; + + if (path_type & 1) { + if (path_dir == face_direction) { + if (height == tile->base_height + 2) + return 1; + } + else if ((path_dir ^ 2) == face_direction && height == tile->base_height) { + return 1; + } + } else { + if (height == tile->base_height) + return 1; + } + + } while (!(tile->flags & MAP_ELEMENT_FLAG_LAST_TILE) && tile++); + + return 0; +} + diff --git a/src/map.h b/src/map.h index 355767bc81..069c0d52c9 100644 --- a/src/map.h +++ b/src/map.h @@ -195,5 +195,7 @@ void map_init(); void map_update_tile_pointers(); int map_element_height(int x, int y); void sub_68B089(); +int map_coord_is_connected(uint16 coordinate, uint8 height, uint8 face_direction); + #endif diff --git a/src/marketing.c b/src/marketing.c new file mode 100644 index 0000000000..15b7192035 --- /dev/null +++ b/src/marketing.c @@ -0,0 +1,126 @@ +/***************************************************************************** + * 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 . + *****************************************************************************/ + +#include "addresses.h" +#include "marketing.h" +#include "news_item.h" +#include "rct2.h" +#include "ride.h" +#include "string_ids.h" +#include "window.h" + +const int advertisingCampaignGuestGenerationProbabilities[] = { 400, 300, 200, 200, 250, 200 }; + +int marketing_get_campaign_guest_generation_probability(int campaign) +{ + int probability = advertisingCampaignGuestGenerationProbabilities[campaign]; + rct_ride *ride; + + // Lower probability of guest generation if price was already low + switch (campaign) { + case ADVERTISING_CAMPAIGN_PARK_ENTRY_FREE: + if (RCT2_GLOBAL(RCT2_ADDRESS_PARK_ENTRANCE_FEE, money16) < 4) + probability /= 8; + break; + case ADVERTISING_CAMPAIGN_PARK_ENTRY_HALF_PRICE: + if (RCT2_GLOBAL(RCT2_ADDRESS_PARK_ENTRANCE_FEE, money16) < 6) + probability /= 8; + break; + case ADVERTISING_CAMPAIGN_RIDE_FREE: + ride = &(RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, rct_ride)[RCT2_ADDRESS(0x01358116, uint8)[campaign]]); + if (ride->price < 3) + probability /= 8; + break; + } + + return probability; +} + +/* + * Update status of marketing campaigns and send produce a news item when they have finished. + * rct2: 0x0069E0C1 + **/ +void marketing_update() +{ + for (int campaign = 0; campaign < ADVERTISING_CAMPAIGN_COUNT; campaign++) { + uint8 campaign_weeks_left = RCT2_ADDRESS(0x01358102, uint8)[campaign]; + if (campaign_weeks_left == 0) + continue; + + window_invalidate_by_id(WC_FINANCES, 0); + + // High bit marks the campaign as inactive, on first check the campaign is set actice + // this makes campaigns run a full x weeks even when started in the middle of a week + RCT2_ADDRESS(0x01358102, uint8)[campaign] &= ~(1 << 7); + if (campaign_weeks_left & (1 << 7)) + continue; + + RCT2_ADDRESS(0x01358102, uint8)[campaign]--; + if (campaign_weeks_left - 1 != 0) + continue; + + int campaign_item = RCT2_ADDRESS(0x01358116, uint8)[campaign]; + + // This sets the string parameters for the marketing types that have an argument. + if (campaign == ADVERTISING_CAMPAIGN_RIDE_FREE || campaign == ADVERTISING_CAMPAIGN_RIDE) { + RCT2_GLOBAL(0x013CE952, uint16) = RCT2_GLOBAL(0x01362942 + 304 * campaign_item, uint16); + RCT2_GLOBAL(0x013CE954, uint32) = RCT2_GLOBAL(0x01362944 + 152 * campaign_item, uint32); + } else if (campaign == ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE) { + campaign_item += 2016; + if (campaign_item >= 2048) + campaign_item += 96; + RCT2_GLOBAL(0x013CE952, uint16) = campaign_item; + } + + news_item_add_to_queue(NEWS_ITEM_MONEY, STR_MARKETING_FINISHED_BASE + campaign, 0); + } +} + +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; + 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->var_C6 = 240; + break; + case ADVERTISING_CAMPAIGN_PARK_ENTRY_HALF_PRICE: + peep->item_standard_flags |= PEEP_ITEM_VOUCHER; + peep->var_F0 = 2; + 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]; + break; + case ADVERTISING_CAMPAIGN_PARK: + break; + case ADVERTISING_CAMPAIGN_RIDE: + peep->var_C5 = RCT2_ADDRESS(0x01358116, uint8)[campaign]; + peep->var_C6 = 240; + break; + } +} \ No newline at end of file diff --git a/src/marketing.h b/src/marketing.h new file mode 100644 index 0000000000..43e99796c9 --- /dev/null +++ b/src/marketing.h @@ -0,0 +1,40 @@ +/***************************************************************************** + * 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 . + *****************************************************************************/ + +#ifndef _MARKETING_H_ +#define _MARKETING_H_ + +#include "peep.h" + +enum { + ADVERTISING_CAMPAIGN_PARK_ENTRY_FREE, + ADVERTISING_CAMPAIGN_RIDE_FREE, + ADVERTISING_CAMPAIGN_PARK_ENTRY_HALF_PRICE, + ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE, + ADVERTISING_CAMPAIGN_PARK, + ADVERTISING_CAMPAIGN_RIDE, + ADVERTISING_CAMPAIGN_COUNT +}; + +int marketing_get_campaign_guest_generation_probability(int campaign); +void marketing_update(); +void marketing_set_guest_campaign(rct_peep *peep, int campaign); + +#endif \ No newline at end of file diff --git a/src/news_item.c b/src/news_item.c index 85eab71a89..f9584cdaa4 100644 --- a/src/news_item.c +++ b/src/news_item.c @@ -97,7 +97,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(39, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16) / 2); + sound_play_panned(SOUND_NEWS_ITEM, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16) / 2); } // Removal of current news item @@ -178,7 +178,7 @@ void news_item_get_subject_location(int type, int subject, int *x, int *y, int * int i; rct_ride *ride; rct_peep *peep; - rct_car *car; + rct_vehicle *vehicle; switch (type) { case NEWS_ITEM_RIDE: @@ -192,7 +192,7 @@ void news_item_get_subject_location(int type, int subject, int *x, int *y, int * *z = map_element_height(*x, *y); break; case NEWS_ITEM_PEEP_ON_RIDE: - peep = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[subject]).peep; + peep = GET_PEEP(subject); *x = peep->x; *y = peep->y; *z = peep->z; @@ -212,16 +212,16 @@ 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 - car = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[ride->train_car_map[peep->current_train]]).car; + vehicle = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[ride->train_car_map[peep->current_train]]).vehicle; // Find the actual car peep is on for (i = 0; i < peep->current_car; i++) - car = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[car->next_car]).car; - *x = car->x; - *y = car->y; - *z = car->z; + vehicle = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[vehicle->next_vehicle_on_train]).vehicle; + *x = vehicle->x; + *y = vehicle->y; + *z = vehicle->z; break; case NEWS_ITEM_PEEP: - peep = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[subject]).peep; + peep = GET_PEEP(subject); *x = peep->x; *y = peep->y; *z = peep->z; @@ -295,12 +295,11 @@ void news_item_open_subject(int type, int subject) { break; case NEWS_ITEM_PEEP_ON_RIDE: case NEWS_ITEM_PEEP: - peep = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[subject]).peep; + peep = GET_PEEP(subject); RCT2_CALLPROC_X(0x006989E9, 0, 0, 0, (int)peep, 0, 0, 0); break; case NEWS_ITEM_MONEY: - // Open finances window - RCT2_CALLPROC_EBPSAFE(0x0069DDF1); + window_finances_open(); break; case NEWS_ITEM_RESEARCH: diff --git a/src/object.c b/src/object.c index c09c3de10a..58f4dd33b6 100644 --- a/src/object.c +++ b/src/object.c @@ -18,8 +18,11 @@ * along with this program. If not, see . *****************************************************************************/ +#include +#include #include "addresses.h" #include "object.h" +#include "sawyercoding.h" /** * @@ -47,19 +50,188 @@ 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) +{ + if (a->flags & 0xF0) { + if ((a->flags & 0x0F) != (b->flags & 0x0F)) + return 0; + if (*((uint32*)a->name) != *((uint32*)b->name)) + return 0; + if (*((uint32*)(&a->name[4])) != *((uint32*)(&b->name[4]))) + return 0; + } else { + if (a->flags != b->flags) + return 0; + if (*((uint32*)a->name) != *((uint32*)b->name)) + return 0; + if (*((uint32*)(&a->name[4])) != *((uint32*)(&b->name[4]))) + return 0; + if (a->checksum != b->checksum) + return 0; + } + + return 1; +} + +static int object_calculate_checksum(rct_object_entry *entry, char *data, int dataLength) +{ + int i; + char *eee = (char*)entry; + int checksum = 0xF369A75B; + char *ccc = (char*)&checksum; + + *ccc ^= eee[0]; + checksum = rol32(checksum, 11); + for (i = 4; i < 12; i++) { + *ccc ^= eee[i]; + checksum = rol32(checksum, 11); + } + for (i = 0; i < dataLength; i++) { + *ccc ^= data[i]; + checksum = rol32(checksum, 11); + } + + return checksum; +} + +int object_paint(int type, int eax, int ebx, int ecx, int edx, int esi, int edi, int ebp) +{ + 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; +} + /** * * rct2: 0x006A9428 */ -int sub_6A9428(rct_object_entry* entry) +int object_get_scenario_text(rct_object_entry *entry) { - RCT2_CALLPROC_X(0x006A9428, 0, 0, 0, 0, 0, 0, (int)entry); - #ifdef _MSC_VER - __asm jb fail - #else - __asm__ goto ( "jb %l0" : : : : fail ); - #endif - return 1; -fail: + // RCT2_CALLPROC_X(0x006A9428, 0, 0, 0, 0, 0, 0, (int)entry); return; + + int i; + rct_object_entry *installedObject = RCT2_GLOBAL(0x009ADAE8, rct_object_entry*); + for (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 = malloc(0x600000); + chunkSize = sawyercoding_read_chunk(file, chunk); + chunk = realloc(chunk, chunkSize); + } else { + chunk = malloc(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; + free(chunk); + return 0; + } + + if (object_paint(openedEntry.flags & 0x0F, 2, 0, 0, 0, (int)chunk, 0, 0)) { + RCT2_GLOBAL(0x00F42BD9, uint8) = 3; + free(chunk); + return 0; + } + + int yyy = RCT2_GLOBAL(0x009ADAF0, uint32); + RCT2_GLOBAL(0x009ADAF0, uint32) = 0x726E; + RCT2_GLOBAL(0x009ADAF8, uint32) = (int)chunk; + *((rct_object_entry*)0x00F42BC8) = openedEntry; + + RCT2_GLOBAL(0x009ADAFC, uint8) = 255; + RCT2_GLOBAL(0x009ADAFD, uint8) = 1; + object_paint(openedEntry.flags & 0x0F, 0, 0, 0, 0, (int)chunk, 0, 0); + RCT2_GLOBAL(0x009ADAFC, uint8) = 0; + RCT2_GLOBAL(0x009ADAFD, uint8) = 0; + RCT2_GLOBAL(0x009ADAF0, uint32) = yyy; + return 1; + } + fclose(file); + } + } + installedObject = object_get_next(installedObject); + } + + RCT2_GLOBAL(0x00F42BD9, uint8) = 0; return 0; +} + +/** + * + * rct2: 0x006A982D + */ +void object_free_scenario_text() +{ + if (RCT2_GLOBAL(0x009ADAF8, void*) != NULL) { + free(RCT2_GLOBAL(0x009ADAF8, void*)); + RCT2_GLOBAL(0x009ADAF8, void*) = NULL; + } +} + +int object_get_length(rct_object_entry *entry) +{ + return (int)object_get_next(entry) - (int)entry; +} + +rct_object_entry *object_get_next(rct_object_entry *entry) +{ + char *pos = (char*)entry; + + // Skip + pos += 16; + + // Skip filename + do { + pos++; + } while (*(pos - 1) != 0); + + // Skip + pos += 4; + + // Skip name + do { + pos++; + } while (*(pos - 1) != 0); + + // Skip + pos += 4; + + // Skip + pos += *pos++ * 16; + + // Skip theme objects + pos += *pos++ * 16; + + // Skip + pos += 4; + + return (rct_object_entry*)pos; } \ No newline at end of file diff --git a/src/object.h b/src/object.h index ea6f47103f..a9d8bdbdfc 100644 --- a/src/object.h +++ b/src/object.h @@ -52,6 +52,9 @@ void object_unload_all(); int object_load(int groupIndex, rct_object_entry *entry); void object_unload(int groupIndex, rct_object_entry_extended *entry); -int sub_6A9428(rct_object_entry* entry); +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); #endif diff --git a/src/object_list.c b/src/object_list.c index 3bb9901414..78d5686d07 100644 --- a/src/object_list.c +++ b/src/object_list.c @@ -51,15 +51,15 @@ int object_entry_group_counts[] = { 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 - (void**)(0x009ACFA4 + (252 * 4)), (rct_object_entry_extended*)(0x00F3F03C + (252 * 20)), // large scenery - (void**)(0x009ACFA4 + (128 * 4)), (rct_object_entry_extended*)(0x00F3F03C + (128 * 20)), // walls - (void**)(0x009ACFA4 + (128 * 4)), (rct_object_entry_extended*)(0x00F3F03C + (128 * 20)), // banners - (void**)(0x009ACFA4 + ( 32 * 4)), (rct_object_entry_extended*)(0x00F3F03C + ( 32 * 20)), // paths - (void**)(0x009ACFA4 + ( 16 * 4)), (rct_object_entry_extended*)(0x00F3F03C + ( 16 * 20)), // path bits - (void**)(0x009ACFA4 + ( 15 * 4)), (rct_object_entry_extended*)(0x00F3F03C + ( 15 * 20)), // scenery sets - (void**)(0x009ACFA4 + ( 19 * 4)), (rct_object_entry_extended*)(0x00F3F03C + ( 19 * 20)), // park entrance - (void**)(0x009ACFA4 + ( 1 * 4)), (rct_object_entry_extended*)(0x00F3F03C + ( 1 * 20)), // water - (void**)(0x009ACFA4 + ( 1 * 4)), (rct_object_entry_extended*)(0x00F3F03C + ( 1 * 20)) // scenario text + (void**)(0x009ACFA4 + (380 * 4)), (rct_object_entry_extended*)(0x00F3F03C + (380 * 20)), // large scenery + (void**)(0x009ACFA4 + (508 * 4)), (rct_object_entry_extended*)(0x00F3F03C + (508 * 20)), // walls + (void**)(0x009ACFA4 + (636 * 4)), (rct_object_entry_extended*)(0x00F3F03C + (636 * 20)), // banners + (void**)(0x009ACFA4 + (668 * 4)), (rct_object_entry_extended*)(0x00F3F03C + (668 * 20)), // paths + (void**)(0x009ACFA4 + (684 * 4)), (rct_object_entry_extended*)(0x00F3F03C + (684 * 20)), // path bits + (void**)(0x009ACFA4 + (699 * 4)), (rct_object_entry_extended*)(0x00F3F03C + (699 * 20)), // scenery sets + (void**)(0x009ACFA4 + (718 * 4)), (rct_object_entry_extended*)(0x00F3F03C + (718 * 20)), // park entrance + (void**)(0x009ACFA4 + (719 * 4)), (rct_object_entry_extended*)(0x00F3F03C + (719 * 20)), // water + (void**)(0x009ACFA4 + (720 * 4)), (rct_object_entry_extended*)(0x00F3F03C + (720 * 20)) // scenario text }; /** @@ -69,42 +69,14 @@ struct { void **data; rct_object_entry_extended *entries; } object_entry_groups[ static void object_list_examine() { int i; - char *object; + rct_object_entry *object; - object = RCT2_GLOBAL(RCT2_ADDRESS_INSTALLED_OBJECT_LIST, char*); + object = RCT2_GLOBAL(RCT2_ADDRESS_INSTALLED_OBJECT_LIST, rct_object_entry*); for (i = 0; i < RCT2_GLOBAL(0x00F42B6C, sint32); i++) { - if (*object & 0xF0) + if (object->flags & 0xF0) RCT2_GLOBAL(0x00F42BDA, uint8) |= 1; - // Skip - object += 16; - - // Skip filename - // printf("%d %s : ", i, object); - do { - object++; - } while (*(object - 1) != 0); - - // Skip - object += 4; - - // Skip name - // printf("%s\n", object); - do { - object++; - } while (*(object - 1) != 0); - - // Skip - object += 4; - - // Skip - object += *object++ * 16; - - // Skip theme objects - object += *object++ * 16; - - // Skip - object += 4; + object = object_get_next(object); } } diff --git a/src/osinterface.c b/src/osinterface.c index 8117a31198..20d8778e88 100644 --- a/src/osinterface.c +++ b/src/osinterface.c @@ -40,7 +40,7 @@ unsigned int gLastKeyPressed; static void osinterface_create_window(); static void osinterface_close_window(); static void osinterface_resize(int width, int height); -static void osinterface_update_palette(char* colours, int start_index, int num_colours); + static SDL_Window *_window; static SDL_Surface *_surface; @@ -91,6 +91,7 @@ static void osinterface_create_window() exit(-1); } + SDL_VERSION(&wmInfo.version); // Get the HWND context if (SDL_GetWindowWMInfo(_window, &wmInfo) != SDL_TRUE) { RCT2_ERROR("SDL_GetWindowWMInfo failed %s", SDL_GetError()); @@ -167,7 +168,7 @@ static void osinterface_resize(int width, int height) gfx_invalidate_screen(); } -static void osinterface_update_palette(char* colours, int start_index, int num_colours) +void osinterface_update_palette(char* colours, int start_index, int num_colours) { SDL_Color base[256]; SDL_Surface *surface; @@ -394,14 +395,14 @@ char* osinterface_open_directory_browser(char *title) { LPMALLOC lpMalloc; // Initialize COM - if (CoInitializeEx(0, COINIT_APARTMENTTHREADED) != S_OK) { + if (FAILED(CoInitializeEx(0, COINIT_APARTMENTTHREADED))) { MessageBox(NULL, _T("Error opening browse window"), _T("ERROR"), MB_OK); CoUninitialize(); return 0; } // Get a pointer to the shell memory allocator - if (SHGetMalloc(&lpMalloc) != S_OK) { + if (FAILED(SHGetMalloc(&lpMalloc))) { MessageBox(NULL, _T("Error opening browse window"), _T("ERROR"), MB_OK); CoUninitialize(); return 0; @@ -428,3 +429,52 @@ char* osinterface_open_directory_browser(char *title) { CoUninitialize(); return outPath; } + +char* osinterface_get_orct2_homefolder() +{ + char *path=NULL; + path = malloc(sizeof(char) * MAX_PATH); + if (path == NULL){ + osinterface_show_messagebox("Error allocating memory!"); + exit(EXIT_FAILURE); + } + + path[0] = '\0'; + + if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, 0, path))) + strcat(path, "\\OpenRCT2"); + + return path; +} + +char *osinterface_get_orct2_homesubfolder(const char *subFolder) +{ + char *path = osinterface_get_orct2_homefolder(); + strcat(path, "\\"); + strcat(path, subFolder); + return path; +} + +int osinterface_file_exists(const char *path) +{ + return !(GetFileAttributes(path) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND); +} + +int osinterface_directory_exists(const char *path) +{ + DWORD dwAttrib = GetFileAttributes(path); + return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY); +} + +int osinterface_ensure_directory_exists(const char *path) +{ + if (osinterface_directory_exists(path)) + return 1; + + return CreateDirectory(path, NULL); +} + +char osinterface_get_path_separator() +{ + return '\\'; +} diff --git a/src/osinterface.h b/src/osinterface.h index 8360d3f1c4..4725902362 100644 --- a/src/osinterface.h +++ b/src/osinterface.h @@ -45,9 +45,18 @@ void osinterface_init(); void osinterface_process_messages(); void osinterface_draw(); void osinterface_free(); +void osinterface_update_palette(char* colours, int start_index, int num_colours); 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); +char* osinterface_get_orct2_homefolder(); +char *osinterface_get_orct2_homesubfolder(const char *subFolder); +int osinterface_file_exists(const char *path); +int osinterface_directory_exists(const char *path); +int osinterface_ensure_directory_exists(const char *path); + +char osinterface_get_path_separator(); + #endif diff --git a/src/park.c b/src/park.c index babef06148..21db6f57b1 100644 --- a/src/park.c +++ b/src/park.c @@ -20,8 +20,11 @@ #include #include "addresses.h" +#include "award.h" #include "finance.h" #include "map.h" +#include "marketing.h" +#include "news_item.h" #include "park.h" #include "peep.h" #include "ride.h" @@ -30,7 +33,18 @@ #include "string_ids.h" #include "window.h" -const int advertisingCampaignGuestGenerationProbabilities[] = { 400, 300, 200, 200, 250, 200 }; +/** + * In a difficult guest generation scenario, no guests will be generated if over this value. + */ +int _suggestedGuestMaximum; + +/** + * Probability out of 65535, of gaining a new guest per game tick. + * new guests per second = 40 * (probability / 65535) + * With a full park rating, non-overpriced entrance fee, less guests than the suggested maximum and four positive awards, + * approximately 1 guest per second can be generated (+60 guests in one minute). + */ +int _guestGenerationProbability; int park_is_open() { @@ -55,8 +69,8 @@ void park_init() RCT2_GLOBAL(0x01357846, uint16) = 0; RCT2_GLOBAL(0x013573FE, uint16) = 0; RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_PARK_RATING, uint16) = 0; - RCT2_GLOBAL(RCT2_ADDRESS_GUEST_GENERATION_PROBABILITY, uint16) = 0; - RCT2_GLOBAL(0x013580EE, uint16) = 0; + _guestGenerationProbability = 0; + RCT2_GLOBAL(RCT2_TOTAL_RIDE_VALUE, uint16) = 0; RCT2_GLOBAL(0x01357CF4, sint32) = -1; for (i = 0; i < 20; i++) @@ -90,7 +104,9 @@ void park_init() RCT2_GLOBAL(RCT2_ADDRESS_CONSTRUCTION_RIGHTS_COST, uint16) = MONEY(40,00); RCT2_GLOBAL(0x01358774, uint16) = 0; RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) = PARK_FLAGS_11 | PARK_FLAGS_SHOW_REAL_GUEST_NAMES; - park_reset_awards_and_history(); + park_reset_history(); + finance_reset_history(); + award_reset(); rct_s6_info *info = (rct_s6_info*)0x0141F570; info->name[0] = '\0'; @@ -101,26 +117,13 @@ void park_init() * * rct2: 0x0066729F */ -void park_reset_awards_and_history() +void park_reset_history() { int i; - - // Reset park rating and guests in park history for (i = 0; i < 32; i++) { RCT2_ADDRESS(RCT2_ADDRESS_PARK_RATING_HISTORY, uint8)[i] = 255; RCT2_ADDRESS(RCT2_ADDRESS_GUESTS_IN_PARK_HISTORY, uint8)[i] = 255; } - - // Reset finance history - for (i = 0; i < 128; i++) { - RCT2_ADDRESS(RCT2_ADDRESS_BALANCE_HISTORY, money32)[i] = MONEY32_UNDEFINED; - RCT2_ADDRESS(RCT2_ADDRESS_WEEKLY_PROFIT_HISTORY, money32)[i] = MONEY32_UNDEFINED; - RCT2_ADDRESS(RCT2_ADDRESS_PARK_VALUE_HISTORY, money32)[i] = MONEY32_UNDEFINED; - } - - // Reset awards - for (i = 0; i < 4; i++) - RCT2_ADDRESS(RCT2_ADDRESS_AWARD_LIST, rct_award)[i].time = 0; } /** @@ -168,7 +171,7 @@ int calculate_park_rating() // Guests { rct_peep* peep; - uint16 sprite_idx; + uint16 spriteIndex; int num_happy_peeps; short _bp; @@ -178,10 +181,7 @@ int calculate_park_rating() // Guests, happiness, ? num_happy_peeps = 0; _bp = 0; - for (sprite_idx = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_PEEP, uint16); sprite_idx != SPRITE_INDEX_NULL; sprite_idx = peep->next) { - peep = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[sprite_idx].peep); - if (peep->type != PEEP_TYPE_GUEST) - continue; + FOR_ALL_GUESTS(spriteIndex, peep) { if (peep->var_2A != 0) continue; if (peep->happiness > 128) @@ -214,11 +214,7 @@ int calculate_park_rating() // _ax = 0; num_rides = 0; - for (i = 0; i < 255; i++) { - ride = &(RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, rct_ride)[i]); - - if (ride->type == RIDE_TYPE_NULL) - continue; + FOR_ALL_RIDES(i, ride) { _ax += 100 - ride->var_199; if (ride->excitement != -1){ @@ -350,14 +346,113 @@ void reset_park_entrances() } /** - * + * Calculates the probability of a new guest. Also sets total ride value and suggested guest maximum. + * Total ride value should probably be set else where, as its not just used for guest generation. + * Suggested guest maximum should probably be an output result, not a global. + * @returns A probability out of 65535 * rct2: 0x0066730A */ static int park_calculate_guest_generation_probability() { - int eax, ebx, ecx, edx, esi, edi, ebp; - RCT2_CALLFUNC_X(0x0066730A, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); - return eax & 0xFFFF; + unsigned int probability; + int i, suggestedMaxGuests, totalRideValue; + rct_ride *ride; + + // Calculate suggested guest maximum (based on ride type) and total ride value + suggestedMaxGuests = 0; + totalRideValue = 0; + FOR_ALL_RIDES(i, ride) { + if (ride->status != RIDE_STATUS_OPEN) + continue; + if (ride->lifecycle_flags & 0x80) + continue; + if (ride->lifecycle_flags & 0x400) + continue; + + // Add guest score for ride type + suggestedMaxGuests += RCT2_GLOBAL(0x0097D21E + (ride->type * 8), uint8); + + // Add ride value + if (ride->reliability != RIDE_RELIABILITY_UNDEFINED) { + int rideValue = ride->reliability - ride->price; + if (rideValue > 0) + totalRideValue += rideValue * 2; + } + } + + // If difficult guest generation, extra guests are available for good rides + if (RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_DIFFICULT_GUEST_GENERATION) { + suggestedMaxGuests = min(suggestedMaxGuests, 1000); + FOR_ALL_RIDES(i, ride) { + if (ride->lifecycle_flags & 0x80) + continue; + if (ride->lifecycle_flags & 0x400) + continue; + + if (!(RCT2_GLOBAL(0x0097CF40 + (ride->type * 8), uint32) & 0x10000000)) + continue; + if (!(RCT2_GLOBAL(0x0097CF40 + (ride->type * 8), uint32) & 0x200)) + continue; + if (!(ride->lifecycle_flags & 0x02)) + continue; + if (ride->var_0E4 < 0x2580000) + continue; + if (ride->excitement < RIDE_RATING(6,00)) + continue; + + // Bonus guests for good ride + suggestedMaxGuests += RCT2_GLOBAL(0x0097D21E + (ride->type * 8), uint8) * 2; + } + } + + suggestedMaxGuests = min(suggestedMaxGuests, 65535); + RCT2_GLOBAL(RCT2_TOTAL_RIDE_VALUE, uint16) = totalRideValue; + _suggestedGuestMaximum = suggestedMaxGuests; + + // Begin with 50 + park rating + probability = 50 + clamp(0, RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_PARK_RATING, uint16) - 200, 650); + + // The more guests, the lower the chance of a new one + int numGuests = RCT2_GLOBAL(RCT2_ADDRESS_GUESTS_IN_PARK, uint16) + RCT2_GLOBAL(RCT2_ADDRESS_GUESTS_HEADING_FOR_PARK, uint16); + if (numGuests > suggestedMaxGuests) { + probability /= 4; + + // Even lower for difficult guest generation + if (RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_DIFFICULT_GUEST_GENERATION) + probability /= 4; + } + + // Reduces chance for any more than 7000 guests + if (numGuests > 7000) + probability /= 4; + + // Check if money is enabled + if (!(RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_11)) { + // Penalty for overpriced entrance fee relative to total ride value + money16 entranceFee = RCT2_GLOBAL(RCT2_ADDRESS_PARK_ENTRANCE_FEE, money16); + if (entranceFee > totalRideValue) { + probability /= 4; + + // Extra penalty for very overpriced entrance fee + if (entranceFee / 2 > totalRideValue) + probability /= 4; + } + } + + // Reward or penalties for park awards + for (i = 0; i < MAX_AWARDS; i++) { + rct_award *award = &RCT2_ADDRESS(RCT2_ADDRESS_AWARD_LIST, rct_award)[i]; + if (award->time == 0) + continue; + + // +/- 0.25% of the probability + if (award_is_positive(award->type)) + probability += probability / 4; + else + probability -= probability / 4; + } + + return probability; } static void get_random_peep_spawn(rct2_peep_spawn *spawn) @@ -370,17 +465,6 @@ static void get_random_peep_spawn(rct2_peep_spawn *spawn) *spawn = peepSpawns[1]; } -static int park_should_generate_new_guest() -{ - if ((scenario_rand() & 0xFFFF) < RCT2_GLOBAL(0x013580EC, uint16)) { - int difficultGeneration = (RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_DIFFICULT_GUEST_GENERATION) != 0; - if (!difficultGeneration || RCT2_GLOBAL(0x0135883C, uint16) + 150 < RCT2_GLOBAL(RCT2_ADDRESS_GUESTS_IN_PARK, uint16)) - return 1; - } - - return 0; -} - static rct_peep *park_generate_new_guest() { rct_peep *peep; @@ -388,9 +472,8 @@ static rct_peep *park_generate_new_guest() get_random_peep_spawn(&spawn); if (spawn.x != 0xFFFF) { - spawn.z *= 16; spawn.direction ^= 2; - peep = peep_generate(spawn.x, spawn.y, spawn.z); + peep = peep_generate(spawn.x, spawn.y, spawn.z * 16); if (peep != NULL) { peep->var_1E = spawn.direction << 3; @@ -412,75 +495,26 @@ static rct_peep *park_generate_new_guest() static rct_peep *park_generate_new_guest_due_to_campaign(int campaign) { rct_peep *peep = park_generate_new_guest(); - if (peep != NULL) { - switch (campaign) { - case ADVERTISING_CAMPAIGN_PARK_ENTRY_FREE: - peep->item_standard_flags |= PEEP_ITEM_VOUCHER; - peep->var_F0 = 0; - 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->var_C6 = 240; - break; - case ADVERTISING_CAMPAIGN_PARK_ENTRY_HALF_PRICE: - peep->item_standard_flags |= PEEP_ITEM_VOUCHER; - peep->var_F0 = 2; - 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]; - break; - case ADVERTISING_CAMPAIGN_PARK: - break; - case ADVERTISING_CAMPAIGN_RIDE: - peep->var_C5 = RCT2_ADDRESS(0x01358116, uint8)[campaign]; - peep->var_C6 = 240; - break; - } - } -} - -static int park_get_campaign_guest_generation_probability(int campaign) -{ - int probability = advertisingCampaignGuestGenerationProbabilities[campaign]; - rct_ride *ride; - - // Lower probability of guest generation if price was already low - switch (campaign) { - case ADVERTISING_CAMPAIGN_PARK_ENTRY_FREE: - if (RCT2_GLOBAL(RCT2_ADDRESS_PARK_ENTRANCE_FEE, money16) < 4) - probability /= 8; - break; - case ADVERTISING_CAMPAIGN_PARK_ENTRY_HALF_PRICE: - if (RCT2_GLOBAL(RCT2_ADDRESS_PARK_ENTRANCE_FEE, money16) < 6) - probability /= 8; - break; - case ADVERTISING_CAMPAIGN_RIDE_FREE: - ride = &(RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, rct_ride)[RCT2_ADDRESS(0x01358116, uint8)[campaign]]); - if (ride->price < 3) - probability /= 8; - break; - } - - return probability; + if (peep != NULL) + marketing_set_guest_campaign(peep, campaign); + return peep; } static void park_generate_new_guests() { - // Check and generate a new guest - if (park_should_generate_new_guest()) - park_generate_new_guest(); + // Generate a new guest for some probability + if ((scenario_rand() & 0xFFFF) < _guestGenerationProbability) { + int difficultGeneration = (RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_DIFFICULT_GUEST_GENERATION) != 0; + if (!difficultGeneration || _suggestedGuestMaximum + 150 >= RCT2_GLOBAL(RCT2_ADDRESS_GUESTS_IN_PARK, uint16)) + park_generate_new_guest(); + } // Extra guests generated by advertising campaigns int campaign; for (campaign = 0; campaign < ADVERTISING_CAMPAIGN_COUNT; campaign++) { if (RCT2_ADDRESS(0x01358102, uint8)[campaign] != 0) { // Random chance of guest generation - if ((scenario_rand() & 0xFFFF) < park_get_campaign_guest_generation_probability(campaign)) + if ((scenario_rand() & 0xFFFF) < marketing_get_campaign_guest_generation_probability(campaign)) park_generate_new_guest_due_to_campaign(campaign); } } @@ -501,7 +535,7 @@ void park_update() RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_PARK_VALUE, money32) = calculate_park_value(); RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_COMPANY_VALUE, money32) = calculate_company_value(); window_invalidate_by_id(WC_FINANCES, 0); - RCT2_GLOBAL(RCT2_ADDRESS_GUEST_GENERATION_PROBABILITY, uint16) = park_calculate_guest_generation_probability(); + _guestGenerationProbability = park_calculate_guest_generation_probability(); RCT2_GLOBAL(0x009A9804, uint16) |= 0x10; window_invalidate_by_id(WC_PARK_INFORMATION, 0); } @@ -510,6 +544,7 @@ void park_update() park_generate_new_guests(); } +<<<<<<< HEAD static uint8 calculate_guest_initial_happiness(uint8 percentage) { if (percentage < 15) { // There is a minimum of 15% happiness @@ -532,4 +567,13 @@ static uint8 calculate_guest_initial_happiness(uint8 percentage) { } } return 40; // This is the lowest possible value +======= +/** + * + * rct2: 0x0066A231 + */ +void park_update_histories() +{ + RCT2_CALLPROC_EBPSAFE(0x0066A231); +>>>>>>> upstream/master } \ No newline at end of file diff --git a/src/park.h b/src/park.h index b3320c8136..cf37909afa 100644 --- a/src/park.h +++ b/src/park.h @@ -26,41 +26,6 @@ #define DECRYPT_MONEY(money) rol32((money) ^ 0xF4EC9621, 13) #define ENCRYPT_MONEY(money) (ror32((money), 13) ^ 0xF4EC9621) -typedef struct { - uint16 time; - uint16 type; -} rct_award; - -enum { - PARK_AWARD_MOST_UNTIDY, - PARK_AWARD_MOST_TIDY, - PARK_AWARD_BEST_ROLLERCOASTERS, - PARK_AWARD_BEST_VALUE, - PARK_AWARD_MOST_BEAUTIFUL, - PARK_AWARD_WORST_VALUE, - PARK_AWARD_SAFEST, - PARK_AWARD_BEST_STAFF, - PARK_AWARD_BEST_FOOD, - PARK_AWARD_WORST_FOOD, - PARK_AWARD_BEST_RESTROOMS, - PARK_AWARD_MOST_DISAPPOINTING, - PARK_AWARD_BEST_WATER_RIDES, - PARK_AWARD_BEST_CUSTOM_DESIGNED_RIDES, - PARK_AWARD_MOST_DAZZLING_RIDE_COLOURS, - PARK_AWARD_MOST_CONFUSING_LAYOUT, - PARK_AWARD_BEST_GENTLE_RIDES, -}; - -enum { - ADVERTISING_CAMPAIGN_PARK_ENTRY_FREE, - ADVERTISING_CAMPAIGN_RIDE_FREE, - ADVERTISING_CAMPAIGN_PARK_ENTRY_HALF_PRICE, - ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE, - ADVERTISING_CAMPAIGN_PARK, - ADVERTISING_CAMPAIGN_RIDE, - ADVERTISING_CAMPAIGN_COUNT -}; - enum { PARK_FLAGS_PARK_OPEN = (1 << 0), PARK_FLAGS_FORBID_LANDSCAPE_CHANGES = (1 << 2), @@ -80,7 +45,7 @@ enum { int park_is_open(); void park_init(); -void park_reset_awards_and_history(); +void park_reset_history(); int park_calculate_size(); int calculate_park_rating(); @@ -89,6 +54,7 @@ money32 calculate_company_value(); void reset_park_entrances(); void park_update(); +void park_update_histories(); uint8 calculate_guest_initial_happiness(); diff --git a/src/peep.c b/src/peep.c index c6f910fe13..7042c04c5d 100644 --- a/src/peep.c +++ b/src/peep.c @@ -20,6 +20,7 @@ #include #include "addresses.h" +#include "audio.h" #include "news_item.h" #include "peep.h" #include "rct2.h" @@ -27,20 +28,16 @@ #include "sprite.h" #include "window.h" +static void peep_update(rct_peep *peep); + int peep_get_staff_count() { - uint16 sprite_index; + uint16 spriteIndex; rct_peep *peep; int count = 0; - sprite_index = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_PEEP, uint16); - while (sprite_index != SPRITE_INDEX_NULL) { - peep = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[sprite_index].peep); - sprite_index = peep->next; - - if (peep->type == PEEP_TYPE_STAFF) - count++; - } + FOR_ALL_STAFF(spriteIndex, peep) + count++; return count; } @@ -52,30 +49,107 @@ int peep_get_staff_count() void peep_update_all() { int i; - uint16 sprite_index; + uint16 spriteIndex; rct_peep* peep; if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 0x0E) return; - sprite_index = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_PEEP, uint16); + spriteIndex = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_PEEP, uint16); i = 0; - while (sprite_index != 0xFFFF) { - peep = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[sprite_index].peep); - sprite_index = peep->next; + while (spriteIndex != SPRITE_INDEX_NULL) { + peep = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[spriteIndex].peep); + spriteIndex = peep->next; if ((i & 0x7F) != (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 0x7F)) { - RCT2_CALLPROC_X(0x0068FC1E, 0, 0, 0, 0, (int)peep, 0, 0); + peep_update(peep); } else { RCT2_CALLPROC_X(0x0068F41A, 0, 0, 0, i, (int)peep, 0, 0); if (peep->var_08 == 4) - RCT2_CALLPROC_X(0x0068FC1E, 0, 0, 0, 0, (int)peep, 0, 0); + peep_update(peep); } i++; } } +/** + * + * rct2: 0x0068FC1E + */ +static void peep_update(rct_peep *peep) +{ + // RCT2_CALLPROC_X(0x0068FC1E, 0, 0, 0, 0, (int)peep, 0, 0); return; + + int i, j; + + if (peep->type == PEEP_TYPE_GUEST) { + if (peep->var_AD != 255) + if (++peep->var_AE < 720) + peep->var_AD = 255; + + // Update thoughts + i = 0; + int ebp = 0; + int edi = -1; + for (i = 0; i < PEEP_MAX_THOUGHTS; i++) { + if (peep->thoughts[i].type == PEEP_THOUGHT_TYPE_NONE) + break; + + if (peep->thoughts[i].var_2 == 1) { + ebp++; + if (++peep->thoughts[i].var_3 >= 220) { + peep->thoughts[i].var_3 = 0; + peep->thoughts[i].var_2++; + ebp--; + } + } else if (peep->thoughts[i].var_2 >= 0) { + if (++peep->thoughts[i].var_3 > 255) { + if (++peep->thoughts[i].var_3 >= 28) { + peep->var_45 |= 1; + + // Clear top thought, push others up + for (j = i; j < PEEP_MAX_THOUGHTS - 1; j++) + peep->thoughts[j].type = peep->thoughts[j + 1].type; + peep->thoughts[PEEP_MAX_THOUGHTS - 1].type = PEEP_THOUGHT_TYPE_NONE; + } + } + } else { + edi = i; + } + } + if (ebp == 0 && edi != -1) { + peep->thoughts[edi].var_2 = 1; + peep->var_45 |= 1; + } + } + + // Walking speed logic + unsigned int stepsToTake = peep->energy; + if (stepsToTake < 95 && peep->state == PEEP_STATE_QUEUING) + stepsToTake = 95; + if ((peep->flags & PEEP_FLAGS_SLOW_WALK) && peep->state != PEEP_STATE_QUEUING) + stepsToTake /= 2; + if (peep->var_71 == 255 && (RCT2_GLOBAL((int)peep + 0x29, uint8) & 4)) { + stepsToTake /= 2; + if (peep->state == PEEP_STATE_QUEUING) + stepsToTake += stepsToTake / 2; + } + + unsigned int carryCheck = peep->var_73 + stepsToTake; + peep->var_73 = carryCheck; + if (carryCheck <= 255) { + // loc_68FD3A + RCT2_CALLPROC_X(0x0068FD3A, 0, 0, 0, 0, (int)peep, 0, 0); + } else { + // loc_68FD2F + RCT2_CALLPROC_X(0x0068FD2F, 0, 0, 0, 0, (int)peep, 0, 0); + switch (peep->state) { + + } + } +} + /** * @@ -85,7 +159,7 @@ void peep_problem_warnings_update() { rct_peep* peep; rct_ride* ride; - uint16 sprite_idx; + uint16 spriteIndex; uint16 guests_in_park = RCT2_GLOBAL(RCT2_ADDRESS_GUESTS_IN_PARK, uint16); int hunger_counter = 0, lost_counter = 0, noexit_counter = 0, thirst_counter = 0, litter_counter = 0, disgust_counter = 0, bathroom_counter = 0 ,vandalism_counter = 0; @@ -93,11 +167,8 @@ void peep_problem_warnings_update() RCT2_GLOBAL(RCT2_ADDRESS_RIDE_COUNT, sint16) = ride_get_count(); // refactor this to somewhere else - - for (sprite_idx = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_PEEP, uint16); sprite_idx != SPRITE_INDEX_NULL; sprite_idx = peep->next) { - peep = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[sprite_idx].peep); - - if (peep->type != PEEP_TYPE_GUEST || peep->var_2A != 0 || peep->thoughts[0].pad_3 > 5) + FOR_ALL_GUESTS(spriteIndex, peep) { + if (peep->var_2A != 0 || peep->thoughts[0].var_2 > 5) continue; switch (peep->thoughts[0].type) { @@ -212,7 +283,7 @@ void peep_problem_warnings_update() void peep_update_crowd_noise() { rct_viewport *viewport; - uint16 sprite_index; + uint16 spriteIndex; rct_peep *peep; int visiblePeeps; @@ -234,15 +305,10 @@ void peep_update_crowd_noise() // Count the number of peeps visible visiblePeeps = 0; - sprite_index = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_PEEP, uint16); - while (sprite_index != SPRITE_INDEX_NULL) { - peep = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[sprite_index].peep); - sprite_index = peep->next; + FOR_ALL_GUESTS(spriteIndex, peep) { if (peep->var_16 == 0x8000) continue; - if (peep->type != PEEP_TYPE_GUEST) - continue; if (viewport->view_x > peep->var_1A) continue; if (viewport->view_x + viewport->view_width < peep->var_16) @@ -293,6 +359,43 @@ void peep_update_crowd_noise() } } +/** + * + * rct2: 0x0069BE9B + */ +void peep_applause() +{ + uint16 spriteIndex; + rct_peep* peep; + + FOR_ALL_GUESTS(spriteIndex, peep) { + if (peep->var_2A != 0) + continue; + + // Release balloon + if (peep->item_standard_flags & PEEP_ITEM_BALLOON) { + peep->item_standard_flags &= ~PEEP_ITEM_BALLOON; + if (peep->x != 0x8000) { + create_balloon(peep->x, peep->y, peep->z + 9, peep->balloon_colour); + peep->var_45 |= 8; + RCT2_CALLPROC_X(0x0069B8CC, 0, 0, 0, 0, (int)peep, 0, 0); + } + } + + // Clap + if ((peep->state == PEEP_STATE_WALKING || peep->state == PEEP_STATE_QUEUING) && peep->var_71 >= 254) { + peep->var_71 = 26; + peep->var_72 = 0; + peep->var_70 = 0; + RCT2_CALLPROC_X(0x00693B58, 0, 0, 0, 0, (int)peep, 0, 0); + RCT2_CALLPROC_X(0x006EC473, 0, 0, 0, 0, (int)peep, 0, 0); + } + } + + // Play applause noise + sound_play_panned(SOUND_APPLAUSE, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16) / 2); +} + /** * * rct2: 0x0069A05D diff --git a/src/peep.h b/src/peep.h index 15c4c766d4..74dfd3f6b8 100644 --- a/src/peep.h +++ b/src/peep.h @@ -307,8 +307,8 @@ enum PEEP_ITEM { typedef struct { uint8 type; uint8 item; - uint8 pad_3; - uint8 pad_4; + uint8 var_2; + uint8 var_3; } rct_peep_thought; typedef struct { @@ -359,7 +359,7 @@ typedef struct { uint8 pad_41[0x2]; uint8 intensity; // 0x43 uint8 nausea_tolerance; // 0x44 - uint8 pad_45; + uint8 var_45; money16 paid_on_drink; // 0x46 uint8 pad_48[0x10]; uint32 item_extra_flags; // 0x58 @@ -372,8 +372,14 @@ typedef struct { uint8 current_train; // 0x6A uint8 current_car; // 0x6B uint8 current_seat; // 0x6C - uint8 pad_6D[0x09]; + uint8 pad_6D[3]; + uint8 var_70; + uint8 var_71; + uint8 var_72; + uint8 var_73; + uint16 pad_74; uint8 var_76; + uint8 pad_77; uint8 var_78; uint8 pad_79[0x03]; uint8 rides_been_on[32]; // 0x7C @@ -382,7 +388,8 @@ typedef struct { money32 cash_spent; // 0xA4 uint8 pad_A8; sint32 time_in_park; // 0xA9 - uint8 pad_AD[0x3]; + uint8 var_AD; + uint16 var_AE; rct_peep_thought thoughts[PEEP_MAX_THOUGHTS]; // 0xB0 uint8 pad_C4; uint8 var_C5; @@ -412,10 +419,30 @@ typedef struct { uint32 item_standard_flags; // 0xFC } rct_peep; +/** Helper macro until rides are stored in this module. */ +#define GET_PEEP(sprite_index) &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[sprite_index].peep) + +/** + * Helper macro loop for enumerating through all the non null rides. To avoid needing a end loop counterpart, statements are + * applied in tautology if statements. + */ +#define FOR_ALL_PEEPS(sprite_index, peep) \ + for (sprite_index = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_PEEP, uint16); sprite_index != SPRITE_INDEX_NULL; sprite_index = peep->next) \ + if ((peep = GET_PEEP(sprite_index)) || 1) + +#define FOR_ALL_GUESTS(sprite_index, peep) \ + FOR_ALL_PEEPS(sprite_index, peep) \ + if (peep->type == PEEP_TYPE_GUEST) + +#define FOR_ALL_STAFF(sprite_index, peep) \ + FOR_ALL_PEEPS(sprite_index, peep) \ + if (peep->type == PEEP_TYPE_STAFF) + int peep_get_staff_count(); void peep_update_all(); void peep_problem_warnings_update(); void peep_update_crowd_noise(); +void peep_applause(); rct_peep *peep_generate(int x, int y, int z); #endif diff --git a/src/rct2.c b/src/rct2.c index 76b81e5e6e..a7b41aedab 100644 --- a/src/rct2.c +++ b/src/rct2.c @@ -157,7 +157,9 @@ void rct2_init() scenario_load_list(); track_load_list(253); gfx_load_g1(); - RCT2_CALLPROC_EBPSAFE(0x006C19AC); + //RCT2_CALLPROC_EBPSAFE(0x006C19AC); //Load character widths + gfx_load_character_widths(); + osinterface_init(); RCT2_CALLPROC_EBPSAFE(0x006BA8E0); // init_audio(); viewport_init_all(); @@ -165,10 +167,10 @@ void rct2_init() get_local_time(); reset_park_entrances(); reset_saved_strings(); - RCT2_CALLPROC_EBPSAFE(0x0069EB13); + RCT2_CALLPROC_EBPSAFE(0x0069EB13); //Sprite list reset/load ride_init_all(); window_guest_list_init_vars_a(); - RCT2_CALLPROC_EBPSAFE(0x006BD3A4); + RCT2_CALLPROC_EBPSAFE(0x006BD3A4); //Peep? map_init(); park_init(); RCT2_CALLPROC_EBPSAFE(0x0066B5C0); // 0x0066B5C0 (part of 0x0066B3E8) screen_game_create_windows() @@ -189,8 +191,7 @@ void rct2_init() void rct2_init_directories() { // check install directory - DWORD dwAttrib = GetFileAttributes(gGeneral_config.game_path); - if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) { + if ( !osinterface_directory_exists(gGeneral_config.game_path) ) { osinterface_show_messagebox("Invalid RCT2 installation path. Please correct in config.ini."); exit(-1); } diff --git a/src/rct2.h b/src/rct2.h index b95864f69c..f5b765b206 100644 --- a/src/rct2.h +++ b/src/rct2.h @@ -64,7 +64,7 @@ typedef unsigned long long uint64; #define OPENRCT2_PLATFORM "Windows" #define OPENRCT2_TIMESTAMP __DATE__ " " __TIME__ -// Represent fixed point numbers +// Represent fixed point numbers. dp = decimal point typedef sint16 fixed16_1dp; typedef sint16 fixed16_2dp; typedef sint32 fixed32_1dp; @@ -74,7 +74,8 @@ typedef sint32 fixed32_2dp; typedef fixed16_1dp money16; typedef fixed32_1dp money32; -// Construct a fixed point number. +// Construct a fixed point number. For example, to create the value 3.65 you +// 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) diff --git a/src/ride.c b/src/ride.c index 7490f60dd3..bbaa1db97f 100644 --- a/src/ride.c +++ b/src/ride.c @@ -20,14 +20,14 @@ #include #include "addresses.h" +#include "map.h" +#include "news_item.h" +#include "sprite.h" #include "ride.h" #include "sprite.h" #include "peep.h" #include "window.h" -#define GET_RIDE(x) (&(RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, rct_ride)[x])) -#define GET_RIDE_MEASUREMENT(x) (&(RCT2_ADDRESS(RCT2_ADDRESS_RIDE_MEASUREMENTS, rct_ride_measurement)[x])) - #pragma region Ride classification table const uint8 gRideClassifications[255] = { @@ -104,11 +104,8 @@ int ride_get_count() rct_ride *ride; int i, count = 0; - for (i = 0; i < MAX_RIDES; i++) { - ride = GET_RIDE(i); - if (ride->type != RIDE_TYPE_NULL) - count++; - } + FOR_ALL_RIDES(i, ride) + count++; return count; } @@ -162,13 +159,10 @@ void ride_init_all() void reset_all_ride_build_dates() { int i; rct_ride *ride; - for (i = 0; i < MAX_RIDES; i++) { - ride = GET_RIDE(i); - if (ride->type != RIDE_TYPE_NULL) { - //mov ax, current_month_year - //sub [esi + 180h], ax - ride->build_date -= RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint16); - } + FOR_ALL_RIDES(i, ride) { + //mov ax, current_month_year + //sub [esi + 180h], ax + ride->build_date -= RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint16); } } @@ -177,17 +171,15 @@ void reset_all_ride_build_dates() { */ void ride_update_favourited_stat() { + int i; rct_ride *ride; + uint16 spriteIndex; rct_peep* peep; - for (int i = 0; i < MAX_RIDES; i++) { - ride = GET_RIDE(i); - if (ride->type != RIDE_TYPE_NULL) - ride->guests_favourite = 0; + FOR_ALL_RIDES(i, ride) + ride->guests_favourite = 0; - } - for (int sprite_idx = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_PEEP, uint16); sprite_idx != SPRITE_INDEX_NULL; sprite_idx = peep->next) { - peep = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[sprite_idx].peep); + FOR_ALL_PEEPS(spriteIndex, peep) { if (peep->var_08 != 4) return; if (peep->favourite_ride != 0xff) { @@ -198,6 +190,156 @@ void ride_update_favourited_stat() } } + window_invalidate_by_id(WC_RIDE_LIST, 0); } + + + +/** + * rct2: 0x006B7C59 + * @return 1 if the coordinate is reachable or has no entrance, 0 otherwise + */ +int ride_entrance_exit_is_reachable(uint16 coordinate, rct_ride* ride, int index) { + int x = ((coordinate >> 8) & 0xFF) << 5, // cx + y = (coordinate & 0xFF) << 5; // ax + uint8 station_height = ride->station_heights[index]; + int tile_idx = ((x << 8) | y) >> 5; + rct_map_element* tile = RCT2_ADDRESS(RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS, rct_map_element*)[tile_idx]; + + while(1) { + uint8 element_type = tile->type & MAP_ELEMENT_TYPE_MASK; + if (element_type == MAP_ELEMENT_TYPE_ENTRANCE && station_height == tile->base_height) { + break; + } else if (tile->flags & MAP_ELEMENT_FLAG_LAST_TILE) { + return 1; + } + tile++; + } + + uint8 face_direction = tile->type & 3; + y -= RCT2_ADDRESS(0x00993CCC, sint16)[face_direction * 2]; + x -= RCT2_ADDRESS(0x00993CCE, sint16)[face_direction * 2]; + tile_idx = ((x << 8) | y) >> 5; + + return map_coord_is_connected(tile_idx, station_height, face_direction); +} + + +void ride_entrance_exit_connected(rct_ride* ride, int ride_idx) +{ + for (int i = 0; i < 4; ++i) { + uint16 station_start = ride->station_starts[i], + entrance = ride->entrances[i], + exit = ride->exits[i]; + + if (station_start == -1 ) + continue; + if (entrance != -1 && !ride_entrance_exit_is_reachable(entrance, ride, i)) { + // name of ride is parameter of the format string + RCT2_GLOBAL(0x013CE952, uint16) = ride->var_04A; + RCT2_GLOBAL(0x013CE954, uint32) = ride->var_04C; + news_item_add_to_queue(1, STR_ENTRANCE_NOT_CONNECTED, ride_idx); + ride->connected_message_throttle = 3; + } + + if (exit != -1 && !ride_entrance_exit_is_reachable(exit, ride, i)) { + // name of ride is parameter of the format string + RCT2_GLOBAL(0x013CE952, uint16) = ride->var_04A; + RCT2_GLOBAL(0x013CE954, uint32) = ride->var_04C; + news_item_add_to_queue(1, STR_EXIT_NOT_CONNECTED, ride_idx); + ride->connected_message_throttle = 3; + } + + } +} + + +void ride_shop_connected(rct_ride* ride, int ride_idx) +{ + uint16 coordinate = ride->station_starts[0]; + if (coordinate == 0xFFFF) + return; + + int x = ((coordinate >> 8) & 0xFF) << 5, // cx + y = (coordinate & 0xFF) << 5; // ax + uint16 entrance_directions = 0; + int tile_idx = ((x << 8) | y) >> 5, count = 0; + rct_map_element* tile = RCT2_ADDRESS(RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS, rct_map_element*)[tile_idx]; + + + while (1) { + uint8 element_type = tile->type & MAP_ELEMENT_TYPE_MASK; + if(element_type == MAP_ELEMENT_TYPE_TRACK && tile->properties.track.ride_index == ride_idx) + break; + + if(tile->flags & MAP_ELEMENT_FLAG_LAST_TILE) + return; + tile++; + } + + uint8 track_type = tile->properties.track.type; + ride = GET_RIDE(tile->properties.track.ride_index); + if (RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + ride->type * 8, uint32) & 0x80000) { + entrance_directions = RCT2_ADDRESS(0x0099CA64, uint8)[track_type * 16]; + } else { + entrance_directions = RCT2_ADDRESS(0x0099BA64, uint8)[track_type * 16]; + } + + + uint8 tile_direction = tile->type & MAP_ELEMENT_DIRECTION_MASK; + entrance_directions <<= tile_direction; + entrance_directions = ((entrance_directions >> 12) | entrance_directions) & 0xF; + + // now each bit in entrance_directions stands for an entrance direction to check + if (entrance_directions == 0) + return; + + for (int count = 0; entrance_directions != 0; ++count) { + if (!(entrance_directions & 1)) { + entrance_directions >>= 1; + continue; + } + entrance_directions >>= 1; + + uint8 face_direction = count ^ 2; // flip direction north<->south, east<->west + y -= RCT2_ADDRESS(0x00993CCC, sint16)[face_direction * 2]; + x -= RCT2_ADDRESS(0x00993CCE, sint16)[face_direction * 2]; + tile_idx = ((x << 8) | y) >> 5; + + if (map_coord_is_connected(tile_idx, tile->base_height, face_direction)) + return; + } + + // name of ride is parameter of the format string + RCT2_GLOBAL(0x013CE952, uint16) = ride->var_04A; + RCT2_GLOBAL(0x013CE954, uint32) = ride->var_04C; + news_item_add_to_queue(1, STR_ENTRANCE_NOT_CONNECTED, ride_idx); + + ride->connected_message_throttle = 3; +} + + + +/** + * rct2: 0x006B7A5E + **/ +void ride_check_all_reachable() +{ + rct_ride *ride; + int i; + + FOR_ALL_RIDES(i, ride) { + if (ride->connected_message_throttle != 0) + ride->connected_message_throttle--; + if (ride->status != RIDE_STATUS_OPEN || ride->connected_message_throttle != 0) + continue; + + if (RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + ride->type * 8, uint32) & 0x20000) + ride_shop_connected(ride, i); + else + ride_entrance_exit_connected(ride, i); + } +} + diff --git a/src/ride.h b/src/ride.h index 6d1c7ab6d7..bcbc0c86dd 100644 --- a/src/ride.h +++ b/src/ride.h @@ -23,6 +23,19 @@ #include "rct2.h" +typedef fixed16_2dp ride_rating; + +// Convenience function for writing ride ratings. The result is a 16 bit signed +// integer. To create the ride rating 3.65 type RIDE_RATING(3,65) +#define RIDE_RATING(whole, fraction) FIXED_2DP(whole, fraction) + +// Used for return values, for functions that modify all three. +typedef struct { + ride_rating excitement; + ride_rating intensity; + ride_rating nausea; +} rating_tuple; + /** * Ride structure. * size: 0x0260 @@ -43,7 +56,8 @@ typedef struct { uint32 var_04C; uint16 overall_view; // 0x050 uint16 station_starts[4]; // 0x052 - uint8 pad_05A[0x10]; + uint8 station_heights[4]; // 0x05A + uint8 pad_05E[0xC]; uint16 entrances[4]; // 0x06A uint16 exits[4]; // 0x072 uint8 pad_07A[0x0C]; @@ -55,7 +69,7 @@ typedef struct { uint8 var_0C8; uint8 var_0C9; - uint8 pad_0CA[0x1B]; + uint8 pad_0CA[0x1A]; sint32 var_0E4; sint32 var_0E8; @@ -65,7 +79,7 @@ typedef struct { uint8 var_114; // Track length? Number of track segments? uint8 var_115; - uint8 pad_116[0x0F]; + uint8 pad_116[0x0E]; sint16 var_124; sint16 var_126; sint16 var_128; @@ -96,13 +110,17 @@ typedef struct { // used in computing excitement, nausea, etc uint8 var_198; uint8 var_199; - uint8 pad_19A[0x1A]; - money32 profit; // 0x1B4 + uint8 pad_19A[0x14]; + uint8 var_1AE; + uint8 connected_message_throttle; + uint32 pad_1B0; + sint32 profit; // 0x1B4 uint8 queue_time[4]; // 0x1B8 - uint8 pad_1BC[0x11]; + uint8 var_1BC; + uint8 pad_1BD[0x10]; uint8 var_1CD; uint16 guests_favourite; // 0x1CE - uint32 lifecycle_flags; + uint32 lifecycle_flags; // 0x1D0 uint8 pad_1D4[0x20]; // 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 @@ -295,8 +313,29 @@ enum { RIDE_COLOUR_SCHEME_DIFFERENT_PER_CAR }; +enum { + RIDE_GROUP_TRANSPORT, + RIDE_GROUP_GENTLE, + RIDE_GROUP_ROLLERCOASTER, + RIDE_GROUP_THRILL, + RIDE_GROUP_WATER, + RIDE_GROUP_SHOP +}; + #define MAX_RIDES 255 #define MAX_RIDE_MEASUREMENTS 8 +#define RIDE_RELIABILITY_UNDEFINED 0xFFFF + +/** Helper macros until rides are stored in this module. */ +#define GET_RIDE(x) (&(RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, rct_ride)[x])) +#define GET_RIDE_MEASUREMENT(x) (&(RCT2_ADDRESS(RCT2_ADDRESS_RIDE_MEASUREMENTS, rct_ride_measurement)[x])) + +/** + * Helper macro loop for enumerating through all the non null rides. + */ +#define FOR_ALL_RIDES(i, ride) \ + for (i = 0; i < MAX_RIDES; i++) \ + if ((ride = GET_RIDE(i))->type != RIDE_TYPE_NULL) extern const uint8 gRideClassifications[255]; @@ -306,5 +345,6 @@ int ride_get_max_queue_time(rct_ride *ride); void ride_init_all(); void reset_all_ride_build_dates(); void ride_update_favourited_stat(); +void ride_check_all_reachable(); #endif diff --git a/src/ride_ratings.h b/src/ride_ratings.h index 28ad8bc451..7b6463c525 100644 --- a/src/ride_ratings.h +++ b/src/ride_ratings.h @@ -22,17 +22,7 @@ #define _RIDE_RATINGS_H_ #include "rct2.h" - -typedef fixed16_2dp ride_rating; - -#define RIDE_RATING(whole, fraction) FIXED_2DP(whole, fraction) - -// Used for return values, for functions that modify all three. -typedef struct { - ride_rating excitement; - ride_rating intensity; - ride_rating nausea; -} rating_tuple; +#include "ride.h" void crooked_house_excitement(rct_ride *ride); void sub_655FD6(rct_ride *ride); diff --git a/src/scenario.c b/src/scenario.c index b35584e0f8..6dc29fa1bf 100644 --- a/src/scenario.c +++ b/src/scenario.c @@ -21,10 +21,12 @@ #include #include #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 "object.h" #include "park.h" @@ -58,14 +60,12 @@ int scenario_load_basic(const char *path) // Checks for a scenario string object (possibly for localisation) if ((s6Info->entry.flags & 0xFF) != 255) { - if (sub_6A9428(&s6Info->entry)) { + if (object_get_scenario_text(&s6Info->entry)) { int ebp = RCT2_GLOBAL(0x009ADAF8, uint32); format_string(s6Info->name, RCT2_GLOBAL(ebp, sint16), NULL); format_string(s6Info->details, RCT2_GLOBAL(ebp + 4, sint16), NULL); RCT2_GLOBAL(0x009AA00C, uint8) = RCT2_GLOBAL(ebp + 6, uint8); - - // Disposes the scenario string object (0x009ADAF8) - RCT2_CALLPROC_EBPSAFE(0x006A982D); + object_free_scenario_text(); } } return 1; @@ -279,7 +279,9 @@ void scenario_load_and_play(const rct_scenario_basic *scenario) RCT2_GLOBAL(RCT2_ADDRESS_INCOME_FROM_ADMISSIONS, uint32) = 0; RCT2_GLOBAL(0x013587D8, uint16) = 63; sub_69E869(); // (loan related, called above already) - park_reset_awards_and_history(); + park_reset_history(); + finance_reset_history(); + award_reset(); reset_all_ride_build_dates(); date_reset(); RCT2_CALLPROC_EBPSAFE(0x00674576); @@ -336,7 +338,7 @@ void scenario_success() uint32 current_val = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_COMPANY_VALUE, uint32); RCT2_GLOBAL(RCT2_ADDRESS_COMPLETED_COMPANY_VALUE, uint32) = current_val; - RCT2_CALLPROC_EBPSAFE(0x0069BE9B); // celebration + peep_applause(); for (i = 0; i < gScenarioListCount; i++) { char *cur_scenario_name = RCT2_ADDRESS(0x135936C, char); @@ -365,20 +367,15 @@ void scenario_success() **/ void scenario_objective5_check() { - int rcs = 0; + int i, rcs = 0; uint8 type_already_counted[256]; rct_ride* ride; memset(type_already_counted, 0, 256); - for (int i = 0; i < MAX_RIDES; i++) { - uint8 subtype_id; - uint32 subtype_p; - ride = &(RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, rct_ride)[i]); - if (ride->type == RIDE_TYPE_NULL) - continue; - subtype_id = (uint8)ride->subtype; - subtype_p = RCT2_GLOBAL(0x009ACFA4 + subtype_id * 4, uint32); + FOR_ALL_RIDES(i, ride) { + uint8 subtype_id = ride->subtype; + uint32 subtype_p = RCT2_GLOBAL(0x009ACFA4 + subtype_id * 4, uint32); if ((RCT2_GLOBAL(subtype_p + 0x1BE, sint8) == 2 || RCT2_GLOBAL(subtype_p + 0x1BF, sint8) == 2) && @@ -400,21 +397,16 @@ void scenario_objective5_check() **/ void scenario_objective8_check() { - int rcs = 0; + int i, rcs = 0; uint8 type_already_counted[256]; rct_ride* ride; sint16 objective_length = RCT2_GLOBAL(RCT2_ADDRESS_OBJECTIVE_NUM_GUESTS, uint16); memset(type_already_counted, 0, 256); - for (int i = 0; i < MAX_RIDES; i++) { - uint8 subtype_id; - uint32 subtype_p; - ride = &(RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, rct_ride)[i]); - if (ride->type == RIDE_TYPE_NULL) - continue; - subtype_id = (uint8)ride->subtype; - subtype_p = RCT2_GLOBAL(0x009ACFA4 + subtype_id * 4, uint32); + FOR_ALL_RIDES(i, ride) { + uint8 subtype_id = ride->subtype; + uint32 subtype_p = RCT2_GLOBAL(0x009ACFA4 + subtype_id * 4, uint32); if ((RCT2_GLOBAL(subtype_p + 0x1BE, sint8) == 2 || RCT2_GLOBAL(subtype_p + 0x1BF, sint8) == 2) && @@ -545,9 +537,9 @@ void scenario_objectives_check() void scenario_entrance_fee_too_high_check() { uint16 x, y; - uint16 magic = RCT2_GLOBAL(0x013580EE, uint16), - park_entrance_fee = RCT2_GLOBAL(RCT2_ADDRESS_PARK_ENTRANCE_FEE, uint16); - int max_fee = magic + (magic / 2); + uint16 totalRideValue = RCT2_GLOBAL(RCT2_TOTAL_RIDE_VALUE, uint16); + uint16 park_entrance_fee = RCT2_GLOBAL(RCT2_ADDRESS_PARK_ENTRANCE_FEE, uint16); + int max_fee = totalRideValue + (totalRideValue / 2); uint32 game_flags = RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32), packed_xy; if ((game_flags & PARK_FLAGS_PARK_OPEN) && park_entrance_fee > max_fee) { @@ -561,49 +553,6 @@ void scenario_entrance_fee_too_high_check() } } - -/* - * Update status of marketing campaigns and send a message when they are done. - * rct2: 0x0069E0C1 - **/ -void scenario_marketing_update() -{ - for (int i = 0; i < 6; ++i) { - uint8 campaign_weeks_left = RCT2_ADDRESS(0x01358102, uint8)[i]; - int campaign_item = 0; - - if (!campaign_weeks_left) - continue; - window_invalidate_by_id(WC_FINANCES, 0); - - // high bit marks the campaign as inactive, on first check the campaign is set actice - // this makes campaigns run a full x weeks even when started in the middle of a week - RCT2_ADDRESS(0x01358102, uint8)[i] &= ~(1 << 7); - if (campaign_weeks_left & (1 << 7)) - continue; - - RCT2_ADDRESS(0x01358102, uint8)[i]--; - if (campaign_weeks_left - 1 != 0) - continue; - - campaign_item = RCT2_ADDRESS(0x01358116, uint8)[i]; - - // this sets the string parameters for the marketing types that have an argument. - if (i == 1 || i == 5) { // free RIDES oh yea - RCT2_GLOBAL(0x013CE952, uint16) = RCT2_GLOBAL(0x01362942 + 304 * campaign_item, uint16);; - RCT2_GLOBAL(0x013CE954, uint32) = RCT2_GLOBAL(0x01362944 + 152 * campaign_item, uint32); - } else if (i == 3) { // free food/merch - campaign_item += 2016; - if (campaign_item >= 2048) - campaign_item += 96; - RCT2_GLOBAL(0x013CE952, uint16) = campaign_item; - } - - news_item_add_to_queue(NEWS_ITEM_MONEY, STR_MARKETING_FINISHED_BASE + i, 0); - } -} - - /* * Scenario and finance related update iteration. * rct2: 0x006C44B1 @@ -630,6 +579,8 @@ void scenario_update() objective_type == 6 || objective_type == 5) { scenario_objectives_check(); } + + window_invalidate_by_id(WC_BOTTOM_TOOLBAR, 0); } @@ -639,9 +590,9 @@ void scenario_update() finance_pay_wages(); finance_pay_research(); finance_pay_interest(); - scenario_marketing_update(); + marketing_update(); peep_problem_warnings_update(); - RCT2_CALLPROC_EBPSAFE(0x006B7A5E); // check ride reachability + ride_check_all_reachable(); ride_update_favourited_stat(); if (month <= 1 && RCT2_GLOBAL(0x009ADAE0, sint32) != -1 && RCT2_GLOBAL(0x009ADAE0 + 14, uint16) & 1) { @@ -660,7 +611,7 @@ void scenario_update() break; } } - RCT2_CALLPROC_EBPSAFE(0x0066A231); // update histories (finance, ratings, etc) + park_update_histories(); park_calculate_size(); } @@ -678,7 +629,7 @@ void scenario_update() RCT2_CALLPROC_EBPSAFE(0x0069DEAD); scenario_objectives_check(); scenario_entrance_fee_too_high_check(); - RCT2_CALLPROC_EBPSAFE(0x0066A86C); // award checks + award_update_all(); } } diff --git a/src/scenario.h b/src/scenario.h index cb3822c147..4ab1e2d2b2 100644 --- a/src/scenario.h +++ b/src/scenario.h @@ -83,6 +83,283 @@ typedef struct { char completed_by[64]; // 0x0270 } rct_scenario_basic; +/* This will be useful for backwards compatibility +typedef struct { + // SC6[0] + rct_s6_header header; + + // SC6[1] + rct_s6_info info; + + // SC6[2] + // packed objects + + // SC6[3] + rct_object_entry objects[721]; + + // SC6[4] + uint16 elapsed_months; + uint16 current_day; + uint32 dword_F663AC; + uint32 scenario_srand_0; + uint32 scenario_srand_1; + + // SC6[5] + rct_map_element map_elements[0x30000]; + + // SC6[6] + uint32 dword_010E63B8; + rct_sprite sprites[10000]; + uint16 sprites_next_index; + uint16 sprites_start_vehicle; + uint16 sprites_start_peep; + uint16 sprites_start_textfx; + uint16 sprites_start_litter; + uint8 pad_013573C6[2]; + uint16 word_013573C8; + uint8 pad_013573CA[4]; + uint16 word_013573CE; + uint16 word_013573D0; + uint8 pad_013573D2[2]; + uint16 word_013573D4; + uint8 pad_013573D6[4]; + uint32 dword_013573D8; + uint32 dword_013573DC; + money32 current_loan; + uint32 park_flags; + money16 park_entrance_fee; + uint16 word_013573EA; + uint16 word_013573EC; + uint8 pad_013573EE[16]; + uint8 byte_013573F0; + uint8 pad_013573F1[2]; + rct2_peep_spawn peep_spawns[2]; + uint8 guest_count_change_modifier; + uint8 byte_013573FF; + uint8 pad_01357400[4]; + uint32 dword_01357404; + uint32 dword_01357408; + uint32 dword_0135740C; + uint32 dword_01357410[5]; + uint32 dword_01357424[8]; + uint32 dword_01357444[128]; + uint32 dword_01357644[128]; + + // SC6[7] + uint16 guests_in_park; + uint16 guests_heading_for_park; + + // Ignored in scenario + money32 expenditure_table[14]; + uint32 dword_01357880[5]; + uint32 dword_01357894; + uint32 dword_01357898; + uint32 dword_0135789C; + uint32 dword_013578A0; + uint32 dword_013578A4[201]; + + // SC6[8] + uint16 last_guests_in_park; + uint8 pad_01357BCA[3]; + uint8 handyman_colour; + uint8 mechanic_colour; + uint8 security_colour; + + // Ignored in scenario + uint32 dword_01357BD0[56]; + + // SC6[9] + uint16 park_rating; + + // Ignored in scenario + uint8 park_rating_history[32]; + uint8 guests_in_park_history[32]; + + // SC6[10] + uint16 word_01357CF2; + uint32 word_01357CF4; + uint8 byte_01357CF8[1000]; + uint32 dword_013580E0[32]; + uint16 word_013580E4[16]; + uint8 byte_013580E6; + uint8 byte_013580E7; + uint8 byte_013580E8; + uint8 byte_013580E9; + uint16 park_size; + uint16 guest_generation_probability; + uint16 total_ride_value; + uint32 dword_013580F0; + uint16 dword_013580F4; + uint8 dword_013580F6; + uint8 dword_013580F7; + uint8 objective_type; + uint8 objective_year; + uint8 pad_013580FA[4]; + money32 objective_currency; + uint16 objective_guests; + uint8 campaign_weeks_left[20]; + uint8 campaign_ride_index[22]; + + // Ignored in scenario + money32 balance_history[128]; + + // SC6[11] + uint32 dword_0135832C; + uint32 current_profit; + uint32 dword_01358334; + uint16 word_01358338; + uint8 pad_0135833A[2]; + + // Ignored in scenario + uint8 pad_0135833C[2]; + money32 park_value; + money32 park_value_history[128]; + + // SC6[12] + money32 completed_company_value; + uint32 total_admissions; + money32 income_from_admissions; + money32 company_value; + uint8 byte_01358750[16]; + rct_award awards[4]; + uint16 word_01358770; + uint16 word_01358772; + uint16 word_01358774; + uint8 pad_01358776[4]; + uint32 dword_01358778[17]; + uint32 dword_013587BC; + uint32 dword_013587C0; + uint32 dword_013587C4; + uint16 dword_013587C8; + uint8 pad_013587CA[16]; + uint32 dword_013587D0; + uint8 pad_013587D4[8]; + uint16 word_013587D8[16]; + money32 cash; + uint8 pad_013587FC[50]; + uint16 word_0135882E; + uint16 word_01358830; + uint16 word_01358832; + uint16 map_size; + uint16 word_01358836; + uint32 word_01358838; + uint16 suggested_max_guests; + uint16 word_0135883E; + uint8 word_01358840; + uint8 word_01358841; + uint8 pad_01358842[4]; + uint32 dword_01358844; + uint8 pad_01358848; + uint32 dword_01358849; + uint8 pad_0135884D[2]; + uint8 dword_0135884E[622]; + uint8 pad_01359206[2]; + uint16 word_01359208; + char scenario_name[64]; + char scenario_description[255]; + uint8 byte_01359349; + uint8 byte_0135934A; + uint8 pad_0135934B[3]; + uint32 dword_0135934C; + uint16 park_entrance_x[4]; + uint16 park_entrance_y[4]; + uint16 park_entrance_z[4]; + uint8 byte_01359368; + uint8 pad_01359369[3]; + uint8 byte_0135936C[256]; + uint8 byte_0135946C[3256]; + uint8 byte_0135A124; + uint8 byte_0135A125; + uint16 word_0135A126; + uint8 byte_0135A128; + uint8 byte_0135A129; + uint8 byte_0135A12A; + uint8 byte_0135A12B[793]; + uint8 byte_0135A444[1200]; + char custom_strings[0x8000]; + uint32 game_ticks_1; + rct_ride rides[255]; + uint16 word_01388698; + uint16 saved_view_x; + uint16 saved_view_y; + uint16 saved_view_zoom_and_rotation; + uint8 byte_013886A0[6000]; + uint8 byte_01389E10[6000]; + uint16 word_0138B580; + uint8 pad_0138B580[2]; + uint16 word_0138B584; + uint16 word_0138B586; + uint16 word_0138B588; + uint16 word_0138B58A; + uint16 word_0138B58C; + uint16 word_0138B58E; + uint8 byte_0138B590; + uint8 byte_0138B591; + uint8 byte_0138B592; + uint8 byte_0138B593; + uint16 word_0138B594; + uint16 word_0138B596; + uint16 word_0138B598; + uint16 word_0138B59A; + uint16 word_0138B59C; + uint16 word_0138B59E; + uint16 word_0138B5A0; + uint16 word_0138B5A2; + uint16 word_0138B5A4; + uint16 word_0138B5A6; + uint16 word_0138B5A8; + uint16 word_0138B5AA; + uint16 word_0138B5AC; + uint16 word_0138B5AE; + uint16 word_0138B5B0; + uint16 word_0138B5B2; + uint16 word_0138B5B4; + uint16 word_0138B5B6; + uint16 word_0138B5B8; + uint16 word_0138B5BA; + uint16 word_0138B5BC; + uint16 word_0138B5BE; + uint16 word_0138B5C0; + uint16 word_0138B5C2; + uint16 word_0138B5C4; + uint16 word_0138B5C6; + uint16 word_0138B5C8; + uint16 word_0138B5CA; + uint16 word_0138B5CC; + uint16 word_0138B5CE[31]; + uint8 ride_measurements[0x25860]; + uint32 dword_13B0E6C; + uint16 word_13B0E70; + uint32 dword_13B0E72[0x6600]; + uint8 byte_13CA672[116]; + uint8 byte_13CA6E6[84]; + uint8 byte_13CA73A[4]; + uint8 unk_13CA73E; + uint8 pad_13CA73E; + uint8 byte_13CA740; + uint8 byte_13CA741; + uint8 byte_13CA7424[4]; + uint8 climate; + uint8 pad_013CA747; + uint16 climate_update_timer; + uint8 current_weather; + uint8 next_weather; + uint8 temperature; + uint8 next_temperature; + uint8 current_weather_effect; + uint8 next_weather_effect; + uint8 current_weather_gloom; + uint8 next_weather_gloom; + uint8 current_rain_level; + uint8 next_rain_level; + rct_news_item news_items[61]; + uint8 byte_13CE730[64]; + uint32 dword_13CE770; + uint16 word_13CE774; + uint16 word_13CE776[217]; +} rct_s6_data; +*/ + enum { SCENARIO_FLAGS_VISIBLE = (1 << 0), SCENARIO_FLAGS_COMPLETED = (1 << 1), diff --git a/src/screenshot.c b/src/screenshot.c index d557297a9e..6db7d4f2bb 100644 --- a/src/screenshot.c +++ b/src/screenshot.c @@ -17,10 +17,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . *****************************************************************************/ +#pragma pack(1) #include #include -#include +#include "osinterface.h" #include "addresses.h" #include "config.h" #include "gfx.h" @@ -29,6 +30,8 @@ #include "string_ids.h" #include "window_error.h" +#include // For MAX_PATH + static int screenshot_dump_bmp(); static int screenshot_dump_png(); @@ -59,17 +62,27 @@ void screenshot_check() static int screenshot_get_next_path(char *path, char *extension) { + char *screenshotPath = osinterface_get_orct2_homesubfolder("screenshot"); + if (!osinterface_ensure_directory_exists(screenshotPath)) { + free(screenshotPath); + + fprintf(stderr, "Unable to save screenshots in OpenRCT2 screenshot directory.\n"); + return -1; + } + int i; for (i = 1; i < 1000; i++) { RCT2_GLOBAL(0x013CE952, uint16) = i; // Glue together path and filename - sprintf(path, "%sSCR%d%s", RCT2_ADDRESS(RCT2_ADDRESS_APP_PATH_SLASH, char), i, extension); + sprintf(path, "%s%cSCR%d%s", screenshotPath, osinterface_get_path_separator(), i, extension); - if (GetFileAttributes(path) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND) + if (!osinterface_file_exists(path)) { return i; + } } + free(screenshotPath); return -1; } @@ -119,22 +132,23 @@ int screenshot_dump_bmp() int i, y, index, width, height, stride; char *buffer, path[MAX_PATH], *row; - HANDLE hFile; - DWORD bytesWritten; + FILE *fp; + unsigned int bytesWritten; // Get a free screenshot path if ((index = screenshot_get_next_path(path, ".bmp")) == -1) return -1; - // Open file for writing - hFile = CreateFile(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) + // Open binary file for writing + if ((fp = fopen(path, "wb")) == NULL){ return -1; + } // Allocate buffer buffer = malloc(0xFFFF); if (buffer == NULL) { - CloseHandle(hFile); + //CloseHandle(hFile); + fclose(fp); return -1; } @@ -149,9 +163,9 @@ int screenshot_dump_bmp() header.bfSize = height * stride + 1038; header.bfOffBits = 1038; - WriteFile(hFile, &header, sizeof(header), &bytesWritten, NULL); - if (bytesWritten != sizeof(header)) { - CloseHandle(hFile); + bytesWritten = fwrite(&header, sizeof(BitmapFileHeader), 1, fp); + if (bytesWritten != 1) { + fclose(fp); free(buffer); } @@ -166,9 +180,9 @@ int screenshot_dump_bmp() info.biYPelsPerMeter = 2520; info.biClrUsed = 246; - WriteFile(hFile, &info, sizeof(info), &bytesWritten, NULL); - if (bytesWritten != sizeof(info)) { - CloseHandle(hFile); + bytesWritten=fwrite(&info, sizeof(BitmapInfoHeader), 1, fp); + if (bytesWritten != 1) { + fclose(fp); free(buffer); } @@ -180,9 +194,9 @@ int screenshot_dump_bmp() buffer[i * 4 + 2] = RCT2_ADDRESS(0x01424680, uint8)[i * 4 + 2]; } - WriteFile(hFile, buffer, 246 * 4, &bytesWritten, NULL); - if (bytesWritten != 246 * 4) { - CloseHandle(hFile); + bytesWritten = fwrite(buffer, sizeof(char), 246*4, fp); + if (bytesWritten != 246*4){ + fclose(fp); free(buffer); } @@ -194,14 +208,14 @@ int screenshot_dump_bmp() memset(buffer, 0, stride); memcpy(buffer, row, dpi->width); - WriteFile(hFile, buffer, stride, &bytesWritten, NULL); - if (bytesWritten != stride) { - CloseHandle(hFile); + bytesWritten=fwrite(buffer, sizeof(char), stride, fp); + if (bytesWritten != stride){ + fclose(fp); free(buffer); } } - CloseHandle(hFile); + fclose(fp); free(buffer); return index; diff --git a/src/sprite.c b/src/sprite.c new file mode 100644 index 0000000000..37157e98b6 --- /dev/null +++ b/src/sprite.c @@ -0,0 +1,31 @@ +/***************************************************************************** + * 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 . + *****************************************************************************/ + +#include "addresses.h" +#include "sprite.h" + +/** + * + * rct2: 0x006736C7 + */ +void create_balloon(int x, int y, int z, int colour) +{ + RCT2_CALLPROC_X(0x006736C7, x, colour << 8, y, z, 0, 0, 0); +} \ No newline at end of file diff --git a/src/sprite.h b/src/sprite.h index a51e448c41..3edb1d5e6f 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -27,6 +27,7 @@ #define SPRITE_LOCATION_NULL 0x8000 #include "peep.h" +#include "vehicle.h" typedef struct { uint8 sprite_identifier; // 0x00 @@ -46,16 +47,6 @@ typedef struct { uint32 var_24; } rct_litter; -typedef struct { - uint8 sprite_idetifier; // 0x00 - uint8 pad_01[0x0D]; - sint16 x; // 0x0E - sint16 y; // 0x10 - sint16 z; // 0x12 - uint8 pad_14[0x2a]; - uint16 next_car; // 0x3E -} rct_car; - /** * Sprite structure. * size: 0x0100 @@ -65,7 +56,9 @@ typedef union { rct_unk_sprite unknown; rct_peep peep; rct_litter litter; - rct_car car; + rct_vehicle vehicle; } rct_sprite; +void create_balloon(int x, int y, int z, int colour); + #endif diff --git a/src/string_ids.c b/src/string_ids.c index 67417e4bf9..081e602b77 100644 --- a/src/string_ids.c +++ b/src/string_ids.c @@ -1085,12 +1085,16 @@ void format_integer(char **dest, int value) *dest = dst; - // Right to left - while (value > 0) { - digit = value % 10; - value /= 10; + if (value == 0) { + *dst++ = '0'; + } else { + // Right to left + while (value > 0) { + digit = value % 10; + value /= 10; - *dst++ = '0' + digit; + *dst++ = '0' + digit; + } } finish = dst; @@ -1121,20 +1125,191 @@ void format_comma_separated_integer(char **dest, int value) *dest = dst; - // Groups of three digits, right to left - groupIndex = 0; - while (value > 0) { - // Append group seperator - if (groupIndex == 3) { - groupIndex = 0; - *dst++ = ','; + if (value == 0) { + *dst++ = '0'; + } else { + // Groups of three digits, right to left + groupIndex = 0; + while (value > 0) { + // Append group seperator + if (groupIndex == 3) { + groupIndex = 0; + *dst++ = ','; + } + + digit = value % 10; + value /= 10; + + *dst++ = '0' + digit; + groupIndex++; } + } + finish = dst; - digit = value % 10; - value /= 10; + // Reverse string + dst--; + while (*dest < dst) { + tmp = **dest; + **dest = *dst; + *dst = tmp; + (*dest)++; + dst--; + } + *dest = finish; +} - *dst++ = '0' + digit; - groupIndex++; +void format_comma_separated_fixed_2dp(char **dest, int value) +{ + int digit, groupIndex; + char *dst = *dest; + char *finish; + char tmp; + + // Negative sign + if (value < 0) { + *dst++ = '-'; + value = -value; + } + + *dest = dst; + + // Two decimal places + digit = value % 10; + value /= 10; + *dst++ = '0' + digit; + digit = value % 10; + value /= 10; + *dst++ = '0' + digit; + *dst++ = '.'; + + if (value == 0) { + *dst++ = '0'; + } else { + // Groups of three digits, right to left + groupIndex = 0; + while (value > 0) { + // Append group seperator + if (groupIndex == 3) { + groupIndex = 0; + *dst++ = ','; + } + + digit = value % 10; + value /= 10; + + *dst++ = '0' + digit; + groupIndex++; + } + } + finish = dst; + + // Reverse string + dst--; + while (*dest < dst) { + tmp = **dest; + **dest = *dst; + *dst = tmp; + (*dest)++; + dst--; + } + *dest = finish; +} + +void format_currency(char **dest, int value) +{ + int digit, groupIndex; + char *dst = *dest; + char *finish; + char tmp; + + // Negative sign + if (value < 0) { + *dst++ = '-'; + value = -value; + } + + // Currency symbol + *dst++ = '£'; + + *dest = dst; + + value /= 10; + if (value == 0) { + *dst++ = '0'; + } else { + // Groups of three digits, right to left + groupIndex = 0; + while (value > 0) { + // Append group seperator + if (groupIndex == 3) { + groupIndex = 0; + *dst++ = ','; + } + + digit = value % 10; + value /= 10; + + *dst++ = '0' + digit; + groupIndex++; + } + } + finish = dst; + + // Reverse string + dst--; + while (*dest < dst) { + tmp = **dest; + **dest = *dst; + *dst = tmp; + (*dest)++; + dst--; + } + *dest = finish; +} + +void format_currency_2dp(char **dest, int value) +{ + int digit, groupIndex; + char *dst = *dest; + char *finish; + char tmp; + + // Negative sign + if (value < 0) { + *dst++ = '-'; + value = -value; + } + + // Currency symbol + *dst++ = '£'; + + *dest = dst; + + // Two decimal places + *dst++ = '0'; + digit = value % 10; + value /= 10; + *dst++ = '0' + digit; + *dst++ = '.'; + + if (value == 0) { + *dst++ = '0'; + } else { + // Groups of three digits, right to left + groupIndex = 0; + while (value > 0) { + // Append group seperator + if (groupIndex == 3) { + groupIndex = 0; + *dst++ = ','; + } + + digit = value % 10; + value /= 10; + + *dst++ = '0' + digit; + groupIndex++; + } } finish = dst; @@ -1174,8 +1349,7 @@ void format_string_code(unsigned char format_code, char **dest, char **args) value = *((sint32*)*args); *args += 4; - // TODO - printf("TODO: FORMAT_COMMA2DP32\n"); + format_comma_separated_fixed_2dp(dest, value); break; case FORMAT_COMMA16: // Pop argument @@ -1196,16 +1370,14 @@ void format_string_code(unsigned char format_code, char **dest, char **args) value = *((sint32*)*args); *args += 4; - // TODO - printf("TODO: FORMAT_CURRENCY2DP\n"); + format_currency_2dp(dest, value); break; case FORMAT_CURRENCY: // Pop argument value = *((sint32*)*args); *args += 4; - // TODO - printf("TODO: FORMAT_CURRENCY\n"); + format_currency(dest, value); break; case FORMAT_STRINGID: case FORMAT_STRINGID2: @@ -1214,6 +1386,7 @@ void format_string_code(unsigned char format_code, char **dest, char **args) *args += 2; format_string_part(dest, value, args); + (*dest)--; break; case FORMAT_STRING: // Pop argument @@ -1228,11 +1401,13 @@ void format_string_code(unsigned char format_code, char **dest, char **args) value = *((uint16*)*args); *args += 2; - uint16 dateArgs[] = { date_get_year(value), date_get_month(value) }; + uint16 dateArgs[] = { date_get_month(value), date_get_year(value) + 1 }; + uint16 *dateArgs2 = dateArgs; char formatString[] = "?, Year ?"; formatString[0] = FORMAT_MONTH; formatString[8] = FORMAT_COMMA16; - format_string_part_from_raw(dest, formatString, (char**)&dateArgs); + format_string_part_from_raw(dest, formatString, (char**)&dateArgs2); + (*dest)--; break; case FORMAT_MONTH: // Pop argument @@ -1338,10 +1513,12 @@ void format_string_part_from_raw(char **dest, const char *src, char **args) *(*dest)++ = code; *(*dest)++ = *src++; *(*dest)++ = *src++; - *(*dest)++ = *src++; - *(*dest)++ = *src++; } else { *(*dest)++ = code; + *(*dest)++ = *src++; + *(*dest)++ = *src++; + *(*dest)++ = *src++; + *(*dest)++ = *src++; } } else if (code <= 'z') { *(*dest)++ = code; @@ -1364,6 +1541,7 @@ void format_string_part(char **dest, rct_string_id format, char **args) // args += (format & 0xC00) >> 9; format &= ~0xC00; strcpy(*dest, RCT2_ADDRESS(0x135A8F4 + (format * 32), char)); + *dest = strchr(*dest, 0) + 1; } else if (format < 0xE000) { // Real name format -= -0xA000; @@ -1371,6 +1549,7 @@ void format_string_part(char **dest, rct_string_id format, char **args) real_names[format % countof(real_names)], real_name_initials[(format >> 10) % countof(real_name_initials)] ); + *dest = strchr(*dest, 0) + 1; } else { // ? RCT2_CALLPROC_EBPSAFE(RCT2_ADDRESS(0x0095AFB8, uint32)[format]); @@ -1498,4 +1677,4 @@ void reset_saved_strings() { for (int i = 0; i < 1024; i++) { RCT2_ADDRESS(0x135A8F4, uint8)[i * 32] = 0; } -} \ No newline at end of file +} diff --git a/src/string_ids.h b/src/string_ids.h index 928f7024dd..5db27533b3 100644 --- a/src/string_ids.h +++ b/src/string_ids.h @@ -341,6 +341,13 @@ enum { STR_SHOW_SUBJECT_TIP = 1937, + STR_FINANCIAL_SUMMARY = 1949, + STR_FINANCIAL_GRAPH = 1950, + STR_PARK_VALUE_GRAPH = 1951, + STR_PROFIT_GRAPH = 1952, + STR_MARKETING = 1953, + STR_RESEARCH_FUNDING = 1954, + STR_CELSIUS_VALUE = 2216, STR_FAHRENHEIT_VALUE = 2217, @@ -365,6 +372,19 @@ enum { STR_MONTH_NOVEMBER = STR_MONTH_JANUARY + 10, STR_MONTH_DECEMBER = STR_MONTH_JANUARY + 11, + STR_RESEARCH_TRANSPORT_RIDES = 2253, + STR_RESEARCH_GENTLE_RIDES = 2254, + STR_RESEARCH_ROLLER_COASTERS = 2255, + STR_RESEARCH_THRILL_RIDES = 2256, + STR_RESEARCH_WATER_RIDES = 2257, + STR_RESEARCH_SHOPS_AND_STALLS = 2258, + STR_RESEARCH_SCENERY_AND_THEMING = 2259, + + STR_RESEARCH_FUNDING_ = 2264, + STR_RESEARCH_PRIORITIES = 2266, + + STR_FINANCES_RESEARCH = 2275, + STR_SELECT_SCENARIO = 2291, STR_CHANGE_BASE_LAND_TIP = 2294, @@ -439,6 +459,10 @@ enum { STR_OBJECTIVE_REPLAY_LOAN_AND_PARK_VALUE = STR_OBJECTIVE_NONE + 10, STR_OBJECTIVE_MONTHLY_FOOD_INCOME = STR_OBJECTIVE_NONE + 11, + STR_MARKETING_CAMPAIGNS_IN_OPERATION = 2409, + STR_MARKETING_CAMPAIGNS_AVAILABLE = 2411, + STR_START_THIS_MARKETING_CAMPAIGN = 2412, + STR_MARKETING_FINISHED_BASE = 2446, STR_MARKETING_FINISHED_FREE_ENTRY = 2446, STR_MARKETING_FINISHED_FREE_RIDES = 2447, @@ -447,6 +471,12 @@ enum { STR_MARKETING_FINISHED_PARK_ADS = 2450, STR_MARKETING_FINISHED_RIDE_ADS = 2451, + STR_FINANCES_SHOW_SUMMARY_TAB_TIP = 2457, + STR_FINANCES_SHOW_CASH_TAB_TIP = 2458, + STR_FINANCES_SHOW_PARK_VALUE_TAB_TIP = 2459, + STR_FINANCES_SHOW_WEEKLY_PROFIT_TAB_TIP = 2460, + STR_FINANCES_SHOW_MARKETING_TAB_TIP = 2461, + STR_PARK_ENTRANCE_TAB_TIP = 2462, STR_PARK_RATING_TAB_TIP = 2463, STR_PARK_GUESTS_TAB_TIP = 2464, @@ -455,6 +485,15 @@ enum { STR_PARK_OBJECTIVE_TAB_TIP = 2467, STR_PARK_AWARDS_TAB_TIP = 2468, + STR_SELECT_LEVEL_OF_RESEARCH_AND_DEVELOPMENT = 2469, + STR_RESEARCH_NEW_TRANSPORT_RIDES = 2470, + STR_RESEARCH_NEW_GENTLE_RIDES = 2471, + STR_RESEARCH_NEW_ROLLER_COASTERS = 2472, + STR_RESEARCH_NEW_THRILL_RIDES = 2473, + STR_RESEARCH_NEW_WATER_RIDES = 2474, + STR_RESEARCH_NEW_SHOPS_AND_STALLS = 2475, + STR_RESEARCH_NEW_SCENERY_AND_THEMING = 2476, + STR_PROFIT_PER_WEEK_AND_PARK_VALUE_TIP = 2482, STR_CONTROLS = 2485, @@ -508,8 +547,29 @@ enum { STR_MOST_CONFUSING_LAYOUT = STR_AWARD_MOST_UNTIDY + 15, STR_BEST_GENTLE_RIDES = STR_AWARD_MOST_UNTIDY + 16, + STR_NEWS_ITEM_AWARD_MOST_UNTIDY = 2831, + STR_NEWS_ITEM_MOST_TIDY = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 1, + STR_NEWS_ITEM_BEST_ROLLERCOASTERS = STR_AWARD_MOST_UNTIDY + 2, + STR_NEWS_ITEM_BEST_VALUE = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 3, + STR_NEWS_ITEM_MOST_BEAUTIFUL = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 4, + STR_NEWS_ITEM_WORST_VALUE = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 5, + STR_NEWS_ITEM_SAFEST = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 6, + STR_NEWS_ITEM_BEST_STAFF = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 7, + STR_NEWS_ITEM_BEST_FOOD = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 8, + STR_NEWS_ITEM_WORST_FOOD = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 9, + STR_NEWS_ITEM_BEST_RESTROOMS = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 10, + STR_NEWS_ITEM_MOST_DISAPPOINTING = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 11, + STR_NEWS_ITEM_BEST_WATER_RIDES = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 12, + STR_NEWS_ITEM_BEST_CUSTOM_DESIGNED_RIDES = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 13, + STR_NEWS_ITEM_MOST_DAZZLING_RIDE_COLOURS = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 14, + STR_NEWS_ITEM_MOST_CONFUSING_LAYOUT = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 15, + STR_NEWS_ITEM_BEST_GENTLE_RIDES = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 16, + STR_NO_RECENT_AWARDS = 2848, + STR_ENTRANCE_NOT_CONNECTED = 2854, + STR_EXIT_NOT_CONNECTED = 2855, + STR_TUTORIAL = 2856, STR_PRESS_KEY_OR_MOUSE_BUTTON_FOR_CONTROL = 2857, diff --git a/src/vehicle.c b/src/vehicle.c new file mode 100644 index 0000000000..a96bb51282 --- /dev/null +++ b/src/vehicle.c @@ -0,0 +1,59 @@ +/***************************************************************************** + * 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 . + *****************************************************************************/ + +#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 = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[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); +} \ No newline at end of file diff --git a/src/vehicle.h b/src/vehicle.h new file mode 100644 index 0000000000..3bddf738fc --- /dev/null +++ b/src/vehicle.h @@ -0,0 +1,40 @@ +/***************************************************************************** + * 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 . + *****************************************************************************/ + +#ifndef _VEHICLE_H_ +#define _VEHICLE_H_ + +#include "rct2.h" + +typedef struct { + uint8 sprite_identifier; // 0x00 + uint8 pad_01[0x03]; + uint16 next; // 0x04 + uint8 pad_06[0x08]; + sint16 x; // 0x0E + sint16 y; // 0x10 + sint16 z; // 0x12 + uint8 pad_14[0x2A]; + uint16 next_vehicle_on_train; // 0x3E +} rct_vehicle; + +void vehicle_update_all(); + +#endif \ No newline at end of file diff --git a/src/widget.c b/src/widget.c index 3fc27264bd..f686f1bd26 100644 --- a/src/widget.c +++ b/src/widget.c @@ -156,6 +156,8 @@ void widget_draw(rct_drawpixelinfo *dpi, rct_window *w, int widgetIndex) widget_scroll_draw(dpi, w, widgetIndex); break; case WWT_CHECKBOX: + widget_checkbox_draw(dpi, w, widgetIndex); + break; case WWT_24: widget_checkbox_draw(dpi, w, widgetIndex); break; @@ -715,7 +717,7 @@ static void widget_closebox_draw(rct_drawpixelinfo *dpi, rct_window *w, int widg } /** -* + * * rct2: 0x006EBAD9 */ static void widget_checkbox_draw(rct_drawpixelinfo *dpi, rct_window *w, int widgetIndex) @@ -736,14 +738,12 @@ static void widget_checkbox_draw(rct_drawpixelinfo *dpi, rct_window *w, int widg colour = w->colours[widget->colour]; // checkbox - if (widget->type != WWT_24) { - gfx_fill_rect_inset(dpi, l, t, l + 9, b - 1, colour, 0x60); + gfx_fill_rect_inset(dpi, l, t, l + 9, b - 1, colour, 0x60); - // fill it when checkbox is pressed - if (widget_is_pressed(w, widgetIndex)) { - RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16) = 224; - gfx_draw_string(dpi, (char*)0x009DED72, colour & 0x7F, l, t); - } + // fill it when checkbox is pressed + if (widget_is_pressed(w, widgetIndex)) { + RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16) = 224; + gfx_draw_string(dpi, (char*)0x009DED72, colour & 0x7F, l, t); } // draw the text @@ -996,3 +996,106 @@ int widget_is_active_tool(rct_window *w, int widgetIndex) return 1; } + +void widget_scroll_get_part(rct_window *w, rct_widget* widget, int x, int y, int *output_x, int *output_y, int *output_scroll_area, int *output_dx) +{ + rct_widget* iterator = w->widgets; + int scroll_id = 0; + while (++iterator != widget) + { + if (iterator->type == WWT_SCROLL) + { + scroll_id++; + break; + } + } + + if ((w->scrolls[scroll_id].flags & 0x01) && y >= (w->y + widget->bottom - 11)) + { + //horizon scrollbar + int rightOffset = 0; + int iteratorLeft = widget->left + w->x; + int iteratorRight = widget->right + w->x; + if (w->scrolls[scroll_id].flags & 0x01) + { + rightOffset = 11; + } + if (x <= (iteratorLeft += 10)) + { + *output_scroll_area = SCROLL_PART_HSCROLLBAR_LEFT; + } + else if (x >= (iteratorRight -= rightOffset)) + { + *output_scroll_area = SCROLL_PART_NONE; + } + else if (x >= (iteratorRight -= 10)) + { + *output_scroll_area = SCROLL_PART_HSCROLLBAR_RIGHT; + } + else if (x < (widget->left + w->x + w->scrolls[scroll_id].h_thumb_left)) + { + *output_scroll_area = SCROLL_PART_HSCROLLBAR_LEFT_TROUGH; + } + else if (x >(widget->left + w->x + w->scrolls[scroll_id].h_thumb_right)) + { + *output_scroll_area = SCROLL_PART_HSCROLLBAR_RIGHT_TROUGH; + } + else + { + *output_scroll_area = SCROLL_PART_HSCROLLBAR_THUMB; + } + } + else if ((w->scrolls[scroll_id].flags & 10) || (x >= w->x + widget->right - 11)) + { + //vertical scrollbar + int bottomOffset = 0; + int iteratorTop = widget->top + w->y; + int iteratorBottom = widget->bottom + w->y; + if (w->scrolls[scroll_id].flags & 0x01) + { + bottomOffset = 11; + } + if (y <= (iteratorTop += 10)) + { + *output_scroll_area = SCROLL_PART_VSCROLLBAR_TOP; + } + else if (y >= (iteratorBottom -= bottomOffset)) + { + *output_scroll_area = SCROLL_PART_NONE; + } + else if (y >= (iteratorBottom -= 10)) + { + *output_scroll_area = SCROLL_PART_VSCROLLBAR_BOTTOM; + } + else if (y < (widget->top + w->y + w->scrolls[scroll_id].v_thumb_top)) + { + *output_scroll_area = SCROLL_PART_VSCROLLBAR_TOP_TROUGH; + } + else if (y > (widget->top + w->y + w->scrolls[scroll_id].v_thumb_bottom)) + { + *output_scroll_area = SCROLL_PART_VSCROLLBAR_BOTTOM_TROUGH; + } + else + { + *output_scroll_area = SCROLL_PART_VSCROLLBAR_THUMB; + } + } + else + { + //view + *output_scroll_area = SCROLL_PART_VIEW; + *output_x = x - widget->left; + *output_y = y - widget->top; + *output_x -= w->x; + *output_y -= w->y; + if (--*output_x < 0 || --*output_y < 0) + { + *output_scroll_area = SCROLL_PART_NONE; + } + else + { + *output_x += w->scrolls[scroll_id].h_left; + *output_y += w->scrolls[scroll_id].v_top; + } + } +} diff --git a/src/widget.h b/src/widget.h index 310e0877c9..28019202b9 100644 --- a/src/widget.h +++ b/src/widget.h @@ -62,5 +62,5 @@ int widget_is_disabled(rct_window *w, int widgetIndex); int widget_is_pressed(rct_window *w, int widgetIndex); int widget_is_highlighted(rct_window *w, int widgetIndex); int widget_is_active_tool(rct_window *w, int widgetIndex); - +void widget_scroll_get_part(rct_window *w, rct_widget* widget, int x, int y, int *output_x, int *output_y, int *output_cx, int *output_dx); #endif diff --git a/src/window.c b/src/window.c index 01333e8809..0a1e9168e1 100644 --- a/src/window.c +++ b/src/window.c @@ -376,7 +376,7 @@ rct_window *window_create(int x, int y, int width, int height, uint32 *event_han // Play sound if (!(flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT))) - sound_play_panned(40, x + (width / 2)); + sound_play_panned(SOUND_WINDOW_OPEN, x + (width / 2)); w->number = 0; w->x = x; diff --git a/src/window.h b/src/window.h index 25e3478852..ec54076f1b 100644 --- a/src/window.h +++ b/src/window.h @@ -81,7 +81,7 @@ typedef struct { * size: 0x12 */ typedef struct { - uint16 flags; // 0x00 + uint16 flags; // 0x00 (0x10 == vertical scrollbar, 0x01 == horizontal scrollbar) sint16 h_left; // 0x02 sint16 h_right; // 0x04 sint16 h_thumb_left; // 0x06 @@ -126,7 +126,7 @@ typedef struct rct_window { sint16 var_484; // viewport target y sint16 var_486; // viewport target z sint16 var_488; // viewport rotation << 8 - sint16 page; // 0x49A + sint16 page; // 0x48A sint16 var_48C; sint16 var_48E; sint16 var_490; @@ -286,7 +286,7 @@ enum { } WINDOW_CLASS; enum PROMPT_MODE { - PM_SAVE_BEFORE_LOAD, + PM_SAVE_BEFORE_LOAD = 0, PM_SAVE_BEFORE_QUIT, PM_SAVE_BEFORE_QUIT2, PM_QUIT @@ -359,6 +359,7 @@ void window_park_entrance_open(); void window_park_guests_open(); void window_park_objective_open(); void window_park_rating_open(); +void window_finances_open(); void window_ride_list_open(); void window_banner_open(); void window_cheats_open(); diff --git a/src/window_banner.c b/src/window_banner.c index 10564eb994..7b27b8fbe2 100644 --- a/src/window_banner.c +++ b/src/window_banner.c @@ -55,7 +55,7 @@ rct_widget window_banner_widgets[] = { static void window_banner_emptysub() { } static void window_banner_mouseup(); -static void window_banner_mousedown(); +static void window_banner_mousedown(int widgetIndex, rct_window*w, rct_widget* widget); static void window_banner_dropdown(); static void window_banner_textinput(); static void window_banner_invalidate(); @@ -182,14 +182,14 @@ static void window_banner_mouseup() } } -static void window_banner_mousedown() +static void window_banner_mousedown(int widgetIndex, rct_window*w, rct_widget* widget) { - short widgetIndex; + //short widgetIndex; #ifdef _MSC_VER - __asm mov widgetIndex, dx + //__asm mov widgetIndex, dx #else - __asm__ ( "mov %[widgetIndex], dx " : [widgetIndex] "+m" (widgetIndex) ); +// __asm__ ( "mov %[widgetIndex], dx " : [widgetIndex] "+m" (widgetIndex) ); #endif diff --git a/src/window_cheats.c b/src/window_cheats.c index 0a18ff0ada..c493268c3e 100644 --- a/src/window_cheats.c +++ b/src/window_cheats.c @@ -58,7 +58,7 @@ static rct_widget window_cheats_money_widgets[] = { { WWT_IMGBTN, 1, 0, WW - 1, 43, WH - 1, 0x0FFFFFFFF, 65535}, // tab content panel { WWT_TAB, 1, 3, 33, 17, 43, 0x2000144E, 2462}, // tab 1 { WWT_TAB, 1, 34, 64, 17, 43, 0x2000144E, 2462}, // tab 2 - { WWT_CLOSEBOX, 1, 4, 74, 47, 63, STR_VERY_HIGH, STR_VERY_HIGH}, // high money + { WWT_CLOSEBOX, 1, 4, 74, 67, 83, STR_VERY_HIGH, STR_VERY_HIGH}, // high money { WIDGETS_END }, }; @@ -69,7 +69,7 @@ static rct_widget window_cheats_guests_widgets[] = { { WWT_IMGBTN, 1, 0, WW - 1, 43, WH - 1, 0x0FFFFFFFF, 65535 }, // tab content panel { WWT_TAB, 1, 3, 33, 17, 43, 0x2000144E, 2462 }, // tab 1 { WWT_TAB, 1, 34, 64, 17, 43, 0x2000144E, 2462 }, // tab 2 - { WWT_CLOSEBOX, 1, 4, 74, 47, 63, STR_EXTREME, STR_EXTREME}, // happy guests + { WWT_CLOSEBOX, 1, 4, 74, 77, 93, STR_EXTREME, STR_EXTREME}, // happy guests { WIDGETS_END }, }; @@ -239,7 +239,7 @@ static void window_cheats_guests_mouseup() #endif rct_peep* peep; - uint16 sprite_idx; + uint16 spriteIndex; switch (widgetIndex) { case WIDX_CLOSE: @@ -250,14 +250,9 @@ static void window_cheats_guests_mouseup() window_cheats_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_HAPPY_GUESTS: - for (sprite_idx = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_PEEP, uint16); sprite_idx != SPRITE_INDEX_NULL; sprite_idx = peep->next) { - peep = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[sprite_idx].peep); - if (peep->type != PEEP_TYPE_GUEST) - continue; - if (peep->var_2A != 0) - continue; - peep->happiness = 255; - } + FOR_ALL_GUESTS(spriteIndex, peep) + if (peep->var_2A == 0) + peep->happiness = 255; window_invalidate_by_id(0x40 | WC_BOTTOM_TOOLBAR, 0); break; } @@ -322,6 +317,26 @@ static void window_cheats_paint() window_draw_widgets(w, dpi); window_cheats_draw_tab_images(dpi, w); + + if (w->page == WINDOW_CHEATS_PAGE_MONEY){ + char buffer[256]; + // Format text (name and version) + sprintf(buffer, "%c%c%s", FORMAT_MEDIUMFONT, FORMAT_BLACK, "Increases your money by 1,000."); + // Draw shadow + gfx_draw_string(dpi, buffer, 0, w->x + 4, w->y + 50); + } + else if (w->page == WINDOW_CHEATS_PAGE_GUESTS){ + char buffer[256]; + // Format text (name and version) + sprintf(buffer, "%c%c%s", FORMAT_MEDIUMFONT, FORMAT_BLACK, "Increases every peeps happiness "); + // Draw shadow + gfx_draw_string(dpi, buffer, 0, w->x + 4, w->y + 50); + // Format text (name and version) + sprintf(buffer, "%c%c%s", FORMAT_MEDIUMFONT, FORMAT_BLACK, "to max."); + // Draw shadow + gfx_draw_string(dpi, buffer, 0, w->x + 4, w->y + 60); + } + } static void window_cheats_draw_tab_images(rct_drawpixelinfo *dpi, rct_window *w) @@ -333,7 +348,7 @@ static void window_cheats_draw_tab_images(rct_drawpixelinfo *dpi, rct_window *w) sprite_idx = 5261; if (w->page == WINDOW_CHEATS_PAGE_MONEY) sprite_idx += (w->var_48E / 2) % 8; - gfx_draw_sprite(dpi, sprite_idx, w->x + w->widgets[WIDX_TAB_1].left, w->y + w->widgets[WIDX_TAB_1].top); + gfx_draw_sprite(dpi, sprite_idx, w->x + w->widgets[WIDX_TAB_1].left, w->y + w->widgets[WIDX_TAB_1].top); } // Guests tab diff --git a/src/window_finances.c b/src/window_finances.c index a2244518dd..335635385e 100644 --- a/src/window_finances.c +++ b/src/window_finances.c @@ -18,16 +18,409 @@ * along with this program. If not, see . *****************************************************************************/ -#include #include "addresses.h" -#include "game.h" +#include "string_ids.h" +#include "widget.h" #include "window.h" enum { - WINDOW_FINANCES_TAB_SUMMARY, - WINDOW_FINANCES_TAB_FINANCIAL_GRAPH, - WINDOW_FINANCES_TAB_VALUE_GRAPH, - WINDOW_FINANCES_TAB_PROFIT_GRAPH, - WINDOW_FINANCES_TAB_MARKETING, - WINDOW_FINANCES_TAB_RESEARCH -} WINDOW_FINANCIAL_TAB; \ No newline at end of file + WINDOW_FINANCES_PAGE_SUMMARY, + WINDOW_FINANCES_PAGE_FINANCIAL_GRAPH, + WINDOW_FINANCES_PAGE_VALUE_GRAPH, + WINDOW_FINANCES_PAGE_PROFIT_GRAPH, + WINDOW_FINANCES_PAGE_MARKETING, + WINDOW_FINANCES_PAGE_RESEARCH, + WINDOW_FINANCES_PAGE_COUNT +}; + +enum { + 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_LOAN = 10, + WIDX_LOAN_INCREASE, + WIDX_LOAN_DECREASE, + + WIDX_CAMPAIGN_1 = 12, + WIDX_CAMPAIGN_2, + WIDX_CAMPAIGN_3, + WIDX_CAMPAIGN_4, + WIDX_CAMPAIGN_5, + WIDX_CAMPAIGN_6, + + WIDX_RESEARCH_FUNDING = 11, + WIDX_RESEARCH_FUNDING_DROPDOWN_BUTTON, + WIDX_TRANSPORT_RIDES = 14, + WIDX_GENTLE_RIDES, + WIDX_ROLLER_COASTERS, + WIDX_THRILL_RIDES, + WIDX_WATER_RIDES, + WIDX_SHOPS_AND_STALLS, + WIDX_SCENERY_AND_THEMING, +}; + +#pragma region Widgets + +static rct_widget window_finances_summary_widgets[] = { + { WWT_FRAME, 0, 0, 529, 0, 256, 0xFFFFFFFF, STR_NONE }, + { WWT_CAPTION, 0, 1, 528, 1, 14, STR_FINANCIAL_SUMMARY, STR_WINDOW_TITLE_TIP }, + { WWT_CLOSEBOX, 0, 517, 527, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, + { WWT_RESIZE, 1, 0, 529, 43, 256, 0xFFFFFFFF, STR_NONE }, + { WWT_TAB, 1, 3, 33, 17, 43, 0x2000144E, STR_FINANCES_SHOW_SUMMARY_TAB_TIP }, + { WWT_TAB, 1, 34, 64, 17, 43, 0x2000144E, STR_FINANCES_SHOW_CASH_TAB_TIP }, + { WWT_TAB, 1, 65, 95, 17, 43, 0x2000144E, STR_FINANCES_SHOW_PARK_VALUE_TAB_TIP }, + { WWT_TAB, 1, 96, 126, 17, 43, 0x2000144E, STR_FINANCES_SHOW_WEEKLY_PROFIT_TAB_TIP }, + { WWT_TAB, 1, 127, 157, 17, 43, 0x2000144E, STR_FINANCES_SHOW_MARKETING_TAB_TIP }, + { WWT_TAB, 1, 158, 188, 17, 43, 0x2000144E, STR_FINANCES_RESEARCH }, + { WWT_SPINNER, 1, 64, 153, 229, 240, 1917, STR_NONE }, + { WWT_DROPDOWN_BUTTON, 1, 142, 152, 230, 234, STR_NUMERIC_UP, STR_NONE }, + { WWT_DROPDOWN_BUTTON, 1, 142, 152, 235, 239, STR_NUMERIC_DOWN, STR_NONE }, + { WIDGETS_END }, +}; + +static rct_widget window_finances_cash_widgets[] = { + { WWT_FRAME, 0, 0, 529, 0, 256, 0xFFFFFFFF, STR_NONE }, + { WWT_CAPTION, 0, 1, 528, 1, 14, STR_FINANCIAL_GRAPH, STR_WINDOW_TITLE_TIP }, + { WWT_CLOSEBOX, 0, 517, 527, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, + { WWT_RESIZE, 1, 0, 529, 43, 256, 0xFFFFFFFF, STR_NONE }, + { WWT_TAB, 1, 3, 33, 17, 43, 0x2000144E, STR_FINANCES_SHOW_SUMMARY_TAB_TIP }, + { WWT_TAB, 1, 34, 64, 17, 43, 0x2000144E, STR_FINANCES_SHOW_CASH_TAB_TIP }, + { WWT_TAB, 1, 65, 95, 17, 43, 0x2000144E, STR_FINANCES_SHOW_PARK_VALUE_TAB_TIP }, + { WWT_TAB, 1, 96, 126, 17, 43, 0x2000144E, STR_FINANCES_SHOW_WEEKLY_PROFIT_TAB_TIP }, + { WWT_TAB, 1, 127, 157, 17, 43, 0x2000144E, STR_FINANCES_SHOW_MARKETING_TAB_TIP }, + { WWT_TAB, 1, 158, 188, 17, 43, 0x2000144E, STR_FINANCES_RESEARCH }, + { WIDGETS_END }, +}; + +static rct_widget window_finances_park_value_widgets[] = { + { WWT_FRAME, 0, 0, 529, 0, 256, 0xFFFFFFFF, STR_NONE }, + { WWT_CAPTION, 0, 1, 528, 1, 14, STR_PARK_VALUE_GRAPH, STR_WINDOW_TITLE_TIP }, + { WWT_CLOSEBOX, 0, 517, 527, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, + { WWT_RESIZE, 1, 0, 529, 43, 256, 0xFFFFFFFF, STR_NONE }, + { WWT_TAB, 1, 3, 33, 17, 43, 0x2000144E, STR_FINANCES_SHOW_SUMMARY_TAB_TIP }, + { WWT_TAB, 1, 34, 64, 17, 43, 0x2000144E, STR_FINANCES_SHOW_CASH_TAB_TIP }, + { WWT_TAB, 1, 65, 95, 17, 43, 0x2000144E, STR_FINANCES_SHOW_PARK_VALUE_TAB_TIP }, + { WWT_TAB, 1, 96, 126, 17, 43, 0x2000144E, STR_FINANCES_SHOW_WEEKLY_PROFIT_TAB_TIP }, + { WWT_TAB, 1, 127, 157, 17, 43, 0x2000144E, STR_FINANCES_SHOW_MARKETING_TAB_TIP }, + { WWT_TAB, 1, 158, 188, 17, 43, 0x2000144E, STR_FINANCES_RESEARCH }, + { WIDGETS_END }, +}; + +static rct_widget window_finances_profit_widgets[] = { + { WWT_FRAME, 0, 0, 529, 0, 256, 0xFFFFFFFF, STR_NONE }, + { WWT_CAPTION, 0, 1, 528, 1, 14, STR_PROFIT_GRAPH, STR_WINDOW_TITLE_TIP }, + { WWT_CLOSEBOX, 0, 517, 527, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, + { WWT_RESIZE, 1, 0, 529, 43, 256, 0xFFFFFFFF, STR_NONE }, + { WWT_TAB, 1, 3, 33, 17, 43, 0x2000144E, STR_FINANCES_SHOW_SUMMARY_TAB_TIP }, + { WWT_TAB, 1, 34, 64, 17, 43, 0x2000144E, STR_FINANCES_SHOW_CASH_TAB_TIP }, + { WWT_TAB, 1, 65, 95, 17, 43, 0x2000144E, STR_FINANCES_SHOW_PARK_VALUE_TAB_TIP }, + { WWT_TAB, 1, 96, 126, 17, 43, 0x2000144E, STR_FINANCES_SHOW_WEEKLY_PROFIT_TAB_TIP }, + { WWT_TAB, 1, 127, 157, 17, 43, 0x2000144E, STR_FINANCES_SHOW_MARKETING_TAB_TIP }, + { WWT_TAB, 1, 158, 188, 17, 43, 0x2000144E, STR_FINANCES_RESEARCH }, + { WIDGETS_END }, +}; + +static rct_widget window_finances_marketing_widgets[] = { + { WWT_FRAME, 0, 0, 529, 0, 256, 0xFFFFFFFF, STR_NONE }, + { WWT_CAPTION, 0, 1, 528, 1, 14, STR_MARKETING, STR_WINDOW_TITLE_TIP }, + { WWT_CLOSEBOX, 0, 517, 527, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, + { WWT_RESIZE, 1, 0, 529, 43, 256, 0xFFFFFFFF, STR_NONE }, + { WWT_TAB, 1, 3, 33, 17, 43, 0x2000144E, STR_FINANCES_SHOW_SUMMARY_TAB_TIP }, + { WWT_TAB, 1, 34, 64, 17, 43, 0x2000144E, STR_FINANCES_SHOW_CASH_TAB_TIP }, + { WWT_TAB, 1, 65, 95, 17, 43, 0x2000144E, STR_FINANCES_SHOW_PARK_VALUE_TAB_TIP }, + { WWT_TAB, 1, 96, 126, 17, 43, 0x2000144E, STR_FINANCES_SHOW_WEEKLY_PROFIT_TAB_TIP }, + { WWT_TAB, 1, 127, 157, 17, 43, 0x2000144E, STR_FINANCES_SHOW_MARKETING_TAB_TIP }, + { WWT_TAB, 1, 158, 188, 17, 43, 0x2000144E, STR_FINANCES_RESEARCH }, + { WWT_GROUPBOX, 2, 3, 526, 47, 91, STR_MARKETING_CAMPAIGNS_IN_OPERATION, STR_NONE }, + { WWT_GROUPBOX, 2, 3, 526, 47, 252, STR_MARKETING_CAMPAIGNS_AVAILABLE, STR_NONE }, + { WWT_IMGBTN, 1, 8, 521, 0, 11, 0xFFFFFFFF, STR_START_THIS_MARKETING_CAMPAIGN }, + { WWT_IMGBTN, 1, 8, 521, 0, 11, 0xFFFFFFFF, STR_START_THIS_MARKETING_CAMPAIGN }, + { WWT_IMGBTN, 1, 8, 521, 0, 11, 0xFFFFFFFF, STR_START_THIS_MARKETING_CAMPAIGN }, + { WWT_IMGBTN, 1, 8, 521, 0, 11, 0xFFFFFFFF, STR_START_THIS_MARKETING_CAMPAIGN }, + { WWT_IMGBTN, 1, 8, 521, 0, 11, 0xFFFFFFFF, STR_START_THIS_MARKETING_CAMPAIGN }, + { WWT_IMGBTN, 1, 8, 521, 0, 11, 0xFFFFFFFF, STR_START_THIS_MARKETING_CAMPAIGN }, + { WIDGETS_END }, +}; + +static rct_widget window_finances_research_widgets[] = { + { WWT_FRAME, 0, 0, 529, 0, 256, 0xFFFFFFFF, STR_NONE }, + { WWT_CAPTION, 0, 1, 528, 1, 14, STR_RESEARCH_FUNDING, STR_WINDOW_TITLE_TIP }, + { WWT_CLOSEBOX, 0, 517, 527, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, + { WWT_RESIZE, 1, 0, 529, 43, 256, 0xFFFFFFFF, STR_NONE }, + { WWT_TAB, 1, 3, 33, 17, 43, 0x2000144E, STR_FINANCES_SHOW_SUMMARY_TAB_TIP }, + { WWT_TAB, 1, 34, 64, 17, 43, 0x2000144E, STR_FINANCES_SHOW_CASH_TAB_TIP }, + { WWT_TAB, 1, 65, 95, 17, 43, 0x2000144E, STR_FINANCES_SHOW_PARK_VALUE_TAB_TIP }, + { WWT_TAB, 1, 96, 126, 17, 43, 0x2000144E, STR_FINANCES_SHOW_WEEKLY_PROFIT_TAB_TIP }, + { WWT_TAB, 1, 127, 157, 17, 43, 0x2000144E, STR_FINANCES_SHOW_MARKETING_TAB_TIP }, + { WWT_TAB, 1, 158, 188, 17, 43, 0x2000144E, STR_FINANCES_RESEARCH }, + { WWT_GROUPBOX, 2, 3, 316, 47, 91, STR_RESEARCH_FUNDING_, STR_NONE }, + { WWT_DROPDOWN, 2, 8, 167, 59, 70, 0xFFFFFFFF, STR_SELECT_LEVEL_OF_RESEARCH_AND_DEVELOPMENT }, + { WWT_DROPDOWN_BUTTON, 2, 156, 166, 60, 69, 876, STR_SELECT_LEVEL_OF_RESEARCH_AND_DEVELOPMENT }, + { WWT_GROUPBOX, 2, 3, 316, 96, 202, STR_RESEARCH_PRIORITIES, STR_NONE }, + { WWT_CHECKBOX, 2, 8, 311, 108, 119, STR_RESEARCH_TRANSPORT_RIDES, STR_RESEARCH_NEW_TRANSPORT_RIDES }, + { WWT_CHECKBOX, 2, 8, 311, 121, 132, STR_RESEARCH_GENTLE_RIDES, STR_RESEARCH_NEW_GENTLE_RIDES }, + { WWT_CHECKBOX, 2, 8, 311, 134, 145, STR_RESEARCH_ROLLER_COASTERS, STR_RESEARCH_NEW_ROLLER_COASTERS }, + { WWT_CHECKBOX, 2, 8, 311, 147, 158, STR_RESEARCH_THRILL_RIDES, STR_RESEARCH_NEW_THRILL_RIDES }, + { WWT_CHECKBOX, 2, 8, 311, 160, 171, STR_RESEARCH_WATER_RIDES, STR_RESEARCH_NEW_WATER_RIDES }, + { WWT_CHECKBOX, 2, 8, 311, 173, 184, STR_RESEARCH_SHOPS_AND_STALLS, STR_RESEARCH_NEW_SHOPS_AND_STALLS }, + { WWT_CHECKBOX, 2, 8, 311, 186, 197, STR_RESEARCH_SCENERY_AND_THEMING, STR_RESEARCH_NEW_SCENERY_AND_THEMING }, + { WIDGETS_END }, +}; + +static rct_widget *window_finances_page_widgets[] = { + window_finances_summary_widgets, + window_finances_cash_widgets, + window_finances_park_value_widgets, + window_finances_profit_widgets, + window_finances_marketing_widgets, + window_finances_research_widgets +}; + +#pragma endregion + +#pragma region Events + +static void* window_finances_page_events[] = { + (void*)0x00988EB8, + (void*)0x00988F28, + (void*)0x00988F98, + (void*)0x00989008, + (void*)0x00989078, + (void*)0x009890E8 +}; + +#pragma endregion + +#pragma region Enabled widgets + +static uint32 window_finances_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_LOAN_INCREASE) | + (1 << WIDX_LOAN_DECREASE), + + (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), + + (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_CAMPAIGN_1) | + (1 << WIDX_CAMPAIGN_2) | + (1 << WIDX_CAMPAIGN_3) | + (1 << WIDX_CAMPAIGN_4) | + (1 << WIDX_CAMPAIGN_5) | + (1 << WIDX_CAMPAIGN_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_RESEARCH_FUNDING) | + (1 << WIDX_RESEARCH_FUNDING_DROPDOWN_BUTTON) | + (1 << WIDX_TRANSPORT_RIDES) | + (1 << WIDX_GENTLE_RIDES) | + (1 << WIDX_ROLLER_COASTERS) | + (1 << WIDX_THRILL_RIDES) | + (1 << WIDX_WATER_RIDES) | + (1 << WIDX_SHOPS_AND_STALLS) | + (1 << WIDX_SCENERY_AND_THEMING) +}; + +#pragma endregion + +const int window_finances_tab_animation_loops[] = { 16, 32, 32, 32, 38, 16 }; + +static void window_finances_set_page(rct_window *w, int page); +static void window_finances_set_pressed_tab(rct_window *w); + +/** + * + * rct2: 0x0069DDF1 + */ +void window_finances_open() +{ + rct_window *w; + + w = window_bring_to_front_by_id(WC_FINANCES, 0); + if (w == NULL) { + w = window_create_auto_pos(530, 257, window_finances_page_events[0], WC_FINANCES, WF_10); + w->widgets = window_finances_page_widgets[0]; + w->enabled_widgets = 0x1BF4; + w->number = 0; + w->page = 0; + w->var_48E = 0; + w->disabled_widgets = 0; + w->colours[0] = 1; + w->colours[1] = 19; + w->colours[2] = 19; + RCT2_CALLPROC_EBPSAFE(0x00684BAE); + } + + w->page = 0; + window_invalidate(w); + w->width = 530; + w->height = 257; + window_invalidate(w); + + w->widgets = window_finances_page_widgets[0]; + w->enabled_widgets = window_finances_page_enabled_widgets[0]; + w->var_020 = RCT2_GLOBAL(0x00988E3C, uint32); + w->event_handlers = window_finances_page_events[0]; + w->pressed_widgets = 0; + w->disabled_widgets = 0; + window_init_scroll_widgets(w); +} + +#pragma region Summary page + +/** + * + * rct2: 0x0069CA99 + */ +static void window_finances_summary_mouseup() +{ + short widgetIndex; + rct_window *w; + + #ifdef _MSC_VER + __asm mov widgetIndex, dx + #else + __asm__ ( "mov %[widgetIndex], dx " : [widgetIndex] "+m" (widgetIndex) ); + #endif + + #ifdef _MSC_VER + __asm mov w, esi + #else + __asm__ ( "mov %[w], esi " : [w] "+m" (w) ); + #endif + + if (widgetIndex == WIDX_CLOSE) + window_close(w); + else if (widgetIndex >= WIDX_TAB_1 && widgetIndex <= WIDX_TAB_6) + window_finances_set_page(w, widgetIndex - WIDX_TAB_1); +} + +/** + * + * rct2: 0x0069CBA6 + */ +static void window_finances_summary_update(rct_window *w) +{ + // Tab animation + if (++w->var_48E >= window_finances_tab_animation_loops[w->page]) + w->var_48E = 0; + widget_invalidate(w->classification, w->number, WIDX_TAB_1); +} + +/** + * + * rct2: 0x0069C732 + */ +static void window_finances_summary_invalidate() +{ + rct_window *w; + + #ifdef _MSC_VER + __asm mov w, esi + #else + __asm__ ( "mov %[w], esi " : [w] "+m" (w) ); + #endif + + if (w->widgets != window_finances_page_widgets[0]) { + w->widgets = window_finances_page_widgets[0]; + window_init_scroll_widgets(w); + } + + window_finances_set_pressed_tab(w); + RCT2_GLOBAL(0x013CE952 + 6, money32) = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_LOAN, money32); +} + +#pragma endregion + +#pragma region Common + +/** + * + * rct2: 0x0069CAC5 + */ +static void window_finances_set_page(rct_window *w, int page) +{ + w->page = page; + w->var_48E = 0; + if (w->viewport != NULL) { + w->viewport->width = 0; + w->viewport = NULL; + } + + w->enabled_widgets = window_finances_page_enabled_widgets[page]; + w->var_020 = RCT2_ADDRESS(0x00988E3C, uint32)[page]; + w->widgets = window_finances_page_widgets[page]; + w->disabled_widgets = 0; + + window_invalidate(w); + if (w->page == WINDOW_FINANCES_PAGE_RESEARCH) { + w->width = 320; + w->height = 207; + } else { + w->width = 530; + w->height = 257; + } + 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); +} + +static void window_finances_set_pressed_tab(rct_window *w) +{ + int i; + for (i = 0; i < WINDOW_FINANCES_PAGE_COUNT; i++) + w->pressed_widgets &= ~(1 << (WIDX_TAB_1 + i)); + w->pressed_widgets |= 1LL << (WIDX_TAB_1 + w->page); +} + +#pragma endregion \ No newline at end of file diff --git a/src/window_footpath.c b/src/window_footpath.c index be584cde20..ab2b8c979a 100644 --- a/src/window_footpath.c +++ b/src/window_footpath.c @@ -111,7 +111,7 @@ static rct_widget window_footpath_widgets[] = { static void window_footpath_emptysub() { } static void window_footpath_close(); static void window_footpath_mouseup(); -static void window_footpath_mousedown(); +static void window_footpath_mousedown(int widgetIndex, rct_window*w, rct_widget* widget); static void window_footpath_dropdown(); static void window_footpath_update(rct_window *w); static void window_footpath_toolupdate(); @@ -307,31 +307,8 @@ static void window_footpath_mouseup() * * rct2: 0x006A7EC5 */ -static void window_footpath_mousedown() +static void window_footpath_mousedown(int widgetIndex, rct_window*w, rct_widget* widget) { - short widgetIndex; - rct_window *w; - rct_widget *widget; - - #ifdef _MSC_VER - __asm mov widgetIndex, dx - #else - __asm__ ( "mov %[widgetIndex], dx " : [widgetIndex] "+m" (widgetIndex) ); - #endif - - #ifdef _MSC_VER - __asm mov w, esi - #else - __asm__ ( "mov %[w], esi " : [w] "+m" (w) ); - #endif - - #ifdef _MSC_VER - __asm mov widget, edi - #else - __asm__ ( "mov %[widget], edi " : [widget] "+m" (widget) ); - #endif - - switch (widgetIndex) { case WIDX_FOOTPATH_TYPE: window_footpath_show_footpath_types_dialog(w, widget, 0); @@ -894,7 +871,7 @@ static void window_footpath_place_path_at_point(int x, int y) // bp = 0x009DEA62 // dx = 0x009DEA60 // cx = 0x009DEA5E - sound_play_panned(6, 0x8001); + sound_play_panned(SOUND_PLACE_ITEM, 0x8001); } } diff --git a/src/window_game_bottom_toolbar.c b/src/window_game_bottom_toolbar.c index 3966c3904f..1d4b4ddf51 100644 --- a/src/window_game_bottom_toolbar.c +++ b/src/window_game_bottom_toolbar.c @@ -172,7 +172,7 @@ static void window_game_bottom_toolbar_mouseup() case WIDX_LEFT_OUTSET: case WIDX_MONEY: if (!(RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & 0x800)) - RCT2_CALLPROC_EBPSAFE(0x0069DDF1); + window_finances_open(); break; case WIDX_GUESTS: window_park_guests_open(); @@ -498,10 +498,20 @@ static void window_game_bottom_toolbar_draw_right_panel(rct_drawpixelinfo *dpi, y = window_game_bottom_toolbar_widgets[WIDX_RIGHT_OUTSET].top + w->y + 2; // Date - RCT2_GLOBAL(0x013CE952, short) = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, sint16); + char *freeStr = (char*)0x009BC677; + freeStr[0] = FORMAT_STRINGID; + freeStr[1] = ' '; + freeStr[2] = FORMAT_MONTHYEAR; + freeStr[3] = 0; + + int month = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, sint16) & 7; + int day = ((RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_TICKS, uint16) * days_in_month[month]) >> 16) & 0xFF; + + RCT2_GLOBAL(0x013CE952, short) = STR_DATE_DAY_1 + day; + RCT2_GLOBAL(0x013CE954, short) = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, sint16); gfx_draw_string_centred( dpi, - 1845, + 3165, x, y, (RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_OVER_WINDOWCLASS, rct_windowclass) == 2 && RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_OVER_WIDGETINDEX, sint32) == WIDX_DATE ? 2 : w->colours[0] & 0x7F), diff --git a/src/window_game_top_toolbar.c b/src/window_game_top_toolbar.c index 5346a67b6f..8a78975390 100644 --- a/src/window_game_top_toolbar.c +++ b/src/window_game_top_toolbar.c @@ -78,7 +78,7 @@ static rct_widget window_game_top_toolbar_widgets[] = { static void window_game_top_toolbar_emptysub() { } static void window_game_top_toolbar_mouseup(); -static void window_game_top_toolbar_mousedown(); +static void window_game_top_toolbar_mousedown(int widgetIndex, rct_window*w, rct_widget* widget); static void window_game_top_toolbar_dropdown(); static void window_game_top_toolbar_invalidate(); static void window_game_top_toolbar_paint(); @@ -166,6 +166,11 @@ static void window_game_top_toolbar_mouseup() game_do_command(0, 1, 0, 0, 2, 0, 0); break; case WIDX_FASTFORWARD: + // This is an excellent place to add in debugging statements and + // print routines, that will be triggered when you press the + // button in the game. Use "git update-index --skip-worktree + // src/window_game_top_toolbar" to avoid committing these changes to + // version control. window_cheats_open(); break; @@ -254,32 +259,10 @@ static void window_game_top_toolbar_mouseup() * * rct2: 0x0066CA3B */ -static void window_game_top_toolbar_mousedown() +static void window_game_top_toolbar_mousedown(int widgetIndex, rct_window*w, rct_widget* widget) { - short widgetIndex; - rct_window *w; - rct_widget *widget; rct_viewport *mainViewport; - #ifdef _MSC_VER - __asm mov widgetIndex, dx - #else - __asm__ ( "mov %[widgetIndex], dx " : [widgetIndex] "+m" (widgetIndex) ); - #endif - - #ifdef _MSC_VER - __asm mov w, esi - #else - __asm__ ( "mov %[w], esi " : [w] "+m" (w) ); - #endif - - #ifdef _MSC_VER - __asm mov widget, edi - #else - __asm__ ( "mov %[widget], edi " : [widget] "+m" (widget) ); - #endif - - if (widgetIndex == WIDX_FILE_MENU) { gDropdownItemsFormat[0] = 882; gDropdownItemsFormat[1] = 883; @@ -384,30 +367,7 @@ static void window_game_top_toolbar_dropdown() break; case 1: // save game tool_cancel(); - { - int eax, ebx, ecx, edx, esi, edi, ebp; - RCT2_CALLFUNC_X(0x006750E9, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); - if (eax == 0) { - gfx_invalidate_screen(); - break; - } - - char *src = (char*)0x0141EF67; - do { - src++; - } while (*src != '.' && *src != '\0'); - strcpy(src, ".SV6"); - strcpy((char*) RCT2_ADDRESS_SAVED_GAMES_PATH_2, (char*) 0x0141EF68); - - eax = 0; - if (RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) & 8) - eax |= 1; - RCT2_CALLPROC_X(0x006754F5, eax, 0, 0, 0, 0, 0, 0); - // check success? - - game_do_command(0, 1047, 0, -1, 0, 0, 0); - gfx_invalidate_screen(); - } + save_game(); break; case 3: // about window_about_open(); diff --git a/src/window_guest_list.c b/src/window_guest_list.c index d235cab756..14fe8d13a7 100644 --- a/src/window_guest_list.c +++ b/src/window_guest_list.c @@ -73,7 +73,7 @@ static rct_widget window_guest_list_widgets[] = { static void window_guest_list_emptysub() { } static void window_guest_list_mouseup(); static void window_guest_list_resize(); -static void window_guest_list_mousedown(); +static void window_guest_list_mousedown(int widgetIndex, rct_window*w, rct_widget* widget); static void window_guest_list_dropdown(); static void window_guest_list_update(rct_window *w); static void window_guest_list_scrollgetsize(); @@ -241,32 +241,9 @@ static void window_guest_list_resize() * * rct2: 0x00699AC4 */ -static void window_guest_list_mousedown() +static void window_guest_list_mousedown(int widgetIndex, rct_window*w, rct_widget* widget) { int i; - short widgetIndex; - rct_window *w; - rct_widget *widget; - - #ifdef _MSC_VER - __asm mov widgetIndex, dx - #else - __asm__ ( "mov %[widgetIndex], dx " : [widgetIndex] "+m" (widgetIndex) ); - #endif - - #ifdef _MSC_VER - __asm mov w, esi - #else - __asm__ ( "mov %[w], esi " : [w] "+m" (w) ); - #endif - - #ifdef _MSC_VER - __asm mov widget, edi - #else - __asm__ ( "mov %[widget], edi " : [widget] "+m" (widget) ); - #endif - - switch (widgetIndex) { case WIDX_TAB_1: case WIDX_TAB_2: @@ -388,7 +365,7 @@ static void window_guest_list_update(rct_window *w) */ static void window_guest_list_scrollgetsize() { - int i, y, numGuests, spriteIdx; + int i, y, numGuests, spriteIndex; rct_window *w; rct_peep *peep; @@ -404,13 +381,7 @@ static void window_guest_list_scrollgetsize() // Count the number of guests numGuests = 0; - spriteIdx = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_PEEP, uint16); - while (spriteIdx != SPRITE_INDEX_NULL) { - peep = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[spriteIdx].peep); - spriteIdx = peep->next; - - if (peep->type != PEEP_TYPE_GUEST) - continue; + FOR_ALL_GUESTS(spriteIndex, peep) { if (peep->var_2A != 0) continue; if (_window_guest_list_selected_filter != -1) @@ -470,7 +441,7 @@ static void window_guest_list_scrollgetsize() */ static void window_guest_list_scrollmousedown() { - int i, spriteIdx; + int i, spriteIndex; short y; rct_window *w; rct_peep *peep; @@ -492,13 +463,7 @@ static void window_guest_list_scrollmousedown() case PAGE_INDIVIDUAL: i = y / 10; i += _window_guest_list_selected_page * 3173; - spriteIdx = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_PEEP, uint16); - while (spriteIdx != SPRITE_INDEX_NULL) { - peep = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[spriteIdx].peep); - spriteIdx = peep->next; - - if (peep->type != PEEP_TYPE_GUEST) - continue; + FOR_ALL_GUESTS(spriteIndex, peep) { if (peep->var_2A != 0) continue; if (_window_guest_list_selected_filter != -1) @@ -683,7 +648,7 @@ static void window_guest_list_paint() static void window_guest_list_scrollpaint() { int eax, ebx, ecx, edx, esi, edi, ebp; - int spriteIdx, format, numGuests, i, j, y; + int spriteIndex, format, numGuests, i, j, y; rct_window *w; rct_drawpixelinfo *dpi; rct_peep *peep; @@ -711,13 +676,7 @@ static void window_guest_list_scrollpaint() y = _window_guest_list_selected_page * -0x7BF2; // For each guest - spriteIdx = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_PEEP, uint16); - while (spriteIdx != SPRITE_INDEX_NULL) { - peep = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[spriteIdx].peep); - spriteIdx = peep->next; - - if (peep->type != PEEP_TYPE_GUEST) - continue; + FOR_ALL_GUESTS(spriteIndex, peep) { peep->var_0C &= ~0x200; if (peep->var_2A != 0) continue; @@ -772,9 +731,9 @@ static void window_guest_list_scrollpaint() thought = &peep->thoughts[j]; if (thought->type == PEEP_THOUGHT_TYPE_NONE) break; - if (thought->pad_3 == 0) + if (thought->var_2 == 0) continue; - if (thought->pad_3 > 5) + if (thought->var_2 > 5) break; ebx = thought->type; @@ -878,7 +837,7 @@ static int sub_69B7EA(rct_peep *peep, int *outEAX) *outEAX = eax; return ebx & 0xFFFF; case VIEW_THOUGHTS: - if (peep->thoughts[0].pad_3 <= 5) { + if (peep->thoughts[0].var_2 <= 5) { eax = peep->thoughts[0].item; ebx = peep->thoughts[0].type; if (peep->thoughts[0].type != PEEP_THOUGHT_TYPE_NONE) { @@ -907,7 +866,7 @@ static int sub_69B7EA(rct_peep *peep, int *outEAX) */ static void window_guest_list_find_groups() { - int spriteIdx, spriteIdx2, groupIndex, faceIndex; + int spriteIndex, spriteIndex2, groupIndex, faceIndex; rct_peep *peep, *peep2; int eax = RCT2_GLOBAL(0x00F663AC, uint32) & 0xFFFFFF00; @@ -921,24 +880,13 @@ static void window_guest_list_find_groups() _window_guest_list_num_groups = 0; // Set all guests to unassigned - spriteIdx = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_PEEP, uint16); - while (spriteIdx != SPRITE_INDEX_NULL) { - peep = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[spriteIdx].peep); - spriteIdx = peep->next; - - if (peep->type != PEEP_TYPE_GUEST || peep->var_2A != 0) - continue; - - peep->var_0C |= (1 << 8); - } + FOR_ALL_GUESTS(spriteIndex, peep) + if (peep->var_2A == 0) + peep->var_0C |= (1 << 8); // For each guest / group - spriteIdx = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_PEEP, uint16); - while (spriteIdx != SPRITE_INDEX_NULL) { - peep = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[spriteIdx].peep); - spriteIdx = peep->next; - - if (peep->type != PEEP_TYPE_GUEST || peep->var_2A != 0 || !(peep->var_0C & (1 << 8))) + FOR_ALL_GUESTS(spriteIndex, peep) { + if (peep->var_2A != 0 || !(peep->var_0C & (1 << 8))) continue; // New group, cap at 240 though @@ -961,12 +909,8 @@ static void window_guest_list_find_groups() _window_guest_list_groups_guest_faces[faceIndex++] = get_guest_face_sprite_small(peep) - 5486; // Find more peeps that belong to same group - spriteIdx2 = peep->next; - while (spriteIdx2 != SPRITE_INDEX_NULL) { - peep2 = &(RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite)[spriteIdx2].peep); - spriteIdx2 = peep2->next; - - if (peep2->type != PEEP_TYPE_GUEST || peep2->var_2A != 0 || !(peep2->var_0C & (1 << 8))) + FOR_ALL_GUESTS(spriteIndex2, peep2) { + if (peep2->var_2A != 0 || !(peep2->var_0C & (1 << 8))) continue; // Get and check if in same group diff --git a/src/window_land.c b/src/window_land.c index 0e092a771b..823c2f3011 100644 --- a/src/window_land.c +++ b/src/window_land.c @@ -52,7 +52,7 @@ static rct_widget window_land_widgets[] = { static void window_land_emptysub() { } static void window_land_close(); static void window_land_mouseup(); -static void window_land_mousedown(); +static void window_land_mousedown(int widgetIndex, rct_window*w, rct_widget* widget); static void window_land_dropdown(); static void window_land_update(rct_window *w); static void window_land_invalidate(); @@ -217,31 +217,9 @@ static void window_land_mouseup() * * rct2: 0x0066407B */ -static void window_land_mousedown() +static void window_land_mousedown(int widgetIndex, rct_window*w, rct_widget* widget) { int i; - short widgetIndex; - rct_window *w; - rct_widget *widget; - - #ifdef _MSC_VER - __asm mov widgetIndex, dx - #else - __asm__ ( "mov %[widgetIndex], dx " : [widgetIndex] "+m" (widgetIndex) ); - #endif - - #ifdef _MSC_VER - __asm mov w, esi - #else - __asm__ ( "mov %[w], esi " : [w] "+m" (w) ); - #endif - - #ifdef _MSC_VER - __asm mov widget, edi - #else - __asm__ ( "mov %[widget], edi " : [widget] "+m" (widget) ); - #endif - switch (widgetIndex) { case WIDX_FLOOR: diff --git a/src/window_map.c b/src/window_map.c index 4c14fb8dd2..84cfd99af1 100644 --- a/src/window_map.c +++ b/src/window_map.c @@ -77,7 +77,7 @@ static rct_widget window_map_widgets[] = { static void window_map_emptysub() { } static void window_map_close(); static void window_map_mouseup(); -static void window_map_mousedown(); +static void window_map_mousedown(int widgetIndex, rct_window*w, rct_widget* widget); static void window_map_update(rct_window *w); static void window_map_scrollgetsize(); static void window_map_scrollmousedown(); @@ -228,7 +228,7 @@ static void window_map_mouseup() * * rct2: 0x0068D040 */ -static void window_map_mousedown() +static void window_map_mousedown(int widgetIndex, rct_window*w, rct_widget* widget) { RCT2_CALLPROC_EBPSAFE(0x0068D040); } diff --git a/src/window_news.c b/src/window_news.c index 4cf448c5ca..3d2c1347ec 100644 --- a/src/window_news.c +++ b/src/window_news.c @@ -161,7 +161,7 @@ static void window_news_update(rct_window *w) return; window_invalidate(w); - sound_play_panned(5, w->x + (w->width / 2)); + sound_play_panned(SOUND_CLICK_2, w->x + (w->width / 2)); newsItems = RCT2_ADDRESS(RCT2_ADDRESS_NEWS_ITEM_LIST, rct_news_item); j = w->var_480; @@ -282,7 +282,7 @@ static void window_news_scrollmousedown() w->var_482 = buttonIndex; w->var_484 = 4; window_invalidate(w); - sound_play_panned(4, w->x + (w->width / 2)); + sound_play_panned(SOUND_CLICK_1, w->x + (w->width / 2)); } } diff --git a/src/window_options.c b/src/window_options.c index fbb9897940..d370b185c5 100644 --- a/src/window_options.c +++ b/src/window_options.c @@ -28,6 +28,8 @@ #include "window.h" #include "window_dropdown.h" +#include + enum WINDOW_OPTIONS_WIDGET_IDX { WIDX_BACKGROUND, WIDX_TITLE, @@ -103,7 +105,7 @@ static rct_widget window_options_widgets[] = { static void window_options_emptysub() { } static void window_options_mouseup(); -static void window_options_mousedown(); +static void window_options_mousedown(int widgetIndex, rct_window*w, rct_widget* widget); static void window_options_dropdown(); static void window_options_update(rct_window *w); static void window_options_paint(); @@ -221,6 +223,7 @@ static void window_options_mouseup() break; case WIDX_SCREEN_EDGE_SCROLLING: RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_EDGE_SCROLLING, uint8) ^= 1; + gGeneral_config.edge_scrolling ^= 1; config_save(); window_invalidate(w); break; @@ -246,11 +249,15 @@ static void window_options_mouseup() break; case WIDX_TILE_SMOOTHING_CHECKBOX: RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) ^= CONFIG_FLAG_DISABLE_SMOOTH_LANDSCAPE; + gGeneral_config.landscape_smoothing = !(RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) + & CONFIG_FLAG_DISABLE_SMOOTH_LANDSCAPE); config_save(); gfx_invalidate_screen(); break; case WIDX_GRIDLINES_CHECKBOX: RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) ^= CONFIG_FLAG_ALWAYS_SHOW_GRIDLINES; + gGeneral_config.always_show_gridlines = RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) + & CONFIG_FLAG_ALWAYS_SHOW_GRIDLINES; config_save(); gfx_invalidate_screen(); @@ -263,12 +270,15 @@ static void window_options_mouseup() break; case WIDX_SAVE_PLUGIN_DATA_CHECKBOX: RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) ^= CONFIG_FLAG_SAVE_PLUGIN_DATA; + gGeneral_config.save_plugin_data = !(RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) + & CONFIG_FLAG_SAVE_PLUGIN_DATA); config_save(); window_invalidate(w); break; case WIDX_SOUND_SW_BUFFER_CHECKBOX: pause_sounds(); RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_SOUND_SW_BUFFER, uint8) ^= 1; + gSound_config.forced_software_buffering ^= 1; config_save(); unpause_sounds(); window_invalidate(w); @@ -280,25 +290,9 @@ static void window_options_mouseup() * * rct2: 0x006BB01B */ -static void window_options_mousedown() +static void window_options_mousedown(int widgetIndex, rct_window*w, rct_widget* widget) { int num_items, i; - short widgetIndex; - rct_window *w; - rct_widget *widget; - - #ifdef _MSC_VER - __asm mov widgetIndex, dx - #else - __asm__ ( "mov %[widgetIndex], dx " : [widgetIndex] "+m" (widgetIndex) ); - #endif - - #ifdef _MSC_VER - __asm mov w, esi - #else - __asm__ ( "mov %[w], esi " : [w] "+m" (w) ); - #endif - widget = &w->widgets[widgetIndex - 1]; @@ -309,7 +303,7 @@ static void window_options_mousedown() // populate the list with the sound devices for (i = 0; i < gAudioDeviceCount; i++) { gDropdownItemsFormat[i] = 1142; - gDropdownItemsArgs[i] = 1170 | ((uint64)gAudioDevices[i].name << 16); + gDropdownItemsArgs[i] = 1170 | ((uint64)(intptr_t)gAudioDevices[i].name << 16); } gDropdownItemsChecked |= (1 << RCT2_GLOBAL(0x9AF280, uint32)); break; @@ -443,9 +437,12 @@ static void window_options_dropdown() case WIDX_HEIGHT_LABELS_DROPDOWN: // reset flag and set it to 1 if height as units is selected RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) &= ~CONFIG_FLAG_SHOW_HEIGHT_AS_UNITS; + gGeneral_config.show_height_as_units = 0; - if (dropdownIndex == 0) - RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) |= CONFIG_FLAG_SHOW_HEIGHT_AS_UNITS; + if (dropdownIndex == 0) { + RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_FLAGS, uint8) |= CONFIG_FLAG_SHOW_HEIGHT_AS_UNITS; + gGeneral_config.show_height_as_units = 1; + } window_options_update_height_markers(); break; @@ -461,17 +458,20 @@ static void window_options_dropdown() // TODO: no clue what this does (and if it's correct) RCT2_GLOBAL(0x009AAC75, uint8) = RCT2_GLOBAL(0x009AF601 + dropdownIndex, uint8); RCT2_GLOBAL(0x009AAC76, uint8) = RCT2_GLOBAL(0x009AF604 + dropdownIndex, uint8); - + gSound_config.sound_quality = (sint8)dropdownIndex; config_save(); window_invalidate(w); break; case WIDX_CURRENCY_DROPDOWN: RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_CURRENCY, uint8) = dropdownIndex | 0xC0; + gGeneral_config.currency_format = (sint8)dropdownIndex; config_save(); gfx_invalidate_screen(); break; case WIDX_DISTANCE_DROPDOWN: RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_METRIC, uint8) = (uint8)dropdownIndex; + gGeneral_config.measurement_format = (sint8)dropdownIndex; + config_save(); window_options_update_height_markers(); break; case WIDX_RESOLUTION_DROPDOWN: @@ -486,6 +486,7 @@ static void window_options_dropdown() case WIDX_TEMPERATURE_DROPDOWN: if (dropdownIndex != RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_TEMPERATURE, uint8)) { RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_TEMPERATURE, uint8) = (uint8)dropdownIndex; + gGeneral_config.temperature_format = (sint8)dropdownIndex; config_save(); gfx_invalidate_screen(); } @@ -676,4 +677,4 @@ static void window_options_update_height_markers() config_save(); gfx_invalidate_screen(); -} \ No newline at end of file +} diff --git a/src/window_park.c b/src/window_park.c index a87c9d0ee4..d47358c7fd 100644 --- a/src/window_park.c +++ b/src/window_park.c @@ -20,6 +20,7 @@ #include #include "addresses.h" +#include "award.h" #include "config.h" #include "date.h" #include "game.h" @@ -217,7 +218,7 @@ static void window_park_emptysub() { } static void window_park_entrance_close(); static void window_park_entrance_mouseup(); static void window_park_entrance_resize(); -static void window_park_entrance_mousedown(); +static void window_park_entrance_mousedown(int widgetIndex, rct_window*w, rct_widget* widget); static void window_park_entrance_dropdown(); static void window_park_entrance_update(rct_window *w); static void window_park_entrance_toolupdate(); @@ -242,7 +243,7 @@ static void window_park_guests_paint(); static void window_park_price_mouseup(); static void window_park_price_resize(); -static void window_park_price_mousedown(); +static void window_park_price_mousedown(int widgetIndex, rct_window*w, rct_widget* widget); static void window_park_price_update(rct_window *w); static void window_park_price_invalidate(); static void window_park_price_paint(); @@ -730,31 +731,8 @@ static void window_park_entrance_resize() * * rct2: 0x006681BF */ -static void window_park_entrance_mousedown() +static void window_park_entrance_mousedown(int widgetIndex, rct_window*w, rct_widget* widget) { - short widgetIndex; - rct_window *w; - rct_widget *widget; - - #ifdef _MSC_VER - __asm mov widgetIndex, dx - #else - __asm__ ( "mov %[widgetIndex], dx " : [widgetIndex] "+m" (widgetIndex) ); - #endif - - #ifdef _MSC_VER - __asm mov w, esi - #else - __asm__ ( "mov %[w], esi " : [w] "+m" (w) ); - #endif - - #ifdef _MSC_VER - __asm mov widget, edi - #else - __asm__ ( "mov %[widget], edi " : [widget] "+m" (widget) ); - #endif - - if (widgetIndex == WIDX_OPEN_OR_CLOSE) { gDropdownItemsFormat[0] = 1142; gDropdownItemsFormat[1] = 1142; @@ -1616,24 +1594,9 @@ static void window_park_price_resize() * * rct2: 0x0066902C */ -static void window_park_price_mousedown() +static void window_park_price_mousedown(int widgetIndex, rct_window*w, rct_widget* widget) { int newFee; - short widgetIndex; - rct_window *w; - - #ifdef _MSC_VER - __asm mov widgetIndex, dx - #else - __asm__ ( "mov %[widgetIndex], dx " : [widgetIndex] "+m" (widgetIndex) ); - #endif - - #ifdef _MSC_VER - __asm mov w, esi - #else - __asm__ ( "mov %[w], esi " : [w] "+m" (w) ); - #endif - switch (widgetIndex) { case WIDX_CLOSE: @@ -2258,13 +2221,13 @@ static void window_park_awards_paint() y = w->y + window_park_awards_widgets[WIDX_PAGE_BACKGROUND].top + 4; count = 0; - for (i = 0; i < 4; i++) { + for (i = 0; i < MAX_AWARDS; i++) { award = &RCT2_ADDRESS(RCT2_ADDRESS_AWARD_LIST, rct_award)[i]; if (award->time == 0) continue; gfx_draw_sprite(dpi, SPR_AWARD_MOST_UNTIDY + award->type, x, y); - gfx_draw_string_left_wrapped(dpi, (void*)STR_AWARD_MOST_UNTIDY, x + 34, y + 6, 180, 0, 0); + gfx_draw_string_left_wrapped(dpi, NULL, x + 34, y + 6, 180, STR_AWARD_MOST_UNTIDY + award->type, 0); y += 32; count++; diff --git a/src/window_ride_list.c b/src/window_ride_list.c index b365ee8eec..8dff8e2433 100644 --- a/src/window_ride_list.c +++ b/src/window_ride_list.c @@ -68,7 +68,7 @@ static rct_widget window_ride_list_widgets[] = { static void window_ride_list_emptysub() { } static void window_ride_list_mouseup(); static void window_ride_list_resize(); -static void window_ride_list_mousedown(); +static void window_ride_list_mousedown(int widgetIndex, rct_window*w, rct_widget* widget); static void window_ride_list_dropdown(); static void window_ride_list_update(rct_window *w); static void window_ride_list_scrollgetsize(); @@ -248,31 +248,9 @@ static void window_ride_list_resize() * * rct2: 0x006B3532 */ -static void window_ride_list_mousedown() +static void window_ride_list_mousedown(int widgetIndex, rct_window*w, rct_widget* widget) { int numItems, i; - short widgetIndex; - rct_window *w; - rct_widget *widget; - - #ifdef _MSC_VER - __asm mov widgetIndex, dx - #else - __asm__ ( "mov %[widgetIndex], dx " : [widgetIndex] "+m" (widgetIndex) ); - #endif - - #ifdef _MSC_VER - __asm mov w, esi - #else - __asm__ ( "mov %[w], esi " : [w] "+m" (w) ); - #endif - - #ifdef _MSC_VER - __asm mov widget, edi - #else - __asm__ ( "mov %[widget], edi " : [widget] "+m" (widget) ); - #endif - if (widgetIndex == WIDX_OPEN_CLOSE_ALL) { gDropdownItemsFormat[0] = STR_CLOSE_ALL; @@ -695,10 +673,7 @@ static void window_ride_list_refresh_list(rct_window *w) rct_ride *ride, *otherRide; countA = countB = 0; - for (i = 0; i < MAX_RIDES; i++) { - ride = &(RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, rct_ride)[i]); - if (ride->type == RIDE_TYPE_NULL) - continue; + FOR_ALL_RIDES(i, ride) { if (w->page != gRideClassifications[ride->type]) continue; @@ -717,10 +692,7 @@ static void window_ride_list_refresh_list(rct_window *w) w->var_476 = countA; j = 0; - for (i = 0; i < MAX_RIDES; i++) { - ride = &(RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, rct_ride)[i]); - if (ride->type == RIDE_TYPE_NULL) - continue; + FOR_ALL_RIDES(i, ride) { if (w->page != gRideClassifications[ride->type]) continue; @@ -844,10 +816,7 @@ static void window_ride_list_close_all(rct_window *w) int i; rct_ride *ride; - for (i = 0; i < MAX_RIDES; i++) { - ride = &RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, rct_ride)[i]; - if (ride->type == RIDE_TYPE_NULL) - continue; + FOR_ALL_RIDES(i, ride) { if (w->page != gRideClassifications[ride->type]) continue; if (ride->status == RIDE_STATUS_CLOSED) @@ -864,10 +833,7 @@ static void window_ride_list_open_all(rct_window *w) int i; rct_ride *ride; - for (i = 0; i < MAX_RIDES; i++) { - ride = &RCT2_ADDRESS(RCT2_ADDRESS_RIDE_LIST, rct_ride)[i]; - if (ride->type == RIDE_TYPE_NULL) - continue; + FOR_ALL_RIDES(i, ride) { if (w->page != gRideClassifications[ride->type]) continue; if (ride->status == RIDE_STATUS_OPEN) diff --git a/src/window_save_prompt.c b/src/window_save_prompt.c index e4893c6414..52413e3cd5 100644 --- a/src/window_save_prompt.c +++ b/src/window_save_prompt.c @@ -117,6 +117,12 @@ void window_save_prompt_open() prompt_mode = RCT2_GLOBAL(RCT2_ADDRESS_SAVE_PROMPT_MODE, uint16); + // do not show save prompt if we're in the title demo and click on load game + if (prompt_mode != PM_QUIT && RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TITLE_DEMO) { + game_load_or_quit_no_save_prompt(); + return; + } + // Check if window is already open window = window_bring_to_front_by_id(WC_SAVE_PROMPT, 0); if (window == NULL) { @@ -146,14 +152,13 @@ void window_save_prompt_open() y, (uint32*)window_save_prompt_events, WC_SAVE_PROMPT, - 0 + WF_TRANSPARENT | WF_STICK_TO_FRONT ); window->widgets = widgets; window->enabled_widgets = enabled_widgets; window_init_scroll_widgets(window); window->colours[0] = 154; - window->flags |= WF_TRANSPARENT; // Pause the game RCT2_GLOBAL(0x009DEA6E, uint8) |= 2; @@ -170,6 +175,12 @@ void window_save_prompt_open() window_save_prompt_widgets[WIDX_LABEL].image = prompt_mode + STR_SAVE_BEFORE_LOADING; if (!gGeneral_config.confirmation_prompt) { + /* game_load_or_quit_no_save_prompt() will exec requested task and close this window + * immediately again. + * TODO restructure these functions when we're sure game_load_or_quit_no_save_prompt() + * and game_load_or_quit() are not called by the original binary anymore. + */ + if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 0x0D) { game_load_or_quit_no_save_prompt(); return; @@ -246,11 +257,12 @@ static void window_save_prompt_mouseup() } else { switch (widgetIndex) { case WIDX_SAVE: - // TODO to avoid data loss, treat SAVE as CANCEL - RCT2_ERROR("TODO"); - window_close(w); - window_save_prompt_close(); - return; + if (!save_game()) { + // user pressed cancel + window_close(w); + window_save_prompt_close(); + return; + } break; case WIDX_DONT_SAVE: break; diff --git a/src/window_title_menu.c b/src/window_title_menu.c index 087b6cd41e..8e2e5aab45 100644 --- a/src/window_title_menu.c +++ b/src/window_title_menu.c @@ -45,7 +45,7 @@ static rct_widget window_title_menu_widgets[] = { static void window_title_menu_emptysub() { } static void window_title_menu_mouseup(); -static void window_title_menu_mousedown(); +static void window_title_menu_mousedown(int widgetIndex, rct_window*w, rct_widget* widget); static void window_title_menu_dropdown(); static void window_title_menu_unknown17(); static void window_title_menu_paint(); @@ -122,31 +122,8 @@ static void window_title_menu_mouseup() } } -static void window_title_menu_mousedown() +static void window_title_menu_mousedown(int widgetIndex, rct_window*w, rct_widget* widget) { - short widgetIndex; - rct_window *w; - rct_widget *widget; - - #ifdef _MSC_VER - __asm mov widgetIndex, dx - #else - __asm__ ( "mov %[widgetIndex], dx " : [widgetIndex] "+m" (widgetIndex) ); - #endif - - #ifdef _MSC_VER - __asm mov w, esi - #else - __asm__ ( "mov %[w], esi " : [w] "+m" (w) ); - #endif - - #ifdef _MSC_VER - __asm mov widget, edi - #else - __asm__ ( "mov %[widget], edi " : [widget] "+m" (widget) ); - #endif - - if (widgetIndex == WIDX_SHOW_TUTORIAL) { gDropdownItemsFormat[0] = STR_TUTORIAL_BEGINNERS; gDropdownItemsFormat[1] = STR_TUTORIAL_CUSTOM_RIDES; diff --git a/src/window_title_scenarioselect.c b/src/window_title_scenarioselect.c index 8f0f34329b..09d185238f 100644 --- a/src/window_title_scenarioselect.c +++ b/src/window_title_scenarioselect.c @@ -59,7 +59,7 @@ static void window_scenarioselect_init_tabs(); static void window_scenarioselect_emptysub() { } static void window_scenarioselect_mouseup(); -static void window_scenarioselect_mousedown(); +static void window_scenarioselect_mousedown(int widgetIndex, rct_window*w, rct_widget* widget); static void window_scenarioselect_scrollgetsize(); static void window_scenarioselect_scrollmousedown(); static void window_scenarioselect_scrollmouseover(); @@ -190,24 +190,8 @@ static void window_scenarioselect_mouseup() window_close(w); } -static void window_scenarioselect_mousedown() +static void window_scenarioselect_mousedown(int widgetIndex, rct_window*w, rct_widget* widget) { - short widgetIndex; - rct_window *w; - - #ifdef _MSC_VER - __asm mov widgetIndex, dx - #else - __asm__ ( "mov %[widgetIndex], dx " : [widgetIndex] "+m" (widgetIndex) ); - #endif - - #ifdef _MSC_VER - __asm mov w, esi - #else - __asm__ ( "mov %[w], esi " : [w] "+m" (w) ); - #endif - - if (widgetIndex >= WIDX_TAB1 && widgetIndex <= WIDX_TAB5) { w->selected_tab = widgetIndex - 4; w->var_494 = 0; @@ -292,7 +276,7 @@ static void window_scenarioselect_scrollmousedown() if (y >= 0) continue; - sound_play_panned(4, w->width / 2 + w->x); + sound_play_panned(SOUND_CLICK_1, w->width / 2 + w->x); scenario_load_and_play(scenario); break; }