From 2d8c473f75c6732a1f99e20cf3d875ffe6e8cece Mon Sep 17 00:00:00 2001 From: wolfreak99 Date: Mon, 19 Feb 2018 15:11:14 -0500 Subject: [PATCH] Sync Peep Spawn over network --- data/language/en-GB.txt | 3 + distribution/changelog.txt | 1 + src/openrct2-ui/windows/Map.cpp | 38 ++--- src/openrct2/Game.cpp | 10 ++ src/openrct2/Game.h | 1 + src/openrct2/actions/GameActionCompat.cpp | 18 ++- .../actions/GameActionRegistration.cpp | 2 + src/openrct2/actions/PlacePeepSpawnAction.hpp | 141 ++++++++++++++++++ src/openrct2/localisation/StringIds.h | 4 + src/openrct2/network/NetworkAction.cpp | 3 +- src/openrct2/network/network.h | 2 +- src/openrct2/world/Map.h | 2 + 12 files changed, 194 insertions(+), 31 deletions(-) create mode 100644 src/openrct2/actions/PlacePeepSpawnAction.hpp diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index c560083913..1d5d1bb291 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -4528,6 +4528,9 @@ STR_6218 :OpenRCT2 Official STR_6219 :Highlight path issues STR_6220 :Make Usable STR_6221 :{SMALLFONT}{BLACK}This will set the ride's known entrance or exit location to the currently selected tile. Only one entrance and exit location can be made usable per stations. +STR_6222 :Can't place peep spawn here... +STR_6223 :Must be outside park boundaries! +STR_6224 :{STRING} placed a peep spawn. ############# # Scenarios # diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 0c085c7e1b..29532930cb 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -65,6 +65,7 @@ - Fix: [#5761] Mini coaster doesn't appear despite being selected. - Fix: [#5809] Support Steam RCT1 file layout when loading CSG images. - Fix: [#5838] Crash when saving very large track designs. +- Fix: [#5901] Placing peep spawn not synced across multiplayer - Fix: [#6101] Rides remain in ride list window briefly after demolition. - Fix: [#6114] Crash when using a non-LL CSG1.DAT. - Fix: [#6115] Random title screen music not random on launch. diff --git a/src/openrct2-ui/windows/Map.cpp b/src/openrct2-ui/windows/Map.cpp index 7eb0bde28f..5005bd047b 100644 --- a/src/openrct2-ui/windows/Map.cpp +++ b/src/openrct2-ui/windows/Map.cpp @@ -177,8 +177,6 @@ static uint32 _currentLine; /** rct2: 0x00F1AD68 */ static std::vector _mapImageData; -static sint32 _nextPeepSpawnIndex = 0; - static void window_map_init_map(); static void window_map_centre_on_view_point(); static void window_map_show_default_scenario_editor_buttons(rct_window *w); @@ -1360,41 +1358,25 @@ static void window_map_place_park_entrance_tool_down(sint32 x, sint32 y) */ static void window_map_set_peep_spawn_tool_down(sint32 x, sint32 y) { - rct_tile_element *tileElement, *surfaceTileElement; + rct_tile_element *tileElement; sint32 mapX, mapY, mapZ, direction; + // Verify footpath exists at location, and retrieve coordinates footpath_get_coordinates_from_pos(x, y, &mapX, &mapY, &direction, &tileElement); if (mapX == 0x8000) return; - surfaceTileElement = map_get_surface_element_at(mapX >> 5, mapY >> 5); - if (surfaceTileElement == nullptr) { - return; - } - if (surfaceTileElement->properties.surface.ownership & 0xF0) { - return; - } - - mapX = mapX + 16 + (word_981D6C[direction].x * 15); - mapY = mapY + 16 + (word_981D6C[direction].y * 15); mapZ = tileElement->base_height / 2; - sint32 peepSpawnIndex = -1; - for (sint32 i = 0; i < MAX_PEEP_SPAWNS; i++) { - if (gPeepSpawns[i].x == PEEP_SPAWN_UNDEFINED) { - peepSpawnIndex = i; - break; - } + bool result = place_peep_spawn({mapX, mapY, mapZ, (uint8)direction}); + if (result) { + audio_play_sound_at_location( + SOUND_PLACE_ITEM, + gCommandPosition.x, + gCommandPosition.y, + gCommandPosition.z + ); } - if (peepSpawnIndex == -1) { - peepSpawnIndex = _nextPeepSpawnIndex; - _nextPeepSpawnIndex = (peepSpawnIndex + 1) % MAX_PEEP_SPAWNS; - } - gPeepSpawns[peepSpawnIndex].x = mapX; - gPeepSpawns[peepSpawnIndex].y = mapY; - gPeepSpawns[peepSpawnIndex].z = mapZ; - gPeepSpawns[peepSpawnIndex].direction = direction; - gfx_invalidate_screen(); } /** diff --git a/src/openrct2/Game.cpp b/src/openrct2/Game.cpp index 2fdac4706f..f510da080f 100644 --- a/src/openrct2/Game.cpp +++ b/src/openrct2/Game.cpp @@ -1008,6 +1008,15 @@ void game_log_multiplayer_command(int command, const int * eax, const int * ebx, format_string(log_msg, 256, STR_LOG_REMOVE_TRACK, args); network_append_server_log(log_msg); } + else if (command == GAME_COMMAND_PLACE_PEEP_SPAWN) + { + char * args[1] = { + (char *) player_name + }; + + format_string(log_msg, 256, STR_LOG_PLACE_PEEP_SPAWN, args); + network_append_server_log(log_msg); + } } void pause_toggle() @@ -1744,4 +1753,5 @@ GAME_COMMAND_POINTER * new_game_command_table[GAME_COMMAND_COUNT] = { game_command_balloon_press, game_command_modify_tile, game_command_edit_scenario_options, + NULL, }; diff --git a/src/openrct2/Game.h b/src/openrct2/Game.h index 8ae71ad6dd..0e5a86de14 100644 --- a/src/openrct2/Game.h +++ b/src/openrct2/Game.h @@ -93,6 +93,7 @@ enum GAME_COMMAND GAME_COMMAND_BALLOON_PRESS, GAME_COMMAND_MODIFY_TILE, GAME_COMMAND_EDIT_SCENARIO_OPTIONS, + GAME_COMMAND_PLACE_PEEP_SPAWN, // GA, TODO: refactor to separate array for just game actions GAME_COMMAND_COUNT }; diff --git a/src/openrct2/actions/GameActionCompat.cpp b/src/openrct2/actions/GameActionCompat.cpp index 6b51871629..65216744ee 100644 --- a/src/openrct2/actions/GameActionCompat.cpp +++ b/src/openrct2/actions/GameActionCompat.cpp @@ -23,6 +23,7 @@ #include "RideSetStatus.hpp" #include "RideSetName.hpp" #include "RideDemolishAction.hpp" +#include "PlacePeepSpawnAction.hpp" #pragma region PlaceParkEntranceAction money32 place_park_entrance(sint16 x, sint16 y, sint16 z, uint8 direction) @@ -232,5 +233,20 @@ { Guard::Assert(false, "GAME_COMMAND_SET_STAFF_NAME DEPRECATED"); } - +#pragma endregion + +#pragma region PlacePeepSpawn + bool place_peep_spawn(CoordsXYZD location) + { + auto gameAction = PlacePeepSpawnAction(location); + auto result = GameActions::Execute(&gameAction); + if (result->Error == GA_ERROR::OK) + { + return true; + } + else + { + return false; + } + } #pragma endregion diff --git a/src/openrct2/actions/GameActionRegistration.cpp b/src/openrct2/actions/GameActionRegistration.cpp index 3965b1ed8c..4ffb238884 100644 --- a/src/openrct2/actions/GameActionRegistration.cpp +++ b/src/openrct2/actions/GameActionRegistration.cpp @@ -23,6 +23,7 @@ #include "RideSetStatus.hpp" #include "RideSetName.hpp" #include "RideDemolishAction.hpp" +#include "PlacePeepSpawnAction.hpp" namespace GameActions { @@ -36,5 +37,6 @@ namespace GameActions Register(); Register(); Register(); + Register(); } } diff --git a/src/openrct2/actions/PlacePeepSpawnAction.hpp b/src/openrct2/actions/PlacePeepSpawnAction.hpp new file mode 100644 index 0000000000..7fa9b793bd --- /dev/null +++ b/src/openrct2/actions/PlacePeepSpawnAction.hpp @@ -0,0 +1,141 @@ +#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#pragma once + +#include "../core/MemoryStream.h" +#include "../localisation/StringIds.h" +#include "../OpenRCT2.h" +#include "GameAction.h" + +#include "../Cheats.h" +#include "../world/Park.h" +#include "../world/Footpath.h" + +static sint32 _nextPeepSpawnIndex = 0; + +struct PlacePeepSpawnAction : public GameActionBase +{ +private: + CoordsXYZD _location; + +public: + PlacePeepSpawnAction() {} + PlacePeepSpawnAction(CoordsXYZD location) : + _location(location) + { + } + + uint16 GetActionFlags() const override + { + return GameActionBase::GetActionFlags() | GA_FLAGS::EDITOR_ONLY | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser& stream) override + { + GameAction::Serialise(stream); + + stream << _location.x << _location.y << _location.z << _location.direction; + } + + GameActionResult::Ptr Query() const override + { + if (!(gScreenFlags & SCREEN_FLAGS_EDITOR) && !gCheatsSandboxMode) + { + return std::make_unique(GA_ERROR::NOT_IN_EDITOR_MODE, STR_ERR_CANT_PLACE_PEEP_SPAWN_HERE, STR_NONE); + } + + gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE; + + gCommandPosition.x = _location.x; + gCommandPosition.y = _location.y; + gCommandPosition.z = _location.z * 2; + + if (!map_check_free_elements_and_reorganise(3)) + { + return std::make_unique(GA_ERROR::NO_FREE_ELEMENTS, STR_ERR_CANT_PLACE_PEEP_SPAWN_HERE, STR_NONE); + } + + if (_location.x <= 16 || _location.y <= 16 || _location.x >= (gMapSizeUnits - 16) || _location.y >= (gMapSizeUnits - 16)) + { + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_ERR_CANT_PLACE_PEEP_SPAWN_HERE, STR_OFF_EDGE_OF_MAP); + } + + rct_tile_element *mapElement, *surfaceMapElement; + // Verify footpath exists at location, and retrieve coordinates + mapElement = map_get_path_element_at(_location.x >> 5, _location.y >> 5, _location.z * 2); + if (mapElement == NULL) { + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_ERR_CANT_PLACE_PEEP_SPAWN_HERE, STR_CAN_ONLY_BE_BUILT_ACROSS_PATHS); + } + + // Verify location is unowned + surfaceMapElement = map_get_surface_element_at(_location.x >> 5, _location.y >> 5); + if (surfaceMapElement == NULL) { + return std::make_unique(GA_ERROR::UNKNOWN, STR_ERR_CANT_PLACE_PEEP_SPAWN_HERE, STR_NONE); + } + if (surfaceMapElement->properties.surface.ownership & 0xF0) { + return std::make_unique(GA_ERROR::INVALID_PARAMETERS, STR_ERR_CANT_PLACE_PEEP_SPAWN_HERE, STR_ERR_MUST_BE_OUTSIDE_PARK_BOUNDARIES); + } + + return std::make_unique(); + } + + GameActionResult::Ptr Execute() const override + { + gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE; + + gCommandPosition.x = _location.x; + gCommandPosition.y = _location.y; + gCommandPosition.z = _location.z * 2; + + // Find empty or next appropriate peep spawn to use + sint32 peepSpawnIndex = -1; + for (sint32 i = 0; i < MAX_PEEP_SPAWNS; i++) { + if (gPeepSpawns[i].x == PEEP_SPAWN_UNDEFINED) { + peepSpawnIndex = i; + break; + } + } + + // If no empty peep spawns exist, use peep spawn next to last one set. + if (peepSpawnIndex == -1) { + peepSpawnIndex = _nextPeepSpawnIndex; + _nextPeepSpawnIndex = (peepSpawnIndex + 1) % MAX_PEEP_SPAWNS; + + // Before the new location is set, clear the old location + sint32 prevX = gPeepSpawns[peepSpawnIndex].x; + gPeepSpawns[peepSpawnIndex].x = PEEP_SPAWN_UNDEFINED; + + map_invalidate_tile_full(prevX, gPeepSpawns[peepSpawnIndex].y); + } + + // Shift the spawn point to the middle of the tile + sint32 middleX, middleY; + middleX = _location.x + 16 + (word_981D6C[_location.direction].x * 15); + middleY = _location.y + 16 + (word_981D6C[_location.direction].y * 15); + + // Set peep spawn + gPeepSpawns[peepSpawnIndex].x = middleX; + gPeepSpawns[peepSpawnIndex].y = middleY; + gPeepSpawns[peepSpawnIndex].z = _location.z; + gPeepSpawns[peepSpawnIndex].direction = _location.direction; + + // Invalidate tile + map_invalidate_tile_full(_location.x, _location.y); + + return std::make_unique(); + } +}; diff --git a/src/openrct2/localisation/StringIds.h b/src/openrct2/localisation/StringIds.h index 96d1d317ed..1f59645f6e 100644 --- a/src/openrct2/localisation/StringIds.h +++ b/src/openrct2/localisation/StringIds.h @@ -3881,6 +3881,10 @@ enum { STR_TILE_INSPECTOR_ENTRANCE_MAKE_USABLE = 6220, STR_TILE_INSPECTOR_ENTRANCE_MAKE_USABLE_TIP = 6221, + STR_ERR_CANT_PLACE_PEEP_SPAWN_HERE = 6222, + STR_ERR_MUST_BE_OUTSIDE_PARK_BOUNDARIES = 6223, + STR_LOG_PLACE_PEEP_SPAWN = 6224, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working STR_COUNT = 32768 }; diff --git a/src/openrct2/network/NetworkAction.cpp b/src/openrct2/network/NetworkAction.cpp index 89806edd0f..b18574bbea 100644 --- a/src/openrct2/network/NetworkAction.cpp +++ b/src/openrct2/network/NetworkAction.cpp @@ -176,7 +176,8 @@ const std::vector NetworkActions::Actions = GAME_COMMAND_SET_LAND_OWNERSHIP, GAME_COMMAND_BUY_LAND_RIGHTS, GAME_COMMAND_PLACE_PARK_ENTRANCE, - GAME_COMMAND_REMOVE_PARK_ENTRANCE + GAME_COMMAND_REMOVE_PARK_ENTRANCE, + GAME_COMMAND_PLACE_PEEP_SPAWN, } }, { STR_ACTION_PARK_FUNDING, "PERMISSION_PARK_FUNDING", diff --git a/src/openrct2/network/network.h b/src/openrct2/network/network.h index 49cbc35eab..d4df6c2726 100644 --- a/src/openrct2/network/network.h +++ b/src/openrct2/network/network.h @@ -50,7 +50,7 @@ struct GameAction; // This define specifies which version of network stream current build uses. // It is used for making sure only compatible builds get connected, even within // single OpenRCT2 version. -#define NETWORK_STREAM_VERSION "30" +#define NETWORK_STREAM_VERSION "31" #define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION #include diff --git a/src/openrct2/world/Map.h b/src/openrct2/world/Map.h index 08c32c9b3f..69f7395551 100644 --- a/src/openrct2/world/Map.h +++ b/src/openrct2/world/Map.h @@ -588,4 +588,6 @@ uint8 tile_element_get_ride_index(const rct_tile_element * tileElement); void FixLandOwnershipTiles(std::initializer_list tiles); void FixLandOwnershipTilesWithOwnership(std::initializer_list tiles, uint8 ownership); +bool place_peep_spawn(CoordsXYZD location); + #endif