Merge with upstream

This commit is contained in:
Maciek Baron 2014-06-14 15:55:07 +01:00
commit 8a39d9d026
72 changed files with 5600 additions and 1044 deletions

6
.gitignore vendored
View File

@ -2,6 +2,12 @@
sdl
# Compiled dll
openrct2.dll
# Build artifacts
.cache
#################
## Eclipse
#################

View File

@ -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"
notifications:
irc: "irc.freenode.net#openrct2-dev"
on_failure: always
on_success: change
cache:
directories:
- cache
apt: true

View File

@ -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/")

19
build.sh Executable file
View File

@ -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

10
clean.sh Normal file
View File

@ -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

78
install.sh Executable file
View File

@ -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

View File

@ -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
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View File

@ -17,6 +17,7 @@
<ItemGroup>
<ClInclude Include="..\src\addresses.h" />
<ClInclude Include="..\src\audio.h" />
<ClInclude Include="..\src\award.h" />
<ClInclude Include="..\src\climate.h" />
<ClInclude Include="..\src\config.h" />
<ClInclude Include="..\src\date.h" />
@ -26,6 +27,7 @@
<ClInclude Include="..\src\gfx.h" />
<ClInclude Include="..\src\intro.h" />
<ClInclude Include="..\src\map.h" />
<ClInclude Include="..\src\marketing.h" />
<ClInclude Include="..\src\news_item.h" />
<ClInclude Include="..\src\object.h" />
<ClInclude Include="..\src\osinterface.h" />
@ -38,7 +40,6 @@
<ClInclude Include="..\src\sawyercoding.h" />
<ClInclude Include="..\src\scenario.h" />
<ClInclude Include="..\src\screenshot.h" />
<ClInclude Include="..\src\settings.h" />
<ClInclude Include="..\src\sprite.h" />
<ClInclude Include="..\src\sprites.h" />
<ClInclude Include="..\src\string_ids.h" />
@ -46,6 +47,7 @@
<ClInclude Include="..\src\track.h" />
<ClInclude Include="..\src\tutorial.h" />
<ClInclude Include="..\src\util.h" />
<ClInclude Include="..\src\vehicle.h" />
<ClInclude Include="..\src\viewport.h" />
<ClInclude Include="..\src\widget.h" />
<ClInclude Include="..\src\window.h" />
@ -57,6 +59,7 @@
<ItemGroup>
<ClCompile Include="..\lodepng\lodepng.c" />
<ClCompile Include="..\src\audio.c" />
<ClCompile Include="..\src\award.c" />
<ClCompile Include="..\src\climate.c" />
<ClCompile Include="..\src\config.c" />
<ClCompile Include="..\src\date.c" />
@ -66,6 +69,7 @@
<ClCompile Include="..\src\gfx.c" />
<ClCompile Include="..\src\intro.c" />
<ClCompile Include="..\src\map.c" />
<ClCompile Include="..\src\marketing.c" />
<ClCompile Include="..\src\news_item.c" />
<ClCompile Include="..\src\object.c" />
<ClCompile Include="..\src\object_list.c" />
@ -80,11 +84,13 @@
<ClCompile Include="..\src\scenario.c" />
<ClCompile Include="..\src\scenario_list.c" />
<ClCompile Include="..\src\screenshot.c" />
<ClCompile Include="..\src\sprite.c" />
<ClCompile Include="..\src\string_ids.c" />
<ClCompile Include="..\src\title.c" />
<ClCompile Include="..\src\track.c" />
<ClCompile Include="..\src\tutorial.c" />
<ClCompile Include="..\src\util.c" />
<ClCompile Include="..\src\vehicle.c" />
<ClCompile Include="..\src\viewport.c" />
<ClCompile Include="..\src\widget.c" />
<ClCompile Include="..\src\window.c" />
@ -199,4 +205,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -126,10 +126,16 @@
<ClInclude Include="..\src\screenshot.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\settings.h">
<ClInclude Include="..\src\finance.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\finance.h">
<ClInclude Include="..\src\vehicle.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\marketing.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\award.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
@ -314,10 +320,22 @@
<ClCompile Include="..\src\object_list.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\vehicle.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\sprite.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\marketing.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\award.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\openrct2.exe">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
</Project>
</Project>

View File

@ -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

View File

@ -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

625
src/award.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#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);
}

58
src/award.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#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

View File

@ -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

View File

@ -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

View File

@ -19,14 +19,12 @@
*****************************************************************************/
#include <stdio.h>
#include <shlobj.h>
#include <windows.h>
#include <tchar.h>
#include <SDL_keycode.h>
#include <ctype.h>
#include "addresses.h"
#include "config.h"
#include "rct2.h"
#include <tchar.h>
#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;

View File

@ -21,8 +21,8 @@
#ifndef _CONFIG_H_
#define _CONFIG_H_
#include <windows.h>
#include "rct2.h"
#include <windows.h> // 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{
//};

View File

@ -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;
}
}
/**
*

View File

@ -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();

View File

@ -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

View File

@ -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

1727
src/gfx.c

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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;

View File

@ -328,4 +328,40 @@ void sub_68B089()
} while (mapElement->base_height == 255);
mapElement++;
RCT2_GLOBAL(0x0140E9A4, rct_map_element*) = mapElement;
}
}
/**
* 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;
}

View File

@ -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

126
src/marketing.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#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;
}
}

40
src/marketing.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#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

View File

@ -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:

View File

@ -18,8 +18,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include <memory.h>
#include <stdio.h>
#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;
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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 '\\';
}

View File

@ -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

View File

@ -20,8 +20,11 @@
#include <windows.h>
#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
}

View File

@ -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();

View File

@ -20,6 +20,7 @@
#include <windows.h>
#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

View File

@ -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

View File

@ -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);
}

View File

@ -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)

View File

@ -20,14 +20,14 @@
#include <windows.h>
#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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -21,10 +21,12 @@
#include <windows.h>
#include <string.h>
#include "addresses.h"
#include "award.h"
#include "date.h"
#include "finance.h"
#include "game.h"
#include "map.h"
#include "marketing.h"
#include "news_item.h"
#include "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();
}
}

View File

@ -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),

View File

@ -17,10 +17,11 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#pragma pack(1)
#include <lodepng.h>
#include <stdio.h>
#include <windows.h>
#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 <windows.h> // 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;

31
src/sprite.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#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);
}

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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,

59
src/vehicle.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "addresses.h"
#include "sprite.h"
#include "vehicle.h"
static void vehicle_update(rct_vehicle *vehicle);
/**
*
* rct2: 0x006D4204
*/
void vehicle_update_all()
{
uint16 sprite_index;
rct_vehicle *vehicle;
if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 2)
return;
if ((RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 4) && RCT2_GLOBAL(0x0141F570, uint8) != 6)
return;
sprite_index = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_VEHICLE, uint16);
while (sprite_index != SPRITE_INDEX_NULL) {
vehicle = &(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);
}

40
src/vehicle.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#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

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -18,16 +18,409 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include <string.h>
#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;
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

View File

@ -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);
}
}

View File

@ -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),

View File

@ -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();

View File

@ -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

View File

@ -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:

View File

@ -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);
}

View File

@ -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));
}
}

View File

@ -28,6 +28,8 @@
#include "window.h"
#include "window_dropdown.h"
#include <stdint.h>
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();
}
}

View File

@ -20,6 +20,7 @@
#include <string.h>
#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++;

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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;
}