diff --git a/data/language/dutch.txt b/data/language/dutch.txt index 52547e9543..8124ca13fc 100644 --- a/data/language/dutch.txt +++ b/data/language/dutch.txt @@ -2678,7 +2678,7 @@ STR_2676 :??? STR_2677 :??? STR_2678 :??? STR_2679 :??? -STR_2680 :??? +STR_2680 :Alle onderzoeken zijn voltooid STR_2681 :{MEDIUMFONT}{BLACK}Krijg 5.000 extra geld STR_2682 :{MEDIUMFONT}{BLACK}Wissel tussen betaalde en gratis entree STR_2683 :{MEDIUMFONT}{BLACK}Alle bezoekers hebben een maximale tevredenheid diff --git a/data/language/english_uk.txt b/data/language/english_uk.txt index 2d1928bf82..ce12abf832 100644 --- a/data/language/english_uk.txt +++ b/data/language/english_uk.txt @@ -3459,3 +3459,4 @@ STR_5122 :Show all vehicles sharing a track/ride type STR_5123 :Renew rides STR_5124 :No Six Flags STR_5125 :All destructable +STR_5126 :Random title music diff --git a/data/language/english_us.txt b/data/language/english_us.txt index 12ee4e49d1..06889b26a8 100644 --- a/data/language/english_us.txt +++ b/data/language/english_us.txt @@ -3471,3 +3471,4 @@ STR_5122 :Show all vehicles sharing a track/ride type STR_5123 :Renew rides STR_5124 :No Six Flags STR_5125 :All destructable +STR_5126 :Random title music diff --git a/data/language/french.txt b/data/language/french.txt index b199308322..4363a996ed 100644 --- a/data/language/french.txt +++ b/data/language/french.txt @@ -842,7 +842,7 @@ STR_0837 :Unable to initialize graphics system STR_0838 :CD key code {INT32} is not valid for your RollerCoaster Tycoon 2 CD !{WINDOW_COLOUR_1}{WINDOW_COLOUR_1}Please un-install RollerCoaster Tycoon 2% and re-install with the correct CD Key Code STR_0839 :{UINT16} x {UINT16} STR_0840 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{UINT16} x {UINT16} -STR_0841 :Desktop window +STR_0841 :En une fenêtre STR_0842 :640x480 plein écran STR_0843 :800x600 plein écran STR_0844 :1024x768 plein écran @@ -1336,10 +1336,10 @@ STR_1331 :{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{POP16}{VEL STR_1332 :{VELOCITY} STR_1333 :{STRINGID} - {STRINGID}{POP16} STR_1334 :{STRINGID} - {STRINGID} {COMMA16} -STR_1335 :{STRINGID} - Entrance{POP16}{POP16} -STR_1336 :{STRINGID} - Station {POP16}{COMMA16} Entrance -STR_1337 :{STRINGID} - Exit{POP16}{POP16} -STR_1338 :{STRINGID} - Station {POP16}{COMMA16} Exit +STR_1335 :{STRINGID} - Entrée{POP16}{POP16} +STR_1336 :{STRINGID} - Entrée de station {POP16}{COMMA16} +STR_1337 :{STRINGID} - Sortie{POP16}{POP16} +STR_1338 :{STRINGID} - Sortie de station {POP16}{COMMA16} STR_1339 :{BLACK}No test results yet... STR_1340 :{WINDOW_COLOUR_2}Max. speed: {BLACK}{VELOCITY} STR_1341 :{WINDOW_COLOUR_2}Ride time: {BLACK}{STRINGID}{STRINGID}{STRINGID}{STRINGID} @@ -1416,7 +1416,7 @@ STR_1411 :{STRINGID} in the way STR_1412 :{WINDOW_COLOUR_3}Data logging not available for this type of ride STR_1413 :{WINDOW_COLOUR_3}Data logging will start when next {STRINGID} leaves {STRINGID} STR_1414 :{SMALLFONT}{BLACK}{DURATION} -STR_1415 :{WINDOW_COLOUR_2}Velocity +STR_1415 :{WINDOW_COLOUR_2}Vitesse STR_1416 :{WINDOW_COLOUR_2}Altitude STR_1417 :{WINDOW_COLOUR_2}Vert.G's STR_1418 :{WINDOW_COLOUR_2}Lat.G's @@ -1436,9 +1436,9 @@ STR_1431 :Marche STR_1432 :Heading for {STRINGID} STR_1433 :Queuing for {STRINGID} STR_1434 :Drowning -STR_1435 :On {STRINGID} -STR_1436 :In {STRINGID} -STR_1437 :At {STRINGID} +STR_1435 :En {STRINGID} +STR_1436 :Dans {STRINGID} +STR_1437 :A {STRINGID} STR_1438 :Sitting STR_1439 :(select location) STR_1440 :Mowing grass @@ -1469,7 +1469,7 @@ STR_1464 :Helix up (small) STR_1465 :Helix up (large) STR_1466 :Helix down (small) STR_1467 :Helix down (large) -STR_1468 :Staff +STR_1468 :Personnel STR_1469 :Ride must start and end with stations STR_1470 :Station not long enough STR_1471 :{WINDOW_COLOUR_2}Vitesse: @@ -2681,37 +2681,37 @@ STR_2676 :??? STR_2677 :??? STR_2678 :??? STR_2679 :??? -STR_2680 :??? -STR_2681 :??? -STR_2682 :??? -STR_2683 :??? -STR_2684 :??? -STR_2685 :??? -STR_2686 :??? -STR_2687 :??? -STR_2688 :??? -STR_2689 :??? -STR_2690 :??? -STR_2691 :??? -STR_2692 :??? -STR_2693 :??? -STR_2694 :??? -STR_2695 :??? -STR_2696 :??? +STR_2680 :All research complete +STR_2681 :{MEDIUMFONT}{BLACK}Increases your money by 5,000 +STR_2682 :{MEDIUMFONT}{BLACK}Toggle between Free and Paid Entry +STR_2683 :{MEDIUMFONT}{BLACK}Increases every peeps happiness to max +STR_2684 :{MEDIUMFONT}{BLACK}Large group of peeps arrive +STR_2685 :Simplex Noise Parameters +STR_2686 :{WINDOW_COLOUR_2}Low: +STR_2687 :{WINDOW_COLOUR_2}High: +STR_2688 :{WINDOW_COLOUR_2}Base Frequency: +STR_2689 :{WINDOW_COLOUR_2}Octaves: +STR_2690 :Map Generation +STR_2691 :{WINDOW_COLOUR_2}Base height: +STR_2692 :{WINDOW_COLOUR_2}Water level: +STR_2693 :{WINDOW_COLOUR_2}Terrain: +STR_2694 :Generate +STR_2695 :Random terrain +STR_2696 :Place trees STR_2697 :??? STR_2698 :??? STR_2699 :??? -STR_2700 :??? -STR_2701 :??? -STR_2702 :??? -STR_2703 :??? -STR_2704 :??? -STR_2705 :??? -STR_2706 :??? -STR_2707 :??? -STR_2708 :??? -STR_2709 :??? -STR_2710 :??? +STR_2700 :Autosave frequency +STR_2701 :Every week +STR_2702 :Every 2 weeks +STR_2703 :Every month +STR_2704 :Every 4 months +STR_2705 :Every year +STR_2706 :Jamais +STR_2707 :Open new window +STR_2708 :{WINDOW_COLOUR_1}Are you sure you want to overwrite {STRINGID}? +STR_2709 :Overwrite +STR_2710 :Type the name of the file. STR_2711 :; STR_2712 := STR_2713 :, @@ -2719,50 +2719,50 @@ STR_2714 :- STR_2715 :. STR_2716 :/ STR_2717 :' -STR_2718 :??? -STR_2719 :??? -STR_2720 :??? -STR_2721 :??? -STR_2722 :??? -STR_2723 :??? -STR_2724 :??? -STR_2725 :??? -STR_2726 :??? -STR_2727 :??? -STR_2728 :??? -STR_2729 :??? -STR_2730 :??? -STR_2731 :??? -STR_2732 :??? -STR_2733 :??? -STR_2734 :??? -STR_2735 :??? +STR_2718 :(up) +STR_2719 :(nouveau fichier) +STR_2720 :{UINT16}s +STR_2721 :{UINT16}s +STR_2722 :{UINT16}m {UINT16}s +STR_2723 :{UINT16}m {UINT16}s +STR_2724 :{UINT16}mins:{UINT16}s +STR_2725 :{UINT16}m {UINT16}s +STR_2726 :{UINT16}m +STR_2727 :{UINT16}m +STR_2728 :{UINT16}h {UINT16}m +STR_2729 :{UINT16}h {UINT16}m +STR_2730 :{UINT16}h {UINT16}m +STR_2731 :{UINT16}h {UINT16}m +STR_2732 :{COMMA16}ft +STR_2733 :{COMMA16}m +STR_2734 :{COMMA16}mph +STR_2735 :{COMMA16}km/h STR_2736 :{MONTH}, an {COMMA16} STR_2737 :{STRINGID} {MONTH}, an {COMMA16} -STR_2738 :??? -STR_2739 :??? -STR_2740 :??? -STR_2741 :??? -STR_2742 :??? -STR_2743 :??? +STR_2738 :Title screen music +STR_2739 :Aucun +STR_2740 :RollerCoaster Tycoon 1 +STR_2741 :RollerCoaster Tycoon 2 +STR_2742 :css50.dat n'est pas trouvé +STR_2743 :Copy data\css17.dat from your RCT1 installation to data\css50.dat in your RCT2 installation. STR_2744 :[ STR_2745 :\ STR_2746 :] STR_2747 :{ENDQUOTES} STR_2748 :Bar -STR_2749 :??? -STR_2750 :??? -STR_2751 :??? -STR_2752 :??? -STR_2753 :??? -STR_2754 :??? -STR_2755 :??? -STR_2756 :??? -STR_2757 :??? -STR_2758 :??? -STR_2759 :??? +STR_2749 :Mon nouveau scénario +STR_2750 :Move all items to top +STR_2751 :Move all items to bottom +STR_2752 :Clear grass +STR_2753 :Mowed grass +STR_2754 :Water plants +STR_2755 :Fix vandalism +STR_2756 :Remove litter +STR_2757 :Force Sun +STR_2758 :Force Thunder +STR_2759 :Zero Clearance # New strings used in the cheats window previously these were ??? -STR_2760 :+5K Money +STR_2760 :+5000 d'argent STR_2761 :Pay For Entrance STR_2762 :Pay For Rides STR_2763 :??? @@ -2771,19 +2771,19 @@ STR_2765 :Large Tram STR_2766 :??? STR_2767 :Freeze Climate STR_2768 :Unfreeze Climate -STR_2769 :Open Park -STR_2770 :Close Park +STR_2769 :Ouvrir le parc +STR_2770 :Fermer le parc STR_2771 :Slower Gamespeed STR_2772 :Faster Gamespeed -STR_2773 :Windowed -STR_2774 :Fullscreen -STR_2775 :Fullscreen (desktop) +STR_2773 :En une fenêtre +STR_2774 :Plein écran +STR_2775 :Plein écran (desktop) STR_2776 :Langue STR_2777 :{MOVE_X}{SMALLFONT}{STRING} STR_2778 :{RIGHTGUILLEMET}{MOVE_X}{SMALLFONT}{STRING} # End of new strings -STR_2779 :??? -STR_2780 :??? +STR_2779 :Viewport #{COMMA16} +STR_2780 :Extra viewport STR_2781 :{STRINGID}:{MOVE_X}{195}{STRINGID}{STRINGID} STR_2782 :SHIFT + STR_2783 :CTRL + @@ -2798,14 +2798,14 @@ STR_2791 :Enter name STR_2792 :Please enter your name for the scenario chart: STR_2793 :{SMALLFONT}(Completed by {STRINGID}) STR_2794 :{WINDOW_COLOUR_2}Completed by: {BLACK}{STRINGID}{NEWLINE}{WINDOW_COLOUR_2} with a company value of: {BLACK}{CURRENCY} -STR_2795 :Sort +STR_2795 :Trier STR_2796 :{SMALLFONT}{BLACK}Sort the ride list into order using the information type displayed STR_2797 :Scroll view when pointer at screen edge STR_2798 :{SMALLFONT}{BLACK}Select whether to scroll the view when the mouse pointer is at the screen edge STR_2799 :{SMALLFONT}{BLACK}View or change control key assignments STR_2800 :{WINDOW_COLOUR_2}Total admissions: {BLACK}{COMMA32} STR_2801 :{WINDOW_COLOUR_2}Income from admissions: {BLACK}{CURRENCY2DP} -STR_2802 :Map +STR_2802 :Plan STR_2803 :{SMALLFONT}{BLACK}Show these guests highlighted on map STR_2804 :{SMALLFONT}{BLACK}Show these staff members highlighted on map STR_2805 :{SMALLFONT}{BLACK}Show map of park @@ -2984,7 +2984,7 @@ STR_2977 :Staff member name STR_2978 :Enter new name for this member of staff: STR_2979 :Can't name staff member... STR_2980 :Too many banners in game -STR_2981 :{RED}No entry - - +STR_2981 :{RED}Accès interdit - - STR_2982 :Banner text STR_2983 :Enter new text for this banner: STR_2984 :Can't set new text for banner... @@ -3059,7 +3059,7 @@ STR_3052 :Golf hole D STR_3053 :Golf hole E STR_3054 :Loading... STR_3055 :Blanc -STR_3056 :Translucent +STR_3056 :Transparent STR_3057 :{WINDOW_COLOUR_2}Construction Marker: STR_3058 :Brick walls STR_3059 :Hedges @@ -3160,9 +3160,9 @@ STR_3153 :{SMALLFONT}{BLACK}Scroll {STRINGID} down fast STR_3154 :{SMALLFONT}{BLACK}Scroll {STRINGID} up/down STR_3155 : STR_3156 : -STR_3157 :map -STR_3158 :graph -STR_3159 :list +STR_3157 :plan +STR_3158 :graphique +STR_3159 :liste STR_3160 :RollerCoaster Tycoon 2: Première visite... STR_3161 :RollerCoaster Tycoon 2: Checking object files... STR_3162 :Unable to allocate enough memory @@ -3170,8 +3170,8 @@ STR_3163 :Installing new data: STR_3164 :{BLACK}{COMMA16} selected (maximum {COMMA16}) STR_3165 : STR_3166 :{BLACK}(ID: -STR_3167 :{WINDOW_COLOUR_2}Includes: {BLACK}{COMMA16} objects -STR_3168 :{WINDOW_COLOUR_2}Text: {BLACK}{STRINGID} +STR_3167 :{WINDOW_COLOUR_2}Inclut: {BLACK}{COMMA16} objets +STR_3168 :{WINDOW_COLOUR_2}Texte: {BLACK}{STRINGID} STR_3169 :Data for the following object not found: STR_3170 :Not enough space for graphics STR_3171 :Too many objects of this type selected @@ -3208,7 +3208,7 @@ STR_3201 :Sélection des objets STR_3202 :Editeur de paysage STR_3203 :Liste d'inventions STR_3204 :Sélection des options -STR_3205 :Objective Selection +STR_3205 :Selection d'objectif STR_3206 :Sauvegarder scénario STR_3207 :Roller Coaster Designer STR_3208 :Track Designs Manager @@ -3289,9 +3289,9 @@ STR_3282 :{SMALLFONT}{BLACK}Choisir objectif et nom du parc STR_3283 :{SMALLFONT}{BLACK}Select rides to be preserved STR_3284 :Sélection des objectifs STR_3285 :Preserved Rides -STR_3286 :{SMALLFONT}{BLACK}Select objective for this scenario +STR_3286 :{SMALLFONT}{BLACK}Selecter un objectif pour ce scénario STR_3287 :{WINDOW_COLOUR_2}Objectif: -STR_3288 :{SMALLFONT}{BLACK}Select climate +STR_3288 :{SMALLFONT}{BLACK}Selecter climat STR_3289 :{WINDOW_COLOUR_2}Climat: STR_3290 :Frais et humide STR_3291 :Chaud @@ -3325,11 +3325,11 @@ STR_3318 :{SMALLFONT}{BLACK}Select which group this scenario appears in STR_3319 :{WINDOW_COLOUR_2}Scenario Group: STR_3320 :Unable to save scenario file... STR_3321 :New objects installed successfully -STR_3322 :{WINDOW_COLOUR_2}Objective: {BLACK}{STRINGID} +STR_3322 :{WINDOW_COLOUR_2}Objectif: {BLACK}{STRINGID} STR_3323 :Missing object data, ID: STR_3324 :Requires Add-On Pack: STR_3325 :Requires an Add-On Pack -STR_3326 :{WINDOW_COLOUR_2}(no image) +STR_3326 :{WINDOW_COLOUR_2}(aucune image) STR_3327 :Starting positions for people not set STR_3328 :Can't advance to next editor stage... STR_3329 :Park entrance not yet built @@ -3450,3 +3450,11 @@ STR_3443 :Page 4 STR_3444 :Page 5 STR_3445 :Définir Zone de Patrouille STR_3446 :Annuler Zone de Patrouille + +# New strings, cleaner +STR_5120 :Show finances button on toolbar +STR_5121 :Show research button on toolbar +STR_5122 :Show all vehicles sharing a track/ride type +STR_5123 :Renouveler attr. +STR_5124 :Pas de Six Flags +STR_5125 :All destructable diff --git a/data/language/hungarian.txt b/data/language/hungarian.txt index c4923aec62..1890084f41 100644 --- a/data/language/hungarian.txt +++ b/data/language/hungarian.txt @@ -2737,8 +2737,8 @@ STR_2732 :??? STR_2733 :??? STR_2734 :??? STR_2735 :??? -STR_2736 :??? -STR_2737 :??? +STR_2736 :{MONTH}, Year {COMMA16} +STR_2737 :{STRINGID} {MONTH}, Year {COMMA16} STR_2738 :??? STR_2739 :??? STR_2740 :??? diff --git a/data/language/spanish_sp.txt b/data/language/spanish_sp.txt index bd27de53b6..c63e1faf2a 100644 --- a/data/language/spanish_sp.txt +++ b/data/language/spanish_sp.txt @@ -2737,8 +2737,8 @@ STR_2732 :??? STR_2733 :??? STR_2734 :??? STR_2735 :??? -STR_2736 :??? -STR_2737 :??? +STR_2736 :{MONTH}, año {COMMA16} +STR_2737 :{STRINGID} {MONTH}, año {COMMA16} STR_2738 :??? STR_2739 :??? STR_2740 :??? diff --git a/data/language/swedish.txt b/data/language/swedish.txt index fa299270a0..6b1b0bd5b3 100644 --- a/data/language/swedish.txt +++ b/data/language/swedish.txt @@ -2739,8 +2739,8 @@ STR_2732 :??? STR_2733 :??? STR_2734 :??? STR_2735 :??? -STR_2736 :??? -STR_2737 :??? +STR_2736 :{MONTH}, Year {COMMA16} +STR_2737 :{STRINGID} {MONTH}, Year {COMMA16} STR_2738 :??? STR_2739 :??? STR_2740 :??? diff --git a/distribution/windows/install.nsi b/distribution/windows/install.nsi index 7d6432b0a0..3ca2dbd40e 100644 --- a/distribution/windows/install.nsi +++ b/distribution/windows/install.nsi @@ -1,7 +1,7 @@ # Version numbers to update !define /ifndef APPV_MAJOR 0 -!define /ifndef APPV_MINOR 2 -!define /ifndef APPV_MAINT 0 +!define /ifndef APPV_MINOR 0 +!define /ifndef APPV_MAINT 2 !define /ifndef APPV_BUILD 0 !define /ifndef APPV_EXTRA "-beta" @@ -488,9 +488,14 @@ DoUninstall: ; You have the same version as this installer. This allows you to Quit InstallerIsOlder: - MessageBox MB_OK|MB_ICONSTOP \ - "You have a newer version of ${APPNAME}.$\nSetup will now exit." - Quit + ;MessageBox MB_OK|MB_ICONSTOP \ + ; "You have a newer version of ${APPNAME}.$\nSetup will now exit." + ;Quit + MessageBox MB_OK|MB_ICONSTOP \ + "You have a newer version of ${APPNAME}.$\nPlease note downgrading is currently untested." + MessageBox MB_OK|MB_ICONQUESTION \ + "Welcome to ${APPNAMEANDVERSION} Setup.$\nThis will allow you to downgrade from version $OLDVERSION." + Goto FinishCallback FinishCallback: ClearErrors diff --git a/readme.md b/readme.md index 781260a842..3b5b0c0d05 100644 --- a/readme.md +++ b/readme.md @@ -58,7 +58,12 @@ The aim is to completely decompile RollerCoaster Tycoon 2 into C so that cross-p # 2 Downloading the game / Building the source code -A third-party offers [downloadable precompiled builds](https://openrct2.com/download). However, building the project is always recommended. +A couple of third parties offer downloadable precompiled builds. However, building the project is always recommended + +[OpenRCT2.com](https://openrct2.com/download) +[OpenRCT.net](https://openrct.net/builds.php) + +There is also a Launcher available from [OpenRCT.net](https://openrct.net/download) that will automatically update your build so that you always have the current version as they are released. ## 2.1 Prerequisites ### Windows: @@ -132,6 +137,8 @@ Translations are in progress for German, Dutch, French, Hungarian, Polish, Spani # 5 More information - [GitHub](https://github.com/IntelOrca/OpenRCT2) - [Facebook](https://www.facebook.com/OpenRCT2) -- [Automated Builds](https://openrct2.com/download) +- [Automated builds](https://openrct2.com/download) +- [Secondary site for automated builds](https://openrct.net/builds.php) +- [Launcher that keeps your copy up-to-date](https://openrct.net/download) - [rct2 subreddit](http://www.reddit.com/r/rct/) - [openrct2 subreddit](http://www.reddit.com/r/openrct2) diff --git a/src/addresses.h b/src/addresses.h index b9ae89c63c..4e70688479 100644 --- a/src/addresses.h +++ b/src/addresses.h @@ -316,6 +316,15 @@ #define RCT2_ADDRESS_TRACK_LIST 0x00F441EC +#define RCT2_ADDRESS_CTRL_PRESS_Z_COORDINATE 0x00F64ECC +#define RCT2_ADDRESS_SHIFT_PRESS_X_COORDINATE 0x00F64ECE +#define RCT2_ADDRESS_SHIFT_PRESS_Y_COORDINATE 0x00F64ED0 +#define RCT2_ADDRESS_SHIFT_PRESS_Z_VECTOR 0x00F64ED2 +#define RCT2_ADDRESS_SCENERY_Z_COORDINATE 0x00F64ED4 + +#define RCT2_ADDRESS_SCENERY_TOOL_CTRL_PRESSED 0x00F64F12 +#define RCT2_ADDRESS_SCENERY_TOOL_SHIFT_PRESSED 0x00F64F13 + #define RCT2_ADDRESS_CURRENT_MONTH_YEAR 0x00F663A8 #define RCT2_ADDRESS_CURRENT_MONTH_TICKS 0x00F663AA #define RCT2_ADDRESS_SCENARIO_TICKS 0x00F663AC diff --git a/src/audio/audio.c b/src/audio/audio.c index de5413da31..9dc0bb10c8 100644 --- a/src/audio/audio.c +++ b/src/audio/audio.c @@ -1427,13 +1427,8 @@ int sound_play_panned(int sound_id, int ebx, sint16 x, sint16 y, sint16 z) RCT2_GLOBAL(0x00F438AD, uint8) = 0; int volume = 0; if (ebx == 0x8001) { - sint16 x2 = x & 0xFFE0; // round by 32 - sint16 y2 = y & 0xFFE0; - if (x2 < 0x1FFF && y2 < 0x1FFF) { - rct_map_element* mapelement = RCT2_ADDRESS(RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS, rct_map_element*)[((y2 * 256 + x2) & 0xFFFF) / 8]; - while (map_element_get_type(mapelement) != MAP_ELEMENT_TYPE_SURFACE) { - mapelement++; - } + rct_map_element* mapelement = map_get_surface_element_at(x / 32, y / 32); + if (mapelement) { if ((mapelement->base_height * 8) - 5 > z) { RCT2_GLOBAL(0x00F438AD, uint8) = 10; } @@ -1554,13 +1549,19 @@ void start_title_music() case 2: musicPathId = PATH_ID_CSS17; break; + case 3: + if (rand() & 1) + musicPathId = PATH_ID_CSS50; + else + musicPathId = PATH_ID_CSS17; + break; } if ((RCT2_GLOBAL(0x009AF284, uint32) & (1 << 0)) && RCT2_GLOBAL(0x009AF59D, uint8) & 1 && RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TITLE_DEMO) { if (!RCT2_GLOBAL(0x009AF600, uint8)) { #ifdef USE_MIXER - gTitleMusicChannel = Mixer_Play_Music(musicPathId); + gTitleMusicChannel = Mixer_Play_Music(musicPathId, true); #else RCT2_GLOBAL(0x014241BC, uint32) = 1; int result = sound_channel_load_file2(3, (char*)get_file_path(musicPathId), 0); diff --git a/src/audio/mixer.cpp b/src/audio/mixer.cpp index 0888df65b4..87105a355c 100644 --- a/src/audio/mixer.cpp +++ b/src/audio/mixer.cpp @@ -31,21 +31,64 @@ extern "C" { Mixer gMixer; -Sample::Sample() +Source::~Source() +{ + +} + +unsigned long Source::GetSome(unsigned long offset, const uint8** data, unsigned long length) +{ + if (offset >= Length()) { + return 0; + } + unsigned long size = length; + if (offset + length > Length()) { + size = Length() - offset; + } + return Read(offset, data, size); +} + +unsigned long Source::Length() +{ + return length; +} + +const AudioFormat& Source::Format() +{ + return format; +} + +Source_Null::Source_Null() +{ + length = 0; +} + +unsigned long Source_Null::Read(unsigned long offset, const uint8** data, unsigned long length) +{ + return 0; +} + +Source_Sample::Source_Sample() { data = 0; length = 0; issdlwav = false; } -Sample::~Sample() +Source_Sample::~Source_Sample() { Unload(); } -bool Sample::Load(const char* filename) +unsigned long Source_Sample::Read(unsigned long offset, const uint8** data, unsigned long length) { - log_verbose("Sample::Load(%s)", filename); + *data = &Source_Sample::data[offset]; + return length; +} + +bool Source_Sample::LoadWAV(const char* filename) +{ + log_verbose("Source_Sample::LoadWAV(%s)", filename); utf8 utf8filename[512]; win1252_to_utf8(utf8filename, filename, sizeof(utf8filename)); @@ -75,9 +118,9 @@ bool Sample::Load(const char* filename) return true; } -bool Sample::LoadCSS1(const char* filename, unsigned int offset) +bool Source_Sample::LoadCSS1(const char* filename, unsigned int offset) { - log_verbose("Sample::LoadCSS1(%s, %d)", filename, offset); + log_verbose("Source_Sample::LoadCSS1(%s, %d)", filename, offset); utf8 utf8filename[512]; win1252_to_utf8(utf8filename, filename, sizeof(utf8filename)); @@ -107,13 +150,18 @@ bool Sample::LoadCSS1(const char* filename, unsigned int offset) format.freq = waveformat.nSamplesPerSec; format.format = AUDIO_S16LSB; format.channels = waveformat.nChannels; - data = new uint8[length]; + data = new (std::nothrow) uint8[length]; + if (!data) { + log_verbose("Unable to allocate data"); + SDL_RWclose(rw); + return false; + } SDL_RWread(rw, data, length, 1); SDL_RWclose(rw); return true; } -void Sample::Unload() +void Source_Sample::Unload() { if (data) { if (issdlwav) { @@ -127,16 +175,11 @@ void Sample::Unload() length = 0; } -bool Sample::Loaded() +bool Source_Sample::Convert(AudioFormat format) { - return data != 0; -} - -bool Sample::Convert(AudioFormat format) -{ - if(Sample::format.format != format.format || Sample::format.channels != format.channels || Sample::format.freq != format.freq){ + if(Source_Sample::format.format != format.format || Source_Sample::format.channels != format.channels || Source_Sample::format.freq != format.freq){ SDL_AudioCVT cvt; - if (SDL_BuildAudioCVT(&cvt, Sample::format.format, Sample::format.channels, Sample::format.freq, format.format, format.channels, format.freq) < 0) { + if (SDL_BuildAudioCVT(&cvt, Source_Sample::format.format, Source_Sample::format.channels, Source_Sample::format.freq, format.format, format.channels, format.freq) < 0) { return false; } cvt.len = length; @@ -149,69 +192,149 @@ bool Sample::Convert(AudioFormat format) Unload(); data = cvt.buf; length = cvt.len_cvt; - Sample::format = format; + Source_Sample::format = format; return true; } return false; } -const uint8* Sample::Data() +Source_SampleStream::Source_SampleStream() { - return data; + length = 0; + rw = NULL; + buffer = 0; + buffersize = 0; } -unsigned long Sample::Length() +Source_SampleStream::~Source_SampleStream() { - return length; + Unload(); } -Stream::Stream() +unsigned long Source_SampleStream::Read(unsigned long offset, const uint8** data, unsigned long length) { - sourcetype = SOURCE_NONE; + if (length > buffersize) { + if (buffer) { + delete[] buffer; + } + buffer = new (std::nothrow) uint8[length]; + if (!buffer) { + return 0; + } + buffersize = length; + } + Sint64 currentposition = SDL_RWtell(rw); + if (currentposition == -1) { + return 0; + } + if (currentposition - databegin != offset) { + Sint64 newposition = SDL_RWseek(rw, databegin + offset, SEEK_SET); + if (newposition == -1) { + return 0; + } + currentposition = newposition; + } + *data = buffer; + int read = SDL_RWread(rw, buffer, 1, length); + if (read == -1) { + return 0; + } + return read; } -unsigned long Stream::GetSome(unsigned long offset, const uint8** data, unsigned long length) +bool Source_SampleStream::LoadWAV(SDL_RWops* rw) { - unsigned long size = length; - switch(sourcetype) { - case SOURCE_SAMPLE: - if (offset >= sample->Length()) { - return 0; - } - if (offset + length > sample->Length()) { - size = sample->Length() - offset; - } - *data = &sample->Data()[offset]; - return size; - break; + Unload(); + if (rw == NULL) { + return false; + } + Source_SampleStream::rw = rw; + Uint32 chunk_id = SDL_ReadLE32(rw); + const Uint32 RIFF = 0x46464952; + if (chunk_id != RIFF) { + log_verbose("Not a WAV file"); + return false; + } + Uint32 chunk_size = SDL_ReadLE32(rw); + Uint32 chunk_format = SDL_ReadLE32(rw); + const Uint32 WAVE = 0x45564157; + if (chunk_format != WAVE) { + log_verbose("Not in WAVE format"); + return false; + } + const Uint32 FMT = 0x20746D66; + Uint32 fmtchunk_size = FindChunk(rw, FMT); + if (!fmtchunk_size) { + log_verbose("Could not find FMT chunk"); + return false; + } + Uint64 chunkstart = SDL_RWtell(rw); + PCMWAVEFORMAT waveformat; + SDL_RWread(rw, &waveformat, sizeof(waveformat), 1); + SDL_RWseek(rw, chunkstart + fmtchunk_size, RW_SEEK_SET); + if (waveformat.wf.wFormatTag != WAVE_FORMAT_PCM) { + log_verbose("Not in proper format"); + return false; + } + format.freq = waveformat.wf.nSamplesPerSec; + switch (waveformat.wBitsPerSample) { + case 8: + format.format = AUDIO_U8; + break; + case 16: + format.format = AUDIO_S16LSB; + break; + default: + log_verbose("Invalid bits per sample"); + return false; + break; + } + format.channels = waveformat.wf.nChannels; + const Uint32 DATA = 0x61746164; + Uint32 datachunk_size = FindChunk(rw, DATA); + if (!datachunk_size) { + log_verbose("Could not find DATA chunk"); + return false; + } + length = datachunk_size; + databegin = SDL_RWtell(rw); + return true; +} + +Uint32 Source_SampleStream::FindChunk(SDL_RWops* rw, Uint32 wanted_id) +{ + Uint32 subchunk_id = SDL_ReadLE32(rw); + Uint32 subchunk_size = SDL_ReadLE32(rw); + if (subchunk_id == wanted_id) { + return subchunk_size; + } + const Uint32 FACT = 0x74636166; + const Uint32 LIST = 0x5453494c; + const Uint32 BEXT = 0x74786562; + const Uint32 JUNK = 0x4B4E554A; + while (subchunk_id == FACT || subchunk_id == LIST || subchunk_id == BEXT || subchunk_id == JUNK) { + SDL_RWseek(rw, subchunk_size, RW_SEEK_CUR); + subchunk_id = SDL_ReadLE32(rw); + subchunk_size = SDL_ReadLE32(rw); + if (subchunk_id == wanted_id) { + return subchunk_size; + } } return 0; } -unsigned long Stream::Length() +void Source_SampleStream::Unload() { - switch(sourcetype) { - case SOURCE_SAMPLE: - return sample->Length(); - break; + if (rw) { + SDL_RWclose(rw); + rw = NULL; } - return 0; -} - -void Stream::SetSource_Sample(Sample& sample) -{ - sourcetype = SOURCE_SAMPLE; - Stream::sample = &sample; -} - -const AudioFormat* Stream::Format() -{ - switch(sourcetype) { - case SOURCE_SAMPLE: - return &sample->format; - break; + length = 0; + if (buffer) { + delete[] buffer; + buffer = 0; } - return 0; + buffersize = 0; } Channel::Channel() @@ -220,10 +343,13 @@ Channel::Channel() SetRate(1); SetVolume(SDL_MIX_MAXVOLUME); oldvolume = 0; + oldvolume_l = 0; + oldvolume_r = 0; SetPan(0.5f); done = true; stopping = false; - stream = 0; + source = 0; + deletesourceondone = false; } Channel::~Channel() @@ -232,11 +358,14 @@ Channel::~Channel() speex_resampler_destroy(resampler); resampler = 0; } + if (deletesourceondone) { + delete source; + } } -void Channel::Play(Stream& stream, int loop = MIXER_LOOP_NONE) +void Channel::Play(Source& source, int loop = MIXER_LOOP_NONE) { - Channel::stream = &stream; + Channel::source = &source; Channel::loop = loop; offset = 0; done = false; @@ -270,8 +399,15 @@ void Channel::SetPan(float pan) if (pan < 0) { Channel::pan = 0; } - volume_l = (float)sin((1.0 - Channel::pan) * M_PI / 2.0); - volume_r = (float)sin(Channel::pan * M_PI / 2.0); + double decibels = (abs(Channel::pan - 0.5) * 2.0) * 100.0; + double attenuation = pow(10, decibels / 20.0); + if (Channel::pan <= 0.5) { + volume_l = 1.0; + volume_r = float(1.0 / attenuation); + } else { + volume_r = 1.0; + volume_l = float(1.0 / attenuation); + } } bool Channel::IsPlaying() @@ -286,14 +422,25 @@ unsigned long Channel::GetOffset() bool Channel::SetOffset(unsigned long offset) { - if (stream && offset < stream->Length()) { - int samplesize = stream->Format()->channels * stream->Format()->BytesPerSample(); + if (source && offset < source->Length()) { + int samplesize = source->Format().channels * source->Format().BytesPerSample(); Channel::offset = (offset / samplesize) * samplesize; return true; } return false; } +Mixer::Mixer() +{ + effectbuffer = 0; + for (int i = 0; i < countof(css1sources); i++) { + css1sources[i] = 0; + } + for (int i = 0; i < countof(musicsources); i++) { + musicsources[i] = 0; + } +} + void Mixer::Init(const char* device) { Close(); @@ -310,10 +457,15 @@ void Mixer::Init(const char* device) format.channels = have.channels; format.freq = have.freq; const char* filename = get_file_path(PATH_ID_CSS1); - for (int i = 0; i < SOUND_MAXID; i++) { - css1samples[i].LoadCSS1(filename, i); - css1samples[i].Convert(format); // convert to audio output format, saves some cpu usage but requires a bit more memory, optional - css1streams[i].SetSource_Sample(css1samples[i]); + for (int i = 0; i < countof(css1sources); i++) { + Source_Sample* source_sample = new Source_Sample; + if (source_sample->LoadCSS1(filename, i)) { + source_sample->Convert(format); // convert to audio output format, saves some cpu usage but requires a bit more memory, optional + css1sources[i] = source_sample; + } else { + css1sources[i] = &source_null; + delete source_sample; + } } effectbuffer = new uint8[(have.samples * format.BytesPerSample() * format.channels)]; SDL_PauseAudioDevice(deviceid, 0); @@ -328,7 +480,22 @@ void Mixer::Close() } Unlock(); SDL_CloseAudioDevice(deviceid); - delete[] effectbuffer; + for (int i = 0; i < countof(css1sources); i++) { + if (css1sources[i] && css1sources[i] != &source_null) { + delete css1sources[i]; + css1sources[i] = 0; + } + } + for (int i = 0; i < countof(musicsources); i++) { + if (musicsources[i] && musicsources[i] != &source_null) { + delete musicsources[i]; + musicsources[i] = 0; + } + } + if (effectbuffer) { + delete[] effectbuffer; + effectbuffer = 0; + } } void Mixer::Lock() @@ -341,13 +508,14 @@ void Mixer::Unlock() SDL_UnlockAudioDevice(deviceid); } -Channel* Mixer::Play(Stream& stream, int loop, bool deleteondone) +Channel* Mixer::Play(Source& source, int loop, bool deleteondone, bool deletesourceondone) { Lock(); - Channel* newchannel = new (std::nothrow) Channel(); + Channel* newchannel = new (std::nothrow) Channel; if (newchannel) { - newchannel->Play(stream, loop); + newchannel->Play(source, loop); newchannel->deleteondone = deleteondone; + newchannel->deletesourceondone = deletesourceondone; newchannel->stopping = false; channels.push_back(newchannel); } @@ -364,13 +532,20 @@ void Mixer::Stop(Channel& channel) bool Mixer::LoadMusic(int pathid) { - if (pathid >= PATH_ID_END) { + if (pathid >= countof(musicsources)) { return false; } - if (!musicsamples[pathid].Loaded()) { + if (!musicsources[pathid]) { const char* filename = get_file_path(pathid); - musicstreams[pathid].SetSource_Sample(musicsamples[pathid]); - return musicsamples[pathid].Load(filename); + Source_Sample* source_sample = new Source_Sample; + if (source_sample->LoadWAV(filename)) { + musicsources[pathid] = source_sample; + return true; + } else { + delete source_sample; + musicsources[pathid] = &source_null; + return false; + } } else { return true; } @@ -394,8 +569,8 @@ void SDLCALL Mixer::Callback(void* arg, uint8* stream, int length) void Mixer::MixChannel(Channel& channel, uint8* data, int length) { - if (channel.stream && !channel.done) { - AudioFormat streamformat = *channel.stream->Format(); + if (channel.source && channel.source->Length() > 0 && !channel.done) { + AudioFormat streamformat = channel.source->Format(); int loaded = 0; SDL_AudioCVT cvt; cvt.len_ratio = 1; @@ -409,9 +584,9 @@ void Mixer::MixChannel(Channel& channel, uint8* data, int length) } int samplestoread = (int)((samples - samplesloaded) * rate); int lengthloaded = 0; - if (channel.offset < channel.stream->Length()) { + if (channel.offset < channel.source->Length()) { bool mustconvert = false; - if (MustConvert(*channel.stream)) { + if (MustConvert(*channel.source)) { if (SDL_BuildAudioCVT(&cvt, streamformat.format, streamformat.channels, streamformat.freq, Mixer::format.format, Mixer::format.channels, Mixer::format.freq) == -1) { break; } @@ -420,7 +595,7 @@ void Mixer::MixChannel(Channel& channel, uint8* data, int length) const uint8* datastream = 0; int toread = (int)(samplestoread / cvt.len_ratio) * samplesize; - int readfromstream = (channel.stream->GetSome(channel.offset, &datastream, toread)); + int readfromstream = (channel.source->GetSome(channel.offset, &datastream, toread)); if (readfromstream == 0) { break; } @@ -519,7 +694,7 @@ void Mixer::MixChannel(Channel& channel, uint8* data, int length) loaded += lengthloaded; - if (channel.loop != 0 && channel.offset >= channel.stream->Length()) { + if (channel.loop != 0 && channel.offset >= channel.source->Length()) { if (channel.loop != -1) { channel.loop--; } @@ -528,7 +703,9 @@ void Mixer::MixChannel(Channel& channel, uint8* data, int length) } while(loaded < length && channel.loop != 0 && !channel.stopping); channel.oldvolume = channel.volume; - if (channel.loop == 0 && channel.offset >= channel.stream->Length()) { + channel.oldvolume_l = channel.volume_l; + channel.oldvolume_r = channel.volume_r; + if (channel.loop == 0 && channel.offset >= channel.source->Length()) { channel.done = true; } } @@ -536,21 +713,19 @@ void Mixer::MixChannel(Channel& channel, uint8* data, int length) void Mixer::EffectPanS16(Channel& channel, sint16* data, int length) { - float left = channel.volume_l; - float right = channel.volume_r; for (int i = 0; i < length * 2; i += 2) { - data[i] = (sint16)(data[i] * left); - data[i + 1] = (sint16)(data[i + 1] * right); + float t = (float)i / (length * 2); + data[i] = (sint16)(data[i] * ((1.0 - t) * channel.oldvolume_l + t * channel.volume_l)); + data[i + 1] = (sint16)(data[i + 1] * ((1.0 - t) * channel.oldvolume_r + t * channel.volume_r)); } } void Mixer::EffectPanU8(Channel& channel, uint8* data, int length) { - float left = channel.volume_l; - float right = channel.volume_r; for (int i = 0; i < length * 2; i += 2) { - data[i] = (uint8)(data[i] * left); - data[i + 1] = (uint8)(data[i + 1] * right); + float t = (float)i / (length * 2); + data[i] = (uint8)(data[i] * ((1.0 - t) * channel.oldvolume_l + t * channel.volume_l)); + data[i + 1] = (uint8)(data[i + 1] * ((1.0 - t) * channel.oldvolume_r + t * channel.volume_r)); } } @@ -574,13 +749,10 @@ void Mixer::EffectFadeU8(uint8* data, int length, int startvolume, int endvolume } } -bool Mixer::MustConvert(Stream& stream) +bool Mixer::MustConvert(Source& source) { - const AudioFormat* streamformat = stream.Format(); - if (!streamformat) { - return false; - } - if (streamformat->format != format.format || streamformat->channels != format.channels || streamformat->freq != format.freq) { + const AudioFormat sourceformat = source.Format(); + if (sourceformat.format != format.format || sourceformat.channels != format.channels || sourceformat.freq != format.freq) { return true; } return false; @@ -609,11 +781,11 @@ void Mixer_Init(const char* device) void* Mixer_Play_Effect(int id, int loop, int volume, float pan, double rate, int deleteondone) { - if (id >= SOUND_MAXID) { + if (id >= countof(gMixer.css1sources)) { return 0; } gMixer.Lock(); - Channel* channel = gMixer.Play(gMixer.css1streams[id], loop, deleteondone != 0); + Channel* channel = gMixer.Play(*gMixer.css1sources[id], loop, deleteondone != 0, false); if (channel) { channel->SetVolume(volume); channel->SetPan(pan); @@ -664,10 +836,33 @@ int Mixer_Channel_SetOffset(void* channel, unsigned long offset) return ((Channel*)channel)->SetOffset(offset); } -void* Mixer_Play_Music(int pathid) +void* Mixer_Play_Music(int pathid, int streaming) { - if (gMixer.LoadMusic(pathid)) { - return gMixer.Play(gMixer.musicstreams[pathid], MIXER_LOOP_INFINITE, false); + if (streaming) { + const char* filename = get_file_path(pathid); + + utf8 utf8filename[512]; + win1252_to_utf8(utf8filename, filename, sizeof(utf8filename)); + + SDL_RWops* rw = SDL_RWFromFile(utf8filename, "rb"); + if (rw == NULL) { + return 0; + } + Source_SampleStream* source_samplestream = new Source_SampleStream; + if (source_samplestream->LoadWAV(rw)) { + Channel* channel = gMixer.Play(*source_samplestream, MIXER_LOOP_INFINITE, false, true); + if (!channel) { + delete source_samplestream; + } + return channel; + } else { + delete source_samplestream; + return 0; + } + } else { + if (gMixer.LoadMusic(pathid)) { + return gMixer.Play(*gMixer.musicsources[pathid], MIXER_LOOP_INFINITE, false, false); + } } return 0; } \ No newline at end of file diff --git a/src/audio/mixer.h b/src/audio/mixer.h index 6920986e92..6575401262 100644 --- a/src/audio/mixer.h +++ b/src/audio/mixer.h @@ -43,45 +43,73 @@ struct AudioFormat { int channels; }; -class Sample +class Source { public: - Sample(); - ~Sample(); - bool Load(const char* filename); - bool LoadCSS1(const char* filename, unsigned int offset); - void Unload(); - bool Loaded(); - bool Convert(AudioFormat format); - const uint8* Data(); - unsigned long Length(); - - friend class Stream; - -private: - AudioFormat format; - uint8* data; - unsigned long length; - bool issdlwav; -}; - -class Stream -{ -public: - Stream(); + virtual ~Source(); unsigned long GetSome(unsigned long offset, const uint8** data, unsigned long length); unsigned long Length(); - void SetSource_Sample(Sample& sample); - const AudioFormat* Format(); + const AudioFormat& Format(); friend class Mixer; +protected: + virtual unsigned long Read(unsigned long offset, const uint8** data, unsigned long length) = 0; + + AudioFormat format; + unsigned long length; +}; + +class Source_Null : public Source +{ +public: + Source_Null(); + +protected: + unsigned long Read(unsigned long offset, const uint8** data, unsigned long length); +}; + +class Source_Sample : public Source +{ +public: + Source_Sample(); + ~Source_Sample(); + bool LoadWAV(const char* filename); + bool LoadCSS1(const char* filename, unsigned int offset); + + friend class Mixer; + +protected: + bool Convert(AudioFormat format); + private: - enum { - SOURCE_NONE = 0, - SOURCE_SAMPLE - } sourcetype; - Sample* sample; + void Unload(); + + uint8* data; + bool issdlwav; + +protected: + unsigned long Read(unsigned long offset, const uint8** data, unsigned long length); +}; + +class Source_SampleStream : public Source +{ +public: + Source_SampleStream(); + ~Source_SampleStream(); + bool LoadWAV(SDL_RWops* rw); + +private: + Uint32 FindChunk(SDL_RWops* rw, Uint32 wanted_id); + void Unload(); + + SDL_RWops* rw; + Uint64 databegin; + uint8* buffer; + unsigned long buffersize; + +protected: + unsigned long Read(unsigned long offset, const uint8** data, unsigned long length); }; class Channel @@ -89,7 +117,7 @@ class Channel public: Channel(); ~Channel(); - void Play(Stream& stream, int loop); + void Play(Source& source, int loop); void SetRate(double rate); void SetVolume(int volume); void SetPan(float pan); @@ -105,28 +133,31 @@ private: double rate; int volume; float volume_l, volume_r; + float oldvolume_l, oldvolume_r; float pan; bool done; bool deleteondone; + bool deletesourceondone; bool stopping; int oldvolume; SpeexResamplerState* resampler; - Stream* stream; + Source* source; }; class Mixer { public: + Mixer(); void Init(const char* device); void Close(); void Lock(); void Unlock(); - Channel* Play(Stream& stream, int loop, bool deleteondone); + Channel* Play(Source& source, int loop, bool deleteondone, bool deletesourceondone); void Stop(Channel& channel); bool LoadMusic(int pathid); - Stream css1streams[SOUND_MAXID]; - Stream musicstreams[PATH_ID_END]; + Source* css1sources[SOUND_MAXID]; + Source* musicsources[PATH_ID_END]; private: static void SDLCALL Callback(void* arg, uint8* data, int length); @@ -135,14 +166,13 @@ private: void EffectPanU8(Channel& channel, uint8* data, int length); void EffectFadeS16(sint16* data, int length, int startvolume, int endvolume); void EffectFadeU8(uint8* data, int length, int startvolume, int endvolume); - bool MustConvert(Stream& stream); + bool MustConvert(Source& source); bool Convert(SDL_AudioCVT& cvt, const uint8* data, unsigned long length, uint8** dataout); SDL_AudioDeviceID deviceid; AudioFormat format; uint8* effectbuffer; - Sample css1samples[SOUND_MAXID]; - Sample musicsamples[PATH_ID_END]; std::list channels; + Source_Null source_null; }; extern "C" @@ -158,7 +188,7 @@ void Mixer_Channel_Rate(void* channel, double rate); int Mixer_Channel_IsPlaying(void* channel); unsigned long Mixer_Channel_GetOffset(void* channel); int Mixer_Channel_SetOffset(void* channel, unsigned long offset); -void* Mixer_Play_Music(int pathid); +void* Mixer_Play_Music(int pathid, int streaming); static int DStoMixerVolume(int volume) { return (int)(SDL_MIX_MAXVOLUME * (SDL_pow(10, (float)volume / 2000))); }; static float DStoMixerPan(int pan) { return (((float)pan + -DSBPAN_LEFT) / DSBPAN_RIGHT) / 2; }; diff --git a/src/drawing/string.c b/src/drawing/string.c index 49c859be70..746ebfa9c5 100644 --- a/src/drawing/string.c +++ b/src/drawing/string.c @@ -1006,11 +1006,11 @@ void gfx_draw_string(rct_drawpixelinfo *dpi, char *buffer, int colour, int x, in skip_char = 1; break; } - ebx = *((uint16*)(buffer - 3)); - eax = ebx & 0x7FFFF; - g1_element = &(RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[eax]); + uint32 image_id = *((uint32*)(buffer - 3)); + uint32 image_offset = image_id & 0x7FFFF; + g1_element = &(RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element)[image_offset]); - gfx_draw_sprite(dpi, ebx, max_x, max_y, 0); + gfx_draw_sprite(dpi, image_id, max_x, max_y, 0); max_x = max_x + g1_element->width; break; diff --git a/src/editor.c b/src/editor.c index 1c5bddf084..b51cd723f2 100644 --- a/src/editor.c +++ b/src/editor.c @@ -265,7 +265,7 @@ void sub_6BD3A4() for (int i = 200; i < 204; i++) RCT2_ADDRESS(RCT2_ADDRESS_STAFF_MODE_ARRAY, uint8)[i] = STAFF_MODE_WALK; - sub_6C0C3F(); + staff_update_greyed_patrol_areas(); } /** diff --git a/src/game.c b/src/game.c index 793e6d61f6..8d1f6a2473 100644 --- a/src/game.c +++ b/src/game.c @@ -244,7 +244,7 @@ void game_update() if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) == INPUT_STATE_RESET || RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) == INPUT_STATE_NORMAL ) { - if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32)) { + if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_VIEWPORT_SCROLLING) { RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) &= ~INPUT_FLAG_VIEWPORT_SCROLLING; break; } @@ -358,7 +358,7 @@ static int game_check_affordability(int cost) } RCT2_GLOBAL(0x13CE952, uint32) = cost; RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = 827; - return 0x80000000; + return MONEY32_UNDEFINED; } static uint32 game_do_command_table[58]; @@ -411,13 +411,13 @@ int game_do_command_p(int command, int *eax, int *ebx, int *ecx, int *edx, int * } cost = *ebx; - if (cost != 0x80000000) { + if (cost != MONEY32_UNDEFINED) { // Check funds insufficientFunds = 0; if (RCT2_GLOBAL(0x009A8C28, uint8) == 1 && !(flags & 4) && !(flags & 0x20) && cost != 0) insufficientFunds = game_check_affordability(cost); - if (insufficientFunds != 0x80000000) { + if (insufficientFunds != MONEY32_UNDEFINED) { *ebx = original_ebx; *edx = original_edx; *esi = original_esi; @@ -438,7 +438,7 @@ int game_do_command_p(int command, int *eax, int *ebx, int *ecx, int *edx, int * } *edx = *ebx; - if (*edx != 0x80000000 && *edx < cost) + if (*edx != MONEY32_UNDEFINED && *edx < cost) cost = *edx; // Decrement nest count @@ -470,7 +470,7 @@ int game_do_command_p(int command, int *eax, int *ebx, int *ecx, int *edx, int * if (RCT2_GLOBAL(0x009A8C28, uint8) == 0 && (flags & 1) && RCT2_GLOBAL(0x0141F568, uint8) == RCT2_GLOBAL(0x013CA740, uint8) && !(flags & 8)) window_error_open(RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TITLE, uint16), RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16)); - return 0x80000000; + return MONEY32_UNDEFINED; } void pause_toggle() @@ -655,11 +655,11 @@ int game_load_save(const char *path) if (!load_success){ set_load_objects_fail_reason(); - if (RCT2_GLOBAL(0x9DE518,uint32) & (1<<5)){ + if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_5){ RCT2_GLOBAL(0x14241BC, uint32) = 2; //call 0x0040705E Sets cursor position and something else. Calls maybe wind func 8 probably pointless RCT2_GLOBAL(0x14241BC, uint32) = 0; - RCT2_GLOBAL(0x9DE518, uint32) &= ~(1<<5); + RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) &= ~INPUT_FLAG_5; } title_load(); rct2_endupdate(); @@ -797,7 +797,7 @@ char save_game() path_set_extension(path, ".SV6"); if (scenario_save(path, gConfigGeneral.save_plugin_data ? 1 : 0)) { - game_do_command(0, 1047, 0, -1, GAME_COMMAND_0, 0, 0); + game_do_command(0, 1047, 0, -1, GAME_COMMAND_SET_RIDE_APPEARANCE, 0, 0); gfx_invalidate_screen(); return 1; } else { @@ -874,21 +874,21 @@ void game_load_or_quit_no_save_prompt() #pragma region Game command function table static uint32 game_do_command_table[58] = { - 0x006B2FC5, + 0, 0x0066397F, 0, 0x006C511D, 0x006C5B69, 0, 0x006B3F0F, - 0x006B49D9, + 0, 0, 0x006B52D4, 0, // 10 0, 0x006660A8, 0x0066640B, - 0x006E0E01, + 0, 0x006E08F4, 0x006E650F, 0, @@ -897,17 +897,17 @@ static uint32 game_do_command_table[58] = { 0, // use new_game_command_table, original: 0x00663CCD, // 20 0x006B53E9, 0x00698D6C, // text input - 0x0068C542, - 0x0068C6D1, + 0, + 0, 0x0068BC01, - 0x006E66A0, - 0x006E6878, + 0, + 0, 0x006C5AE9, 0, // use new_game_command_table, original: 0x006BEFA1, 29 - 0x006C09D1, // 30 - 0x006C0B83, - 0x006C0BB5, - 0x00669C6D, + 0, // 30 + 0, + 0, + 0, 0, 0x006649BD, 0x006666E7, @@ -916,20 +916,20 @@ static uint32 game_do_command_table[58] = { 0, 0, // 40 0x006E519A, - 0x006E5597, + 0, 0x006B893C, - 0x006B8E1B, + 0, 0, 0, 0x006D13FE, 0, 0x006CDEE4, - 0x006B9E6D, // 50 - 0x006BA058, - 0x006E0F26, - 0x006E56B5, - 0x006B909A, - 0x006BA16A, + 0, // 50 + 0, + 0, + 0, + 0, + 0, 0x006648E3, 0 }; @@ -937,21 +937,21 @@ static uint32 game_do_command_table[58] = { void game_command_emptysub(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp) {} static GAME_COMMAND_POINTER* new_game_command_table[58] = { - game_command_emptysub, + game_command_set_ride_appearance, game_command_emptysub, game_pause_toggle, game_command_emptysub, game_command_emptysub, game_load_or_quit, game_command_emptysub, - game_command_emptysub, + game_command_demolish_ride, game_command_set_ride_status, game_command_emptysub, game_command_set_ride_name, // 10 game_command_set_ride_setting, game_command_emptysub, game_command_emptysub, - game_command_emptysub, + game_command_remove_scenery, game_command_emptysub, game_command_emptysub, game_command_place_footpath, @@ -960,16 +960,16 @@ static GAME_COMMAND_POINTER* new_game_command_table[58] = { game_command_change_surface_style, // 20 game_command_emptysub, game_command_emptysub, + game_command_raise_land, + game_command_lower_land, game_command_emptysub, - game_command_emptysub, - game_command_emptysub, - game_command_emptysub, - game_command_emptysub, + game_command_raise_water, + game_command_lower_water, game_command_emptysub, game_command_hire_new_staff_member, //game_command_emptysub, - game_command_emptysub, // 30 - game_command_emptysub, - game_command_emptysub, + game_command_set_staff_patrol, // 30 + game_command_fire_staff_member, + game_command_set_staff_order, game_command_set_park_name, game_command_set_park_open, game_command_emptysub, @@ -979,20 +979,20 @@ static GAME_COMMAND_POINTER* new_game_command_table[58] = { game_command_set_park_entrance_fee, game_command_update_staff_colour, // 40 game_command_emptysub, + game_command_remove_fence, game_command_emptysub, - game_command_emptysub, - game_command_emptysub, + game_command_remove_large_scenery, game_command_set_current_loan, game_command_set_research_funding, game_command_emptysub, game_command_start_campaign, game_command_emptysub, - game_command_emptysub, // 50 - game_command_emptysub, - game_command_emptysub, - game_command_emptysub, - game_command_emptysub, - game_command_emptysub, + game_command_place_banner, // 50 + game_command_remove_banner, + game_command_set_scenery_colour, + game_command_set_fence_colour, + game_command_set_large_scenery_colour, + game_command_set_banner_colour, game_command_emptysub, game_command_clear_scenery }; diff --git a/src/game.h b/src/game.h index 410b596d04..a60d5cd648 100644 --- a/src/game.h +++ b/src/game.h @@ -24,7 +24,7 @@ #include "common.h" enum GAME_COMMAND { - GAME_COMMAND_0, + GAME_COMMAND_SET_RIDE_APPEARANCE, GAME_COMMAND_1, GAME_COMMAND_TOGGLE_PAUSE, // 2 GAME_COMMAND_3, //Has something to do with ride construction @@ -68,14 +68,14 @@ enum GAME_COMMAND { GAME_COMMAND_41, GAME_COMMAND_REMOVE_FENCE, GAME_COMMAND_43, - GAME_COMMAND_44, + GAME_COMMAND_REMOVE_LARGE_SCENERY, GAME_COMMAND_SET_CURRENT_LOAN, // 45 GAME_COMMAND_SET_RESEARCH_FUNDING, // 46 GAME_COMMAND_47, GAME_COMMAND_START_MARKETING_CAMPAIGN, // 48 GAME_COMMAND_49, - GAME_COMMAND_50, // New banner? (possibly scenery) - GAME_COMMAND_51, // Remove banner + GAME_COMMAND_PLACE_BANNER, // New banner? (possibly scenery) + GAME_COMMAND_REMOVE_BANNER, // Remove banner GAME_COMMAND_52, GAME_COMMAND_53, GAME_COMMAND_54, diff --git a/src/input.c b/src/input.c index 33fe278e00..fce9a72caa 100644 --- a/src/input.c +++ b/src/input.c @@ -283,7 +283,7 @@ static void game_handle_input_mouse(int x, int y, int state) if (w->classification != RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DRAG_WINDOWCLASS, rct_windowclass) || w->number != RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DRAG_WINDOWNUMBER, rct_windownumber) || - !(RCT2_GLOBAL(0x9DE518, uint32)&(1 << 3)))break; + !(RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_TOOL_ACTIVE)) break; w = window_find_by_number( RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, rct_windowclass), @@ -298,7 +298,7 @@ static void game_handle_input_mouse(int x, int y, int state) RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) = 0; if (RCT2_GLOBAL(0x9DE52E, rct_windownumber) != w->number)break; - if ((RCT2_GLOBAL(0x9DE518, uint32)&(1 << 3))){ + if ((RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_TOOL_ACTIVE)){ w = window_find_by_number( RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, rct_windowclass), RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER, rct_windownumber) @@ -308,7 +308,7 @@ static void game_handle_input_mouse(int x, int y, int state) RCT2_CALLPROC_X(w->event_handlers[WE_TOOL_UP], x, y, 0, (int)RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WIDGETINDEX, uint16), (int)w, 0, 0); } else{ - if ((RCT2_GLOBAL(0x9DE518, uint32) & (1 << 4))) + if ((RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_4)) break; viewport_interaction_left_click(x, y); @@ -811,7 +811,7 @@ void process_mouse_over(int x, int y) switch (window->widgets[widgetId].type){ case WWT_VIEWPORT: - if ((RCT2_GLOBAL(0x9DE518, int) & 0x8) == 0) { + if ((RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, int) & INPUT_FLAG_TOOL_ACTIVE) == 0) { if (viewport_interaction_left_over(x, y)) { sub_6ED990(CURSOR_HAND_POINT); return; @@ -897,7 +897,7 @@ void process_mouse_over(int x, int y) */ void process_mouse_tool(int x, int y) { - if (RCT2_GLOBAL(0x9DE518, uint32) & (1 << 3)) + if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_TOOL_ACTIVE) { rct_window* w = window_find_by_number( RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, uint8), @@ -951,9 +951,9 @@ void input_state_widget_pressed(int x, int y, int state, int widgetIndex, rct_wi window_event_mouse_down_call(w, widgetIndex); } - if (RCT2_GLOBAL(0x009DE518, uint32) & 1) return; + if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_WIDGET_PRESSED) return; - RCT2_GLOBAL(0x009DE518, uint32) |= 1; + RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) |= INPUT_FLAG_WIDGET_PRESSED; widget_invalidate_by_number(cursor_w_class, cursor_w_number, widgetIndex); return; case 3: @@ -976,9 +976,9 @@ void input_state_widget_pressed(int x, int y, int state, int widgetIndex, rct_wi if (cursor_w_class != w->classification || cursor_w_number != w->number || widgetIndex != cursor_widgetIndex) goto dropdown_cleanup; dropdown_index = -1; - if (RCT2_GLOBAL(0x9DE518, uint32) & 2){ - if (!(RCT2_GLOBAL(0x9DE518, uint32) & 4)){ - RCT2_GLOBAL(0x9DE518, uint32) |= (1 << 2); + if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_DROPDOWN_STAY_OPEN){ + if (!(RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_DROPDOWN_MOUSE_UP)){ + RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) |= INPUT_FLAG_DROPDOWN_MOUSE_UP; return; } } @@ -986,8 +986,8 @@ void input_state_widget_pressed(int x, int y, int state, int widgetIndex, rct_wi window_close_by_class(WC_DROPDOWN); cursor_w = window_find_by_number(cursor_w_class, cursor_w_number); - if (RCT2_GLOBAL(0x9DE518, uint32) & 1) { - RCT2_GLOBAL(0x9DE518, uint32) &= 0xFFFE; + if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_WIDGET_PRESSED) { + RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) &= 0xFFFE; widget_invalidate_by_number(cursor_w_class, cursor_w_number, cursor_widgetIndex); } @@ -1030,8 +1030,8 @@ void input_state_widget_pressed(int x, int y, int state, int widgetIndex, rct_wi RCT2_GLOBAL(0x9DE528, uint16) = 0; if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_STATE, uint8) != 5){ // Hold down widget and drag outside of area?? - if (RCT2_GLOBAL(0x9DE518, uint32) & 1){ - RCT2_GLOBAL(0x9DE518, uint32) &= 0xFFFE; + if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_WIDGET_PRESSED){ + RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) &= 0xFFFE; widget_invalidate_by_number(cursor_w_class, cursor_w_number, cursor_widgetIndex); } return; diff --git a/src/input.h b/src/input.h index e28adf0b09..3d36382961 100644 --- a/src/input.h +++ b/src/input.h @@ -26,11 +26,12 @@ enum { INPUT_FLAG_WIDGET_PRESSED = (1 << 0), - // Related to dropdowns, set on flag 0x80 - INPUT_FLAG_1 = (1 << 1), + // The dropdown can stay open if the mouse is released, set on flag DROPDOWN_FLAG_STAY_OPEN + INPUT_FLAG_DROPDOWN_STAY_OPEN = (1 << 1), - // Related to dropdowns - INPUT_FLAG_2 = (1 << 2), + // The mouse has been released and the dropdown is still open + // INPUT_FLAG_DROPDOWN_STAY_OPEN is already set if this happens + INPUT_FLAG_DROPDOWN_MOUSE_UP = (1 << 2), INPUT_FLAG_TOOL_ACTIVE = (1 << 3), diff --git a/src/interface/viewport.c b/src/interface/viewport.c index ef09d18d8c..80b93835be 100644 --- a/src/interface/viewport.c +++ b/src/interface/viewport.c @@ -45,19 +45,34 @@ struct paint_struct{ uint32 var_04; uint16 attached_x; // 0x08 uint16 attached_y; // 0x0A - uint8 var_0C; - uint8 pad_0D; - paint_struct* next_attached_ps; //0x0E - uint8 pad_12[2]; + union { + struct { + uint8 var_0C; + uint8 pad_0D; + paint_struct* next_attached_ps; //0x0E + uint16 pad_12; + }; + struct { + uint16 some_x; // 0x0C + uint16 some_y; // 0x0E + uint16 other_x; // 0x10 + uint16 other_y; // 0x12 + }; + }; uint16 x; // 0x14 uint16 y; // 0x16 - uint8 pad_18[2]; + uint16 var_18; uint8 var_1A; - uint8 pad_1B; + uint8 var_1B; paint_struct* attached_ps; //0x1C paint_struct* var_20; paint_struct* var_24; uint8 sprite_type; //0x28 + uint8 var_29; + uint16 pad_2A; + uint16 map_x; // 0x2C + uint16 map_y; // 0x2E + rct_map_element *mapElement; // 0x30 }; /** @@ -1346,6 +1361,133 @@ void sub_0x68B6C2(){ } } +void sub_688217_helper(uint16 ax, uint8 flag) +{ + paint_struct *ps; + paint_struct *ps_next = RCT2_GLOBAL(0x00EE7884, paint_struct*); + + do { + ps = ps_next; + ps_next = ps_next->var_24; + if (ps_next == NULL) return; + } while (ax > ps_next->var_18); + + RCT2_GLOBAL(0x00F1AD14, paint_struct*) = ps; + + do { + ps = ps->var_24; + if (ps == NULL) break; + + if (ps->var_18 > ax + 1) { + ps->var_1B = 1 << 7; + } else if (ps->var_18 == ax + 1) { + ps->var_1B = (1 << 1) | (1 << 0); + } else if (ps->var_18 == ax) { + ps->var_1B = flag | (1 << 0); + } + } while (ps->var_18 <= ax + 1); + + ps = RCT2_GLOBAL(0x00F1AD14, paint_struct*); + + while (true) { + while (true) { + ps_next = ps->var_24; + if (ps_next == NULL) return; + if (ps_next->var_1B & (1 << 7)) return; + if (ps_next->var_1B & (1 << 0)) break; + ps = ps_next; + } + + ps_next->var_1B &= ~(1 << 0); + RCT2_GLOBAL(0x00F1AD18, paint_struct*) = ps; + + uint16 my_attached_x = ps_next->attached_x; + uint16 my_attached_y = ps_next->attached_y; + uint16 my_some_x = ps_next->some_x; + uint16 my_some_y = ps_next->some_y; + uint16 my_other_x = ps_next->other_x; + uint16 my_other_y = ps_next->other_y; + + while (true) { + ps = ps_next; + ps_next = ps_next->var_24; + if (ps_next == NULL) break; + if (ps_next->var_1B & (1 << 7)) break; + if (!(ps_next->var_1B & (1 << 1))) continue; + + int yes = 0; + switch (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint32)) { + case 0: + if (my_some_y >= ps_next->some_x && my_other_y >= ps_next->attached_y && my_other_x >= ps_next->attached_x + && !(my_some_x < ps_next->some_y && my_attached_y < ps_next->other_y && my_attached_x < ps_next->other_x)) + yes = 1; + break; + case 1: + if (my_some_y >= ps_next->some_x && my_other_y >= ps_next->attached_y && my_other_x < ps_next->attached_x + && !(my_some_x < ps_next->some_y && my_attached_y < ps_next->other_y && my_attached_x >= ps_next->other_x)) + yes = 1; + break; + case 2: + if (my_some_y >= ps_next->some_x && my_other_y < ps_next->attached_y && my_other_x < ps_next->attached_x + && !(my_some_x < ps_next->some_y && my_attached_y >= ps_next->other_y && my_attached_x >= ps_next->other_x)) + yes = 1; + break; + case 3: + if (my_some_y >= ps_next->some_x && my_other_y < ps_next->attached_y && my_other_x >= ps_next->attached_x + && !(my_some_x < ps_next->some_y && my_attached_y >= ps_next->other_y && my_attached_x < ps_next->other_x)) + yes = 1; + break; + } + + if (yes) { + ps->var_24 = ps_next->var_24; + paint_struct *ps_temp = RCT2_GLOBAL(0x00F1AD18, paint_struct*)->var_24; + RCT2_GLOBAL(0x00F1AD18, paint_struct*)->var_24 = ps_next; + ps_next->var_24 = ps_temp; + ps_next = ps; + } + } + + ps = RCT2_GLOBAL(0x00F1AD18, paint_struct*); + } +} + +/** +* +* rct2: 0x00688217 +*/ +void sub_688217() +{ + paint_struct *ps = RCT2_GLOBAL(0x00EE7888, paint_struct*); + paint_struct *ps_next; + RCT2_GLOBAL(0x00EE7888, uint32) += 0x34; // 0x34 is size of paint_struct? + RCT2_GLOBAL(0x00EE7884, paint_struct*) = ps; + ps->var_24 = NULL; + uint32 edi = RCT2_GLOBAL(0x00F1AD0C, uint32); + if (edi == -1) + return; + + do { + ps_next = RCT2_GLOBAL(0x00F1A50C + 4 * edi, paint_struct*); + if (ps_next != NULL) { + ps->var_24 = ps_next; + do { + ps = ps_next; + ps_next = ps_next->var_24; + } while (ps_next != NULL); + } + } while (++edi <= RCT2_GLOBAL(0x00F1AD10, uint32)); + + uint32 eax = RCT2_GLOBAL(0x00F1AD0C, uint32); + + sub_688217_helper(eax & 0xFFFF, 1 << 1); + + eax = RCT2_GLOBAL(0x00F1AD0C, uint32); + + while (++eax < RCT2_GLOBAL(0x00F1AD10, uint32)) + sub_688217_helper(eax & 0xFFFF, 0); +} + /** * * rct2:0x00685CBF @@ -1439,7 +1581,8 @@ void viewport_paint(rct_viewport* viewport, rct_drawpixelinfo* dpi, int left, in sub_0x68615B(0xEE788C); //Memory copy sub_0x68B6C2(); //RCT2_CALLPROC_X(0x68B6C2, 0, 0, 0, 0, 0, 0, 0); //Big function call 4 rotation versions - RCT2_CALLPROC_X(0x688217, start_x, ebx, ecx, (int)bits_pointer, esi, (int)dpi2, ebp); //Move memory + sub_688217(); + //RCT2_CALLPROC_X(0x688217, start_x, ebx, ecx, (int)bits_pointer, esi, (int)dpi2, ebp); //Move memory sub_688485(); //RCT2_CALLPROC_EBPSAFE(0x688485); //Big function call @@ -1466,10 +1609,10 @@ void viewport_paint(rct_viewport* viewport, rct_drawpixelinfo* dpi, int left, in * viewport: edi */ void sub_688972(int screenX, int screenY, sint16 *x, sint16 *y, rct_viewport **viewport) { - int my_x, my_y, z; + int my_x, my_y, z, interactionType; rct_viewport *myViewport; - get_map_coordinates_from_pos(screenX, screenY, 0xFFFE, &my_x, &my_y, &z, NULL, &myViewport); - if (z == 0) { + get_map_coordinates_from_pos(screenX, screenY, VIEWPORT_INTERACTION_MASK_TERRAIN, &my_x, &my_y, &interactionType, NULL, &myViewport); + if (interactionType == VIEWPORT_INTERACTION_ITEM_NONE) { *x = 0x8000; return; } @@ -1727,6 +1870,79 @@ void viewport_set_visibility(uint8 mode) } } +/** + * Stores some info about the element pointed at, if requested for this particular type through the interaction mask. + * rct2: 0x00688697 + */ +void store_interaction_info(paint_struct *ps) +{ + if (RCT2_GLOBAL(0x0141F569, uint8) == 0) return; + + if (ps->sprite_type == VIEWPORT_INTERACTION_ITEM_NONE + || ps->sprite_type == 11 // 11 as a type seems to not exist, maybe part of the typo mentioned later on. + || ps->sprite_type > VIEWPORT_INTERACTION_ITEM_BANNER) return; + + uint16 mask; + if (ps->sprite_type == VIEWPORT_INTERACTION_ITEM_BANNER) + // I think CS made a typo here. Let's replicate the original behaviour. + mask = 1 << (ps->sprite_type - 3); + else + mask = 1 << (ps->sprite_type - 1); + + if (!(RCT2_GLOBAL(0x009AC154, uint16) & mask)) { + RCT2_GLOBAL(0x009AC148, uint8) = ps->sprite_type; + RCT2_GLOBAL(0x009AC149, uint8) = ps->var_29; + RCT2_GLOBAL(0x009AC14C, uint32) = ps->map_x; + RCT2_GLOBAL(0x009AC14E, uint32) = ps->map_y; + RCT2_GLOBAL(0x009AC150, rct_map_element*) = ps->mapElement; + } +} + +/** + * rct2: 0x0068862C + */ +void sub_68862C() +{ + rct_drawpixelinfo *dpi = RCT2_GLOBAL(0x0140E9A8, rct_drawpixelinfo*); + paint_struct *ps = RCT2_GLOBAL(0x00EE7884, paint_struct*), *old_ps, *next_ps, *attached_ps; + uint32 eax = 0xBBBBBBBB, ebx = 0xBBBBBBBB, ecx = 0xBBBBBBBB, edx = 0xBBBBBBBB, esi = 0xBBBBBBBB, edi = 0xBBBBBBBB, ebp = 0xBBBBBBBB; + + while ((ps = ps->var_24) != NULL) { + old_ps = ps; + + next_ps = ps; + while (next_ps != NULL) { + ps = next_ps; + ebx = ps->image_id; + ecx = ps->x; + edx = ps->y; + edi = (uint32)dpi; + ebp = (uint32)ps; + //sub_679023(ps->image_id, ps->x, ps->y, dpi); + RCT2_CALLFUNC_X(0x00679023, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + store_interaction_info(ps); + + next_ps = ps->var_20; + } + + attached_ps = ps->attached_ps; + while (attached_ps != NULL) { + esi = (uint32)attached_ps; + ebp = (uint32)ps; + ecx = (attached_ps->attached_x + ps->x) & 0xFFFF; + edx = (attached_ps->attached_y + ps->y) & 0xFFFF; + ebx = attached_ps->image_id; + //sub_679023(ebx, ecx, edx, dpi); + RCT2_CALLFUNC_X(0x00679023, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + store_interaction_info(ps); + + attached_ps = attached_ps->next_attached_ps; + } + + ps = old_ps; + } +} + /** * * rct2: 0x00685ADC @@ -1739,7 +1955,7 @@ void viewport_set_visibility(uint8 mode) * mapElement: edx * viewport: edi */ -void get_map_coordinates_from_pos(int screenX, int screenY, int flags, int *x, int *y, int *z, rct_map_element **mapElement, rct_viewport **viewport) +void get_map_coordinates_from_pos(int screenX, int screenY, int flags, int *x, int *y, int *interactionType, rct_map_element **mapElement, rct_viewport **viewport) { RCT2_GLOBAL(0x9AC154, uint16_t) = flags & 0xFFFF; RCT2_GLOBAL(0x9AC148, uint8_t) = 0; @@ -1772,12 +1988,12 @@ void get_map_coordinates_from_pos(int screenX, int screenY, int flags, int *x, i RCT2_GLOBAL(0x140E9A8, rct_drawpixelinfo*) = dpi; sub_0x68615B(0xEE788C); sub_0x68B6C2(); - RCT2_CALLPROC_X(0x688217, 0, 0, 0, 0, 0, 0, 0); - RCT2_CALLPROC_X(0x68862C, 0, 0, 0, 0, 0, 0, 0); + sub_688217(); + sub_68862C(); } if (viewport != NULL) *viewport = myviewport; } - if (z != NULL) *z = RCT2_GLOBAL(0x9AC148, uint8_t); + if (interactionType != NULL) *interactionType = RCT2_GLOBAL(0x9AC148, uint8_t); if (x != NULL) *x = (int)RCT2_GLOBAL(0x9AC14C, int16_t); if (y != NULL) *y = (int)RCT2_GLOBAL(0x9AC14E, int16_t); if (mapElement != NULL) *mapElement = RCT2_GLOBAL(0x9AC150, rct_map_element*); diff --git a/src/interface/viewport.h b/src/interface/viewport.h index e53019738f..7bd3d1b23d 100644 --- a/src/interface/viewport.h +++ b/src/interface/viewport.h @@ -46,10 +46,11 @@ enum { enum { VIEWPORT_INTERACTION_ITEM_NONE, - - VIEWPORT_INTERACTION_ITEM_SPRITE = 2, + VIEWPORT_INTERACTION_ITEM_TERRAIN, + VIEWPORT_INTERACTION_ITEM_SPRITE, VIEWPORT_INTERACTION_ITEM_RIDE, - VIEWPORT_INTERACTION_ITEM_SCENERY = 5, + VIEWPORT_INTERACTION_ITEM_WATER, + VIEWPORT_INTERACTION_ITEM_SCENERY, VIEWPORT_INTERACTION_ITEM_FOOTPATH, VIEWPORT_INTERACTION_ITEM_FOOTPATH_ITEM, VIEWPORT_INTERACTION_ITEM_PARK, @@ -59,6 +60,21 @@ enum { }; +enum { + VIEWPORT_INTERACTION_MASK_NONE = 0, + VIEWPORT_INTERACTION_MASK_TERRAIN = ~(1 << (VIEWPORT_INTERACTION_ITEM_TERRAIN - 1)), + VIEWPORT_INTERACTION_MASK_SPRITE = ~(1 << (VIEWPORT_INTERACTION_ITEM_SPRITE - 1)), + VIEWPORT_INTERACTION_MASK_RIDE = ~(1 << (VIEWPORT_INTERACTION_ITEM_RIDE - 1)), + VIEWPORT_INTERACTION_MASK_WATER = ~(1 << (VIEWPORT_INTERACTION_ITEM_WATER - 1)), + VIEWPORT_INTERACTION_MASK_SCENERY = ~(1 << (VIEWPORT_INTERACTION_ITEM_SCENERY - 1)), + VIEWPORT_INTERACTION_MASK_FOOTPATH = ~(1 << (VIEWPORT_INTERACTION_ITEM_FOOTPATH - 1)), + VIEWPORT_INTERACTION_MASK_FOOTPATH_ITEM = ~(1 << (VIEWPORT_INTERACTION_ITEM_FOOTPATH_ITEM - 1)), + VIEWPORT_INTERACTION_MASK_PARK = ~(1 << (VIEWPORT_INTERACTION_ITEM_PARK - 1)), + VIEWPORT_INTERACTION_MASK_WALL = ~(1 << (VIEWPORT_INTERACTION_ITEM_WALL - 1)), + VIEWPORT_INTERACTION_MASK_LARGE_SCENERY = ~(1 << (VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY - 1)), + VIEWPORT_INTERACTION_MASK_BANNER = ~(1 << (VIEWPORT_INTERACTION_ITEM_BANNER - 2)), // Note the -2 for BANNER +}; + typedef struct { int type; int x; @@ -97,7 +113,7 @@ void show_construction_rights(); void hide_construction_rights(); void viewport_set_visibility(uint8 mode); -void get_map_coordinates_from_pos(int screenX, int screenY, int flags, int *x, int *y, int *z, rct_map_element **mapElement, rct_viewport **viewport); +void get_map_coordinates_from_pos(int screenX, int screenY, int flags, int *x, int *y, int *interactionType, rct_map_element **mapElement, rct_viewport **viewport); int viewport_interaction_get_item_left(int x, int y, viewport_interaction_info *info); int viewport_interaction_left_over(int x, int y); @@ -105,5 +121,6 @@ int viewport_interaction_left_click(int x, int y); int viewport_interaction_get_item_right(int x, int y, viewport_interaction_info *info); int viewport_interaction_right_over(int x, int y); int viewport_interaction_right_click(int x, int y); +void sub_68A15E(int screenX, int screenY, short *x, short *y, int *direction, rct_map_element **mapElement); #endif diff --git a/src/interface/viewport_interaction.c b/src/interface/viewport_interaction.c index 9a33a1821d..7c131adaae 100644 --- a/src/interface/viewport_interaction.c +++ b/src/interface/viewport_interaction.c @@ -29,6 +29,7 @@ #include "../world/map.h" #include "../world/scenery.h" #include "../world/sprite.h" +#include "../input.h" #include "viewport.h" static void viewport_interaction_remove_scenery(rct_map_element *mapElement, int x, int y); @@ -58,7 +59,7 @@ int viewport_interaction_get_item_left(int x, int y, viewport_interaction_info * if ((RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TRACK_DESIGNER) && s6Info->var_000 != 6) return info->type = VIEWPORT_INTERACTION_ITEM_NONE; - get_map_coordinates_from_pos(x, y, 0xFF79, &info->x, &info->y, &info->type, &info->mapElement, NULL); + get_map_coordinates_from_pos(x, y, VIEWPORT_INTERACTION_MASK_SPRITE & VIEWPORT_INTERACTION_MASK_RIDE & VIEWPORT_INTERACTION_MASK_PARK, &info->x, &info->y, &info->type, &info->mapElement, NULL); mapElement = info->mapElement; sprite = (rct_sprite*)mapElement; @@ -178,7 +179,7 @@ int viewport_interaction_get_item_right(int x, int y, viewport_interaction_info if ((RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TRACK_DESIGNER) && s6Info->var_000 != 6) return info->type = VIEWPORT_INTERACTION_ITEM_NONE; - get_map_coordinates_from_pos(x, y, 9, &info->x, &info->y, &info->type, &info->mapElement, NULL); + get_map_coordinates_from_pos(x, y, ~(VIEWPORT_INTERACTION_MASK_TERRAIN & VIEWPORT_INTERACTION_MASK_WATER), &info->x, &info->y, &info->type, &info->mapElement, NULL); mapElement = info->mapElement; sprite = (rct_sprite*)mapElement; @@ -264,7 +265,7 @@ int viewport_interaction_get_item_right(int x, int y, viewport_interaction_info return info->type; } - if ((RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & 0x48) != 0x48) + if ((RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & (INPUT_FLAG_6 | INPUT_FLAG_TOOL_ACTIVE)) != (INPUT_FLAG_6 | INPUT_FLAG_TOOL_ACTIVE)) if (window_find_by_class(WC_RIDE_CONSTRUCTION) == NULL && window_find_by_class(WC_FOOTPATH) == NULL) return info->type = VIEWPORT_INTERACTION_ITEM_NONE; @@ -283,7 +284,7 @@ int viewport_interaction_get_item_right(int x, int y, viewport_interaction_info return info->type; case VIEWPORT_INTERACTION_ITEM_FOOTPATH_ITEM: - sceneryEntry = RCT2_ADDRESS(0x9ADA50, rct_scenery_entry*)[mapElement->properties.path.additions & 0x0F]; + sceneryEntry = g_pathBitSceneryEntries[(mapElement->properties.path.additions & 0x0F) - 1]; RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS + 0, uint16) = 1164; if (mapElement->flags & 0x20) { RCT2_GLOBAL(RCT2_ADDRESS_MAP_TOOLTIP_ARGS + 2, uint16) = 3124; @@ -473,7 +474,7 @@ static void viewport_interaction_remove_park_wall(rct_map_element *mapElement, i { rct_scenery_entry* sceneryEntry; - sceneryEntry = g_wallSceneryEntries[mapElement->properties.fence.slope]; + sceneryEntry = g_wallSceneryEntries[mapElement->properties.fence.type]; if (sceneryEntry->wall.var_0D != 0xFF){ window_sign_small_open(mapElement->properties.fence.item[0]); } else { @@ -496,12 +497,9 @@ static void viewport_interaction_remove_park_wall(rct_map_element *mapElement, i */ static void viewport_interaction_remove_large_scenery(rct_map_element *mapElement, int x, int y) { - int ebx; rct_scenery_entry* sceneryEntry; - ebx = mapElement->properties.scenerymultiple.type; - ebx |= (mapElement->properties.scenerymultiple.index & 0x3) << 8; - sceneryEntry = g_largeSceneryEntries[ebx]; + sceneryEntry = g_largeSceneryEntries[mapElement->properties.scenerymultiple.type & MAP_ELEMENT_LARGE_TYPE_MASK]; if (sceneryEntry->large_scenery.var_11 != 0xFF){ int id = (mapElement->type & 0xC0) | @@ -514,8 +512,8 @@ static void viewport_interaction_remove_large_scenery(rct_map_element *mapElemen x, 1 | ((mapElement->type & 0x3) << 8), y, - mapElement->base_height | ((mapElement->properties.scenerymultiple.index >> 2) << 8), - GAME_COMMAND_44, + mapElement->base_height | ((mapElement->properties.scenerymultiple.type >> 10) << 8), + GAME_COMMAND_REMOVE_LARGE_SCENERY, 0, 0 ); @@ -560,4 +558,72 @@ static rct_peep *viewport_interaction_get_closest_peep(int x, int y, int maxDist } return closestPeep; +} + +/** + * + * rct2: 0x0068A15E + */ +void sub_68A15E(int screenX, int screenY, short *x, short *y, int *direction, rct_map_element **mapElement) +{ + int my_x, my_y, z, interactionType; + rct_map_element *myMapElement; + rct_viewport *viewport; + get_map_coordinates_from_pos(screenX, screenY, VIEWPORT_INTERACTION_MASK_TERRAIN & VIEWPORT_INTERACTION_MASK_WATER, &my_x, &my_y, &interactionType, &myMapElement, &viewport); + + if (interactionType == VIEWPORT_INTERACTION_ITEM_NONE) { + *x = 0x8000; + return; + } + + RCT2_GLOBAL(0x00F1AD3E, uint8) = interactionType; + RCT2_GLOBAL(0x00F1AD30, rct_map_element*) = myMapElement; + + if (interactionType == VIEWPORT_INTERACTION_ITEM_WATER) { + z = myMapElement->properties.surface.terrain; + z = (z & MAP_ELEMENT_WATER_HEIGHT_MASK) << 4; + } + + RCT2_GLOBAL(0x00F1AD3C, uint16) = z; + RCT2_GLOBAL(0x00F1AD34, sint16) = my_x; + RCT2_GLOBAL(0x00F1AD36, sint16) = my_y; + RCT2_GLOBAL(0x00F1AD38, sint16) = my_x + 31; + RCT2_GLOBAL(0x00F1AD3A, sint16) = my_y + 31; + + rct_xy16 start_vp_pos = screen_coord_to_viewport_coord(viewport, screenX, screenY); + rct_xy16 map_pos = { my_x + 16, my_y + 16 }; + + for (int i = 0; i < 5; i++) { + if (RCT2_GLOBAL(0x00F1AD3E, uint8) != 4) { + z = map_element_height(map_pos.x, map_pos.y); + } else { + z = RCT2_GLOBAL(0x00F1AD3C, uint16); + } + map_pos = viewport_coord_to_map_coord(start_vp_pos.x, start_vp_pos.y, z); + map_pos.x = clamp(RCT2_GLOBAL(0x00F1AD34, sint16), map_pos.x, RCT2_GLOBAL(0x00F1AD38, sint16)); + map_pos.y = clamp(RCT2_GLOBAL(0x00F1AD36, sint16), map_pos.y, RCT2_GLOBAL(0x00F1AD3A, sint16)); + } + + // Determine to which edge the cursor is closest + int myDirection; + int mod_x = map_pos.x & 0x1F; + int mod_y = map_pos.y & 0x1F; + if (mod_x < mod_y) { + if (mod_x + mod_y < 32) { + myDirection = 0; + } else { + myDirection = 1; + } + } else { + if (mod_x + mod_y < 32) { + myDirection = 3; + } else { + myDirection = 2; + } + } + + *x = map_pos.x & ~0x1F; + *y = map_pos.y & ~0x1F; + if (direction != NULL) *direction = myDirection; + if (mapElement != NULL) *mapElement = myMapElement; } \ No newline at end of file diff --git a/src/localisation/string_ids.h b/src/localisation/string_ids.h index f1fef1b95e..eca3eac66f 100644 --- a/src/localisation/string_ids.h +++ b/src/localisation/string_ids.h @@ -192,6 +192,8 @@ enum { STR_LOCATE_SUBJECT_TIP = 1027, STR_OFF_EDGE_OF_MAP = 1028, + STR_CAN_ONLY_BUILD_THIS_ON_LAND = 1034, + STR_LOAD_GAME_DIALOG_TITLE = 1036, STR_LOAD_LANDSCAPE_DIALOG_TITLE = 1037, STR_CONVERT_SAVED_GAME_TO_SCENARIO_1038 = 1038, @@ -319,6 +321,7 @@ enum { STR_BUILD_FOOTPATH_TIP = 1173, + STR_BANNER_SIGN_IN_THE_WAY = 1174, STR_CANT_BUILD_THIS_ON_SLOPED_FOOTPATH = 1175, STR_CANT_BUILD_FOOTPATH_HERE = 1176, STR_CANT_REMOVE_FOOTPATH_FROM_HERE = 1177, @@ -377,6 +380,7 @@ enum { STR_SHOPS_STALLS_TIP = 1228, STR_ROTATE_OBJECTS_90 = 1327, + STR_LEVEL_LAND_REQUIRED = 1328, STR_LAUNCH_SPEED = 1329, STR_LAUNCH_SPEED_TIP = 1330, @@ -739,6 +743,8 @@ enum { STR_CANT_DEMOLISH_RIDE = 2248, + STR_CAN_ONLY_BE_BUILT_ACROSS_PATHS = 2252, + STR_RESEARCH_TRANSPORT_RIDES = 2253, STR_RESEARCH_GENTLE_RIDES = 2254, STR_RESEARCH_ROLLER_COASTERS = 2255, @@ -1066,6 +1072,8 @@ enum { STR_MUSIC_ACKNOWLEDGEMENTS_ELLIPSIS = 2862, STR_MUSIC_ACKNOWLEDGEMENTS = 2863, + STR_TOO_MANY_BANNERS_IN_GAME = 2980, + STR_CHANGE_BANNER_TEXT_TIP = 2986, STR_SET_AS_NO_ENTRY_BANNER_TIP = 2987, STR_DEMOLISH_BANNER_TIP = 2988, diff --git a/src/management/finance.h b/src/management/finance.h index ffd8da01d5..547d4d513e 100644 --- a/src/management/finance.h +++ b/src/management/finance.h @@ -26,7 +26,7 @@ typedef int rct_expenditure_type; enum { - RCT_EXPENDITURE_TYPE_RIDE_CONSRUCTION, + RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION, RCT_EXPENDITURE_TYPE_RIDE_RUNNING_COSTS, RCT_EXPENDITURE_TYPE_LAND_PURCHASE, RCT_EXPENDITURE_TYPE_LANDSCAPING, diff --git a/src/object.c b/src/object.c index 4be671b373..7f2be9d177 100644 --- a/src/object.c +++ b/src/object.c @@ -907,10 +907,11 @@ int paint_large_scenery(int flags, int ebx, int ecx, int edx, rct_drawpixelinfo* chunk += 1038; } - scenery_type->large_scenery.var_0C = (uint32)chunk; + scenery_type->large_scenery.tiles = (rct_large_scenery_tile*)chunk; + // skip over large scenery tiles while (*((uint16*)chunk) != 0xFFFF){ - chunk += 9; + chunk += sizeof(rct_large_scenery_tile); } chunk += 2; @@ -937,7 +938,7 @@ int paint_large_scenery(int flags, int ebx, int ecx, int edx, rct_drawpixelinfo* rct_scenery_entry* scenery_type = (rct_scenery_entry*)esi; scenery_type->name = 0; scenery_type->image = 0; - scenery_type->large_scenery.var_0C = 0; + scenery_type->large_scenery.tiles = 0; scenery_type->large_scenery.scenery_tab_id = 0; scenery_type->large_scenery.var_12 = 0; scenery_type->large_scenery.var_16 = 0; diff --git a/src/peep/peep.c b/src/peep/peep.c index 2bec185976..779273b61a 100644 --- a/src/peep/peep.c +++ b/src/peep/peep.c @@ -175,6 +175,21 @@ static void sub_693BE5(rct_peep* peep, uint8 al){ sub_693B58(peep); } +/** +* +* rct2: 0x0069A512 +*/ +void remove_peep_from_ride(rct_peep* peep) +{ + if (peep->state == PEEP_STATE_QUEUING) { + remove_peep_from_queue(peep); + } + peep_decrement_num_riders(peep); + peep->state = PEEP_STATE_1; + peep_window_state_update(peep); + sub_693BE5(peep, 0); +} + static void peep_state_reset(rct_peep* peep){ peep_decrement_num_riders(peep); peep->state = PEEP_STATE_1; @@ -198,8 +213,8 @@ void peep_check_if_lost(rct_peep* peep){ peep->var_F4 = 230; } peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_LOST, 0xFF); - if (peep->happiness_growth_rate < 30) peep->happiness_growth_rate = 0; - else peep->happiness_growth_rate -= 30; + + peep->happiness_growth_rate = max(peep->happiness_growth_rate - 30, 0); } /* rct2: 0x69C26B @@ -211,8 +226,7 @@ void peep_check_cant_find_ride(rct_peep* peep){ if (peep->var_C6 == 30 || peep->var_C6 == 60){ peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_CANT_FIND, peep->guest_heading_to_ride_id); - if (peep->happiness_growth_rate < 30) peep->happiness_growth_rate = 0; - else peep->happiness_growth_rate -= 30; + peep->happiness_growth_rate = max(peep->happiness_growth_rate - 30, 0); } peep->var_C6--; @@ -237,8 +251,7 @@ void peep_check_cant_find_exit(rct_peep* peep){ if (peep->var_C6 == 1){ peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_CANT_FIND_EXIT, 0xFF); - if (peep->happiness_growth_rate < 30) peep->happiness_growth_rate = 0; - else peep->happiness_growth_rate -= 30; + peep->happiness_growth_rate = max(peep->happiness_growth_rate - 30, 0); } if (--peep->var_C6 == 0) peep->var_C6 = 90; @@ -558,7 +571,7 @@ void peep_sprite_remove(rct_peep* peep){ //RCT2_CALLPROC_X(0x69A535, 0, 0, 0, 0, (int)peep, 0, 0); //return; - RCT2_CALLPROC_X(0x0069A512, 0, 0, 0, 0, (int)peep, 0, 0); + remove_peep_from_ride(peep); invalidate_sprite((rct_sprite*)peep); window_close_by_number(WC_PEEP, peep->sprite_index); @@ -575,7 +588,7 @@ void peep_sprite_remove(rct_peep* peep){ RCT2_ADDRESS(RCT2_ADDRESS_STAFF_MODE_ARRAY, uint8)[peep->staff_id] = 0; peep->type = 0xFF; - sub_6C0C3F(); + staff_update_greyed_patrol_areas(); peep->type = PEEP_TYPE_STAFF; news_item_disable_news(NEWS_ITEM_PEEP, peep->sprite_index); @@ -3109,7 +3122,7 @@ static int peep_update_walking_find_bench(rct_peep* peep){ uint8 additions = map_element->properties.path.additions & 0xF; if (!additions) return 0; - rct_scenery_entry* sceneryEntry = RCT2_ADDRESS(0x9ADA50, rct_scenery_entry*)[additions]; + rct_scenery_entry* sceneryEntry = g_pathBitSceneryEntries[additions - 1]; if (!(sceneryEntry->path_bit.var_06 & 0x2))return 0; @@ -3186,7 +3199,7 @@ static int peep_update_walking_find_bin(rct_peep* peep){ uint8 additions = map_element->properties.path.additions & 0xF; if (!additions) return 0; - rct_scenery_entry* sceneryEntry = RCT2_ADDRESS(0x9ADA50, rct_scenery_entry*)[additions]; + rct_scenery_entry* sceneryEntry = g_pathBitSceneryEntries[additions - 1]; if (!(sceneryEntry->path_bit.var_06 & 0x1))return 0; @@ -3263,7 +3276,7 @@ static void peep_update_walking_break_scenery(rct_peep* peep){ uint8 additions = map_element->properties.path.additions & 0xF; if (!additions) return; - rct_scenery_entry* sceneryEntry = RCT2_ADDRESS(0x9ADA50, rct_scenery_entry*)[additions]; + rct_scenery_entry* sceneryEntry = g_pathBitSceneryEntries[additions - 1]; if (!(sceneryEntry->path_bit.var_06 & 0x4))return; @@ -3439,7 +3452,7 @@ static void peep_update_using_bin(rct_peep* peep){ return; } - rct_scenery_entry* sceneryEntry = RCT2_ADDRESS(0x9ADA50, rct_scenery_entry*)[additions]; + rct_scenery_entry* sceneryEntry = g_pathBitSceneryEntries[additions - 1]; if (!(sceneryEntry->path_bit.var_06 & 1)){ peep_state_reset(peep); return; @@ -3830,7 +3843,7 @@ static int peep_update_patrolling_find_bin(rct_peep* peep){ if (additions == 0)return 0; - rct_scenery_entry* sceneryEntry = RCT2_ADDRESS(0x9ADA50, rct_scenery_entry*)[additions]; + rct_scenery_entry* sceneryEntry = g_pathBitSceneryEntries[additions - 1]; if (!(sceneryEntry->path_bit.var_06 & 1)) return 0; @@ -4123,7 +4136,7 @@ static void peep_update_walking(rct_peep* peep){ if (additions){ if (!(map_element->properties.path.additions & 0x80)){ - rct_scenery_entry* sceneryEntry = RCT2_ADDRESS(0x9ADA50, rct_scenery_entry*)[additions]; + rct_scenery_entry* sceneryEntry = g_pathBitSceneryEntries[additions - 1]; if (!(sceneryEntry->path_bit.var_06 & 0x2)) ebp = 9; } @@ -4564,7 +4577,7 @@ void peep_update_crowd_noise() // Load and play crowd noise #ifdef USE_MIXER if (!gCrowdSoundChannel) { - gCrowdSoundChannel = Mixer_Play_Music(PATH_ID_CSS2); + gCrowdSoundChannel = Mixer_Play_Music(PATH_ID_CSS2, false); } if (gCrowdSoundChannel) { Mixer_Channel_Volume(gCrowdSoundChannel, DStoMixerVolume(volume)); diff --git a/src/peep/peep.h b/src/peep/peep.h index 0286344365..9dd9d26a89 100644 --- a/src/peep/peep.h +++ b/src/peep/peep.h @@ -389,7 +389,7 @@ typedef struct { uint8 energy; // 0x38 uint8 energy_growth_rate; // 0x39 uint8 happiness; // 0x3A - sint8 happiness_growth_rate; // 0x3B + uint8 happiness_growth_rate; // 0x3B uint8 nausea; // 0x3C uint8 nausea_growth_rate; // 0x3D uint8 hunger; // 0x3E @@ -570,6 +570,7 @@ int peep_check_easteregg_name(int index, rct_peep *peep); int peep_get_easteregg_name_id(rct_peep *peep); int peep_is_mechanic(rct_peep *peep); int peep_has_food(rct_peep* peep); +void peep_sprite_remove(rct_peep* peep); void peep_window_state_update(rct_peep* peep); void peep_decrement_num_riders(rct_peep* peep); @@ -583,6 +584,7 @@ void peep_insert_new_thought(rct_peep *peep, uint8 thought_type, uint8 thought_a void peep_set_map_tooltip(rct_peep *peep); void sub_693B58(rct_peep* peep); +void remove_peep_from_ride(rct_peep* peep); void remove_peep_from_queue(rct_peep* peep); #endif diff --git a/src/peep/staff.c b/src/peep/staff.c index 7d6300d1f3..16fe6f79b2 100644 --- a/src/peep/staff.c +++ b/src/peep/staff.c @@ -70,7 +70,7 @@ void game_command_hire_new_staff_member(int* eax, int* ebx, int* ecx, int* edx, RCT2_GLOBAL(0x009DEA62, uint16) = _dx; if (RCT2_GLOBAL(0x13573C8, uint16) < 0x190) { - *ebx = 0x80000000; + *ebx = MONEY32_UNDEFINED; RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = STR_TOO_MANY_PEOPLE_IN_GAME; return; } @@ -82,7 +82,7 @@ void game_command_hire_new_staff_member(int* eax, int* ebx, int* ecx, int* edx, } if (i == STAFF_MAX_COUNT) { - *ebx = 0x80000000; + *ebx = MONEY32_UNDEFINED; RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = STR_TOO_MANY_STAFF_IN_GAME; return; } @@ -96,7 +96,7 @@ void game_command_hire_new_staff_member(int* eax, int* ebx, int* ecx, int* edx, if (newPeep == NULL) { - *ebx = 0x80000000; + *ebx = MONEY32_UNDEFINED; RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = STR_TOO_MANY_PEOPLE_IN_GAME; return; } @@ -211,6 +211,95 @@ void game_command_hire_new_staff_member(int* eax, int* ebx, int* ecx, int* edx, *edi = newPeep->sprite_index; } +/** + * + * rct2: 0x006C0BB5 + */ +void game_command_set_staff_order(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp) +{ + RCT2_GLOBAL(RCT2_ADDRESS_NEXT_EXPENDITURE_TYPE, uint8) = 40; + uint8 order_id = *ebx >> 8; + uint16 sprite_id = *edx; + if(*ebx & GAME_COMMAND_FLAG_APPLY){ + rct_peep *peep = &g_sprite_list[sprite_id].peep; + if(order_id & 0x80){ // change costume + uint8 sprite_type = order_id & ~0x80; + sprite_type += 4; + peep->sprite_type = sprite_type; + peep->flags &= ~PEEP_FLAGS_SLOW_WALK; + if(RCT2_ADDRESS(0x00982134, uint8)[sprite_type] & 1){ + peep->flags |= PEEP_FLAGS_SLOW_WALK; + } + peep->action_frame = 0; + sub_693B58(peep); + invalidate_sprite((rct_sprite*)peep); + window_invalidate_by_number(WC_PEEP, sprite_id); + window_invalidate_by_class(WC_STAFF_LIST); + }else{ + peep->staff_orders = order_id; + window_invalidate_by_number(WC_PEEP, sprite_id); + window_invalidate_by_class(WC_STAFF_LIST); + } + } + *ebx = 0; +} + +/** + * + * rct2: 0x006C09D1 + */ +void game_command_set_staff_patrol(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp) +{ + if(*ebx & GAME_COMMAND_FLAG_APPLY){ + int x = *eax; + int y = *ecx; + uint16 sprite_id = *edx; + rct_peep *peep = &g_sprite_list[sprite_id].peep; + int patrolOffset = peep->staff_id * (64 * 64 / 8); + int patrolIndex = ((x & 0x1F80) >> 7) | ((y & 0x1F80) >> 1); + int mask = 1 << (patrolIndex & 0x1F); + int base = patrolIndex >> 5; + + uint32 *patrolBits = (uint32*)(0x013B0E72 + patrolOffset + (base * 4)); + *patrolBits ^= mask; + + int ispatrolling = 0; + for(int i = 0; i < 128; i++){ + ispatrolling |= *(uint32*)(0x013B0E72 + patrolOffset + (i * 4)); + } + + RCT2_ADDRESS(RCT2_ADDRESS_STAFF_MODE_ARRAY, uint8)[peep->staff_id] &= ~2; + if(ispatrolling){ + RCT2_ADDRESS(RCT2_ADDRESS_STAFF_MODE_ARRAY, uint8)[peep->staff_id] |= 2; + } + + for(int y2 = 0; y2 < 4; y2++){ + for(int x2 = 0; x2 < 4; x2++){ + map_invalidate_tile_full((x & 0x1F80) + (x2 * 32), (y & 0x1F80) + (y2 * 32)); + } + } + staff_update_greyed_patrol_areas(); + } + *ebx = 0; +} + +/** + * + * rct2: 0x006C0B83 + */ +void game_command_fire_staff_member(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp) +{ + RCT2_GLOBAL(RCT2_ADDRESS_NEXT_EXPENDITURE_TYPE, uint8) = 40; + if(*ebx & GAME_COMMAND_FLAG_APPLY){ + window_close_by_class(WC_FIRE_PROMPT); + uint16 sprite_id = *edx; + rct_peep *peep = &g_sprite_list[sprite_id].peep; + remove_peep_from_ride(peep); + peep_sprite_remove(peep); + } + *ebx = 0; +} + /* * Updates the colour of the given staff type. */ @@ -233,29 +322,33 @@ uint16 hire_new_staff_member(uint8 staffType) int result = game_do_command_p(GAME_COMMAND_HIRE_NEW_STAFF_MEMBER, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); - if (result == 0x80000000) + if (result == MONEY32_UNDEFINED) return 0xFFFF; return edi; } -void sub_6C0C3F() +/** + * + * rct2: 0x006C0C3F + */ +void staff_update_greyed_patrol_areas() { - register rct_peep* peep; + rct_peep* peep; - for (register uint8 staff_type = 0; staff_type < STAFF_TYPE_COUNT; ++staff_type) + for (int staff_type = 0; staff_type < STAFF_TYPE_COUNT; ++staff_type) { - for (register uint8 i = 0; i < 128; ++i) - RCT2_ADDRESS(0x13B0E72 + (staff_type + STAFF_MAX_COUNT) * 512, uint32)[i] = 0; + for (int i = 0; i < 128; ++i) + RCT2_ADDRESS(0x13B0E72 + ((staff_type + STAFF_MAX_COUNT) * 512), uint32)[i] = 0; - for (register uint16 sprite_index = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_PEEP, uint16); sprite_index != SPRITE_INDEX_NULL; sprite_index = peep->next) + for (uint16 sprite_index = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_PEEP, uint16); sprite_index != SPRITE_INDEX_NULL; sprite_index = peep->next) { peep = GET_PEEP(sprite_index); if (peep->type == PEEP_TYPE_STAFF && staff_type == peep->staff_type) { - for (register uint8 i = 0; i < 128; ++i) - RCT2_ADDRESS(0x13B0E72 + (staff_type + STAFF_MAX_COUNT) * 512, uint32)[i] |= RCT2_ADDRESS(0x13B0E72 + peep->staff_id * 512, uint32)[i]; + for (int i = 0; i < 128; ++i) + RCT2_ADDRESS(0x13B0E72 + ((staff_type + STAFF_MAX_COUNT) * 512), uint32)[i] |= RCT2_ADDRESS(0x13B0E72 + (peep->staff_id * 512), uint32)[i]; } } diff --git a/src/peep/staff.h b/src/peep/staff.h index 18dff988dc..51467efecc 100644 --- a/src/peep/staff.h +++ b/src/peep/staff.h @@ -51,10 +51,13 @@ enum STAFF_ORDERS{ void game_command_update_staff_colour(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp); void game_command_hire_new_staff_member(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp); +void game_command_set_staff_order(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp); +void game_command_set_staff_patrol(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp); +void game_command_fire_staff_member(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp); void update_staff_colour(uint8 staffType, uint16 color); uint16 hire_new_staff_member(uint8 staffType); -void sub_6C0C3F(); +void staff_update_greyed_patrol_areas(); int mechanic_is_location_in_patrol(rct_peep *mechanic, int x, int y); void staff_reset_stats(); diff --git a/src/platform/shared.c b/src/platform/shared.c index 4361a35451..22a1977507 100644 --- a/src/platform/shared.c +++ b/src/platform/shared.c @@ -191,6 +191,7 @@ static void platform_resize(int width, int height) rct_drawpixelinfo *screenDPI; int newScreenBufferSize; void *newScreenBuffer; + uint32 flags; if (_surface != NULL) SDL_FreeSurface(_surface); @@ -247,6 +248,18 @@ static void platform_resize(int width, int height) window_relocate_windows(width, height); gfx_invalidate_screen(); + + // Check if the window has been resized in windowed mode and update the config file accordingly + // This is called in rct2_update_2 and is only called after resizing a window has finished + flags = SDL_GetWindowFlags(gWindow); + if ((flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED | + SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) == 0) { + if (width != gConfigGeneral.window_width || height != gConfigGeneral.window_height) { + gConfigGeneral.window_width = width; + gConfigGeneral.window_height = height; + config_save_default(); + } + } } void platform_update_palette(char* colours, int start_index, int num_colours) diff --git a/src/rct1.c b/src/rct1.c index bdbeee9d3d..6cb651210a 100644 --- a/src/rct1.c +++ b/src/rct1.c @@ -62,7 +62,7 @@ bool rct1_read_sc4(const char *path, rct1_s4 *s4) long length, decodedLength; bool success; - if (!readentirefile(path, &buffer, &length)) { + if (!readentirefile(path, (void**)&buffer, &length)) { RCT2_GLOBAL(RCT2_ADDRESS_ERROR_TYPE, uint8) = 255; RCT2_GLOBAL(RCT2_ADDRESS_ERROR_STRING_ID, uint16) = 3011; return 0; @@ -92,7 +92,7 @@ bool rct1_read_sv4(const char *path, rct1_s4 *s4) long length, decodedLength; bool success; - if (!readentirefile(path, &buffer, &length)) { + if (!readentirefile(path, (void**)&buffer, &length)) { RCT2_GLOBAL(RCT2_ADDRESS_ERROR_TYPE, uint8) = 255; RCT2_GLOBAL(RCT2_ADDRESS_ERROR_STRING_ID, uint16) = 3011; return 0; @@ -230,17 +230,17 @@ void rct1_fix_landscape() s6Info->category = 4; format_string(s6Info->details, STR_NO_DETAILS_YET, NULL); s6Info->name[0] = 0; - if (RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint8) & PARK_FLAGS_NO_MONEY) { - RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint8) |= PARK_FLAGS_NO_MONEY_SCENARIO; + if (RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_NO_MONEY) { + RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) |= PARK_FLAGS_NO_MONEY_SCENARIO; } else { - RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint8) &= PARK_FLAGS_NO_MONEY_SCENARIO; + RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) &= ~PARK_FLAGS_NO_MONEY_SCENARIO; } if (RCT2_GLOBAL(RCT2_ADDRESS_PARK_ENTRANCE_FEE, money16) == MONEY_FREE) { - RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint8) |= PARK_FLAGS_PARK_FREE_ENTRY; + RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) |= PARK_FLAGS_PARK_FREE_ENTRY; } else { - RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint8) &= ~PARK_FLAGS_PARK_FREE_ENTRY; + RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) &= ~PARK_FLAGS_PARK_FREE_ENTRY; } - RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint8) &= ~PARK_FLAGS_18; + RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) &= ~PARK_FLAGS_18; RCT2_GLOBAL(RCT2_ADDRESS_GUEST_INITIAL_CASH, money16) = clamp( MONEY(10,00), RCT2_GLOBAL(RCT2_ADDRESS_GUEST_INITIAL_CASH, money16), diff --git a/src/rct2.c b/src/rct2.c index 0221d9ecdf..f70848a2a7 100644 --- a/src/rct2.c +++ b/src/rct2.c @@ -72,6 +72,7 @@ int rct2_init() RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TICKS, int) = 0; RCT2_GLOBAL(0x009AC310, char*) = RCT2_GLOBAL(RCT2_ADDRESS_CMDLINE, char*); get_system_time(); + srand(time(0)); RCT2_GLOBAL(0x009DEA69, short) = RCT2_GLOBAL(RCT2_ADDRESS_OS_TIME_DAY, short); RCT2_GLOBAL(0x009DEA6B, short) = RCT2_GLOBAL(RCT2_ADDRESS_OS_TIME_MONTH, short); if (!rct2_init_directories()) diff --git a/src/ride/ride.c b/src/ride/ride.c index 97c0ef68d2..395223874e 100644 --- a/src/ride/ride.c +++ b/src/ride/ride.c @@ -27,12 +27,14 @@ #include "../localisation/date.h" #include "../localisation/localisation.h" #include "../management/finance.h" +#include "../management/marketing.h" #include "../management/news_item.h" #include "../peep/peep.h" #include "../peep/staff.h" #include "../scenario.h" #include "../util/util.h" #include "../windows/error.h" +#include "../world/banner.h" #include "../world/map.h" #include "../world/sprite.h" #include "ride.h" @@ -556,7 +558,7 @@ int ride_create_ride(ride_list_item listItem) esi = GAME_COMMAND_6; game_do_command_p(esi, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); - return ebx == 0x80000000 ? -1 : edi; + return ebx == MONEY32_UNDEFINED ? -1 : edi; } /** @@ -2445,32 +2447,34 @@ int ride_music_params_update(sint16 x, sint16 y, sint16 z, uint8 rideIndex, uint uint8 vol1 = -1; uint8 vol2 = -1; - if (pany < 0) { - pany = -pany; + int panx2 = panx; + int pany2 = pany; + if (pany2 < 0) { + pany2 = -pany2; } - if (pany > 6143) { - pany = 6143; + if (pany2 > 6143) { + pany2 = 6143; } - pany -= 2048; - if (pany > 0) { - pany = -((pany / 4) - 1024) / 4; - vol1 = (uint8)pany; - if (pany >= 256) { + pany2 -= 2048; + if (pany2 > 0) { + pany2 = -((pany2 / 4) - 1024) / 4; + vol1 = (uint8)pany2; + if (pany2 >= 256) { vol1 = -1; } } - if (panx < 0) { - panx = -panx; + if (panx2 < 0) { + panx2 = -panx2; } - if (panx > 6143) { - panx = 6143; + if (panx2 > 6143) { + panx2 = 6143; } - panx -= 2048; - if (panx > 0) { - panx = -((panx / 4) - 1024) / 4; - vol2 = (uint8)panx; - if (panx >= 256) { + panx2 -= 2048; + if (panx2 > 0) { + panx2 = -((panx2 / 4) - 1024) / 4; + vol2 = (uint8)panx2; + if (panx2 >= 256) { vol2 = -1; } } @@ -2660,7 +2664,7 @@ void ride_music_update_final() rct_ride_music_info* ride_music_info = &RCT2_GLOBAL(0x009AF1C8, rct_ride_music_info*)[ride_music_params->tuneid]; #ifdef USE_MIXER rct_ride_music* ride_music = &gRideMusicList[ebx]; - ride_music->sound_channel = Mixer_Play_Music(ride_music_info->pathid); + ride_music->sound_channel = Mixer_Play_Music(ride_music_info->pathid, true); if (ride_music->sound_channel) { ride_music->volume = ride_music_params->volume; ride_music->pan = ride_music_params->pan; @@ -2676,7 +2680,7 @@ void ride_music_update_final() } Mixer_Channel_SetOffset(ride_music->sound_channel, offset); } else { - RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_MUSIC, uint8) = 0; + //RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_MUSIC, uint8) = 0; } #else const char* filename = get_file_path(ride_music_info->pathid); @@ -2781,13 +2785,13 @@ void game_command_set_ride_setting(int *eax, int *ebx, int *ecx, int *edx, int * if (setting == 0){ if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN){ RCT2_GLOBAL(0x141E9AC, uint16) = 1796; - *ebx = 0x80000000; + *ebx = MONEY32_UNDEFINED; return; } if (ride->status != RIDE_STATUS_CLOSED){ RCT2_GLOBAL(0x141E9AC, uint16) = 1006; - *ebx = 0x80000000; + *ebx = MONEY32_UNDEFINED; return; } } @@ -2796,7 +2800,7 @@ void game_command_set_ride_setting(int *eax, int *ebx, int *ecx, int *edx, int * if (setting == 0 || setting == 4 || setting == 8 || setting == 9) { RCT2_GLOBAL(0x141E9AC, uint16) = 1797; - *ebx = 0x80000000; + *ebx = MONEY32_UNDEFINED; return; } } @@ -2805,7 +2809,7 @@ void game_command_set_ride_setting(int *eax, int *ebx, int *ecx, int *edx, int * ride->lifecycle_flags & RIDE_LIFECYCLE_CABLE_LIFT && new_value > 1){ RCT2_GLOBAL(0x141E9AC, uint16) = 3141; - *ebx = 0x80000000; + *ebx = MONEY32_UNDEFINED; return; } @@ -3590,6 +3594,262 @@ void game_command_set_ride_name(int *eax, int *ebx, int *ecx, int *edx, int *esi *ebx = 0; } +/** + * + * rct2: 0x006CB7FB + */ +int ride_get_refund_price(int ride_id) +{ + uint8 oldpaused = RCT2_GLOBAL(RCT2_ADDRESS_GAME_PAUSED, uint8); + RCT2_GLOBAL(RCT2_ADDRESS_GAME_PAUSED, uint8) = 0; + RCT2_GLOBAL(0x00F4413A, int) = 0; + for(int x = 0; x < 8192; x += 32){ + for(int y = 0; y < 8192; y += 32){ + int tile_idx = ((y * 256) + x) / 32; + rct_map_element* map_element = RCT2_ADDRESS(RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS, rct_map_element*)[tile_idx]; + do{ + if((map_element->type & MAP_ELEMENT_TYPE_MASK) == MAP_ELEMENT_TYPE_TRACK && map_element->properties.track.ride_index == ride_id){ + int eax, ebx, ecx, edx, esi, edi, ebp; + eax = x; + ebx = ((map_element->type & MAP_ELEMENT_DIRECTION_MASK) << 8) | 0x01; + ecx = y; + edx = map_element->properties.track.type; + edi = map_element->base_height * 8; + if(map_element->properties.track.type == 101){ + edx = 2 << 8 | map_element->properties.track.ride_index; + int oldeax = eax; + int oldebx = ebx; + int oldecx = ecx; + int oldedx = edx; + + ebx = oldebx; + ebx |= 0 << 0; + RCT2_GLOBAL(0x00F4413A, int) += game_do_command_p(GAME_COMMAND_38, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + + ebx = oldebx; + ebx |= 1 << 8; + ecx = oldecx; + ecx += 16; + edx = oldedx; + RCT2_GLOBAL(0x00F4413A, int) += game_do_command_p(GAME_COMMAND_38, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + + ebx = oldebx; + ebx |= 2 << 8; + eax = oldeax; + eax += 16; + ecx = oldecx; + ecx += 16; + edx = oldedx; + RCT2_GLOBAL(0x00F4413A, int) += game_do_command_p(GAME_COMMAND_38, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + + ebx = oldebx; + ebx |= 3 << 8; + eax = oldeax; + eax += 16; + ecx = oldecx; + edx = oldedx; + RCT2_GLOBAL(0x00F4413A, int) += game_do_command_p(GAME_COMMAND_38, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + }else{ + edx |= 0xFF << 8; + edx &= ((map_element->properties.track.sequence & 0xF) << 8) | 0xFF; + RCT2_GLOBAL(0x00F4413A, int) += game_do_command_p(GAME_COMMAND_4, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + } + y -= 32; + break; + } + map_element++; + }while(!((map_element - 1)->flags & MAP_ELEMENT_FLAG_LAST_TILE)); + } + } + RCT2_GLOBAL(RCT2_ADDRESS_GAME_PAUSED, uint8) = oldpaused; + return RCT2_GLOBAL(0x00F4413A, int); +} + +/** + * + * rct2: 0x006B49D9 + */ +void game_command_demolish_ride(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp) +{ + uint8 ride_id = *(uint8*)edx; + + RCT2_GLOBAL(0x009DEA5E, uint16) = 0; + RCT2_GLOBAL(0x009DEA60, uint16) = 0; + RCT2_GLOBAL(0x009DEA62, uint16) = 0; + rct_ride *ride = &g_ride_list[ride_id]; + int x = 0, y = 0, z = 0; + if(ride->overall_view != (uint16)-1){ + x = ((ride->overall_view & 0xFF) * 32) + 16; + y = ((ride->overall_view >> 8) * 32) + 16; + z = map_element_height(x, y); + RCT2_GLOBAL(0x009DEA5E, uint16) = x; + RCT2_GLOBAL(0x009DEA60, uint16) = y; + RCT2_GLOBAL(0x009DEA62, uint16) = z; + } + if(!(*ebx & 0x40) && RCT2_GLOBAL(RCT2_ADDRESS_GAME_PAUSED, uint8)){ + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED; + *ebx = MONEY32_UNDEFINED; + return; + }else{ + if(*ebx & GAME_COMMAND_FLAG_APPLY){ + if(!(*ebx & 8)){ + window_close_by_number(WC_RIDE_CONSTRUCTION, ride_id); + } + window_close_by_number(WC_RIDE, ride_id); + window_close_by_number(WC_DEMOLISH_RIDE_PROMPT, ride_id); + window_close_by_class(WC_NEW_CAMPAIGN); + if(RCT2_GLOBAL(0x01358103, uint8) && ride_id == RCT2_GLOBAL(0x01358117, uint8)){ + RCT2_GLOBAL(0x01358103, uint8) = 0; + } + if(RCT2_GLOBAL(0x01358107, uint8) && ride_id == RCT2_GLOBAL(0x0135811B, uint8)){ + RCT2_GLOBAL(0x01358107, uint8) = 0; + } + ride_clear_for_construction(ride_id); + ride_remove_peeps(ride_id); + RCT2_CALLPROC_X(0x00696707, 0, 0, 0, ride_id, 0, 0, 0); + *ebx = ride_get_refund_price(ride_id); + + RCT2_CALLPROC(0x006CB945); + news_item_disable_news(NEWS_ITEM_RIDE, ride_id); + + for(int i = 0; i < MAX_BANNERS; i++){ + rct_banner *banner = &gBanners[i]; + if(banner->type != BANNER_NULL && banner->flags & BANNER_FLAG_2 && banner->colour == ride_id){ + banner->flags &= 0xFB; + banner->string_idx = 778; + } + } + + uint16 spriteIndex; + rct_peep *peep; + FOR_ALL_GUESTS(spriteIndex, peep){ + uint8 ride_id_bit = ride_id & 0x3; + uint8 ride_id_offset = ride_id / 8; + peep->rides_been_on[ride_id_offset] &= ~(1 << ride_id_bit); // clear ride from potentially being in rides_been_on + if(peep->state == PEEP_STATE_WATCHING){ + if(peep->current_ride == ride_id){ + peep->current_ride = MAX_RIDES; + if(peep->time_to_stand >= 50){ // make peep stop watching the ride + peep->time_to_stand = 50; + } + } + } + // remove any free voucher for this ride from peep + if(peep->item_standard_flags & PEEP_ITEM_VOUCHER){ + if(peep->voucher_type == VOUCHER_TYPE_RIDE_FREE && peep->voucher_arguments == ride_id){ + peep->item_standard_flags &= ~(PEEP_ITEM_VOUCHER); + } + } + // remove any photos of this ride from peep + if(peep->item_standard_flags & PEEP_ITEM_PHOTO){ + if(peep->photo1_ride_ref == ride_id){ + peep->item_standard_flags &= ~PEEP_ITEM_PHOTO; + } + } + if(peep->item_extra_flags && PEEP_ITEM_PHOTO2){ + if(peep->photo2_ride_ref == ride_id){ + peep->item_extra_flags &= ~PEEP_ITEM_PHOTO2; + } + } + if(peep->item_extra_flags && PEEP_ITEM_PHOTO3){ + if(peep->photo3_ride_ref == ride_id){ + peep->item_extra_flags &= ~PEEP_ITEM_PHOTO3; + } + } + if(peep->item_extra_flags && PEEP_ITEM_PHOTO4){ + if(peep->photo4_ride_ref == ride_id){ + peep->item_extra_flags &= ~PEEP_ITEM_PHOTO4; + } + } + if(peep->guest_heading_to_ride_id == ride_id){ + peep->guest_heading_to_ride_id = MAX_RIDES; + } + if(peep->favourite_ride == ride_id){ + peep->favourite_ride = MAX_RIDES; + } + for (int i = 0; i < PEEP_MAX_THOUGHTS; i++) { + if(peep->thoughts[i].item == ride_id){ + // Clear top thought, push others up + memmove(&peep->thoughts[i], &peep->thoughts[i + 1], sizeof(rct_peep_thought)*(PEEP_MAX_THOUGHTS - i - 1)); + peep->thoughts[PEEP_MAX_THOUGHTS - 1].type = PEEP_THOUGHT_TYPE_NONE; + } + } + } + + user_string_free(ride->name); + ride->type = RIDE_TYPE_NULL; + window_invalidate_by_class(WC_RIDE_LIST); + RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_PARK_VALUE, money32) = calculate_park_value(); + RCT2_GLOBAL(0x009DEA5E, uint16) = x; + RCT2_GLOBAL(0x009DEA60, uint16) = y; + RCT2_GLOBAL(0x009DEA62, uint16) = z; + RCT2_GLOBAL(RCT2_ADDRESS_NEXT_EXPENDITURE_TYPE, uint8) = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; + return; + }else{ + *ebx = 0; + RCT2_GLOBAL(RCT2_ADDRESS_NEXT_EXPENDITURE_TYPE, uint8) = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; + return; + } + } +} + +/** + * + * rct2: 0x006B2FC5 + */ +void game_command_set_ride_appearance(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp) +{ + if(*ebx & GAME_COMMAND_FLAG_APPLY){ + uint8 ride_id = *edx; + uint8 type = *ebx >> 8; + uint8 value = *edx >> 8; + int index = *edi; + rct_ride *ride = &g_ride_list[ride_id]; + switch(type){ + case 0: + ride->track_colour_main[index] = value; + gfx_invalidate_screen(); + break; + case 1: + ride->track_colour_additional[index] = value; + gfx_invalidate_screen(); + break; + case 2: + *((uint8*)(&ride->vehicle_colours[index])) = value; + RCT2_CALLPROC_X(0x006DE102, 0, 0, 0, ride_id, 0, 0, 0); + break; + case 3: + *((uint8*)(&ride->vehicle_colours[index]) + 1) = value; + RCT2_CALLPROC_X(0x006DE102, 0, 0, 0, ride_id, 0, 0, 0); + break; + case 4: + ride->track_colour_supports[index] = value; + gfx_invalidate_screen(); + break; + case 5: + ride->colour_scheme_type &= ~(RIDE_COLOUR_SCHEME_DIFFERENT_PER_TRAIN | RIDE_COLOUR_SCHEME_DIFFERENT_PER_CAR); + ride->colour_scheme_type |= value; + for(int i = 1; i < countof(ride->vehicle_colours); i++){ + ride->vehicle_colours[i] = ride->vehicle_colours[0]; + ride->vehicle_colours_extended[i] = ride->vehicle_colours_extended[0]; + } + RCT2_CALLPROC_X(0x006DE102, 0, 0, 0, ride_id, 0, 0, 0); + break; + case 6: + ride->entrance_style = value; + RCT2_GLOBAL(0x01358840, uint8) = value; + gfx_invalidate_screen(); + break; + case 7: + ride->vehicle_colours_extended[index] = value; + RCT2_CALLPROC_X(0x006DE102, 0, 0, 0, ride_id, 0, 0, 0); + break; + } + window_invalidate_by_number(WC_RIDE, ride_id); + } + *ebx = 0; +} + bool ride_type_has_flag(int rideType, int flag) { return (RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + (rideType * 8), uint32) & flag) != 0; diff --git a/src/ride/ride.h b/src/ride/ride.h index 9c85974e66..ce89235380 100644 --- a/src/ride/ride.h +++ b/src/ride/ride.h @@ -764,6 +764,9 @@ void game_command_set_ride_status(int *eax, int *ebx, int *ecx, int *edx, int *e void ride_set_name(int rideIndex, const char *name); void game_command_set_ride_name(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp); void game_command_set_ride_setting(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp); +int ride_get_refund_price(int ride_id); +void game_command_demolish_ride(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp); +void game_command_set_ride_appearance(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp); int get_var_10E_unk_1(rct_ride* ride); int get_var_10E_unk_2(rct_ride* ride); diff --git a/src/ride/track.c b/src/ride/track.c index 27c128ef40..d9d4f2f96e 100644 --- a/src/ride/track.c +++ b/src/ride/track.c @@ -784,7 +784,7 @@ int sub_6D2189(int* cost, uint8* ride_id){ edx = track_design->type | (entry_index << 8); esi = GAME_COMMAND_6; - if (0x80000000 == game_do_command_p(GAME_COMMAND_6, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp)) return 1; + if (MONEY32_UNDEFINED == game_do_command_p(GAME_COMMAND_6, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp)) return 1; // bh *ride_id = edi & 0xFF; @@ -840,7 +840,7 @@ int sub_6D2189(int* cost, uint8* ride_id){ edi = sub_6D01B3((*ride_id << 8) | bl, map_size, map_size, z); RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) = backup_park_flags; - if (edi != 0x80000000){ + if (edi != MONEY32_UNDEFINED){ if (!find_object_in_entry_group(&track_design->vehicle_object, &entry_type, &entry_index)){ RCT2_GLOBAL(0xF44151, uint8) |= 4; diff --git a/src/scenario.c b/src/scenario.c index 44c9ab020e..836002e88e 100644 --- a/src/scenario.c +++ b/src/scenario.c @@ -314,7 +314,7 @@ int scenario_load_and_play_from_path(const char *path) RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_PROFIT, money32) = 0; RCT2_GLOBAL(0x01358334, money32) = 0; RCT2_GLOBAL(0x01358338, uint16) = 0; - RCT2_GLOBAL(RCT2_ADDRESS_COMPLETED_COMPANY_VALUE, uint32) = 0x80000000; + RCT2_GLOBAL(RCT2_ADDRESS_COMPLETED_COMPANY_VALUE, uint32) = MONEY32_UNDEFINED; RCT2_GLOBAL(RCT2_ADDRESS_TOTAL_ADMISSIONS, uint32) = 0; RCT2_GLOBAL(RCT2_ADDRESS_INCOME_FROM_ADMISSIONS, uint32) = 0; RCT2_GLOBAL(0x013587D8, uint16) = 63; @@ -517,7 +517,7 @@ void scenario_objectives_check() park_value = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_PARK_VALUE, sint32); - if ( scenario_completed_company_value != 0x80000000) + if ( scenario_completed_company_value != MONEY32_UNDEFINED) return; switch (objective_type) { diff --git a/src/windows/banner.c b/src/windows/banner.c index 8f0ac1605f..76626d71cf 100644 --- a/src/windows/banner.c +++ b/src/windows/banner.c @@ -201,7 +201,7 @@ static void window_banner_mouseup() window_close(w); break; case WIDX_BANNER_DEMOLISH: - game_do_command(x, 1, y, map_element->base_height | (map_element->properties.banner.position << 8), GAME_COMMAND_51, 0, 0); + game_do_command(x, 1, y, map_element->base_height | (map_element->properties.banner.position << 8), GAME_COMMAND_REMOVE_BANNER, 0, 0); break; case WIDX_BANNER_TEXT: window_text_input_open(w, WIDX_BANNER_TEXT, 2982, 2983, gBanners[w->number].string_idx, 0, 32); @@ -244,7 +244,7 @@ static void window_banner_mousedown(int widgetIndex, rct_window*w, rct_widget* w widget->top + w->y, widget->bottom - widget->top + 1, w->colours[1], - 0x80, + DROPDOWN_FLAG_STAY_OPEN, 13, widget->right - widget->left - 3); diff --git a/src/windows/clear_scenery.c b/src/windows/clear_scenery.c index 9c5b514f56..3a93c30365 100644 --- a/src/windows/clear_scenery.c +++ b/src/windows/clear_scenery.c @@ -103,7 +103,7 @@ void window_clear_scenery_open() window_init_scroll_widgets(window); window_push_others_below(window); - RCT2_GLOBAL(0x00F1AD62, uint32) = 0x80000000; + RCT2_GLOBAL(0x00F1AD62, uint32) = MONEY32_UNDEFINED; window->colours[0] = 24; window->colours[1] = 24; window->colours[2] = 24; @@ -209,7 +209,7 @@ static void window_clear_scenery_paint() // Draw cost amount x = (window_clear_scenery_widgets[WIDX_PREVIEW].left + window_clear_scenery_widgets[WIDX_PREVIEW].right) / 2 + w->x; y = window_clear_scenery_widgets[WIDX_PREVIEW].bottom + w->y + 5; - if (RCT2_GLOBAL(0x00F1AD62, uint32) != 0x80000000 && RCT2_GLOBAL(0x00F1AD62, uint32) != 0) + if (RCT2_GLOBAL(0x00F1AD62, uint32) != MONEY32_UNDEFINED && RCT2_GLOBAL(0x00F1AD62, uint32) != 0) gfx_draw_string_centred(dpi, 986, x, y, 0, (void*)0x00F1AD62); } diff --git a/src/windows/dropdown.c b/src/windows/dropdown.c index 2a02739add..d993f2fd48 100644 --- a/src/windows/dropdown.c +++ b/src/windows/dropdown.c @@ -136,9 +136,9 @@ void window_dropdown_show_text_custom_width(int x, int y, int extray, uint8 colo memcpy((void*)0x009DEBA4, gDropdownItemsFormat, 40 * 2); memcpy((void*)0x009DEBF4, gDropdownItemsArgs, 40 * 8); - RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) &= ~(INPUT_FLAG_1 | INPUT_FLAG_2); - if (flags & 0x80) - RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) |= INPUT_FLAG_1; + RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) &= ~(INPUT_FLAG_DROPDOWN_STAY_OPEN | INPUT_FLAG_DROPDOWN_MOUSE_UP); + if (flags & DROPDOWN_FLAG_STAY_OPEN) + RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) |= INPUT_FLAG_DROPDOWN_STAY_OPEN; window_dropdown_close(); _dropdown_num_columns = 1; @@ -213,9 +213,9 @@ void window_dropdown_show_image(int x, int y, int extray, uint8 colour, uint8 fl memcpy((void*)0x009DEBA4, gDropdownItemsFormat, 40 * 2); memcpy((void*)0x009DEBF4, gDropdownItemsArgs, 40 * 8); - RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) &= ~(INPUT_FLAG_1 | INPUT_FLAG_2); - if (flags & 0x80) - RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) |= INPUT_FLAG_1; + RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) &= ~(INPUT_FLAG_DROPDOWN_STAY_OPEN | INPUT_FLAG_DROPDOWN_MOUSE_UP); + if (flags & DROPDOWN_FLAG_STAY_OPEN) + RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) |= INPUT_FLAG_DROPDOWN_STAY_OPEN; // Close existing dropdown window_dropdown_close(); @@ -411,19 +411,6 @@ void window_dropdown_show_colour_available(rct_window *w, rct_widget *widget, ui if (availableColours & (1 << i)) numItems++; - // Show dropdown - window_dropdown_show_image( - w->x + widget->left, - w->y + widget->top, - widget->bottom - widget->top + 1, - dropdownColour, - 0x80, - numItems, - 12, - 12, - gAppropriateImageDropdownItemsPerRow[numItems] - ); - // Set items for (i = 0; i < 32; i++) { if (availableColours & (1 << i)) { @@ -434,4 +421,18 @@ void window_dropdown_show_colour_available(rct_window *w, rct_widget *widget, ui gDropdownItemsArgs[i] = ((uint64)i << 32) | (0x20000000 | (i << 19) | 5059); } } + + // Show dropdown + window_dropdown_show_image( + w->x + widget->left, + w->y + widget->top, + widget->bottom - widget->top + 1, + dropdownColour, + DROPDOWN_FLAG_STAY_OPEN, + numItems, + 12, + 12, + gAppropriateImageDropdownItemsPerRow[numItems] + ); + } diff --git a/src/windows/dropdown.h b/src/windows/dropdown.h index 76370cd212..e2285714e2 100644 --- a/src/windows/dropdown.h +++ b/src/windows/dropdown.h @@ -25,6 +25,11 @@ #define DROPDOWN_SEPARATOR 0 +enum +{ + DROPDOWN_FLAG_STAY_OPEN = (1 << 7) +}; + extern int gAppropriateImageDropdownItemsPerRow[]; extern int gDropdownNumItems; diff --git a/src/windows/editor_object_selection.c b/src/windows/editor_object_selection.c index ebd4dfa96c..2b53a55c57 100644 --- a/src/windows/editor_object_selection.c +++ b/src/windows/editor_object_selection.c @@ -214,7 +214,7 @@ void window_editor_object_selection_open() 400, (uint32*)window_editor_object_selection_events, WC_EDITOR_OBJECT_SELECTION, - WF_STICK_TO_FRONT + WF_10 ); window->widgets = window_editor_object_selection_widgets; diff --git a/src/windows/editor_objective_options.c b/src/windows/editor_objective_options.c index bc41e94f7a..fac111c1ae 100644 --- a/src/windows/editor_objective_options.c +++ b/src/windows/editor_objective_options.c @@ -507,7 +507,7 @@ static void window_editor_objective_options_show_objective_dropdown(rct_window * w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0x80, + DROPDOWN_FLAG_STAY_OPEN, numItems, dropdownWidget->right - dropdownWidget->left - 3 ); @@ -537,7 +537,7 @@ static void window_editor_objective_options_show_climate_dropdown(rct_window *w) w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0x80, + DROPDOWN_FLAG_STAY_OPEN, 4, dropdownWidget->right - dropdownWidget->left - 3 ); @@ -561,7 +561,7 @@ static void window_editor_objective_options_show_category_dropdown(rct_window *w w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0x80, + DROPDOWN_FLAG_STAY_OPEN, 5, dropdownWidget->right - dropdownWidget->left - 3 ); diff --git a/src/windows/editor_scenario_options.c b/src/windows/editor_scenario_options.c index bbcdccbb80..9dbb05174f 100644 --- a/src/windows/editor_scenario_options.c +++ b/src/windows/editor_scenario_options.c @@ -1049,7 +1049,7 @@ static void window_editor_scenario_options_park_mousedown(int widgetIndex, rct_w w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top - 1, w->colours[1], - 0x80, + DROPDOWN_FLAG_STAY_OPEN, 2, dropdownWidget->right - dropdownWidget->left - 3 ); diff --git a/src/windows/finances.c b/src/windows/finances.c index ec42a904f5..bff2716d38 100644 --- a/src/windows/finances.c +++ b/src/windows/finances.c @@ -865,7 +865,7 @@ static void window_finances_financial_graph_paint() money32 *balanceHistory = RCT2_ADDRESS(RCT2_ADDRESS_BALANCE_HISTORY, money32); for (i = 0; i < 64; i++) { money32 balance = balanceHistory[i]; - if (balance == 0x80000000) + if (balance == MONEY32_UNDEFINED) continue; // Modifier balance then keep halfing until less than 127 pixels @@ -983,7 +983,7 @@ static void window_finances_park_value_graph_paint() money32 *parkValueHistory = RCT2_ADDRESS(RCT2_ADDRESS_PARK_VALUE_HISTORY, money32); for (i = 0; i < 64; i++) { money32 balance = parkValueHistory[i]; - if (balance == 0x80000000) + if (balance == MONEY32_UNDEFINED) continue; // Modifier balance then keep halfing until less than 255 pixels @@ -1101,7 +1101,7 @@ static void window_finances_profit_graph_paint() money32 *weeklyProfitHistory = RCT2_ADDRESS(RCT2_ADDRESS_WEEKLY_PROFIT_HISTORY, money32); for (i = 0; i < 64; i++) { money32 balance = weeklyProfitHistory[i]; - if (balance == 0x80000000) + if (balance == MONEY32_UNDEFINED) continue; // Modifier balance then keep halfing until less than 127 pixels @@ -1375,7 +1375,7 @@ static void window_finances_research_mousedown(int widgetIndex, rct_window *w, r w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0x80, + DROPDOWN_FLAG_STAY_OPEN, 4, dropdownWidget->right - dropdownWidget->left - 3 ); diff --git a/src/windows/footpath.c b/src/windows/footpath.c index da1c7b4437..64e9e9fa3a 100644 --- a/src/windows/footpath.c +++ b/src/windows/footpath.c @@ -667,15 +667,15 @@ static void window_footpath_mousedown_slope(int slope) */ static void window_footpath_set_provisional_path_at_point(int x, int y) { - int z, slope, pathType; + int slope, pathType, interactionType; rct_map_element *mapElement; map_invalidate_selection_rect(); RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_FLAGS, uint16) &= ~(1 << 2); - get_map_coordinates_from_pos(x, y, 0xFFDE, &x, &y, &z, &mapElement, NULL); + get_map_coordinates_from_pos(x, y, VIEWPORT_INTERACTION_MASK_FOOTPATH & VIEWPORT_INTERACTION_MASK_TERRAIN, &x, &y, &interactionType, &mapElement, NULL); - if (z == 0) { + if (interactionType == VIEWPORT_INTERACTION_ITEM_NONE) { RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_FLAGS, uint16) &= ~(1 << 0); footpath_provisional_update(); } else { @@ -695,7 +695,7 @@ static void window_footpath_set_provisional_path_at_point(int x, int y) // Set provisional path slope = RCT2_ADDRESS(0x0098D8B4, uint8)[mapElement->properties.surface.slope & 0x1F]; - if (z == 6) + if (interactionType == VIEWPORT_INTERACTION_ITEM_FOOTPATH) slope = mapElement->properties.surface.slope & 7; pathType = (RCT2_GLOBAL(RCT2_ADDRESS_SELECTED_PATH_TYPE, uint8) << 7) + RCT2_GLOBAL(RCT2_ADDRESS_SELECTED_PATH_ID, uint8); @@ -752,7 +752,7 @@ static void window_footpath_set_selection_start_bridge_at_point(int screenX, int */ static void window_footpath_place_path_at_point(int x, int y) { - int z, presentType, selectedType, cost; + int interactionType, presentType, selectedType, z, cost; rct_map_element *mapElement; if (RCT2_GLOBAL(RCT2_ADDRESS_PATH_ERROR_OCCURED, uint8) != 0) @@ -760,13 +760,13 @@ static void window_footpath_place_path_at_point(int x, int y) footpath_provisional_update(); - get_map_coordinates_from_pos(x, y, 0xFFDE, &x, &y, &z, &mapElement, NULL); - if (z == 0) + get_map_coordinates_from_pos(x, y, VIEWPORT_INTERACTION_MASK_FOOTPATH & VIEWPORT_INTERACTION_MASK_TERRAIN, &x, &y, &interactionType, &mapElement, NULL); + if (interactionType == VIEWPORT_INTERACTION_ITEM_NONE) return; // Set path presentType = RCT2_ADDRESS(0x0098D8B4, uint8)[mapElement->properties.path.type & 0x1F]; - if (z == 6) + if (interactionType == VIEWPORT_INTERACTION_ITEM_FOOTPATH) presentType = mapElement->properties.path.type & 7; z = mapElement->base_height; selectedType = (RCT2_GLOBAL(RCT2_ADDRESS_SELECTED_PATH_TYPE, uint8) << 7) + RCT2_GLOBAL(RCT2_ADDRESS_SELECTED_PATH_ID, uint8); diff --git a/src/windows/guest.c b/src/windows/guest.c index c792b3cd11..e1c7af4e7e 100644 --- a/src/windows/guest.c +++ b/src/windows/guest.c @@ -33,6 +33,7 @@ #include "../interface/widget.h" #include "../interface/window.h" #include "../world/footpath.h" +#include "../input.h" #include "dropdown.h" #include "error.h" @@ -555,7 +556,7 @@ void window_guest_overview_close(){ window_get_register(w); - if (RCT2_GLOBAL(0x9DE518,uint32) & (1<<3)){ + if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_TOOL_ACTIVE){ if (w->classification == RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS,rct_windowclass) && w->number == RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER,rct_windownumber)) tool_cancel(); @@ -622,7 +623,7 @@ void window_guest_overview_mouse_up(){ w->var_48C = peep->x; - RCT2_CALLPROC_X(0x0069A512, 0, 0, 0, 0, (int)peep, 0, 0); + remove_peep_from_ride(peep); invalidate_sprite((rct_sprite*)peep); sprite_move(0x8000, peep->y, peep->z, (rct_sprite*)peep); @@ -645,7 +646,7 @@ void window_guest_overview_mouse_up(){ /* rct2: 0x696AA0 */ void window_guest_set_page(rct_window* w, int page){ - if (RCT2_GLOBAL(0x9DE518,uint32) & (1 << 3)) + if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_TOOL_ACTIVE) { if(w->number == RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER, rct_windownumber) && w->classification == RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, rct_windowclass)) @@ -1157,9 +1158,9 @@ void window_guest_overview_tool_update(){ RCT2_GLOBAL(RCT2_ADDRESS_PICKEDUP_PEEP_SPRITE, sint32) = -1; - int ebx; - get_map_coordinates_from_pos(x, y, 0, NULL, NULL, &ebx, NULL, NULL); - if (ebx == 0) + int interactionType; + get_map_coordinates_from_pos(x, y, VIEWPORT_INTERACTION_MASK_NONE, NULL, NULL, &interactionType, NULL, NULL); + if (interactionType == VIEWPORT_INTERACTION_ITEM_NONE) return; x--; @@ -1171,7 +1172,7 @@ void window_guest_overview_tool_update(){ rct_peep* peep; peep = GET_PEEP(w->number); - ebx = (RCT2_ADDRESS(0x982708, uint32*)[peep->sprite_type * 2])[22]; + int ebx = (RCT2_ADDRESS(0x982708, uint32*)[peep->sprite_type * 2])[22]; ebx += w->var_492 >> 2; int ebp = peep->tshirt_colour << 19; @@ -1237,8 +1238,7 @@ void window_guest_overview_tool_down(){ peep->action_sprite_type = 0xFF; peep->var_C4 = 0; - peep->happiness_growth_rate -= 10; - if (peep->happiness_growth_rate < 0)peep->happiness_growth_rate = 0; + peep->happiness_growth_rate = max(peep->happiness_growth_rate - 10, 0); sub_693B58(peep); tool_cancel(); diff --git a/src/windows/guest_list.c b/src/windows/guest_list.c index 856a8d59c4..4634aa33a6 100644 --- a/src/windows/guest_list.c +++ b/src/windows/guest_list.c @@ -339,7 +339,7 @@ static void window_guest_list_mousedown(int widgetIndex, rct_window*w, rct_widge w->y + widget->top, widget->bottom - widget->top + 1, w->colours[1], - 0x80, + DROPDOWN_FLAG_STAY_OPEN, _window_guest_list_num_pages, widget->right - widget->left - 3 ); @@ -363,7 +363,7 @@ static void window_guest_list_mousedown(int widgetIndex, rct_window*w, rct_widge w->y + widget->top, widget->bottom - widget->top + 1, w->colours[1], - 0x80, + DROPDOWN_FLAG_STAY_OPEN, 2, widget->right - widget->left - 3 ); diff --git a/src/windows/loadsave.c b/src/windows/loadsave.c index 0ffdb04abd..ca43a637bd 100644 --- a/src/windows/loadsave.c +++ b/src/windows/loadsave.c @@ -255,6 +255,10 @@ static void window_loadsave_mouseup() char filename[MAX_PATH], filter[MAX_PATH]; int result; + strcpy(filename, _directory); + if (_type & LOADSAVETYPE_SAVE){ + strcat(filename, (char*)RCT2_ADDRESS_SCENARIO_NAME); + } memset(filter, '\0', MAX_PATH); strncpy(filter, "*", MAX_PATH); strncat(filter, _extension, MAX_PATH); @@ -605,7 +609,7 @@ static void window_loadsave_select(rct_window *w, const char *path) if (scenario_save((char*)path, gConfigGeneral.save_plugin_data ? 1 : 0)) { window_close(w); - game_do_command(0, 1047, 0, -1, GAME_COMMAND_0, 0, 0); + game_do_command(0, 1047, 0, -1, GAME_COMMAND_SET_RIDE_APPEARANCE, 0, 0); gfx_invalidate_screen(); } else { diff --git a/src/windows/map.c b/src/windows/map.c index 2efeb2caf7..bf6b64d0c4 100644 --- a/src/windows/map.c +++ b/src/windows/map.c @@ -319,8 +319,8 @@ static void window_map_mouseup() RCT2_GLOBAL(0x9E32D2, sint8) = 0; - if (!(RCT2_GLOBAL(0x9DE518, sint32) & (1 << 6))) // Remove? - RCT2_GLOBAL(0x9DE518, sint32) |= (1 << 6); + if (!(RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, sint32) & INPUT_FLAG_6)) // Remove? + RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, sint32) |= INPUT_FLAG_6; show_gridlines(); show_land_rights(); @@ -369,14 +369,19 @@ static void window_map_mouseup() */ static void window_map_mousedown(int widgetIndex, rct_window*w, rct_widget* widget) { - // The normal map window doesn't have widget 8 or 9. - // I assume these widgets refer to the Scenario Editor's map window. - if (widgetIndex == 8) { + // These widgets all refer to the Scenario Editor's map window. + if (widgetIndex == WIDX_MAP_SIZE_SPINNER_UP) { RCT2_CALLPROC_X(0x0068D641, 0, 0, 0, widgetIndex, (int)w, 0, 0); } - else if (widgetIndex == 9) { + else if (widgetIndex == WIDX_MAP_SIZE_SPINNER_DOWN) { RCT2_CALLPROC_X(0x0068D6B4, 0, 0, 0, widgetIndex, (int)w, 0, 0); } + else if (widgetIndex == WIDX_SET_LAND_RIGHTS) + { + // When unselecting the land rights tool, reset the size so the number doesn't + // stay in the map window. + RCT2_GLOBAL(RCT2_ADDRESS_LAND_TOOL_SIZE, sint16) = 1; + } } /** diff --git a/src/windows/new_campaign.c b/src/windows/new_campaign.c index 31e6a07263..e3d6e4c18d 100644 --- a/src/windows/new_campaign.c +++ b/src/windows/new_campaign.c @@ -273,7 +273,7 @@ static void window_new_campaign_mousedown(int widgetIndex, rct_window *w, rct_wi w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0x80, + DROPDOWN_FLAG_STAY_OPEN, numItems, dropdownWidget->right - dropdownWidget->left - 3 ); @@ -295,7 +295,7 @@ static void window_new_campaign_mousedown(int widgetIndex, rct_window *w, rct_wi w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0x80, + DROPDOWN_FLAG_STAY_OPEN, numItems, dropdownWidget->right - dropdownWidget->left - 3 ); diff --git a/src/windows/options.c b/src/windows/options.c index 27cbdf50d2..473bbc9eda 100644 --- a/src/windows/options.c +++ b/src/windows/options.c @@ -221,7 +221,7 @@ void window_options_open() if (w != NULL) return; - w = window_create_centred(WW, WH, (uint32*)window_options_events, WC_OPTIONS, WF_STICK_TO_FRONT); + w = window_create_centred(WW, WH, (uint32*)window_options_events, WC_OPTIONS, 0); w->widgets = window_options_widgets; w->enabled_widgets = (1ULL << WIDX_CLOSE) | @@ -396,12 +396,15 @@ static void window_options_mousedown(int widgetIndex, rct_window*w, rct_widget* gDropdownItemsChecked = gConfigGeneral.show_height_as_units ? 1 : 2; break; case WIDX_TITLE_MUSIC_DROPDOWN: - num_items = 3; + num_items = 4; - for (i = 0; i < num_items; i++) { + for (i = 0; i < num_items - 1; i++) { gDropdownItemsFormat[i] = 1142; gDropdownItemsArgs[i] = 2739 + i; } + // Random title music + gDropdownItemsFormat[3] = 1142; + gDropdownItemsArgs[3] = 5126; window_options_show_dropdown(w, widget, num_items); @@ -556,7 +559,7 @@ static void window_options_dropdown() window_options_update_height_markers(); break; case WIDX_TITLE_MUSIC_DROPDOWN: - if (dropdownIndex == 1 && !platform_file_exists(get_file_path(PATH_ID_CSS50))) { + if ((dropdownIndex == 1 || dropdownIndex == 3) && !platform_file_exists(get_file_path(PATH_ID_CSS50))) { window_error_open(2742, 2743); } else { gConfigSound.title_music = (sint8)dropdownIndex; @@ -837,7 +840,7 @@ static void window_options_paint() gfx_draw_string_left(dpi, 2738, w, 12, w->x + 10, w->y + window_options_widgets[WIDX_TITLE_MUSIC].top + 1); gfx_draw_string_left( dpi, - 2739 + gConfigSound.title_music, + (gConfigSound.title_music == 3 ? 5126 : 2739 + gConfigSound.title_music), NULL, 12, w->x + window_options_widgets[WIDX_TITLE_MUSIC].left + 1, @@ -866,7 +869,7 @@ static void window_options_show_dropdown(rct_window *w, rct_widget *widget, int w->y + widget->top, widget->bottom - widget->top + 1, w->colours[1], - 0x80, + DROPDOWN_FLAG_STAY_OPEN, num_items, widget->right - widget->left - 3 ); diff --git a/src/windows/research.c b/src/windows/research.c index 447d539788..e75ac0256f 100644 --- a/src/windows/research.c +++ b/src/windows/research.c @@ -472,7 +472,7 @@ static void window_research_funding_mousedown(int widgetIndex, rct_window *w, rc w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0x80, + DROPDOWN_FLAG_STAY_OPEN, 4, dropdownWidget->right - dropdownWidget->left - 3 ); diff --git a/src/windows/ride.c b/src/windows/ride.c index 03e02001a7..2e31744ec7 100644 --- a/src/windows/ride.c +++ b/src/windows/ride.c @@ -2308,7 +2308,7 @@ static void window_ride_vehicle_mousedown(int widgetIndex, rct_window *w, rct_wi w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0x80, + DROPDOWN_FLAG_STAY_OPEN, numItems, widget->right - dropdownWidget->left ); @@ -2321,7 +2321,7 @@ static void window_ride_vehicle_mousedown(int widgetIndex, rct_window *w, rct_wi w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0x80, + DROPDOWN_FLAG_STAY_OPEN, ride->var_0CC, widget->right - dropdownWidget->left ); @@ -2343,7 +2343,7 @@ static void window_ride_vehicle_mousedown(int widgetIndex, rct_window *w, rct_wi w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0x80, + DROPDOWN_FLAG_STAY_OPEN, maxCars - minCars + 1, widget->right - dropdownWidget->left ); @@ -2759,7 +2759,7 @@ static void window_ride_mode_dropdown(rct_window *w, rct_widget *widget) w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0, + DROPDOWN_FLAG_STAY_OPEN, numAvailableModes, widget->right - dropdownWidget->left ); @@ -2791,7 +2791,7 @@ static void window_ride_load_dropdown(rct_window *w, rct_widget *widget) w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0, + DROPDOWN_FLAG_STAY_OPEN, 5, widget->right - dropdownWidget->left ); @@ -3323,7 +3323,7 @@ static void window_ride_maintenance_mousedown(int widgetIndex, rct_window *w, rc w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0, + DROPDOWN_FLAG_STAY_OPEN, 7, widget->right - dropdownWidget->left ); @@ -3545,9 +3545,9 @@ static void window_ride_set_track_colour_scheme(rct_window *w, int x, int y) newColourScheme = (uint8)(*((uint16*)&w->var_494)); - int z; + int interactionType; - get_map_coordinates_from_pos(x, y, -5, &x, &y, &z, &mapElement, NULL); + get_map_coordinates_from_pos(x, y, VIEWPORT_INTERACTION_MASK_RIDE, &x, &y, &interactionType, &mapElement, NULL); // Get map coordinates from point /*int eax, ebx, ecx, edx, esi, edi, ebp; eax = x; @@ -3558,7 +3558,7 @@ static void window_ride_set_track_colour_scheme(rct_window *w, int x, int y) y = ecx & 0xFFFF; mapElement = (rct_map_element*)edx;*/ - if ((/*ebx*/z & 0xFF) != 3) + if (interactionType != VIEWPORT_INTERACTION_ITEM_RIDE) return; if (mapElement->properties.track.ride_index != w->number) return; @@ -3676,7 +3676,7 @@ static void window_ride_colour_mousedown(int widgetIndex, rct_window *w, rct_wid w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0, + DROPDOWN_FLAG_STAY_OPEN, 4, widget->right - dropdownWidget->left ); @@ -3703,7 +3703,7 @@ static void window_ride_colour_mousedown(int widgetIndex, rct_window *w, rct_wid w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0, + DROPDOWN_FLAG_STAY_OPEN, 4, widget->right - dropdownWidget->left ); @@ -3725,7 +3725,7 @@ static void window_ride_colour_mousedown(int widgetIndex, rct_window *w, rct_wid w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0x80, + DROPDOWN_FLAG_STAY_OPEN, countof(window_ride_entrance_style_list), widget->right - dropdownWidget->left ); @@ -3743,7 +3743,7 @@ static void window_ride_colour_mousedown(int widgetIndex, rct_window *w, rct_wid w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0, + DROPDOWN_FLAG_STAY_OPEN, rideEntry->max_cars_in_train > 1 ? 3 : 2, widget->right - dropdownWidget->left ); @@ -3766,7 +3766,7 @@ static void window_ride_colour_mousedown(int widgetIndex, rct_window *w, rct_wid w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0x80, + DROPDOWN_FLAG_STAY_OPEN, numItems, widget->right - dropdownWidget->left ); @@ -3808,22 +3808,22 @@ static void window_ride_colour_dropdown() window_invalidate(w); break; case WIDX_TRACK_MAIN_COLOUR: - game_do_command(0, (0 << 8) | 1, 0, (dropdownIndex << 8) | w->number, GAME_COMMAND_0, *((uint16*)&w->var_494), 0); + game_do_command(0, (0 << 8) | 1, 0, (dropdownIndex << 8) | w->number, GAME_COMMAND_SET_RIDE_APPEARANCE, *((uint16*)&w->var_494), 0); break; case WIDX_TRACK_ADDITIONAL_COLOUR: - game_do_command(0, (1 << 8) | 1, 0, (dropdownIndex << 8) | w->number, GAME_COMMAND_0, *((uint16*)&w->var_494), 0); + game_do_command(0, (1 << 8) | 1, 0, (dropdownIndex << 8) | w->number, GAME_COMMAND_SET_RIDE_APPEARANCE, *((uint16*)&w->var_494), 0); break; case WIDX_TRACK_SUPPORT_COLOUR: - game_do_command(0, (4 << 8) | 1, 0, (dropdownIndex << 8) | w->number, GAME_COMMAND_0, *((uint16*)&w->var_494), 0); + game_do_command(0, (4 << 8) | 1, 0, (dropdownIndex << 8) | w->number, GAME_COMMAND_SET_RIDE_APPEARANCE, *((uint16*)&w->var_494), 0); break; case WIDX_MAZE_STYLE_DROPDOWN: - game_do_command(0, (4 << 8) | 1, 0, (dropdownIndex << 8) | w->number, GAME_COMMAND_0, *((uint16*)&w->var_494), 0); + game_do_command(0, (4 << 8) | 1, 0, (dropdownIndex << 8) | w->number, GAME_COMMAND_SET_RIDE_APPEARANCE, *((uint16*)&w->var_494), 0); break; case WIDX_ENTRANCE_STYLE_DROPDOWN: - game_do_command(0, (6 << 8) | 1, 0, (window_ride_entrance_style_list[dropdownIndex] << 8) | w->number, GAME_COMMAND_0, 0, 0); + game_do_command(0, (6 << 8) | 1, 0, (window_ride_entrance_style_list[dropdownIndex] << 8) | w->number, GAME_COMMAND_SET_RIDE_APPEARANCE, 0, 0); break; case WIDX_VEHICLE_COLOUR_SCHEME_DROPDOWN: - game_do_command(0, (5 << 8) | 1, 0, (dropdownIndex << 8) | w->number, GAME_COMMAND_0, 0, 0); + game_do_command(0, (5 << 8) | 1, 0, (dropdownIndex << 8) | w->number, GAME_COMMAND_SET_RIDE_APPEARANCE, 0, 0); w->var_48C = 0; break; case WIDX_VEHICLE_COLOUR_INDEX_DROPDOWN: @@ -3831,13 +3831,13 @@ static void window_ride_colour_dropdown() window_invalidate(w); break; case WIDX_VEHICLE_MAIN_COLOUR: - game_do_command(0, (2 << 8) | 1, 0, (dropdownIndex << 8) | w->number, GAME_COMMAND_0, w->var_48C, 0); + game_do_command(0, (2 << 8) | 1, 0, (dropdownIndex << 8) | w->number, GAME_COMMAND_SET_RIDE_APPEARANCE, w->var_48C, 0); break; case WIDX_VEHICLE_ADDITIONAL_COLOUR_1: - game_do_command(0, (3 << 8) | 1, 0, (dropdownIndex << 8) | w->number, GAME_COMMAND_0, w->var_48C, 0); + game_do_command(0, (3 << 8) | 1, 0, (dropdownIndex << 8) | w->number, GAME_COMMAND_SET_RIDE_APPEARANCE, w->var_48C, 0); break; case WIDX_VEHICLE_ADDITIONAL_COLOUR_2: - game_do_command(0, (7 << 8) | 1, 0, (dropdownIndex << 8) | w->number, GAME_COMMAND_0, w->var_48C, 0); + game_do_command(0, (7 << 8) | 1, 0, (dropdownIndex << 8) | w->number, GAME_COMMAND_SET_RIDE_APPEARANCE, w->var_48C, 0); break; } } @@ -4327,21 +4327,24 @@ static void window_ride_music_mousedown(int widgetIndex, rct_window *w, rct_widg window_ride_current_music_style_order[numItems++] = MUSIC_STYLE_CUSTOM_MUSIC_2; } + for (i = 0; i < numItems; i++) { + gDropdownItemsFormat[i] = 1142; + gDropdownItemsArgs[i] = STR_MUSIC_STYLE_START + window_ride_current_music_style_order[i]; + } + window_dropdown_show_text_custom_width( w->x + dropdownWidget->left, w->y + dropdownWidget->top, dropdownWidget->bottom - dropdownWidget->top + 1, w->colours[1], - 0, + DROPDOWN_FLAG_STAY_OPEN, numItems, widget->right - dropdownWidget->left ); for (i = 0; i < numItems; i++) { - gDropdownItemsFormat[i] = 1142; if (window_ride_current_music_style_order[i] == ride->music) gDropdownItemsChecked = (1 << i); - gDropdownItemsArgs[i] = STR_MUSIC_STYLE_START + window_ride_current_music_style_order[i]; } } diff --git a/src/windows/ride_construction.c b/src/windows/ride_construction.c index 33c1ee1b02..4486c5a9a5 100644 --- a/src/windows/ride_construction.c +++ b/src/windows/ride_construction.c @@ -169,7 +169,7 @@ rct_window *window_construction_open() window_push_others_right(w); show_gridlines(); - RCT2_GLOBAL(0xF44070, uint32) = 0x80000000; + RCT2_GLOBAL(0xF44070, uint32) = MONEY32_UNDEFINED; RCT2_GLOBAL(0xF440CD, uint8) = 8; RCT2_GLOBAL(0xF440CE, uint8) = 18; RCT2_GLOBAL(0xF440CF, uint8) = 4; @@ -307,7 +307,7 @@ void window_construction_mouseup_demolish(rct_window* w){ RCT2_CALLPROC_X(0x6C9BA5, 0, 0, 0, 0, (int)w, 0, 0); return; - RCT2_GLOBAL(0xF44070, uint32) = 0x80000000; + RCT2_GLOBAL(0xF44070, uint32) = MONEY32_UNDEFINED; sub_6C9627(); RCT2_GLOBAL(0xF440B8, uint8) = 3; @@ -440,7 +440,7 @@ void window_construction_paint() short string_y = RCT2_GLOBAL(0x9D7C08, int16_t) + w->y - 23; if (RCT2_GLOBAL(0xF440A6, uint8_t) != 4) gfx_draw_string_centred(dpi, 1407, string_x, string_y, 0, w); string_y += 11; - if (RCT2_GLOBAL(0xF44070, uint32_t) != 0x80000000 && !(RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32_t) & 0x800)) + if (RCT2_GLOBAL(0xF44070, uint32_t) != MONEY32_UNDEFINED && !(RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32_t) & 0x800)) gfx_draw_string_centred(dpi, 1408, string_x, string_y, 0, (void*)0xF44070); } diff --git a/src/windows/ride_list.c b/src/windows/ride_list.c index 29b244ee63..329c6be84d 100644 --- a/src/windows/ride_list.c +++ b/src/windows/ride_list.c @@ -253,7 +253,15 @@ static void window_ride_list_mousedown(int widgetIndex, rct_window*w, rct_widget gDropdownItemsFormat[i] = 1142; gDropdownItemsArgs[i] = STR_STATUS + i; } - window_dropdown_show_text_custom_width(w->x + widget->left, w->y + widget->top, widget->bottom - widget->top, w->colours[1], 0x80, numItems, widget->right - widget->left - 3); + window_dropdown_show_text_custom_width( + w->x + widget->left, + w->y + widget->top, + widget->bottom - widget->top, + w->colours[1], + DROPDOWN_FLAG_STAY_OPEN, + numItems, + widget->right - widget->left - 3 + ); gDropdownItemsChecked |= (1 << _window_ride_list_information_type); } } diff --git a/src/windows/scenery.c b/src/windows/scenery.c index 7982964f20..06b9e9e984 100644 --- a/src/windows/scenery.c +++ b/src/windows/scenery.c @@ -458,13 +458,13 @@ void window_scenery_open() window_scenery_update_scroll(window); show_gridlines(); window_scenery_rotation = 3; - RCT2_GLOBAL(0x00F64F12, uint8) = 0; - RCT2_GLOBAL(0x00F64F13, uint8) = 0; + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_CTRL_PRESSED, uint8) = 0; + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_SHIFT_PRESSED, uint8) = 0; window->scenery.selected_scenery_id = -1; window->scenery.hover_counter = 0; window_push_others_below(window); RCT2_GLOBAL(0x00F64F0D, uint8) = 0; - RCT2_GLOBAL(0x00F64EB4, uint32) = 0x80000000; + RCT2_GLOBAL(0x00F64EB4, uint32) = MONEY32_UNDEFINED; RCT2_GLOBAL(0x00F64EC0, uint16) = 0; window_scenery_is_repaint_scenery_tool_on = 0; // repaint colored scenery tool state window_scenery_is_build_cluster_tool_on = 0; // build cluster tool state @@ -503,9 +503,9 @@ void window_scenery_close() { window_get_register(w); - RCT2_CALLPROC_EBPSAFE(0x006E2712); + scenery_remove_ghost_tool_placement(); hide_gridlines(); - RCT2_CALLPROC_X(0x006CB70A, 0, 0, 0, 0, 0, 0, 0); + viewport_set_visibility(0); if (window_scenery_is_scenery_tool_active()) tool_cancel(); @@ -543,7 +543,7 @@ static void window_scenery_mouseup() case WIDX_SCENERY_ROTATE_OBJECTS_BUTTON: window_scenery_rotation++; window_scenery_rotation = window_scenery_rotation % 4; - RCT2_CALLPROC_EBPSAFE(0x006E2712); + scenery_remove_ghost_tool_placement(); window_invalidate(w); break; case WIDX_SCENERY_REPAINT_SCENERY_BUTTON: @@ -643,7 +643,7 @@ static void window_scenery_mousedown(int widgetIndex, rct_window* w, rct_widget* if (widgetIndex >= WIDX_SCENERY_TAB_1 && widgetIndex <= WIDX_SCENERY_TAB_20) { window_scenery_active_tab_index = widgetIndex - WIDX_SCENERY_TAB_1; window_invalidate(w); - RCT2_GLOBAL(0x00F64EB4, uint32) = 0x80000000; + RCT2_GLOBAL(0x00F64EB4, uint32) = MONEY32_UNDEFINED; window_scenery_update_scroll(w); } } @@ -817,7 +817,7 @@ void window_scenery_scrollmousedown() window_scenery_is_repaint_scenery_tool_on &= 0xFE; sound_play_panned(4, (w->width >> 1) + w->x, 0, 0, 0); w->scenery.hover_counter = -16; - RCT2_GLOBAL(0x00F64EB4, uint32) = 0x80000000; + RCT2_GLOBAL(0x00F64EB4, uint32) = MONEY32_UNDEFINED; window_invalidate(w); } @@ -1057,7 +1057,7 @@ void window_scenery_paint() price = sceneryEntry->small_scenery.price * 10; } - if (w->scenery.selected_scenery_id == -1 && RCT2_GLOBAL(0x00F64EB4, uint32) != 0x80000000) { + if (w->scenery.selected_scenery_id == -1 && RCT2_GLOBAL(0x00F64EB4, uint32) != MONEY32_UNDEFINED) { price = RCT2_GLOBAL(0x00F64EB4, uint32); } diff --git a/src/windows/sign.c b/src/windows/sign.c index cd9fbf95bc..c77defa2c8 100644 --- a/src/windows/sign.c +++ b/src/windows/sign.c @@ -174,9 +174,7 @@ void window_sign_open(rct_windownumber number) while (1){ if (map_element_get_type(map_element) == MAP_ELEMENT_TYPE_SCENERY_MULTIPLE) { - int ebx = map_element->properties.scenerymultiple.type; - ebx |= (map_element->properties.scenerymultiple.index & 0x3) << 8; - rct_scenery_entry* scenery_entry = g_largeSceneryEntries[ebx]; + rct_scenery_entry* scenery_entry = g_largeSceneryEntries[map_element->properties.scenerymultiple.type & MAP_ELEMENT_LARGE_TYPE_MASK]; if (scenery_entry->large_scenery.var_11 != 0xFF){ int id = (map_element->type & 0xC0) | ((map_element->properties.scenerymultiple.colour[0] & 0xE0) >> 2) | @@ -242,9 +240,7 @@ static void window_sign_mouseup() case WIDX_SIGN_DEMOLISH: while (1){ if (map_element_get_type(map_element) == MAP_ELEMENT_TYPE_SCENERY_MULTIPLE) { - int ebx = map_element->properties.scenerymultiple.type; - ebx |= (map_element->properties.scenerymultiple.index & 0x3) << 8; - rct_scenery_entry* scenery_entry = g_largeSceneryEntries[ebx]; + rct_scenery_entry* scenery_entry = g_largeSceneryEntries[map_element->properties.scenerymultiple.type & MAP_ELEMENT_LARGE_TYPE_MASK]; if (scenery_entry->large_scenery.var_11 != 0xFF){ int id = (map_element->type & 0xC0) | ((map_element->properties.scenerymultiple.colour[0] & 0xE0) >> 2) | @@ -259,8 +255,8 @@ static void window_sign_mouseup() x, 1 | ((map_element->type&0x3) << 8), y, - map_element->base_height | ((map_element->properties.scenerymultiple.index >> 2) << 8), - GAME_COMMAND_44, + map_element->base_height | ((map_element->properties.scenerymultiple.type >> 10) << 8), + GAME_COMMAND_REMOVE_LARGE_SCENERY, 0, 0); break; @@ -323,9 +319,7 @@ static void window_sign_dropdown() while (1){ if (map_element_get_type(map_element) == MAP_ELEMENT_TYPE_SCENERY_MULTIPLE) { - int ebx = map_element->properties.scenerymultiple.type; - ebx |= (map_element->properties.scenerymultiple.index & 0x3) << 8; - rct_scenery_entry* scenery_entry = g_largeSceneryEntries[ebx]; + rct_scenery_entry* scenery_entry = g_largeSceneryEntries[map_element->properties.scenerymultiple.type & MAP_ELEMENT_LARGE_TYPE_MASK]; if (scenery_entry->large_scenery.var_11 != 0xFF){ int id = (map_element->type & 0xC0) | ((map_element->properties.scenerymultiple.colour[0] & 0xE0) >> 2) | @@ -337,7 +331,7 @@ static void window_sign_dropdown() map_element++; } - int edx = map_element->base_height | ((map_element->properties.scenerymultiple.index >> 2) << 8); + int edx = map_element->base_height | ((map_element->properties.scenerymultiple.type >> 10) << 8); int ebp = w->list_information_type | (w->var_492 << 8); int ebx = (map_element->type & 0x3) << 8; RCT2_CALLPROC_X(0x6B9B05, x, ebx, y, edx, 0, w->number, ebp); @@ -501,7 +495,7 @@ void window_sign_small_open(rct_windownumber number){ while (1){ if (map_element_get_type(map_element) == MAP_ELEMENT_TYPE_FENCE) { - rct_scenery_entry* scenery_entry = g_wallSceneryEntries[map_element->properties.fence.slope]; + rct_scenery_entry* scenery_entry = g_wallSceneryEntries[map_element->properties.fence.type]; if (scenery_entry->wall.var_0D != 0xFF){ if (map_element->properties.fence.item[0] == w->number) break; @@ -515,7 +509,7 @@ void window_sign_small_open(rct_windownumber number){ w->list_information_type = map_element->properties.fence.item[1] & 0x1F; w->var_492 = (map_element->properties.fence.item[1] >> 5) | ((map_element->flags&0x60) >> 2); - w->var_48C = map_element->properties.fence.slope; + w->var_48C = map_element->properties.fence.type; view_x += 16; view_y += 16; @@ -564,7 +558,7 @@ static void window_sign_small_mouseup() case WIDX_SIGN_DEMOLISH: while (1){ if (map_element_get_type(map_element) == MAP_ELEMENT_TYPE_FENCE) { - rct_scenery_entry* scenery_entry = g_wallSceneryEntries[map_element->properties.fence.slope]; + rct_scenery_entry* scenery_entry = g_wallSceneryEntries[map_element->properties.fence.type]; if (scenery_entry->wall.var_0D != 0xFF){ if (map_element->properties.fence.item[0] == w->number) break; @@ -626,7 +620,7 @@ static void window_sign_small_dropdown() while (1){ if (map_element_get_type(map_element) == MAP_ELEMENT_TYPE_FENCE) { - rct_scenery_entry* scenery_entry = g_wallSceneryEntries[map_element->properties.fence.slope]; + rct_scenery_entry* scenery_entry = g_wallSceneryEntries[map_element->properties.fence.type]; if (scenery_entry->wall.var_0D != 0xFF){ if (map_element->properties.fence.item[0] == w->number) break; diff --git a/src/windows/staff.c b/src/windows/staff.c index 8dfb1e503a..21bdf50562 100644 --- a/src/windows/staff.c +++ b/src/windows/staff.c @@ -30,6 +30,7 @@ #include "../world/footpath.h" #include "../world/sprite.h" #include "../world/scenery.h" +#include "../input.h" #include "dropdown.h" #include "error.h" @@ -375,7 +376,7 @@ void window_staff_overview_close() window_get_register(w); - if (RCT2_GLOBAL(0x9DE518, uint32) & (1 << 3)){ + if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_TOOL_ACTIVE){ if (w->classification == RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, rct_windowclass) && w->number == RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER, rct_windownumber)) tool_cancel(); @@ -388,7 +389,7 @@ void window_staff_overview_close() */ void window_staff_set_page(rct_window* w, int page) { - if (RCT2_GLOBAL(0x9DE518,uint32) & (1 << 3)) + if (RCT2_GLOBAL(RCT2_ADDRESS_INPUT_FLAGS, uint32) & INPUT_FLAG_TOOL_ACTIVE) { if(w->number == RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWNUMBER, rct_windownumber) && w->classification == RCT2_GLOBAL(RCT2_ADDRESS_TOOL_WINDOWCLASS, rct_windowclass)) @@ -457,7 +458,7 @@ void window_staff_overview_mouseup() w->var_48C = peep->x; - RCT2_CALLPROC_X(0x0069A512, 0, 0, 0, 0, (int)peep, 0, 0); + remove_peep_from_ride(peep); invalidate_sprite((rct_sprite*)peep); sprite_move( 0x8000, peep->y, peep->z, (rct_sprite*)peep); @@ -573,19 +574,18 @@ void window_staff_overview_dropdown() for (int i = 0; i < 128; i++) { - RCT2_GLOBAL(0x13B0E72 + ebx + i * 4, uint32) = 0; + RCT2_ADDRESS(0x13B0E72 + (peep->staff_id * 512), uint32)[i] = 0; } - RCT2_GLOBAL(RCT2_ADDRESS_STAFF_MODE_ARRAY + edi, uint16) &= 0xFD; // bug?? + RCT2_ADDRESS(RCT2_ADDRESS_STAFF_MODE_ARRAY, uint8)[peep->staff_id] &= ~2; - window_invalidate(w); - //RCT2_CALLPROC_EBPSAFE(0x006C0C3F); - sub_6C0C3F(); + gfx_invalidate_screen(); + staff_update_greyed_patrol_areas(); } else { if (!tool_set(w, widgetIndex, 22)) { show_gridlines(); RCT2_GLOBAL(0x009DEA50, sint16) = w->number; - window_invalidate(w); + gfx_invalidate_screen(); } } } @@ -1085,9 +1085,9 @@ void window_staff_overview_tool_update(){ RCT2_GLOBAL(RCT2_ADDRESS_PICKEDUP_PEEP_SPRITE, sint32) = -1; - int z; - get_map_coordinates_from_pos(x, y, 0, NULL, NULL, &z, NULL, NULL); - if (z == 0) + int interactionType; + get_map_coordinates_from_pos(x, y, VIEWPORT_INTERACTION_MASK_NONE, NULL, NULL, &interactionType, NULL, NULL); + if (interactionType == VIEWPORT_INTERACTION_ITEM_NONE) return; x--; @@ -1357,7 +1357,7 @@ void window_staff_options_mousedown(int widgetIndex, rct_window* w, rct_widget* int y = widget->top + w->y; int extray = widget->bottom - widget->top + 1; int width = widget->right - widget->left - 3; - window_dropdown_show_text_custom_width(x, y, extray, w->colours[1], 0x80, no_entries, width); + window_dropdown_show_text_custom_width(x, y, extray, w->colours[1], DROPDOWN_FLAG_STAY_OPEN, no_entries, width); // See above note. gDropdownItemsChecked = item_checked; diff --git a/src/windows/title_menu.c b/src/windows/title_menu.c index c528527fc1..19c0cf3823 100644 --- a/src/windows/title_menu.c +++ b/src/windows/title_menu.c @@ -130,7 +130,7 @@ static void window_title_menu_mousedown(int widgetIndex, rct_window*w, rct_widge w->y + widget->top, widget->bottom - widget->top + 1, w->colours[0] | 0x80, - 0x80, + DROPDOWN_FLAG_STAY_OPEN, 3 ); } else if (widgetIndex == WIDX_GAME_TOOLS) { @@ -143,7 +143,7 @@ static void window_title_menu_mousedown(int widgetIndex, rct_window*w, rct_widge w->y + widget->top, widget->bottom - widget->top + 1, w->colours[0] | 0x80, - 0x80, + DROPDOWN_FLAG_STAY_OPEN, 4 ); } diff --git a/src/windows/title_scenarioselect.c b/src/windows/title_scenarioselect.c index c527d9950a..3ad4edef4d 100644 --- a/src/windows/title_scenarioselect.c +++ b/src/windows/title_scenarioselect.c @@ -116,7 +116,7 @@ void window_scenarioselect_open() 334, (uint32*)window_scenarioselect_events, WC_SCENARIO_SELECT, - WF_STICK_TO_FRONT | WF_10 + WF_10 ); window->widgets = window_scenarioselect_widgets; diff --git a/src/windows/top_toolbar.c b/src/windows/top_toolbar.c index 85b933d396..5f5c543df2 100644 --- a/src/windows/top_toolbar.c +++ b/src/windows/top_toolbar.c @@ -29,7 +29,9 @@ #include "../interface/window.h" #include "../interface/viewport.h" #include "../localisation/localisation.h" +#include "../scenario.h" #include "../world/scenery.h" +#include "../world/banner.h" #include "dropdown.h" enum { @@ -345,7 +347,7 @@ static void window_top_toolbar_mousedown(int widgetIndex, rct_window*w, rct_widg w->y + widget->top, widget->bottom - widget->top + 1, w->colours[0] | 0x80, - 0x80, + DROPDOWN_FLAG_STAY_OPEN, numItems ); break; @@ -626,70 +628,817 @@ static void window_top_toolbar_paint() } } +/* rct2: 0x006E3158 */ +static void repaint_scenery_tool_down(sint16 x, sint16 y, sint16 widgetIndex){ + //RCT2_CALLPROC_X(0x6E2CC6, x, y, 0, widgetIndex, 0, 0, 0); + //return; + // ax, cx, bl + int grid_x, grid_y, type; + // edx + rct_map_element* map_element; + uint16 flags = + VIEWPORT_INTERACTION_MASK_SCENERY & + VIEWPORT_INTERACTION_MASK_WALL & + VIEWPORT_INTERACTION_MASK_LARGE_SCENERY & + VIEWPORT_INTERACTION_MASK_BANNER; + // This is -2 as banner is 12 but flags are offset different + + // not used + rct_viewport* viewport; + get_map_coordinates_from_pos(x, y, flags, &grid_x, &grid_y, &type, &map_element, &viewport); + + switch (type){ + case VIEWPORT_INTERACTION_ITEM_SCENERY: + { + rct_scenery_entry* scenery_entry = g_smallSceneryEntries[map_element->properties.scenery.type]; + + // If can't repaint + if (!(scenery_entry->small_scenery.flags & + (SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR | + SMALL_SCENERY_FLAG10))) + return; + + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_STRING_ID, rct_string_id) = 3103; + game_do_command( + grid_x, + 1 | (map_element->type << 8), + grid_y, + map_element->base_height | (map_element->properties.scenery.type << 8), + GAME_COMMAND_52, + 0, + window_scenery_primary_colour | (window_scenery_secondary_colour << 8)); + break; + } + case VIEWPORT_INTERACTION_ITEM_WALL: + { + rct_scenery_entry* scenery_entry = g_wallSceneryEntries[map_element->properties.fence.type]; + + // If can't repaint + if (!(scenery_entry->wall.flags & + (WALL_SCENERY_FLAG1 | + WALL_SCENERY_FLAG2))) + return; + + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_STRING_ID, rct_string_id) = 3103; + game_do_command( + grid_x, + 1 | (window_scenery_primary_colour << 8), + grid_y, + (map_element->type & MAP_ELEMENT_DIRECTION_MASK) | (map_element->base_height << 8), + GAME_COMMAND_53, + 0, + window_scenery_secondary_colour | (window_scenery_tertiary_colour << 8)); + break; + } + case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: + { + rct_scenery_entry* scenery_entry = g_largeSceneryEntries[map_element->properties.scenerymultiple.type & MAP_ELEMENT_LARGE_TYPE_MASK]; + + // If can't repaint + if (!(scenery_entry->large_scenery.flags & + (1 << 0))) + return; + + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_STRING_ID, rct_string_id) = 3103; + game_do_command( + grid_x, + 1 | ((map_element->type & MAP_ELEMENT_DIRECTION_MASK) << 8), + grid_y, + map_element->base_height | ((map_element->properties.scenerymultiple.type >> 10) << 8), + GAME_COMMAND_54, + 0, + window_scenery_primary_colour | (window_scenery_secondary_colour << 8)); + break; + } + case VIEWPORT_INTERACTION_ITEM_BANNER: + { + rct_banner* banner = &gBanners[map_element->properties.banner.index]; + rct_scenery_entry* scenery_entry = g_bannerSceneryEntries[banner->type]; + + // If can't repaint + if (!(scenery_entry->banner.flags & + (1 << 0))) + return; + + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_STRING_ID, rct_string_id) = 3103; + game_do_command( + grid_x, + 1, + grid_y, + map_element->base_height | ((map_element->properties.banner.position & 0x3) << 8), + GAME_COMMAND_55, + 0, + window_scenery_primary_colour | (window_scenery_secondary_colour << 8)); + break; + } + default: + return; + } +} + +void sub_689604(sint16 x, sint16 y, sint16* grid_x, sint16* grid_y, uint8* cl){ + int eax = x, ebx = y, ecx = 0, edx = 0, esi = 0, edi = 0, ebp = 0; + + RCT2_CALLFUNC_X(0x00689604, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + + *grid_x = eax; + *grid_y = ebx; + *cl = ecx; +} + +void sub_68964B(sint16 x, sint16 y, sint16 z, sint16* grid_x, sint16* grid_y, uint8* cl){ + int eax = x, ebx = y, ecx = 0, edx = 0, esi = 0, edi = 0, ebp = z; + + RCT2_CALLFUNC_X(0x0068964B, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + + *grid_x = eax; + *grid_y = ebx; + *cl = ecx; +} + +void sub_689692(sint16 x, sint16 y, sint16* grid_x, sint16* grid_y, uint8* cl){ + int eax = x, ebx = y, ecx = 0, edx = 0, esi = 0, edi = 0, ebp = 0; + + RCT2_CALLFUNC_X(0x00689692, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + + *grid_x = eax; + *grid_y = ebx; + *cl = ecx; +} + +void sub_6896DC(sint16 x, sint16 y, sint16 z, sint16* grid_x, sint16* grid_y, uint8* cl){ + int eax = x, ebx = y, ecx = 0, edx = 0, esi = 0, edi = 0, ebp = z; + + RCT2_CALLFUNC_X(0x006896DC, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + + *grid_x = eax; + *grid_y = ebx; + *cl = ecx; +} + +void sub_6894D4(sint16 x, sint16 y, sint16 z, sint16* grid_x, sint16* grid_y){ + int eax = x, ebx = y, ecx = 0, edx = 0, esi = 0, edi = 0, ebp = z; + + RCT2_CALLFUNC_X(0x006894D4, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + + *grid_x = eax; + *grid_y = ebx; +} + +/* rct2: 0x006E1F34 + * Outputs + * eax : grid_x + * ebx : parameter_1 + * ecx : grid_y + * edx : parameter_2 + * edi : parameter_3 + */ +void sub_6E1F34(sint16 x, sint16 y, uint16 selected_scenery, sint16* grid_x, sint16* grid_y, uint32* parameter_1, uint32* parameter_2, uint32* parameter_3){ + rct_window* w = window_find_by_class(WC_SCENERY); + + if (w == NULL) + { + *grid_x = 0x8000; + return; + } + + uint8 scenery_type = selected_scenery >> 8; + // Not sure what this type is yet. + uint8 type = 0; + + if (scenery_type == 0){ + rct_scenery_entry* scenery_entry = g_smallSceneryEntries[selected_scenery]; + + if (scenery_entry->small_scenery.flags & SMALL_SCENERY_FLAG18){ + type = 1; + } + } + else if (scenery_type == 2 || scenery_type == 3){ + type = 1; + } + + if (type == 0){ + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_CTRL_PRESSED, uint8) = 0; + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_SHIFT_PRESSED, uint8) = 0; + } + else{ + if (RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_CTRL_PRESSED, uint8) == 0){ + // CTRL pressed + if (RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8) & (1 << 1)){ + rct_map_element* map_element; + uint16 flags = + VIEWPORT_INTERACTION_MASK_TERRAIN & + VIEWPORT_INTERACTION_MASK_RIDE & + VIEWPORT_INTERACTION_MASK_SCENERY & + VIEWPORT_INTERACTION_MASK_FOOTPATH & + VIEWPORT_INTERACTION_MASK_WALL & + VIEWPORT_INTERACTION_MASK_LARGE_SCENERY; + int interaction_type; + get_map_coordinates_from_pos(x, y, flags, NULL, NULL, &interaction_type, &map_element, NULL); + + if (interaction_type != VIEWPORT_INTERACTION_ITEM_NONE){ + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_CTRL_PRESSED, uint8) = 1; + RCT2_GLOBAL(RCT2_ADDRESS_CTRL_PRESS_Z_COORDINATE, uint16) = map_element->base_height * 8; + } + } + } + else{ + // CTRL not pressed + if (!(RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8) & (1 << 1))){ + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_CTRL_PRESSED, uint8) = 0; + } + } + + if (RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_SHIFT_PRESSED, uint8) == 0){ + // SHIFT pressed + if (RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8) & (1 << 0)){ + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_SHIFT_PRESSED, uint8) = 1; + RCT2_GLOBAL(RCT2_ADDRESS_SHIFT_PRESS_X_COORDINATE, uint16) = x; + RCT2_GLOBAL(RCT2_ADDRESS_SHIFT_PRESS_Y_COORDINATE, uint16) = y; + RCT2_GLOBAL(RCT2_ADDRESS_SHIFT_PRESS_Z_VECTOR, uint16) = 0; + } + } + else{ + // SHIFT pressed + if (RCT2_GLOBAL(RCT2_ADDRESS_PLACE_OBJECT_MODIFIER, uint8) & (1 << 0)){ + RCT2_GLOBAL(RCT2_ADDRESS_SHIFT_PRESS_Z_VECTOR, sint16) = + (RCT2_GLOBAL(RCT2_ADDRESS_SHIFT_PRESS_Y_COORDINATE, sint16) - y + 4) & 0xFFF8; + + x = RCT2_GLOBAL(RCT2_ADDRESS_SHIFT_PRESS_X_COORDINATE, sint16); + y = RCT2_GLOBAL(RCT2_ADDRESS_SHIFT_PRESS_Y_COORDINATE, sint16); + } + else{ + // SHIFT not pressed + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_SHIFT_PRESSED, uint8) = 0; + } + } + } + + switch (scenery_type){ + case 0: + { + // Small scenery + rct_scenery_entry* scenery = g_smallSceneryEntries[selected_scenery]; + if (!(scenery->small_scenery.flags & SMALL_SCENERY_FLAG_FULL_TILE)){ + uint8 cl = 0; + + // If CTRL not pressed + if (RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_CTRL_PRESSED, uint8) == 0){ + sub_689604(x, y, grid_x, grid_y, &cl); + + if (*grid_x == (sint16)0x8000) + return; + + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) = 0; + + // If SHIFT pressed + if (RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_SHIFT_PRESSED, uint8) != 0){ + + rct_map_element* map_element = map_get_surface_element_at(*grid_x / 32, *grid_y / 32); + + if (map_element == NULL){ + *grid_x = 0x8000; + return; + } + + sint16 z = (map_element->base_height * 8) & 0xFFF0; + z += RCT2_GLOBAL(RCT2_ADDRESS_SHIFT_PRESS_Z_VECTOR, sint16); + + if (z < 16){ + z = 16; + } + + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) = z; + } + } + else{ + sint16 z = RCT2_GLOBAL(RCT2_ADDRESS_CTRL_PRESS_Z_COORDINATE, sint16); + + sub_68964B(x, y, z, grid_x, grid_y, &cl); + + // If SHIFT pressed + if (RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_SHIFT_PRESSED, uint8) != 0){ + z += RCT2_GLOBAL(RCT2_ADDRESS_SHIFT_PRESS_Z_VECTOR, sint16); + } + + if (z < 16){ + z = 16; + } + + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) = z; + } + + if (*grid_x == (sint16)0x8000) + return; + + uint8 rotation = window_scenery_rotation; + + if (!(scenery->small_scenery.flags & SMALL_SCENERY_FLAG4)){ + rotation = scenario_rand() & 0xFF; + } + + rotation -= RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8); + rotation &= 0x3; + + // Also places it in lower but think thats for clobering + *parameter_1 = (selected_scenery & 0xFF) << 8; + *parameter_2 = cl ^ (1 << 1) | (window_scenery_primary_colour << 8); + *parameter_3 = rotation | (window_scenery_secondary_colour << 16); + return; + } + + // If CTRL not pressed + if (RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_CTRL_PRESSED, uint8) == 0){ + uint16 flags = + VIEWPORT_INTERACTION_MASK_TERRAIN & + VIEWPORT_INTERACTION_MASK_WATER; + int interaction_type = 0; + rct_map_element* map_element; + + int map_x, map_y; + get_map_coordinates_from_pos(x, y, flags, &map_x, &map_y, &interaction_type, &map_element, NULL); + *grid_x = (sint16)map_x; + *grid_y = (sint16)map_y; + + if (interaction_type == VIEWPORT_INTERACTION_ITEM_NONE) + { + *grid_x = 0x8000; + return; + } + + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) = 0; + uint16 water_height = map_element->properties.surface.terrain & MAP_ELEMENT_WATER_HEIGHT_MASK; + if (water_height != 0){ + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) = water_height * 16; + } + + // If SHIFT pressed + if (RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_SHIFT_PRESSED, uint8) != 0){ + rct_map_element* map_element = map_get_surface_element_at(*grid_x / 32, *grid_y / 32); + + if (map_element == NULL){ + *grid_x = 0x8000; + return; + } + + sint16 z = (map_element->base_height * 8) & 0xFFF0; + z += RCT2_GLOBAL(RCT2_ADDRESS_SHIFT_PRESS_Z_VECTOR, sint16); + + if (z < 16){ + z = 16; + } + + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) = z; + } + } + else{ + sint16 z = RCT2_GLOBAL(RCT2_ADDRESS_CTRL_PRESS_Z_COORDINATE, sint16); + sub_6894D4(x, y, z, grid_x, grid_y); + + // If SHIFT pressed + if (RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_SHIFT_PRESSED, uint8) != 0){ + z += RCT2_GLOBAL(RCT2_ADDRESS_SHIFT_PRESS_Z_VECTOR, sint16); + } + + if (z < 16){ + z = 16; + } + + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) = z; + } + + if (*grid_x == (sint16)0x8000) + return; + + *grid_x &= 0xFFE0; + *grid_y &= 0xFFE0; + uint8 rotation = window_scenery_rotation; + + if (!(scenery->small_scenery.flags & SMALL_SCENERY_FLAG4)){ + rotation = scenario_rand() & 0xFF; + } + + rotation -= RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8); + rotation &= 0x3; + + // Also places it in lower but think thats for clobering + *parameter_1 = (selected_scenery & 0xFF) << 8; + *parameter_2 = 0 | (window_scenery_primary_colour << 8); + *parameter_3 = rotation | (window_scenery_secondary_colour << 16); + break; + } + case 1: + { + // Path bits + + uint16 flags = + VIEWPORT_INTERACTION_MASK_FOOTPATH & + VIEWPORT_INTERACTION_MASK_FOOTPATH_ITEM; + int interaction_type = 0; + rct_map_element* map_element; + + int map_x, map_y; + get_map_coordinates_from_pos(x, y, flags, &map_x, &map_y, &interaction_type, &map_element, NULL); + *grid_x = (sint16)map_x; + *grid_y = (sint16)map_y; + + if (interaction_type == VIEWPORT_INTERACTION_ITEM_NONE) + { + *grid_x = 0x8000; + return; + } + + *parameter_1 = 0 | ((map_element->properties.path.type & 0x7) << 8); + *parameter_2 = map_element->base_height | ((map_element->properties.path.type >> 4) << 8); + if (map_element->type & 1){ + *parameter_2 |= 0x8000; + } + *parameter_3 = (selected_scenery & 0xFF) + 1; + break; + } + case 2: + { + // Walls + uint8 cl; + // If CTRL not pressed + if (RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_CTRL_PRESSED, uint8) == 0){ + sub_689692(x, y, grid_x, grid_y, &cl); + + if (*grid_x == (sint16)0x8000) + return; + + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) = 0; + + // If SHIFT pressed + if (RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_SHIFT_PRESSED, uint8) != 0){ + rct_map_element* map_element = map_get_surface_element_at(*grid_x / 32, *grid_y / 32); + + if (map_element == NULL){ + *grid_x = 0x8000; + return; + } + + sint16 z = (map_element->base_height * 8) & 0xFFF0; + z += RCT2_GLOBAL(RCT2_ADDRESS_SHIFT_PRESS_Z_VECTOR, sint16); + + if (z < 16){ + z = 16; + } + + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) = z; + } + } + else{ + sint16 z = RCT2_GLOBAL(RCT2_ADDRESS_CTRL_PRESS_Z_COORDINATE, sint16); + sub_6896DC(x, y, z, grid_x, grid_y, &cl); + + // If SHIFT pressed + if (RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_SHIFT_PRESSED, uint8) != 0){ + z += RCT2_GLOBAL(RCT2_ADDRESS_SHIFT_PRESS_Z_VECTOR, sint16); + } + + if (z < 16){ + z = 16; + } + + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) = z; + } + + if (*grid_x == (sint16)0x8000) + return; + + RCT2_GLOBAL(0x00F64F15, uint8) = window_scenery_secondary_colour; + RCT2_GLOBAL(0x00F64F16, uint8) = window_scenery_tertiary_colour; + // Also places it in lower but think thats for clobering + *parameter_1 = (selected_scenery & 0xFF) << 8; + *parameter_2 = cl | (window_scenery_primary_colour << 8); + *parameter_3 = 0; + break; + } + case 3: + { + // Large scenery + + // If CTRL not pressed + if (RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_CTRL_PRESSED, uint8) == 0){ + sub_68A15E(x, y, grid_x, grid_y, NULL, NULL); + + if (*grid_x == (sint16)0x8000) + return; + + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) = 0; + + // If SHIFT pressed + if (RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_SHIFT_PRESSED, uint8) != 0){ + rct_map_element* map_element = map_get_surface_element_at(*grid_x / 32, *grid_y / 32); + + if (map_element == NULL){ + *grid_x = 0x8000; + return; + } + + sint16 z = (map_element->base_height * 8) & 0xFFF0; + z += RCT2_GLOBAL(RCT2_ADDRESS_SHIFT_PRESS_Z_VECTOR, sint16); + + if (z < 16){ + z = 16; + } + + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) = z; + } + } + else{ + sint16 z = RCT2_GLOBAL(RCT2_ADDRESS_CTRL_PRESS_Z_COORDINATE, sint16); + sub_6894D4(x, y, z, grid_x, grid_y); + + // If SHIFT pressed + if (RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_SHIFT_PRESSED, uint8) != 0){ + z += RCT2_GLOBAL(RCT2_ADDRESS_SHIFT_PRESS_Z_VECTOR, sint16); + } + + if (z < 16){ + z = 16; + } + + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) = z; + } + + if (*grid_x == (sint16)0x8000) + return; + + *grid_x &= 0xFFE0; + *grid_y &= 0xFFE0; + + uint8 rotation = window_scenery_rotation; + rotation -= RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8); + rotation &= 0x3; + + *parameter_1 = (rotation << 8); + *parameter_2 = window_scenery_primary_colour | (window_scenery_secondary_colour << 8); + *parameter_3 = selected_scenery & 0xFF; + break; + } + case 4: + { + // Banner + + uint16 flags = + VIEWPORT_INTERACTION_MASK_FOOTPATH & + VIEWPORT_INTERACTION_MASK_FOOTPATH_ITEM; + int interaction_type = 0; + rct_map_element* map_element; + + int map_x, map_y; + get_map_coordinates_from_pos(x, y, flags, &map_x, &map_y, &interaction_type, &map_element, NULL); + *grid_x = (sint16)map_x; + *grid_y = (sint16)map_y; + + if (interaction_type == VIEWPORT_INTERACTION_ITEM_NONE) + { + *grid_x = 0x8000; + return; + } + + uint8 rotation = window_scenery_rotation; + rotation -= RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8); + rotation &= 0x3; + + sint16 z = map_element->base_height; + + if (map_element->properties.path.type & (1 << 2)){ + if (rotation != ((map_element->properties.path.type & 3) ^ 2)){ + z += 2; + } + } + + z /= 2; + + // Also places it in lower but think thats for clobering + *parameter_1 = (selected_scenery & 0xFF) << 8; + *parameter_2 = z | (rotation << 8); + *parameter_3 = window_scenery_primary_colour; + break; + } + } +} + /** * rct2: 0x6e2cc6 */ static void window_top_toolbar_scenery_tool_down(short x, short y, rct_window* w, short widgetIndex){ - RCT2_CALLPROC_EBPSAFE(0x006E2712); + scenery_remove_ghost_tool_placement(); if (window_scenery_is_repaint_scenery_tool_on & 1){ - //6e3158 - RCT2_CALLPROC_X(0x6E2CC6, x, y, 0, widgetIndex, (int)w, 0, 0); + repaint_scenery_tool_down(x, y, widgetIndex); + return; } int selected_tab = window_scenery_selected_scenery_by_tab[window_scenery_active_tab_index]; + uint8 scenery_type = (selected_tab & 0xFF00) >> 8; + uint8 selected_scenery = selected_tab & 0xFF; + if (selected_tab == -1) return; - sint16 grid_x, grid_y, grid_z; - uint8 item_colour; - uint8 model_type; + sint16 grid_x, grid_y; int ebp = selected_tab; + uint32 parameter_1, parameter_2, parameter_3; - { - int eax = x, ebx = y, ecx = 0, edx = 0, esi = 0, edi = 0; - RCT2_CALLFUNC_X(0x6E1F34, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); - item_colour = edi; - model_type = (ebx & 0xFF00) >> 8; - grid_x = eax; - grid_y = ecx; - grid_z = edx; - } + sub_6E1F34(x, y, selected_tab, &grid_x, &grid_y, ¶meter_1, ¶meter_2, ¶meter_3); - if (grid_x == 0x8000)return; + if (grid_x == (sint16)0x8000)return; - if (ebp >= 1024){ - RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_STRING_ID, rct_string_id) = 1161; - - // The return value will be banner id but the input is colour - int banner_id = item_colour; - - int ebx = (model_type << 8) | 1; - - { - int esi = 0, eax = grid_x, ecx = grid_y, edx = grid_z; - game_do_command_p(GAME_COMMAND_50, &eax, &ebx, &ecx, &edx, &esi, &banner_id, &ebp); + switch (scenery_type){ + case 0: + { + int cluster_size = 1; + if (window_scenery_is_build_cluster_tool_on){ + cluster_size = 35; } - if (ebx == 0x80000000)return; + for (; cluster_size > 0; cluster_size--){ + + int cluster_z_coordinate = RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16); + rct_scenery_entry* scenery = g_smallSceneryEntries[(parameter_1 >> 8) & 0xFF]; + + sint16 cur_grid_x = grid_x; + sint16 cur_grid_y = grid_y; + + if (window_scenery_is_build_cluster_tool_on){ + if (!(scenery->small_scenery.flags & SMALL_SCENERY_FLAG_FULL_TILE)){ + parameter_2 &= 0xFF00; + parameter_2 |= scenario_rand() & 3; + } + + cur_grid_x += ((scenario_rand() % 16) - 8) * 32; + cur_grid_y += ((scenario_rand() % 16) - 8) * 32; + + if (!(scenery->small_scenery.flags & SMALL_SCENERY_FLAG4)){ + RCT2_GLOBAL(0x00F64EC0, uint16)++; + RCT2_GLOBAL(0x00F64EC0, uint16) &= 3; + } + } + + uint8 bl = 1; + if (RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) != 0 && + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_SHIFT_PRESSED, uint8) != 0){ + bl = 20; + } + uint8 success = 0; + + for (; bl != 0; bl--){ + RCT2_GLOBAL(0x009A8C29, uint8) |= 1; + + int ebx = parameter_1; + ebx &= 0xFF00; + if (window_scenery_is_build_cluster_tool_on){ + ebx |= 0x9; + } + else{ + ebx |= GAME_COMMAND_FLAG_APPLY; + } + + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_STRING_ID, rct_string_id) = 1161; + + int cost = game_do_command(cur_grid_x, ebx, cur_grid_y, parameter_2, GAME_COMMAND_15, RCT2_GLOBAL(0x00F64EC0, uint8) | (parameter_3 & 0xFFFF0000), RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16)); + + + RCT2_GLOBAL(0x009A8C29, uint8) &= ~1; + + if (cost != MONEY32_UNDEFINED){ + window_close_by_class(WC_ERROR); + sound_play_panned(SOUND_PLACE_ITEM, 0x8001, RCT2_GLOBAL(0x009DEA5E, uint16), RCT2_GLOBAL(0x009DEA60, uint16), RCT2_GLOBAL(0x009DEA62, uint16)); + success = 1; + break; + } + + if (RCT2_GLOBAL(0x00141E9AC, rct_string_id) == 827 || + RCT2_GLOBAL(0x00141E9AC, rct_string_id) == 1032){ + break; + } + + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) += 8; + } + + if (!success && !window_scenery_is_build_cluster_tool_on){ + sound_play_panned(SOUND_ERROR, 0x8001, RCT2_GLOBAL(0x009DEA5E, uint16), RCT2_GLOBAL(0x009DEA60, uint16), RCT2_GLOBAL(0x009DEA62, uint16)); + return; + } + + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) = cluster_z_coordinate; + } + break; + } + case 1: + { + // Path Bits + int ebx = parameter_1; + ebx &= 0xFF00; + ebx |= 0x81; + + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_STRING_ID, rct_string_id) = 1161; + + int cost = game_do_command(grid_x, ebx, grid_y, parameter_2, GAME_COMMAND_PLACE_PATH, parameter_3, 0); + + if (cost == MONEY32_UNDEFINED){ + return; + } + + sound_play_panned(SOUND_PLACE_ITEM, 0x8001, RCT2_GLOBAL(0x009DEA5E, uint16), RCT2_GLOBAL(0x009DEA60, uint16), RCT2_GLOBAL(0x009DEA62, uint16)); + break; + } + case 2: + { + // Walls + uint8 bl = 1; + if (RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) != 0 && + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_SHIFT_PRESSED, uint8) != 0){ + bl = 20; + } + + for (; bl != 0; bl--){ + RCT2_GLOBAL(0x009A8C29, uint8) |= 1; + + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_STRING_ID, rct_string_id) = 1811; + + int ebx = (parameter_1 & 0xFF00) | 1; + + int cost = game_do_command(grid_x, ebx, grid_y, parameter_2, GAME_COMMAND_41, RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16), RCT2_GLOBAL(0x00F64F15, uint16)); + + RCT2_GLOBAL(0x009A8C29, uint8) &= ~1; + + if (cost != MONEY32_UNDEFINED){ + window_close_by_class(WC_ERROR); + sound_play_panned(SOUND_PLACE_ITEM, 0x8001, RCT2_GLOBAL(0x009DEA5E, uint16), RCT2_GLOBAL(0x009DEA60, uint16), RCT2_GLOBAL(0x009DEA62, uint16)); + return; + } + + if (RCT2_GLOBAL(0x00141E9AC, rct_string_id) == 827 || + RCT2_GLOBAL(0x00141E9AC, rct_string_id) == 1032){ + break; + } + + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) += 8; + } + + sound_play_panned(SOUND_ERROR, 0x8001, RCT2_GLOBAL(0x009DEA5E, uint16), RCT2_GLOBAL(0x009DEA60, uint16), RCT2_GLOBAL(0x009DEA62, uint16)); + break; + } + case 3: + { + // Large Scenery + uint8 bl = 1; + if (RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) != 0 && + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_TOOL_SHIFT_PRESSED, uint8) != 0){ + bl = 20; + } + + for (; bl != 0; bl--){ + RCT2_GLOBAL(0x009A8C29, uint8) |= 1; + + int ebx = (parameter_1 & 0xFF00) | 1; + + int cost = game_do_command(grid_x, ebx, grid_y, parameter_2, GAME_COMMAND_43, parameter_3, RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16)); + + + RCT2_GLOBAL(0x009A8C29, uint8) &= ~1; + + if (cost != MONEY32_UNDEFINED){ + window_close_by_class(WC_ERROR); + sound_play_panned(SOUND_PLACE_ITEM, 0x8001, RCT2_GLOBAL(0x009DEA5E, uint16), RCT2_GLOBAL(0x009DEA60, uint16), RCT2_GLOBAL(0x009DEA62, uint16)); + return; + } + + if (RCT2_GLOBAL(0x00141E9AC, rct_string_id) == 827 || + RCT2_GLOBAL(0x00141E9AC, rct_string_id) == 1032){ + break; + } + + RCT2_GLOBAL(RCT2_ADDRESS_SCENERY_Z_COORDINATE, sint16) += 8; + } + + sound_play_panned(SOUND_ERROR, 0x8001, RCT2_GLOBAL(0x009DEA5E, uint16), RCT2_GLOBAL(0x009DEA60, uint16), RCT2_GLOBAL(0x009DEA62, uint16)); + } + break; + case 4: + { + // Banners + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_STRING_ID, rct_string_id) = 1161; + + // The return value will be banner id but the input is colour (param 3) + int banner_id = parameter_3; + + int cost; + { + int esi = 0, eax = grid_x, ecx = grid_y, edx = parameter_2, ebx = (parameter_1 & 0xFF00) | 1; + cost = game_do_command_p(GAME_COMMAND_PLACE_BANNER, &eax, &ebx, &ecx, &edx, &esi, &banner_id, &ebp); + } + + if (cost == MONEY32_UNDEFINED)return; sound_play_panned(SOUND_PLACE_ITEM, 0x8001, RCT2_GLOBAL(0x009DEA5E, uint16), RCT2_GLOBAL(0x009DEA60, uint16), RCT2_GLOBAL(0x009DEA62, uint16)); window_banner_open(banner_id); + break; } - else if (ebp >= 768){ - //6e301c - RCT2_CALLPROC_X(0x6E2CC6, x, y, 0, widgetIndex, (int)w, 0, 0); - } - else if (ebp >= 512){ - //6e2f2e - RCT2_CALLPROC_X(0x6E2CC6, x, y, 0, widgetIndex, (int)w, 0, 0); - } - else if (ebp >= 256){ - //6e2eda - RCT2_CALLPROC_X(0x6E2CC6, x, y, 0, widgetIndex, (int)w, 0, 0); - } - else{ - //6e2d2d - RCT2_CALLPROC_X(0x6E2CC6, x, y, 0, widgetIndex, (int)w, 0, 0); } } @@ -779,34 +1528,33 @@ static void window_top_toolbar_tool_down(){ * * rct2: 0x006644DD */ -void sub_6644DD(int ebx){ +void selection_raise_land(uint8 flags){ + int center_x = (RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_A_X, uint16) + + RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_X, uint16) + ) / 2; + int center_y = (RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_A_Y, uint16) + + RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_Y, uint16) + ) / 2; - int ax = RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_A_X, uint16); - int cx = RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_A_Y, uint16); - ax += RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_X, uint16); - cx += RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_Y, uint16); - ax >>= 1; - cx >>= 1; - ax += 0x10; - cx += 0x10; - uint32 dx = RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_X, uint16); - uint32 bp = RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_Y, uint16); - dx <<= 16; - bp <<= 16; - dx += RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_A_X, uint16); - bp += RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_A_Y, uint16); + center_x += 16; + center_y += 16; + + uint32 dx = RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_A_X, uint16) | + (RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_X, uint16) << 16); + uint32 bp = RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_A_Y, uint16) | + (RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_Y, uint16) << 16); RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_STRING_ID, rct_string_id) = STR_CANT_RAISE_LAND_HERE; if (RCT2_GLOBAL(RCT2_ADDRESS_LAND_TOOL_SIZE, sint16) == 0) { int di = 1; - game_do_command(ax, ebx, cx, dx, GAME_COMMAND_EDIT_LAND_SMOOTH, di, bp); + game_do_command(center_x, flags, center_y, dx, GAME_COMMAND_EDIT_LAND_SMOOTH, di, bp); } else { int di = RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_TYPE, uint16); - game_do_command(ax, ebx, cx, dx, GAME_COMMAND_RAISE_LAND, di, bp); + game_do_command(center_x, flags, center_y, dx, GAME_COMMAND_RAISE_LAND, di, bp); } } @@ -814,33 +1562,32 @@ void sub_6644DD(int ebx){ * * rct2: 0x006645B3 */ -void sub_6645B3(int ebx){ +void selection_lower_land(uint8 flags){ + int center_x = (RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_A_X, uint16) + + RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_X, uint16) + ) / 2; + int center_y = (RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_A_Y, uint16) + + RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_Y, uint16) + ) / 2; - int ax = RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_A_X, uint16); - int cx = RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_A_Y, uint16); - ax += RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_X, uint16); - cx += RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_Y, uint16); - ax >>= 1; - cx >>= 1; - ax += 0x10; - cx += 0x10; - uint32 dx = RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_X, uint16); - uint32 bp = RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_Y, uint16); - dx <<= 16; - bp <<= 16; - dx += RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_A_X, uint16); - bp += RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_A_Y, uint16); + center_x += 16; + center_y += 16; + + uint32 dx = RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_A_X, uint16) | + (RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_X, uint16) << 16); + uint32 bp = RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_A_Y, uint16) | + (RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_Y, uint16) << 16); RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_STRING_ID, rct_string_id) = STR_CANT_LOWER_LAND_HERE; if (RCT2_GLOBAL(RCT2_ADDRESS_LAND_TOOL_SIZE, sint16) == 0) { int di = 0xFFFF; - game_do_command(ax, ebx, cx, dx, GAME_COMMAND_EDIT_LAND_SMOOTH, di, bp); + game_do_command(center_x, flags, center_y, dx, GAME_COMMAND_EDIT_LAND_SMOOTH, di, bp); } else { int di = RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_TYPE, uint16); - game_do_command(ax, ebx, cx, dx, GAME_COMMAND_LOWER_LAND, di, bp); + game_do_command(center_x, flags, center_y, dx, GAME_COMMAND_LOWER_LAND, di, bp); } } @@ -865,35 +1612,27 @@ void window_top_toolbar_land_tool_drag(short x, short y) if (!viewport) return; - sint16 dx = 0xFFF0; - dx >>= viewport->zoom; + sint16 tile_height = -16 / (1 << viewport->zoom); - y -= RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DRAG_LAST_Y, uint16); + int y_diff = y - RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DRAG_LAST_Y, uint16); - if (y <= dx) { - RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DRAG_LAST_Y, uint16) += dx; + if (y_diff <= tile_height) { + RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DRAG_LAST_Y, uint16) += tile_height; - y = (y & 0xFF00) | 1; // mov bl, 1 - - sub_6644DD(y); - - RCT2_GLOBAL(RCT2_ADDRESS_LAND_RAISE_COST, uint32) = 0x80000000; - RCT2_GLOBAL(RCT2_ADDRESS_LAND_LOWER_COST, uint32) = 0x80000000; + selection_raise_land(GAME_COMMAND_FLAG_APPLY); + RCT2_GLOBAL(RCT2_ADDRESS_LAND_RAISE_COST, uint32) = MONEY32_UNDEFINED; + RCT2_GLOBAL(RCT2_ADDRESS_LAND_LOWER_COST, uint32) = MONEY32_UNDEFINED; return; } - dx = -dx; + if (y_diff >= -tile_height) { + RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DRAG_LAST_Y, uint16) -= tile_height; - if (y >= dx) { - RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DRAG_LAST_Y, uint16) += dx; + selection_lower_land(GAME_COMMAND_FLAG_APPLY); - y = (y & 0xFF00) | 1; // mov bl, 1 - - sub_6645B3(y); - - RCT2_GLOBAL(RCT2_ADDRESS_LAND_RAISE_COST, uint32) = 0x80000000; - RCT2_GLOBAL(RCT2_ADDRESS_LAND_LOWER_COST, uint32) = 0x80000000; + RCT2_GLOBAL(RCT2_ADDRESS_LAND_RAISE_COST, uint32) = MONEY32_UNDEFINED; + RCT2_GLOBAL(RCT2_ADDRESS_LAND_LOWER_COST, uint32) = MONEY32_UNDEFINED; return; } @@ -939,8 +1678,8 @@ void window_top_toolbar_water_tool_drag(short x, short y) RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_X, uint16), RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_Y, uint16) ); - RCT2_GLOBAL(RCT2_ADDRESS_WATER_RAISE_COST, uint32) = 0x80000000; - RCT2_GLOBAL(RCT2_ADDRESS_WATER_LOWER_COST, uint32) = 0x80000000; + RCT2_GLOBAL(RCT2_ADDRESS_WATER_RAISE_COST, uint32) = MONEY32_UNDEFINED; + RCT2_GLOBAL(RCT2_ADDRESS_WATER_LOWER_COST, uint32) = MONEY32_UNDEFINED; return; } @@ -961,8 +1700,8 @@ void window_top_toolbar_water_tool_drag(short x, short y) RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_X, uint16), RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_B_Y, uint16) ); - RCT2_GLOBAL(RCT2_ADDRESS_WATER_RAISE_COST, uint32) = 0x80000000; - RCT2_GLOBAL(RCT2_ADDRESS_WATER_LOWER_COST, uint32) = 0x80000000; + RCT2_GLOBAL(RCT2_ADDRESS_WATER_RAISE_COST, uint32) = MONEY32_UNDEFINED; + RCT2_GLOBAL(RCT2_ADDRESS_WATER_LOWER_COST, uint32) = MONEY32_UNDEFINED; return; } @@ -1008,7 +1747,8 @@ static void window_top_toolbar_tool_drag() window_top_toolbar_water_tool_drag(x, y); break; case WIDX_SCENERY: - RCT2_CALLPROC_X(0x006E2CBC, x, y, 0, widgetIndex, (int)w, 0, 0); + if (window_scenery_is_repaint_scenery_tool_on & 1) + window_top_toolbar_scenery_tool_down(x, y, w, widgetIndex); break; } } diff --git a/src/windows/track_place.c b/src/windows/track_place.c index 001c4ff014..9b8d777689 100644 --- a/src/windows/track_place.c +++ b/src/windows/track_place.c @@ -300,75 +300,6 @@ static void window_track_place_draw_mini_preview() } } -/** - * - * rct2: 0x0068A15E - */ -static void sub_68A15E(int screenX, int screenY, short *x, short *y, int *direction, rct_map_element **mapElement) -{ - int my_x, my_y, z; - rct_map_element *myMapElement; - rct_viewport *viewport; - get_map_coordinates_from_pos(screenX, screenY, 0xFFF6, &my_x, &my_y, &z, &myMapElement, &viewport); - - if (z == 0) { - *x = 0x8000; - return; - } - - RCT2_GLOBAL(0x00F1AD3E, uint8) = z; - RCT2_GLOBAL(0x00F1AD30, rct_map_element*) = myMapElement; - - if (z == 4) { - // myMapElement appears to be water - z = myMapElement->properties.surface.terrain; - z = (z & MAP_ELEMENT_WATER_HEIGHT_MASK) << 4; - } - - RCT2_GLOBAL(0x00F1AD3C, uint16) = z; - RCT2_GLOBAL(0x00F1AD34, sint16) = my_x; - RCT2_GLOBAL(0x00F1AD36, sint16) = my_y; - RCT2_GLOBAL(0x00F1AD38, sint16) = my_x + 31; - RCT2_GLOBAL(0x00F1AD3A, sint16) = my_y + 31; - - rct_xy16 start_vp_pos = screen_coord_to_viewport_coord(viewport, screenX, screenY); - rct_xy16 map_pos = { my_x + 16, my_y + 16 }; - - for (int i = 0; i < 5; i++) { - if (RCT2_GLOBAL(0x00F1AD3E, uint8) != 4) { - z = map_element_height(map_pos.x, map_pos.y); - } else { - z = RCT2_GLOBAL(0x00F1AD3C, uint16); - } - map_pos = viewport_coord_to_map_coord(start_vp_pos.x, start_vp_pos.y, z); - map_pos.x = clamp(RCT2_GLOBAL(0x00F1AD34, sint16), map_pos.x, RCT2_GLOBAL(0x00F1AD38, sint16)); - map_pos.y = clamp(RCT2_GLOBAL(0x00F1AD36, sint16), map_pos.y, RCT2_GLOBAL(0x00F1AD3A, sint16)); - } - - // Determine to which edge the cursor is closest - int myDirection; - int mod_x = map_pos.x & 0x1F; - int mod_y = map_pos.y & 0x1F; - if (mod_x < mod_y) { - if (mod_x + mod_y < 32) { - myDirection = 0; - } else { - myDirection = 1; - } - } else { - if (mod_x + mod_y < 32) { - myDirection = 3; - } else { - myDirection = 2; - } - } - - *x = map_pos.x & ~0x1F; - *y = map_pos.y & ~0x1F; - if (direction != NULL) *direction = myDirection; - if (mapElement != NULL) *mapElement = myMapElement; -} - /** * * rct2: 0x006D017F diff --git a/src/windows/viewport.c b/src/windows/viewport.c index 9fb7fcf6cc..0caaa347fe 100644 --- a/src/windows/viewport.c +++ b/src/windows/viewport.c @@ -176,7 +176,7 @@ static void window_viewport_mouseup() case WIDX_LOCATE: mainWindow = window_get_main(); if (mainWindow != NULL) { - get_map_coordinates_from_pos(w->x + (w->width / 2), w->y + (w->height / 2), 0, &x, &y, NULL, NULL, NULL); + get_map_coordinates_from_pos(w->x + (w->width / 2), w->y + (w->height / 2), VIEWPORT_INTERACTION_MASK_NONE, &x, &y, NULL, NULL, NULL); window_scroll_to_location(mainWindow, x, y, map_element_height(x, y)); } break; diff --git a/src/world/banner.c b/src/world/banner.c index 172ab08f7d..306b1cad26 100644 --- a/src/world/banner.c +++ b/src/world/banner.c @@ -19,6 +19,8 @@ *****************************************************************************/ #include "../addresses.h" +#include "../game.h" +#include "../localisation/localisation.h" #include "banner.h" rct_banner *gBanners = (rct_banner*)0x0135A124; @@ -32,3 +34,34 @@ void banner_init() { gBanners[i].type = BANNER_NULL; } } + +/* rct2: 0x006BA278 + * Creates a new banner and returns the index of the banner + * If the flag GAME_COMMAND_FLAG_APPLY is NOT set then returns + * the first unused index but does NOT mark the banner as created. + * returns 0xFF on failure. + */ +int create_new_banner(uint8 flags){ + int banner_index = 0; + for (; banner_index < MAX_BANNERS; banner_index++){ + if (gBanners[banner_index].type == BANNER_NULL){ + break; + } + } + + if (banner_index == MAX_BANNERS){ + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_TOO_MANY_BANNERS_IN_GAME; + return BANNER_NULL; + } + + if (flags & GAME_COMMAND_FLAG_APPLY){ + rct_banner* banner = &gBanners[banner_index]; + + banner->flags = 0; + banner->type = 0; + banner->string_idx = 778; + banner->colour = 2; + banner->text_colour = 2; + } + return banner_index; +} \ No newline at end of file diff --git a/src/world/banner.h b/src/world/banner.h index 5e7843818a..c3ee3300f9 100644 --- a/src/world/banner.h +++ b/src/world/banner.h @@ -44,5 +44,6 @@ enum{ extern rct_banner *gBanners; void banner_init(); +int create_new_banner(uint8 flags); #endif diff --git a/src/world/climate.c b/src/world/climate.c index e50af29358..d687c1e5ff 100644 --- a/src/world/climate.c +++ b/src/world/climate.c @@ -191,8 +191,6 @@ void climate_update() } void climate_force_weather(uint8 weather){ - gClimateNextWeather = 0; - RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_WEATHER, sint8) = weather; RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_WEATHER_GLOOM, sint8) = climate_weather_data[weather].gloom_level; RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_RAIN_LEVEL, sint8) = climate_weather_data[weather].rain_level; diff --git a/src/world/footpath.c b/src/world/footpath.c index 8a0f7c594e..e853ac5645 100644 --- a/src/world/footpath.c +++ b/src/world/footpath.c @@ -24,6 +24,7 @@ #include "../util/util.h" #include "footpath.h" #include "map.h" +#include "scenery.h" void sub_673883(int x, int y, int z); void sub_69A48B(int x, int y, int z); @@ -201,8 +202,8 @@ static money32 footpath_element_update(int x, int y, rct_map_element *mapElement } if (RCT2_GLOBAL(0x00F3EF88, uint16) != 0) { - uint8 *unk = RCT2_ADDRESS(0x009ADA50, uint8*)[RCT2_GLOBAL(0x00F3EF88, uint16)]; - uint16 unk6 = RCT2_GLOBAL(unk + 6, uint16); + rct_scenery_entry* scenery_entry = g_pathBitSceneryEntries[RCT2_GLOBAL(0x00F3EF88, uint16) - 1]; + uint16 unk6 = scenery_entry->path_bit.var_06; if ((unk6 & 0x80) && (mapElement->properties.path.type & 4)) { RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_CANT_BUILD_THIS_ON_SLOPED_FOOTPATH; @@ -224,13 +225,13 @@ static money32 footpath_element_update(int x, int y, rct_map_element *mapElement return MONEY32_UNDEFINED; } - RCT2_GLOBAL(0x00F3EFD9, money32) += RCT2_GLOBAL(unk + 10, money16); + RCT2_GLOBAL(0x00F3EFD9, money32) += scenery_entry->path_bit.price; } if (flags & (1 << 4)) return MONEY32_UNDEFINED; - if (!(flags & (1 << 6))) { + if (flags & (1 << 6)) { if (mapElement->properties.path.additions & 0x0F) { RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_NONE; return MONEY32_UNDEFINED; @@ -259,8 +260,8 @@ static money32 footpath_element_update(int x, int y, rct_map_element *mapElement mapElement->properties.path.additions = (mapElement->properties.path.additions & 0xF0) | RCT2_GLOBAL(0x00F3EF88, uint8); mapElement->flags &= ~0x20; if (RCT2_GLOBAL(0x00F3EF88, uint16) != 0) { - uint8 *unk = RCT2_ADDRESS(0x009ADA50, uint8*)[RCT2_GLOBAL(0x00F3EF88, uint16)]; - uint16 unk6 = RCT2_GLOBAL(unk + 6, uint16); + rct_scenery_entry* scenery_entry = g_pathBitSceneryEntries[RCT2_GLOBAL(0x00F3EF88, uint16) - 1]; + uint16 unk6 = scenery_entry->path_bit.var_06; if (unk6 & 1) mapElement->properties.path.addition_status = 255; } @@ -288,7 +289,7 @@ static money32 footpath_element_update(int x, int y, rct_map_element *mapElement return RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_NO_MONEY ? 0 : RCT2_GLOBAL(0x00F3EFD9, money32); } -static money32 footpath_place_real(int type, int x, int y, int z, int slope, int flags) +static money32 footpath_place_real(int type, int x, int y, int z, int slope, int flags, uint8 path_bit_type) { rct_map_element *mapElement; @@ -307,7 +308,7 @@ static money32 footpath_place_real(int type, int x, int y, int z, int slope, int RCT2_GLOBAL(0x00F3EFD9, money32) = 0; RCT2_GLOBAL(0x00F3EFA4, uint8) = 0; - RCT2_GLOBAL(0x00F3EF88, uint16) = 0; // di + RCT2_GLOBAL(0x00F3EF88, uint16) = path_bit_type; // di if (x >= RCT2_GLOBAL(0x01358830, uint16) || y >= RCT2_GLOBAL(0x01358830, uint16)) { RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, rct_string_id) = STR_OFF_EDGE_OF_MAP; @@ -344,7 +345,7 @@ void remove_banners_at_element(int x, int y, rct_map_element* mapElement){ if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_PATH)return; else if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_BANNER)continue; - game_do_command(x, 1, y, mapElement->base_height | mapElement->properties.banner.position << 8, GAME_COMMAND_51, 0, 0); + game_do_command(x, 1, y, mapElement->base_height | mapElement->properties.banner.position << 8, GAME_COMMAND_REMOVE_BANNER, 0, 0); mapElement--; } } @@ -393,7 +394,7 @@ void game_command_place_footpath(int *eax, int *ebx, int *ecx, int *edx, int *es if (*ebx & (1 << 5)) RCT2_CALLFUNC_X(0x006A61DE, eax, ebx, ecx, edx, esi, edi, ebp); else - *ebx = footpath_place_real((*edx >> 8) & 0xFF, *eax & 0xFFFF, *ecx & 0xFFFF, *edx & 0xFF, (*ebx >> 8) & 0xFF, *ebx & 0xFF); + *ebx = footpath_place_real((*edx >> 8) & 0xFF, *eax & 0xFFFF, *ecx & 0xFFFF, *edx & 0xFF, (*ebx >> 8) & 0xFF, *ebx & 0xFF, *edi & 0xFF); } /** @@ -490,23 +491,22 @@ void footpath_provisional_update() */ void footpath_get_coordinates_from_pos(int screenX, int screenY, int *x, int *y, int *direction, rct_map_element **mapElement) { - int z; + int z, interactionType; rct_map_element *myMapElement; rct_viewport *viewport; - get_map_coordinates_from_pos(screenX, screenY, 0xFFDF, x, y, &z, &myMapElement, &viewport); - if (z != 6 || !(viewport->flags & (VIEWPORT_FLAG_UNDERGROUND_INSIDE | VIEWPORT_FLAG_HIDE_BASE | VIEWPORT_FLAG_HIDE_VERTICAL))) { - get_map_coordinates_from_pos(screenX, screenY, 0xFFDE, x, y, &z, &myMapElement, &viewport); - if (z == 0) { + get_map_coordinates_from_pos(screenX, screenY, VIEWPORT_INTERACTION_MASK_FOOTPATH, x, y, &interactionType, &myMapElement, &viewport); + if (interactionType != VIEWPORT_INTERACTION_ITEM_FOOTPATH || !(viewport->flags & (VIEWPORT_FLAG_UNDERGROUND_INSIDE | VIEWPORT_FLAG_HIDE_BASE | VIEWPORT_FLAG_HIDE_VERTICAL))) { + get_map_coordinates_from_pos(screenX, screenY, VIEWPORT_INTERACTION_MASK_FOOTPATH & VIEWPORT_INTERACTION_MASK_TERRAIN, x, y, &interactionType, &myMapElement, &viewport); + if (interactionType == VIEWPORT_INTERACTION_ITEM_NONE) { if (x != NULL) *x = 0x8000; return; } } - RCT2_GLOBAL(0x00F1AD3E, uint8) = z; + RCT2_GLOBAL(0x00F1AD3E, uint8) = interactionType; RCT2_GLOBAL(0x00F1AD30, rct_map_element*) = myMapElement; - if (z == 6) { - // mapElement appears to be a footpath + if (interactionType == VIEWPORT_INTERACTION_ITEM_FOOTPATH) { z = myMapElement->base_height * 8; if (myMapElement->properties.path.type & (1 << 2)) z += 8; @@ -572,10 +572,10 @@ void footpath_get_coordinates_from_pos(int screenX, int screenY, int *x, int *y, void footpath_bridge_get_info_from_pos(int screenX, int screenY, int *x, int *y, int *direction, rct_map_element **mapElement) { // First check if we point at an entrance or exit. In that case, we would want the path coming from the entrance/exit. - int z; + int interactionType; rct_viewport *viewport; - get_map_coordinates_from_pos(screenX, screenY, 0xFFFB, x, y, &z, mapElement, &viewport); - if (z == 3 + get_map_coordinates_from_pos(screenX, screenY, VIEWPORT_INTERACTION_MASK_RIDE, x, y, &interactionType, mapElement, &viewport); + if (interactionType == VIEWPORT_INTERACTION_ITEM_RIDE && viewport->flags & (VIEWPORT_FLAG_UNDERGROUND_INSIDE | VIEWPORT_FLAG_HIDE_BASE | VIEWPORT_FLAG_HIDE_VERTICAL) && map_element_get_type(*mapElement) == MAP_ELEMENT_TYPE_ENTRANCE) { int ebp = (*mapElement)->properties.entrance.type << 4; @@ -589,8 +589,8 @@ void footpath_bridge_get_info_from_pos(int screenX, int screenY, int *x, int *y, } } - get_map_coordinates_from_pos(screenX, screenY, 0xFFDA, x, y, &z, mapElement, &viewport); - if (z == 3 && map_element_get_type(*mapElement) == MAP_ELEMENT_TYPE_ENTRANCE) { + get_map_coordinates_from_pos(screenX, screenY, VIEWPORT_INTERACTION_MASK_RIDE & VIEWPORT_INTERACTION_MASK_FOOTPATH & VIEWPORT_INTERACTION_MASK_TERRAIN, x, y, &interactionType, mapElement, &viewport); + if (interactionType == VIEWPORT_INTERACTION_ITEM_RIDE && map_element_get_type(*mapElement) == MAP_ELEMENT_TYPE_ENTRANCE) { int ebp = (*mapElement)->properties.entrance.type << 4; int bl = (*mapElement)->properties.entrance.index & 0xF; // Seems to be always 0? // The table at 0x0097B974 is only 48 bytes big diff --git a/src/world/footpath.h b/src/world/footpath.h index 86574919b5..3745ae05d2 100644 --- a/src/world/footpath.h +++ b/src/world/footpath.h @@ -48,5 +48,6 @@ void footpath_provisional_remove(); void footpath_provisional_update(); void footpath_get_coordinates_from_pos(int screenX, int screenY, int *x, int *y, int *direction, rct_map_element **mapElement); void footpath_bridge_get_info_from_pos(int screenX, int screenY, int *x, int *y, int *direction, rct_map_element **mapElement); +void sub_673883(int x, int y, int z); #endif diff --git a/src/world/map.c b/src/world/map.c index 76f5f4af6d..5ceacf057e 100644 --- a/src/world/map.c +++ b/src/world/map.c @@ -19,6 +19,7 @@ *****************************************************************************/ #include "../addresses.h" +#include "../audio/audio.h" #include "../game.h" #include "../interface/window.h" #include "../localisation/date.h" @@ -27,7 +28,9 @@ #include "../scenario.h" #include "banner.h" #include "climate.h" +#include "footpath.h" #include "map.h" +#include "map_animation.h" #include "park.h" #include "scenery.h" @@ -580,66 +583,525 @@ void map_invalidate_tile(int x, int y, int zLow, int zHigh) * * rct2: 0x006E0E01 */ -money32 map_try_clear_scenery(int x, int y, rct_map_element *mapElement, int flags) +void game_command_remove_scenery(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp) { + int x = *eax; + int y = *ecx; + uint8 base_height = *edx; + uint8 scenery_type = *edx >> 8; + uint8 map_element_type = *ebx >> 8; money32 cost; - rct_scenery_entry *entry; - - entry = g_smallSceneryEntries[mapElement->properties.scenery.type]; + + rct_scenery_entry *entry = g_smallSceneryEntries[scenery_type]; cost = entry->small_scenery.removal_price * 10; RCT2_GLOBAL(RCT2_ADDRESS_NEXT_EXPENDITURE_TYPE, uint8) = RCT_EXPENDITURE_TYPE_LANDSCAPING * 4; - RCT2_GLOBAL(0x009DEA5E, uint32) = x * 32 + 16; - RCT2_GLOBAL(0x009DEA60, uint32) = y * 32 + 16; - RCT2_GLOBAL(0x009DEA62, uint32) = mapElement->base_height * 8; + RCT2_GLOBAL(0x009DEA5E, uint32) = x + 16; + RCT2_GLOBAL(0x009DEA60, uint32) = y + 16; + RCT2_GLOBAL(0x009DEA62, uint32) = base_height * 8; - x *= 32; - y *= 32; - - if (!(flags & 0x40) && RCT2_GLOBAL(RCT2_ADDRESS_GAME_PAUSED, uint8) != 0) { + if (!(*ebx & 0x40) && RCT2_GLOBAL(RCT2_ADDRESS_GAME_PAUSED, uint8) != 0) { RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED; - return MONEY32_UNDEFINED; + *ebx = MONEY32_UNDEFINED; + return; } - if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR) && !(flags & 0x40)) { + if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR) && !(*ebx & 0x40)) { // Check if allowed to remove item if (RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_FORBID_TREE_REMOVAL) { if (entry->small_scenery.height > 64) { RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = STR_FORBIDDEN_BY_THE_LOCAL_AUTHORITY; - return MONEY32_UNDEFINED; + *ebx = MONEY32_UNDEFINED; + return; } } // Check if the land is owned - if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR)) - if (!map_is_location_owned(x, y, RCT2_GLOBAL(0x009DEA62, uint32))) - return MONEY32_UNDEFINED; + if (!map_is_location_owned(x, y, RCT2_GLOBAL(0x009DEA62, uint32))){ + *ebx = MONEY32_UNDEFINED; + return; + } } - if ((flags & 0x40) && !(mapElement->flags & 0x10)) - return 0; + rct_map_element* map_element = map_get_first_element_at(x / 32, y / 32); + while(map_element->type != map_element_type || + map_element->base_height != base_height || + map_element->properties.scenery.type != scenery_type || + (*ebx & 0x40) && !(map_element->flags & MAP_ELEMENT_FLAG_5)){ + map_element++; + if((map_element - 1)->flags & MAP_ELEMENT_FLAG_LAST_TILE){ + *ebx = 0; + return; + } + } // Remove element - if (flags & 1) { + if (*ebx & GAME_COMMAND_FLAG_APPLY) { map_invalidate_tile_full(x, y); - map_element_remove(mapElement); + map_element_remove(map_element); } - return RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_NO_MONEY ? 0 : cost; + *ebx = (RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_NO_MONEY ? 0 : cost); } /** * - * rct2: 0x006E5597 + * rct2: 0x006B8E1B */ -money32 sub_6E5597(int x, int y, int dl, int dh, int bl) +void game_command_remove_large_scenery(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp) { - int eax, ebx, ecx, edx, esi, edi, ebp; - eax = x * 32; - ecx = y * 32; - ebx = bl & 0xFF; - edx = ((dh & 0xFF) << 8) | (dl & 0xFF); - RCT2_CALLFUNC_X(0x006E5597, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); - return ebx; + uint8 base_height = *edx; + uint8 scenerymultiple_index = *edx >> 8; + uint8 map_element_direction = *ebx >> 8; + int x = *eax; + int y = *ecx; + int z = map_element_height(x, y); + RCT2_GLOBAL(0x009DEA5E, uint16) = x + 16; + RCT2_GLOBAL(0x009DEA60, uint16) = y + 16; + RCT2_GLOBAL(0x009DEA62, uint16) = z; + RCT2_GLOBAL(RCT2_ADDRESS_NEXT_EXPENDITURE_TYPE, uint8) = 12; + + if (!(*ebx & 0x40) && RCT2_GLOBAL(RCT2_ADDRESS_GAME_PAUSED, uint8) != 0) { + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED; + *ebx = MONEY32_UNDEFINED; + return; + } + + rct_map_element* map_element = map_get_first_element_at(x / 32, y / 32); + while(map_element_get_type(map_element) != MAP_ELEMENT_TYPE_SCENERY_MULTIPLE || + map_element->base_height != base_height || + map_element->properties.scenerymultiple.type >> 10 != scenerymultiple_index || + (map_element->type & MAP_ELEMENT_DIRECTION_MASK) != map_element_direction){ + map_element++; + if((map_element - 1)->flags & MAP_ELEMENT_FLAG_LAST_TILE){ + *ebx = 0; + return; + } + } + if((*ebx & 0x40) && !(map_element->flags & MAP_ELEMENT_FLAG_5)){ + *ebx = 0; + return; + } + int ecx2 = map_element->properties.scenerymultiple.type >> 10; + rct_scenery_entry* scenery_entry = RCT2_ADDRESS(RCT2_ADDRESS_LARGE_SCENERY_ENTRIES, rct_scenery_entry*)[map_element->properties.scenerymultiple.type & 0x3FF]; + if(scenery_entry->large_scenery.var_11 != 0xFF){ + uint8 banner_num = map_element->type & MAP_ELEMENT_QUADRANT_MASK; + banner_num |= (map_element->properties.scenerymultiple.colour[0] & 0xE0) >> 2; + banner_num |= (map_element->properties.scenerymultiple.colour[1] & 0xE0) >> 5; + if(gBanners[banner_num].type != BANNER_NULL){ + window_close_by_number(WC_BANNER, banner_num); + gBanners[banner_num].type = BANNER_NULL; + user_string_free(gBanners[banner_num].string_idx); + } + } + int x2 = scenery_entry->large_scenery.tiles[ecx2].x_offset; + int y2 = scenery_entry->large_scenery.tiles[ecx2].y_offset; + int z2 = (base_height * 8) - scenery_entry->large_scenery.tiles[ecx2].z_offset; + switch(map_element->type & MAP_ELEMENT_DIRECTION_MASK){ + case MAP_ELEMENT_DIRECTION_WEST: + break; + case MAP_ELEMENT_DIRECTION_NORTH:{ + int temp = x2; + x2 = y2; + y2 = -temp; + }break; + case MAP_ELEMENT_DIRECTION_EAST: + x2 = -x2; + y2 = -y2; + break; + case MAP_ELEMENT_DIRECTION_SOUTH:{ + int temp = y2; + y2 = x2; + x2 = -temp; + }break; + } + x2 = -x2 + x; + y2 = -y2 + y; + int i = 0; + while(1){ + if(scenery_entry->large_scenery.tiles[i].x_offset == -1){ + *ebx = scenery_entry->large_scenery.removal_price * 10; + if(RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_NO_MONEY){ + *ebx = 0; + } + return; + } + int x3 = scenery_entry->large_scenery.tiles[i].x_offset; + int y3 = scenery_entry->large_scenery.tiles[i].y_offset; + int z3 = scenery_entry->large_scenery.tiles[i].z_offset; + switch(map_element->type & MAP_ELEMENT_DIRECTION_MASK){ + case MAP_ELEMENT_DIRECTION_WEST: + break; + case MAP_ELEMENT_DIRECTION_NORTH:{ + int temp = x3; + x3 = y3; + y3 = -temp; + }break; + case MAP_ELEMENT_DIRECTION_EAST: + x3 = -x3; + y3 = -y3; + break; + case MAP_ELEMENT_DIRECTION_SOUTH:{ + int temp = y3; + y3 = x3; + x3 = -temp; + }break; + } + x3 += x2; + y3 += y2; + z3 += z2; + if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR)){ + if (!map_is_location_owned(x3, y3, z3)){ + *ebx = MONEY32_UNDEFINED; + return; + } + } + + if(*ebx & GAME_COMMAND_FLAG_APPLY){ + rct_map_element* map_element = map_get_first_element_at(x3 / 32, y3 / 32); + uint8 tile_not_found = 1; + do + { + if (map_element_get_type(map_element) != MAP_ELEMENT_TYPE_SCENERY_MULTIPLE) + continue; + + if ((map_element->type & MAP_ELEMENT_DIRECTION_MASK) != map_element_direction) + continue; + + if ((map_element->properties.scenerymultiple.type >> 10) != i) + continue; + + if (map_element->base_height != z3 / 8) + continue; + + map_invalidate_tile_full(x3, y3); + map_element_remove(map_element); + tile_not_found = 0; + break; + } while (!map_element_is_last_for_tile(map_element++)); + + if (tile_not_found){ + log_error("Tile not found when trying to remove element!"); + } + } + + i++; + } +} + +/** + * + * rct2: 0x006BA058 + */ +void game_command_remove_banner(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp) +{ + int x = *eax; + int y = *ecx; + uint8 base_height = *edx; + uint8 banner_position = *edx >> 8; + int z = base_height * 8; + RCT2_GLOBAL(RCT2_ADDRESS_NEXT_EXPENDITURE_TYPE, uint8) = 12; + RCT2_GLOBAL(0x009DEA5E, uint16) = x + 16; + RCT2_GLOBAL(0x009DEA60, uint16) = y + 16; + RCT2_GLOBAL(0x009DEA62, uint16) = z; + if(!(*ebx & 0x40) && RCT2_GLOBAL(0x009DEA6E, uint8) != 0){ + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED; + *ebx = MONEY32_UNDEFINED; + return; + } + if(!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR) && !map_is_location_owned(x, y, z - 16)){ + *ebx = MONEY32_UNDEFINED; + return; + } + rct_map_element* map_element = map_get_first_element_at(x / 32, y / 32); + while(map_element->type != MAP_ELEMENT_TYPE_BANNER || + map_element->properties.banner.position != banner_position){ + map_element++; + if((map_element - 1)->flags & MAP_ELEMENT_FLAG_LAST_TILE){ + *ebx = MONEY32_UNDEFINED; + return; + } + } + rct_banner *banner = &gBanners[map_element->properties.banner.index]; + uint8 banner_type = banner->type; + if(*ebx & GAME_COMMAND_FLAG_APPLY){ + window_close_by_number(WC_BANNER, map_element->properties.banner.index); + user_string_free(banner->string_idx); + banner->type = BANNER_NULL; + map_invalidate_tile(x, y, z, z + 32); + map_element_remove(map_element); + } + rct_scenery_entry *scenery_entry = (rct_scenery_entry*)object_entry_groups[OBJECT_TYPE_BANNERS].chunks[banner_type]; + *ebx = (scenery_entry->banner.price * -3) / 4; + if(RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_NO_MONEY){ + *ebx = 0; + } +} + +/** + * + * rct2: 0x006E0F26 + */ +void game_command_set_scenery_colour(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp) +{ + RCT2_GLOBAL(RCT2_ADDRESS_NEXT_EXPENDITURE_TYPE, uint8) = 12; + int x = *eax; + int y = *ecx; + uint8 base_height = *edx; + uint8 scenery_type = *edx >> 8; + uint8 map_element_type = *ebx >> 8; + uint8 color1 = *ebp; + uint8 color2 = *ebp >> 8; + int z = base_height * 8; + RCT2_GLOBAL(0x009DEA5E, uint16) = x + 16; + RCT2_GLOBAL(0x009DEA60, uint16) = y + 16; + RCT2_GLOBAL(0x009DEA62, uint16) = z; + if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR)){ + if (!map_is_location_owned(x, y, z)){ + *ebx = MONEY32_UNDEFINED; + return; + } + } + + rct_map_element* map_element = map_get_first_element_at(x / 32, y / 32); + while(map_element->type != map_element_type || + map_element->base_height != base_height || + map_element->properties.scenery.type != scenery_type){ + map_element++; + if((map_element - 1)->flags & MAP_ELEMENT_FLAG_LAST_TILE){ + *ebx = 0; + return; + } + } + if((*ebx & 0x40) && !(map_element->flags & MAP_ELEMENT_FLAG_5)){ + *ebx = 0; + return; + } + if(*ebx & GAME_COMMAND_FLAG_APPLY){ + map_element->properties.scenery.colour_1 &= 0xE0; + map_element->properties.scenery.colour_1 |= color1; + map_element->properties.scenery.colour_2 &= 0xE0; + map_element->properties.scenery.colour_2 |= color2; + map_invalidate_tile_full(x, y); + } + + *ebx = 0; +} + +/** + * + * rct2: 0x006E56B5 + */ +void game_command_set_fence_colour(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp) +{ + RCT2_GLOBAL(RCT2_ADDRESS_NEXT_EXPENDITURE_TYPE, uint8) = 12; + int x = *eax; + int y = *ecx; + uint8 map_element_direction = *edx; + uint8 base_height = *edx >> 8; + uint8 color1 = *ebx >> 8; + uint8 color2 = *ebp; + uint8 color3 = *ebp >> 8; + int z = base_height * 8; + RCT2_GLOBAL(0x009DEA5E, uint16) = x + 16; + RCT2_GLOBAL(0x009DEA60, uint16) = y + 16; + RCT2_GLOBAL(0x009DEA62, uint16) = z; + if(!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR)){ + if(!map_is_location_in_park(x, y)){ + *ebx = MONEY32_UNDEFINED; + return; + } + rct_map_element* map_element = map_get_first_element_at(x / 32, y / 32); + while(map_element_get_type(map_element) != MAP_ELEMENT_TYPE_FENCE || + map_element->base_height != base_height || + (map_element->type & MAP_ELEMENT_DIRECTION_MASK) != map_element_direction|| + ((*ebx & 0x40) && !(map_element->flags & MAP_ELEMENT_FLAG_5))){ + map_element++; + if((map_element - 1)->flags & MAP_ELEMENT_FLAG_LAST_TILE){ + *ebx = 0; + return; + } + } + if((*ebx & 0x40) && !(map_element->flags & MAP_ELEMENT_FLAG_5)){ + *ebx = 0; + return; + } + if(*ebx & GAME_COMMAND_FLAG_APPLY){ + rct_scenery_entry* scenery_entry = RCT2_ADDRESS(RCT2_ADDRESS_WALL_SCENERY_ENTRIES, rct_scenery_entry*)[map_element->properties.fence.type]; + map_element->properties.fence.item[1] &= 0xE0; + map_element->properties.fence.item[1] |= color1; + map_element->flags &= 0x9F; + map_element->properties.fence.item[1] &= 0x1F; + map_element->properties.fence.item[1] |= (color2 & 0x7) * 32; + map_element->flags |= (color2 & 0x18) * 4; + if(scenery_entry->wall.flags & 0x80){ + map_element->properties.fence.item[0] = color3; + } + map_invalidate_tile(x, y, z, z + 0x48); + } + } + *ebx = 0; +} + +/** + * + * rct2: 0x006B909A + */ +void game_command_set_large_scenery_colour(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp) +{ + RCT2_GLOBAL(RCT2_ADDRESS_NEXT_EXPENDITURE_TYPE, uint8) = 12; + int x = *eax; + int y = *ecx; + uint8 map_element_direction = *ebx >> 8; + uint8 base_height = *edx; + uint8 scenerymultiple_index = *edx >> 8; + uint8 color1 = *ebp; + uint8 color2 = *ebp >> 8; + int z = map_element_height(x, y); + RCT2_GLOBAL(0x009DEA5E, uint16) = x + 16; + RCT2_GLOBAL(0x009DEA60, uint16) = y + 16; + RCT2_GLOBAL(0x009DEA62, uint16) = z; + + + rct_map_element* map_element = map_get_first_element_at(x / 32, y / 32); + while(map_element_get_type(map_element) != MAP_ELEMENT_TYPE_SCENERY_MULTIPLE || + map_element->base_height != base_height || + map_element->properties.scenerymultiple.type >> 10 != scenerymultiple_index || + (map_element->type & MAP_ELEMENT_DIRECTION_MASK) != map_element_direction){ + map_element++; + if((map_element - 1)->flags & MAP_ELEMENT_FLAG_LAST_TILE){ + *ebx = 0; + return; + } + } + if((*ebx & 0x40) && !(map_element->flags & MAP_ELEMENT_FLAG_5)){ + *ebx = 0; + return; + } + int ecx2 = map_element->properties.scenerymultiple.type >> 10; + rct_scenery_entry* scenery_entry = RCT2_ADDRESS(RCT2_ADDRESS_LARGE_SCENERY_ENTRIES, rct_scenery_entry*)[map_element->properties.scenerymultiple.type & 0x3FF]; + int x2 = scenery_entry->large_scenery.tiles[ecx2].x_offset; + int y2 = scenery_entry->large_scenery.tiles[ecx2].y_offset; + int z2 = (base_height * 8) - scenery_entry->large_scenery.tiles[ecx2].z_offset; + switch(map_element->type & MAP_ELEMENT_DIRECTION_MASK){ + case MAP_ELEMENT_DIRECTION_WEST: + break; + case MAP_ELEMENT_DIRECTION_NORTH:{ + int temp = x2; + x2 = y2; + y2 = -temp; + }break; + case MAP_ELEMENT_DIRECTION_EAST: + x2 = -x2; + y2 = -y2; + break; + case MAP_ELEMENT_DIRECTION_SOUTH:{ + int temp = y2; + y2 = x2; + x2 = -temp; + }break; + } + x2 = -x2 + x; + y2 = -y2 + y; + int i = 0; + while(1){ + if(scenery_entry->large_scenery.tiles[i].x_offset == -1){ + *ebx = 0; + return; + } + int x3 = scenery_entry->large_scenery.tiles[i].x_offset; + int y3 = scenery_entry->large_scenery.tiles[i].y_offset; + int z3 = scenery_entry->large_scenery.tiles[i].z_offset; + switch(map_element->type & MAP_ELEMENT_DIRECTION_MASK){ + case MAP_ELEMENT_DIRECTION_WEST: + break; + case MAP_ELEMENT_DIRECTION_NORTH:{ + int temp = x3; + x3 = y3; + y3 = -temp; + }break; + case MAP_ELEMENT_DIRECTION_EAST: + x3 = -x3; + y3 = -y3; + break; + case MAP_ELEMENT_DIRECTION_SOUTH:{ + int temp = y3; + y3 = x3; + x3 = -temp; + }break; + } + x3 += x2; + y3 += y2; + z3 += z2; + if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR)){ + if (!map_is_location_owned(x3, y3, z3)){ + *ebx = MONEY32_UNDEFINED; + return; + } + } + + if(*ebx & GAME_COMMAND_FLAG_APPLY){ + rct_map_element* map_element = map_get_first_element_at(x3 / 32, y3 / 32); + while(map_element_get_type(map_element) != MAP_ELEMENT_TYPE_SCENERY_MULTIPLE || + (map_element->type & MAP_ELEMENT_DIRECTION_MASK) != map_element_direction || + map_element->properties.scenerymultiple.type >> 10 != i || + map_element->base_height != base_height){ + map_element++; + } + map_element->properties.scenerymultiple.colour[0] &= 0xE0; + map_element->properties.scenerymultiple.colour[0] |= color1; + map_element->properties.scenerymultiple.colour[1] &= 0xE0; + map_element->properties.scenerymultiple.colour[1] |= color2; + map_invalidate_tile_full(x3, y3); + } + + i++; + } + *ebx = 0; +} + +/** + * + * rct2: 0x006BA16A + */ +void game_command_set_banner_colour(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp) +{ + RCT2_GLOBAL(RCT2_ADDRESS_NEXT_EXPENDITURE_TYPE, uint8) = 12; + int x = *eax; + int y = *ecx; + uint8 base_height = *edx; + uint8 banner_position = *edx >> 8; + uint8 color = *ebp; + int z = (base_height * 8); + RCT2_GLOBAL(0x009DEA5E, uint16) = x + 16; + RCT2_GLOBAL(0x009DEA60, uint16) = y + 16; + RCT2_GLOBAL(0x009DEA62, uint16) = z; + + if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR)){ + if (!map_is_location_owned(x, y, z - 16)){ + *ebx = MONEY32_UNDEFINED; + return; + } + } + + if(*ebx & GAME_COMMAND_FLAG_APPLY){ + rct_map_element* map_element = map_get_first_element_at(x / 32, y / 32); + while(map_element->type != MAP_ELEMENT_TYPE_BANNER || + map_element->properties.banner.position != banner_position){ + map_element++; + if((map_element - 1)->flags & MAP_ELEMENT_FLAG_LAST_TILE){ + *ebx = MONEY32_UNDEFINED; + return; + } + } + rct_window* window = window_find_by_number(WC_BANNER, map_element->properties.banner.index); + if(window){ + window_invalidate(window); + } + gBanners[map_element->properties.banner.index].colour = color; + map_invalidate_tile(x, y, z, z + 32); + } + + *ebx = 0; } money32 sub_6A67C0(int x, int y, int z, int flags) @@ -685,8 +1147,15 @@ restart_from_beginning: goto restart_from_beginning; #endif break; - case MAP_ELEMENT_TYPE_SCENERY: - cost = map_try_clear_scenery(x, y, mapElement, flags); + case MAP_ELEMENT_TYPE_SCENERY:{ + int eax = x * 32; + int ebx = (mapElement->type << 8) | flags; + int ecx = y * 32; + int edx = (mapElement->properties.scenery.type << 8) | (mapElement->base_height); + int esi, edi, ebp; + game_command_remove_scenery(&eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + cost = ebx; + if (cost == MONEY32_UNDEFINED) return MONEY32_UNDEFINED; @@ -694,9 +1163,16 @@ restart_from_beginning: if (flags & 1) goto restart_from_beginning; - break; - case MAP_ELEMENT_TYPE_FENCE: - cost = sub_6E5597(x, y, mapElement->type & 3, mapElement->base_height, flags); + }break; + case MAP_ELEMENT_TYPE_FENCE:{ + int eax = x * 32; + int ebx = flags; + int ecx = y * 32; + int edx = (mapElement->base_height << 8) | (mapElement->type & MAP_ELEMENT_DIRECTION_MASK); + int esi, edi, ebp; + game_command_remove_fence(&eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + cost = ebx; + if (cost == MONEY32_UNDEFINED) return MONEY32_UNDEFINED; @@ -704,7 +1180,7 @@ restart_from_beginning: if (flags & 1) goto restart_from_beginning; - break; + }break; } } while (!map_element_is_last_for_tile(mapElement++)); @@ -890,6 +1366,418 @@ void game_command_change_surface_style(int* eax, int* ebx, int* ecx, int* edx, i ); } +/** + * + * rct2: 0x0068C542 + */ +void game_command_raise_land(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp) +{ + int x = *eax; + int y = *ecx; + int z = map_element_height(*eax, *ecx); + int ax = (uint16)*edx; + int ay = (uint16)*ebp; + int bx = (uint16)(*edx >> 16); + int by = (uint16)(*ebp >> 16); + uint16 selection_type = *edi; + + int cost = MONEY32_UNDEFINED; + + if(*ebx & GAME_COMMAND_FLAG_APPLY && RCT2_GLOBAL(0x009A8C28, uint8) == 1){ + sound_play_panned(SOUND_PLACE_ITEM, 0x8001, x, y, z); + } + + uint8 min_height = 0xFF; + + // find lowest map element in selection + for(int yi = ay; yi <= by; yi += 32){ + for(int xi = ax; xi <= bx; xi += 32){ + rct_map_element* map_element = map_get_surface_element_at(xi / 32, yi / 32); + if(min_height > map_element->base_height){ + min_height = map_element->base_height; + } + } + } + + for(int yi = ay; yi <= by; yi += 32){ + for(int xi = ax; xi <= bx; xi += 32){ + rct_map_element* map_element = map_get_surface_element_at(xi / 32, yi / 32); + uint8 height = map_element->base_height; + if(height <= min_height){ + uint8 dh = RCT2_ADDRESS(0x00981A1E, uint8)[(selection_type * 32) + (map_element->properties.surface.slope & MAP_ELEMENT_SLOPE_MASK)]; // lookup table + if(dh & 0x20){ // needs to be raised, otherwise just the slope type changes + height += 2; + dh &= ~0x20; + } + int ebx2 = *ebx; + int edx2 = (dh << 8) + height; + int edi2 = selection_type * 32; + RCT2_CALLFUNC_X(0x0066397F, &xi, &ebx2, &yi, &edx2, (int*)&map_element, &edi2, ebp); // actually apply the change + if(ebx2 != MONEY32_UNDEFINED){ + if(cost == MONEY32_UNDEFINED){ + cost = ebx2; + }else{ + cost += ebx2; + } + } + } + } + } + RCT2_GLOBAL(0x141F56C, uint8) = 12; + RCT2_GLOBAL(0x009DEA5E, uint32) = x; + RCT2_GLOBAL(0x009DEA60, uint32) = y; + RCT2_GLOBAL(0x009DEA62, uint32) = z; + *ebx = cost; +} + +/** + * + * rct2: 0x0068C6D1 + */ +void game_command_lower_land(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp) +{ + int x = *eax; + int y = *ecx; + int z = map_element_height(*eax, *ecx); + int ax = (uint16)*edx; + int ay = (uint16)*ebp; + int bx = (uint16)(*edx >> 16); + int by = (uint16)(*ebp >> 16); + uint16 selection_type = *edi; + + int cost = MONEY32_UNDEFINED; + + if(*ebx & GAME_COMMAND_FLAG_APPLY && RCT2_GLOBAL(0x009A8C28, uint8) == 1){ + sound_play_panned(SOUND_PLACE_ITEM, 0x8001, x, y, z); + } + + uint8 max_height = 0; + + // find highest map element in selection + for(int yi = ay; yi <= by; yi += 32){ + for(int xi = ax; xi <= bx; xi += 32){ + rct_map_element* map_element = map_get_surface_element_at(xi / 32, yi / 32); + uint8 base_height = map_element->base_height; + if(map_element->properties.surface.slope & 0xF){ + base_height += 2; + } + if(map_element->properties.surface.slope & 0x10){ + base_height += 2; + } + if(max_height < base_height){ + max_height = base_height; + } + } + } + + for(int yi = ay; yi <= by; yi += 32){ + for(int xi = ax; xi <= bx; xi += 32){ + rct_map_element* map_element = map_get_surface_element_at(xi / 32, yi / 32); + uint8 height = map_element->base_height; + if(map_element->properties.surface.slope & 0xF){ + height += 2; + } + if(map_element->properties.surface.slope & 0x10){ + height += 2; + } + if(height >= max_height){ + height = map_element->base_height; + uint8 dh = RCT2_ADDRESS(0x00981ABE, uint8)[(selection_type * 32) + (map_element->properties.surface.slope & MAP_ELEMENT_SLOPE_MASK)]; // lookup table + if(dh & 0x20){ // needs to be lowered, otherwise just the slope type changes + height -= 2; + dh &= ~0x20; + } + int ebx2 = *ebx; + int edx2 = (dh << 8) + height; + int edi2 = selection_type * 32; + RCT2_CALLFUNC_X(0x0066397F, &xi, &ebx2, &yi, &edx2, (int*)&map_element, &edi2, ebp); // actually apply the change + if(ebx2 != MONEY32_UNDEFINED){ + if(cost == MONEY32_UNDEFINED){ + cost = ebx2; + }else{ + cost += ebx2; + } + } + } + } + } + RCT2_GLOBAL(0x141F56C, uint8) = 12; + RCT2_GLOBAL(0x009DEA5E, uint32) = x; + RCT2_GLOBAL(0x009DEA60, uint32) = y; + RCT2_GLOBAL(0x009DEA62, uint32) = z; + *ebx = cost; +} + +/** + * + * rct2: 0x006E66A0 + */ +void game_command_raise_water(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp) +{ + int ax = (uint16)*eax; + int ay = (uint16)*ecx; + int bx = (uint16)*edi; + int by = (uint16)*ebp; + + int cost = 0; + + uint8 max_height = 0xFF; + + for(int yi = ay; yi <= by; yi += 32){ + for(int xi = ax; xi <= bx; xi += 32){ + rct_map_element* map_element = map_get_surface_element_at(xi / 32, yi / 32); + uint8 height = map_element->base_height; + if(map_element->properties.surface.terrain & 0x1F){ + height = (map_element->properties.surface.terrain & 0x1F) * 2; + } + if(max_height > height){ + max_height = height; + } + } + } + + for(int yi = ay; yi <= by; yi += 32){ + for(int xi = ax; xi <= bx; xi += 32){ + rct_map_element* map_element = map_get_surface_element_at(xi / 32, yi / 32); + + if(map_element->base_height <= max_height){ + uint8 height = (map_element->properties.surface.terrain & 0x1F); + if(height){ + height *= 2; + if(height > max_height){ + continue; + } + height += 2; + }else{ + height = map_element->base_height + 2; + } + int eax2 = xi, ebx2 = *ebx, ecx2 = yi, edx2 = (max_height << 8) + height, esi2, edi2, ebp2; + ebx2 = game_do_command_p(GAME_COMMAND_16, &eax2, &ebx2, &ecx2, &edx2, &esi2, &edi2, &ebp2); + if(ebx2 == MONEY32_UNDEFINED){ + *ebx = MONEY32_UNDEFINED; + return; + }else{ + cost += ebx2; + } + } + } + } + if(*ebx & GAME_COMMAND_FLAG_APPLY){ + int x = ((ax + bx) / 2) + 16; + int y = ((ay + by) / 2) + 16; + int z = map_element_height(x, y); + sint16 water_height_z = z >> 16; + sint16 base_height_z = z; + z = water_height_z; + if(!z){ + z = base_height_z; + } + RCT2_GLOBAL(0x009DEA5E, uint32) = x; + RCT2_GLOBAL(0x009DEA60, uint32) = y; + RCT2_GLOBAL(0x009DEA62, uint32) = z; + sound_play_panned(SOUND_LAYING_OUT_WATER, 0x8001, x, y, z); + } + *ebx = cost; +} + +/** + * + * rct2: 0x006E6878 + */ +void game_command_lower_water(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp) +{ + int ax = (uint16)*eax; + int ay = (uint16)*ecx; + int bx = (uint16)*edi; + int by = (uint16)*ebp; + + int cost = 0; + + uint8 min_height = 0; + + for(int yi = ay; yi <= by; yi += 32){ + for(int xi = ax; xi <= bx; xi += 32){ + rct_map_element* map_element = map_get_surface_element_at(xi / 32, yi / 32); + + uint8 height = map_element->properties.surface.terrain & 0x1F; + if(height){ + height *= 2; + if(height > min_height){ + min_height = height; + } + } + } + } + + for(int yi = ay; yi <= by; yi += 32){ + for(int xi = ax; xi <= bx; xi += 32){ + rct_map_element* map_element = map_get_surface_element_at(xi / 32, yi / 32); + + uint8 height = (map_element->properties.surface.terrain & 0x1F); + if(height){ + height *= 2; + if(height < min_height){ + continue; + } + height -= 2; + int ebx2 = game_do_command(xi, *ebx, yi, (min_height << 8) + height, GAME_COMMAND_16, 0, 0); + if(ebx2 == MONEY32_UNDEFINED){ + *ebx = MONEY32_UNDEFINED; + return; + }else{ + cost += ebx2; + } + } + } + } + if(*ebx & GAME_COMMAND_FLAG_APPLY){ + int x = ((ax + bx) / 2) + 16; + int y = ((ay + by) / 2) + 16; + int z = map_element_height(x, y); + sint16 water_height_z = z >> 16; + sint16 base_height_z = z; + z = water_height_z; + if(!z){ + z = base_height_z; + } + RCT2_GLOBAL(0x009DEA5E, uint32) = x; + RCT2_GLOBAL(0x009DEA60, uint32) = y; + RCT2_GLOBAL(0x009DEA62, uint32) = z; + sound_play_panned(SOUND_LAYING_OUT_WATER, 0x8001, x, y, z); + } + *ebx = cost; +} + +/** + * + * rct2: 0x006E5597 + */ +void game_command_remove_fence(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp) +{ + int x = *eax; + int y = *ecx; + uint8 base_height = (*edx >> 8); + uint8 direction = *edx; + + RCT2_GLOBAL(0x141F56C, uint8) = 12; + if(!(*ebx & 0x40) && RCT2_GLOBAL(RCT2_ADDRESS_GAME_PAUSED, uint8) != 0){ + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED; + *ebx = MONEY32_UNDEFINED; + return; + } + if(!(*ebx & 0x40) && !(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR) && !map_is_location_owned(x, y, base_height * 8)){ + *ebx = MONEY32_UNDEFINED; + return; + } + rct_map_element* map_element = map_get_first_element_at(x / 32, y / 32); + while(map_element_get_type(map_element) != MAP_ELEMENT_TYPE_FENCE || + map_element->base_height != base_height || + (map_element->type & MAP_ELEMENT_DIRECTION_MASK) != direction || + ((*ebx & 0x40) && !(map_element->flags & MAP_ELEMENT_FLAG_5))){ + map_element++; + if((map_element - 1)->flags & MAP_ELEMENT_FLAG_LAST_TILE){ + *ebx = 0; + return; + } + } + rct_scenery_entry* scenery_entry = RCT2_ADDRESS(RCT2_ADDRESS_WALL_SCENERY_ENTRIES, rct_scenery_entry*)[map_element->properties.fence.type]; + if(scenery_entry->wall.var_0D != 0xFF){ + rct_banner* banner = &gBanners[map_element->properties.fence.item[0]]; + if(banner->type != BANNER_NULL){ + window_close_by_number(WC_BANNER, map_element->properties.fence.item[0]); + banner->type = BANNER_NULL; + user_string_free(banner->string_idx); + } + } + map_invalidate_tile(x, y, map_element->base_height * 8, (map_element->base_height * 8) + 72); + map_element_remove(map_element); + *ebx = 0; +} + +/** + * + * rct2: 0x006B9E6D + */ +void game_command_place_banner(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp) +{ + int x = (uint16)*eax; + int y = (uint16)*ecx; + uint8 base_height = *edx; + uint8 edge = *edx >> 8; + uint8 colour = *edi; + uint8 type = *ebx >> 8; + RCT2_GLOBAL(0x009DEA5E, uint32) = x + 16; + RCT2_GLOBAL(0x009DEA60, uint32) = y + 16; + RCT2_GLOBAL(0x009DEA62, uint32) = base_height * 16; + RCT2_GLOBAL(0x141F56C, uint8) = 12; + if(RCT2_GLOBAL(RCT2_ADDRESS_GAME_PAUSED, uint8) == 0){ + if(sub_68B044() && x < 8192 && y < 8192){ + rct_map_element* map_element = map_get_first_element_at(x / 32, y / 32); + int dl = base_height * 2; + int ch = (base_height - 1) * 2; + while(map_element_get_type(map_element) != MAP_ELEMENT_TYPE_PATH || + (map_element->base_height != dl && map_element->base_height != ch) || + !(map_element->properties.path.edges & (1 << edge))){ + map_element++; + if((map_element - 1)->flags & MAP_ELEMENT_FLAG_LAST_TILE){ + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = STR_CAN_ONLY_BE_BUILT_ACROSS_PATHS; + *ebx = MONEY32_UNDEFINED; + return; + } + } + if(!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR) && !map_is_location_owned(x, y, base_height * 16)){ + *ebx = MONEY32_UNDEFINED; + return; + } + map_element = map_get_first_element_at(x / 32, y / 32); + dl = (base_height + 1) * 2; + while(map_element->type != MAP_ELEMENT_TYPE_BANNER || + map_element->base_height != dl || + (map_element->properties.banner.position & 0x3) != edge){ + map_element++; + if((map_element - 1)->flags & MAP_ELEMENT_FLAG_LAST_TILE){ + int banner_index = create_new_banner(*ebx); + if(banner_index == BANNER_NULL){ + *ebx = MONEY32_UNDEFINED; + return; + } + *edi = banner_index; + if(*ebx & GAME_COMMAND_FLAG_APPLY){ + rct_map_element* new_map_element = map_element_insert(x / 32, y / 32, (base_height + 1) * 2, 0); + gBanners[banner_index].type = type; + gBanners[banner_index].colour = colour; + gBanners[banner_index].x = x / 32; + gBanners[banner_index].y = y / 32; + new_map_element->type = MAP_ELEMENT_TYPE_BANNER; + new_map_element->clearance_height = new_map_element->base_height + 2; + new_map_element->properties.banner.position = edge; + new_map_element->properties.banner.flags = 0xFF; + new_map_element->properties.banner.unused = 0; + new_map_element->properties.banner.index = banner_index; + if(*ebx & 0x40){ + new_map_element->flags |= 0x10; + } + map_invalidate_tile_full(x, y); + map_animation_create(0x0A, x, y, new_map_element->base_height); + } + rct_scenery_entry *scenery_entry = (rct_scenery_entry*)object_entry_groups[OBJECT_TYPE_BANNERS].chunks[type]; + *ebx = scenery_entry->banner.price; + if(RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) & PARK_FLAGS_NO_MONEY){ + *ebx = 0; + } + return; + } + } + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = STR_BANNER_SIGN_IN_THE_WAY; + *ebx = MONEY32_UNDEFINED; + return; + } + }else{ + RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED; + } + *ebx = MONEY32_UNDEFINED; +} + /** * * rct2: 0x006EC6D7 @@ -1083,6 +1971,17 @@ int map_can_construct_at(int x, int y, int zLow, int zHigh, uint8 bl) return map_can_construct_with_clear_at(x, y, zLow, zHigh, (void*)0xFFFFFFFF, bl); } +/** + * + * rct2: 0x006BA278 + */ +int sub_6BA278(int ebx) +{ + int eax, ecx, edx, esi, edi, ebp; + RCT2_CALLFUNC_X(0x006BA278, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + return eax; +} + /** * * rct2: 0x006E5935 @@ -1105,7 +2004,7 @@ void map_remove_intersecting_walls(int x, int y, int z0, int z1, int direction) if (direction != (mapElement->type & 3)) continue; - sceneryEntry = g_wallSceneryEntries[mapElement->properties.fence.slope]; + sceneryEntry = g_wallSceneryEntries[mapElement->properties.fence.type]; if (sceneryEntry->wall.var_0D != 255) { bannerIndex = mapElement->properties.fence.item[0]; banner = &gBanners[bannerIndex]; diff --git a/src/world/map.h b/src/world/map.h index e45cf71503..963ecd07f3 100644 --- a/src/world/map.h +++ b/src/world/map.h @@ -64,13 +64,12 @@ typedef struct { } rct_map_element_entrance_properties; typedef struct { - uint8 slope; //4 + uint8 type; //4 uint8 item[3]; //5 } rct_map_element_fence_properties; typedef struct { - uint8 type; //4 - uint8 index; //5 + uint16 type; //4 uint8 colour[2]; //6 } rct_map_element_scenerymultiple_properties; @@ -130,6 +129,7 @@ enum { }; enum { + MAP_ELEMENT_FLAG_5 = (1 << 4), MAP_ELEMENT_FLAG_BROKEN = (1 << 5), MAP_ELEMENT_FLAG_LAST_TILE = (1 << 7) }; @@ -212,6 +212,8 @@ enum { #define MAX_MAP_ELEMENTS 196608 #define MAX_TILE_MAP_ELEMENT_POINTERS (256 * 256) +#define MAP_ELEMENT_LARGE_TYPE_MASK 0x3FF + #define TILE_UNDEFINED_MAP_ELEMENT (rct_map_element*)-1 typedef struct { @@ -271,9 +273,23 @@ int sub_68B044(); rct_map_element *map_element_insert(int x, int y, int z, int flags); int map_can_construct_with_clear_at(int x, int y, int zLow, int zHigh, void *clearFunc, uint8 bl); int map_can_construct_at(int x, int y, int zLow, int zHigh, uint8 bl); +int sub_6BA278(int ebx); +void game_command_remove_scenery(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp); +void game_command_remove_large_scenery(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp); +void game_command_remove_banner(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp); +void game_command_set_scenery_colour(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp); +void game_command_set_fence_colour(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp); +void game_command_set_large_scenery_colour(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp); +void game_command_set_banner_colour(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp); void game_command_clear_scenery(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp); void game_command_change_surface_style(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp); +void game_command_raise_land(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp); +void game_command_lower_land(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp); +void game_command_raise_water(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp); +void game_command_lower_water(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp); +void game_command_remove_fence(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp); +void game_command_place_banner(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp); #define GET_MAP_ELEMENT(x) (&(RCT2_ADDRESS(RCT2_ADDRESS_MAP_ELEMENTS, rct_map_element)[x])) #define TILE_MAP_ELEMENT_POINTER(x) (RCT2_ADDRESS(RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS, rct_map_element*)[x]) diff --git a/src/world/map_animation.c b/src/world/map_animation.c index b481cb0029..3a31fe5d82 100644 --- a/src/world/map_animation.c +++ b/src/world/map_animation.c @@ -48,7 +48,7 @@ void map_animation_create(int type, int x, int y, int z) { rct_map_animation *aobj = &gAnimatedObjects[0]; int numAnimatedObjects = RCT2_GLOBAL(0x0138B580, uint16); - for (; numAnimatedObjects > 0; aobj++) { + for (int i = 0; i < numAnimatedObjects; i++, aobj++) { if (aobj->x != x) continue; if (aobj->y != y) diff --git a/src/world/scenery.c b/src/world/scenery.c index 3a9e5d2dc1..3a05927927 100644 --- a/src/world/scenery.c +++ b/src/world/scenery.c @@ -109,4 +109,90 @@ void scenery_increase_age(int x, int y, rct_map_element *mapElement) mapElement->properties.scenery.age++; map_invalidate_tile(x, y, mapElement->base_height * 8, mapElement->clearance_height * 8); } +} + + +/* 0x006E2712 */ +void scenery_remove_ghost_tool_placement(){ + sint16 x, y, z; + + x = RCT2_GLOBAL(0x00F64EC4, sint16); + y = RCT2_GLOBAL(0x00F64EC6, sint16); + z = RCT2_GLOBAL(0x00F64F09, uint8); + + if (RCT2_GLOBAL(0x00F64F0D, uint8) & (1 << 0)){ + RCT2_GLOBAL(0x00F64F0D, uint8) &= ~(1 << 0); + + game_do_command( + x, + 105 | (RCT2_GLOBAL(0x00F64F0C, uint8) << 8), + y, + z | (RCT2_GLOBAL(0x00F64EDA, uint8) << 8), + GAME_COMMAND_REMOVE_SCENERY, + 0, + 0); + } + + if (RCT2_GLOBAL(0x00F64F0D, uint8) & (1 << 1)){ + RCT2_GLOBAL(0x00F64F0D, uint8) &= ~(1 << 1); + + rct_map_element* map_element = map_get_first_element_at(x / 32, y / 32); + + do{ + if (map_element_get_type(map_element) != MAP_ELEMENT_TYPE_PATH) + continue; + + if (map_element->base_height != z) + continue; + + game_do_command( + x, + 233 | (RCT2_GLOBAL(0x00F64F0F, uint8) << 8), + y, + z | (RCT2_GLOBAL(0x00F64F10, uint8) << 8), + GAME_COMMAND_PLACE_PATH, + RCT2_GLOBAL(0x00F64EAC, uint32) & 0xFFFF0000, + 0); + break; + } while (!map_element_is_last_for_tile(map_element++)); + } + + if (RCT2_GLOBAL(0x00F64F0D, uint8) & (1 << 2)){ + RCT2_GLOBAL(0x00F64F0D, uint8) &= ~(1 << 2); + + game_do_command( + x, + 105 | (RCT2_GLOBAL(0x00F64F0C, uint8) << 8), + y, + RCT2_GLOBAL(0x00F64F11, uint8) |(z << 8), + GAME_COMMAND_REMOVE_FENCE, + 0, + 0); + } + + if (RCT2_GLOBAL(0x00F64F0D, uint8) & (1 << 3)){ + RCT2_GLOBAL(0x00F64F0D, uint8) &= ~(1 << 3); + + game_do_command( + x, + 105 | (RCT2_GLOBAL(0x00F64EC0, uint8) << 8), + y, + z, + GAME_COMMAND_REMOVE_LARGE_SCENERY, + 0, + 0); + } + + if (RCT2_GLOBAL(0x00F64F0D, uint8) & (1 << 4)){ + RCT2_GLOBAL(0x00F64F0D, uint8) &= ~(1 << 4); + + game_do_command( + x, + 105, + y, + z | (RCT2_GLOBAL(0x00F64EC0, uint8) << 8), + GAME_COMMAND_REMOVE_BANNER, + 0, + 0); + } } \ No newline at end of file diff --git a/src/world/scenery.h b/src/world/scenery.h index 5740eb5765..3d2e6a98e9 100644 --- a/src/world/scenery.h +++ b/src/world/scenery.h @@ -56,14 +56,27 @@ typedef enum { SMALL_SCENERY_FLAG18 = (1 << 17), // 0x20000 SMALL_SCENERY_FLAG19 = (1 << 18), // 0x40000 SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR = (1 << 19), // 0x80000 + SMALL_SCENERY_FLAG20 = (1 << 20), // 0x100000 + SMALL_SCENERY_FLAG21 = (1 << 21), // 0x200000 + SMALL_SCENERY_FLAG22 = (1 << 22), // 0x400000 + SMALL_SCENERY_FLAG23 = (1 << 23), // 0x800000 + SMALL_SCENERY_FLAG24 = (1 << 24), // 0x1000000 + SMALL_SCENERY_FLAG25 = (1 << 25), // 0x2000000 } SMALL_SCENERY_FLAGS; +typedef struct { + sint16 x_offset; + sint16 y_offset; + sint16 z_offset; + uint8 pad_6[3]; +} rct_large_scenery_tile; + typedef struct { uint8 tool_id; // 0x06 uint8 flags; // 0x07 sint16 price; // 0x08 sint16 removal_price; // 0x0A - uint32 var_0C; + rct_large_scenery_tile* tiles; // 0x0C uint8 scenery_tab_id; // 0x10 uint8 var_11; uint32 var_12; @@ -141,6 +154,9 @@ enum { #define g_largeSceneryEntries ((rct_scenery_entry**)object_entry_groups[OBJECT_TYPE_LARGE_SCENERY].chunks) #define g_wallSceneryEntries ((rct_scenery_entry**)object_entry_groups[OBJECT_TYPE_WALLS].chunks) #define g_bannerSceneryEntries ((rct_scenery_entry**)object_entry_groups[OBJECT_TYPE_BANNERS].chunks) + +// Often 0x009ADA50 is used for pathBits this is 1 entry before g_pathBitSceneryEntries and is used +// because 0 represents no path bits on a path. So remember to remove 1 when using it for 0x009ADA50 #define g_pathBitSceneryEntries ((rct_scenery_entry**)object_entry_groups[OBJECT_TYPE_PATH_BITS].chunks) #define g_scenerySetEntries ((rct_scenery_set_entry**)object_entry_groups[OBJECT_TYPE_SCENERY_SETS].chunks) @@ -159,5 +175,6 @@ void init_scenery(); void scenery_update_tile(int x, int y); void scenery_update_age(int x, int y, rct_map_element *mapElement); void scenery_set_default_placement_configuration(); +void scenery_remove_ghost_tool_placement(); #endif \ No newline at end of file