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

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

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,6 +320,18 @@
<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">

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)) {
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,17 +71,14 @@ 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)
FOR_ALL_STAFF(spriteIndex, peep)
finance_payment(wage_table[peep->staff_type] / 4, RCT_EXPENDITURE_TYPE_WAGES);
}
}
/**
* Pays the current research level's cost.
@ -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

1721
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

@ -329,3 +329,39 @@ void sub_68B089()
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())
// 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)
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)
FOR_ALL_RIDES(i, ride)
count++;
}
return count;
}
@ -162,32 +159,27 @@ 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) {
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);
}
}
}
/**
* rct2: 0x006AC916
*/
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)
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);
bytesWritten = fwrite(buffer, sizeof(char), 246*4, fp);
if (bytesWritten != 246*4){
CloseHandle(hFile);
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);
bytesWritten=fwrite(buffer, sizeof(char), stride, fp);
if (bytesWritten != stride){
CloseHandle(hFile);
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,6 +1085,9 @@ void format_integer(char **dest, int value)
*dest = dst;
if (value == 0) {
*dst++ = '0';
} else {
// Right to left
while (value > 0) {
digit = value % 10;
@ -1092,6 +1095,7 @@ void format_integer(char **dest, int value)
*dst++ = '0' + digit;
}
}
finish = dst;
// Reverse string
@ -1121,6 +1125,9 @@ void format_comma_separated_integer(char **dest, int value)
*dest = dst;
if (value == 0) {
*dst++ = '0';
} else {
// Groups of three digits, right to left
groupIndex = 0;
while (value > 0) {
@ -1136,6 +1143,174 @@ void format_comma_separated_integer(char **dest, int value)
*dst++ = '0' + digit;
groupIndex++;
}
}
finish = dst;
// Reverse string
dst--;
while (*dest < dst) {
tmp = **dest;
**dest = *dst;
*dst = tmp;
(*dest)++;
dst--;
}
*dest = finish;
}
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;
// Reverse string
@ -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]);

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;
@ -736,7 +738,6 @@ 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);
// fill it when checkbox is pressed
@ -744,7 +745,6 @@ static void widget_checkbox_draw(rct_drawpixelinfo *dpi, rct_window *w, int widg
RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16) = 224;
gfx_draw_string(dpi, (char*)0x009DED72, colour & 0x7F, l, t);
}
}
// draw the text
if (widget->image == -1)
@ -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;
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)

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

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