From 94750f4e34d69c5eb69206d6201f2bdce940d3e7 Mon Sep 17 00:00:00 2001 From: spacek531 Date: Wed, 29 May 2024 03:32:41 -0700 Subject: [PATCH] Add plugin API to VehicleCrashedParticle --- distribution/changelog.txt | 1 + distribution/openrct2.d.ts | 62 +++++ src/openrct2/entity/Particle.cpp | 44 ++-- src/openrct2/entity/Particle.h | 7 + src/openrct2/libopenrct2.vcxproj | 4 +- src/openrct2/scripting/ScriptEngine.cpp | 2 + src/openrct2/scripting/ScriptEngine.h | 2 +- .../scripting/bindings/entity/ScParticle.cpp | 225 ++++++++++++++++++ .../scripting/bindings/entity/ScParticle.hpp | 54 +++++ .../scripting/bindings/world/ScMap.cpp | 19 +- 10 files changed, 400 insertions(+), 20 deletions(-) create mode 100644 src/openrct2/scripting/bindings/entity/ScParticle.cpp create mode 100644 src/openrct2/scripting/bindings/entity/ScParticle.hpp diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 0b3dc7332f..85dfc09bef 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -6,6 +6,7 @@ - Feature: [#21853] Enlarged UI mode. - Feature: [#21893, #22065] On launch, the game now indicates what system is being initialised. - Feature: [#21913] [Plugin] Allow precise and safe control of peep animations. +- Feature: [#22046] [Plugin] Add interface for crashed vehicle particle. - Feature: [#22087] [Plugin] Expose guests’ favourite rides to the plugin API. - Improved: [#19870] Allow using new colours in UI themes. - Improved: [#21853] Dropdowns now automatically use multiple columns if they are too tall for the screen. diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index 5ce6d9b9a3..9f55127706 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -2123,6 +2123,11 @@ declare global { supports: number; } + interface CrashedVehicleColour { + body: number; + trim: number; + } + interface VehicleColour { body: number; trim: number; @@ -2608,6 +2613,63 @@ declare global { "waiting_to_depart" | "waiting_to_start"; + type CrashParticleType = "corner" | "rod" | "wheel" | "panel" | "seat"; + + /** + * Override properties for launch data. All properties except colour are randomly + * chosen if not overridden, using the same algorithm as regular crashed particles. + */ + interface CrashParticleLaunchData { + colours?: CrashedVehicleColour; + timeToLive?: number; + velocity?: CoordsXYZ; + crashParticleType?: CrashParticleType; + frame?: number; + } + + /** + * Represents a vehicle explosion particle. They are emitted during a vehicle + * crash and will bounce until their timer expires and they are automatically + * removed. + */ + interface CrashedVehicleParticle extends Entity { + /** + * The colour of the particle. + */ + colours: CrashedVehicleColour; + + /** + * The lifetime of the particle in ticks. Default value 65535. Entity is + * automatically removed at 0. + */ + timeToLive: number; + + /** + * The particle velocity. + */ + velocity: CoordsXYZ; + + /** + * The acceleration of the particle in the x, y, and z directions. + */ + acceleration: CoordsXYZ; + + /** + * The type of crash particle. + */ + crashParticleType: CrashParticleType; + + /** + * The current frame of the crash particle. + */ + frame: number; + + /** + * Sets the sprite bounds and launches the particle. + */ + launch(launchData?: CrashParticleLaunchData): void; + } + /** * Represents a guest or staff member. * @deprecated since version 34, use guest or staff instead. diff --git a/src/openrct2/entity/Particle.cpp b/src/openrct2/entity/Particle.cpp index 7303f4e0c9..d83a05acff 100644 --- a/src/openrct2/entity/Particle.cpp +++ b/src/openrct2/entity/Particle.cpp @@ -19,7 +19,7 @@ #include // TODO: Create constants in sprites.h -static constexpr uint32_t _VehicleCrashParticleSprites[] = { +static constexpr uint32_t _VehicleCrashParticleSprites[kCrashedVehicleParticleNumberTypes] = { 22577, 22589, 22601, 22613, 22625, }; @@ -47,6 +47,27 @@ template<> bool EntityBase::Is() const { return Type == EntityType::CrashSplash; } + +void VehicleCrashParticle::SetSpriteData() +{ + SpriteData.Width = 8; + SpriteData.HeightMin = 8; + SpriteData.HeightMax = 8; +} + +void VehicleCrashParticle::Launch() +{ + frame = (ScenarioRand() & 0xFF) * kCrashedVehicleParticleNumberSprites; + time_to_live = (ScenarioRand() & 0x7F) + 140; + crashed_sprite_base = ScenarioRandMax(kCrashedVehicleParticleNumberTypes); + acceleration_x = (static_cast(ScenarioRand() & 0xFFFF)) * 4; + acceleration_y = (static_cast(ScenarioRand() & 0xFFFF)) * 4; + acceleration_z = (ScenarioRand() & 0xFFFF) * 4 + 0x10000; + velocity_x = 0; + velocity_y = 0; + velocity_z = 0; +} + /** * * rct2: 0x006735A1 @@ -56,22 +77,11 @@ void VehicleCrashParticle::Create(VehicleColour& colours, const CoordsXYZ& vehic VehicleCrashParticle* sprite = CreateEntity(); if (sprite != nullptr) { + sprite->MoveTo(vehiclePos); sprite->colour[0] = colours.Body; sprite->colour[1] = colours.Trim; - sprite->SpriteData.Width = 8; - sprite->SpriteData.HeightMin = 8; - sprite->SpriteData.HeightMax = 8; - sprite->MoveTo(vehiclePos); - - sprite->frame = (ScenarioRand() & 0xFF) * 12; - sprite->time_to_live = (ScenarioRand() & 0x7F) + 140; - sprite->crashed_sprite_base = ScenarioRandMax(static_cast(std::size(_VehicleCrashParticleSprites))); - sprite->acceleration_x = (static_cast(ScenarioRand() & 0xFFFF)) * 4; - sprite->acceleration_y = (static_cast(ScenarioRand() & 0xFFFF)) * 4; - sprite->acceleration_z = (ScenarioRand() & 0xFFFF) * 4 + 0x10000; - sprite->velocity_x = 0; - sprite->velocity_y = 0; - sprite->velocity_z = 0; + sprite->SetSpriteData(); + sprite->Launch(); } } @@ -129,8 +139,8 @@ void VehicleCrashParticle::Update() } MoveTo(newLoc); - frame += 85; - if (frame >= 3072) + frame += kCrashedVehicleParticleFrameIncrement; + if (frame >= (kCrashedVehicleParticleNumberSprites * kCrashedVehicleParticleFrameToSprite)) { frame = 0; } diff --git a/src/openrct2/entity/Particle.h b/src/openrct2/entity/Particle.h index 62b7340f18..6510831fb7 100644 --- a/src/openrct2/entity/Particle.h +++ b/src/openrct2/entity/Particle.h @@ -16,6 +16,11 @@ struct CoordsXYZ; struct PaintSession; struct VehicleColour; +constexpr int32_t kCrashedVehicleParticleFrameToSprite = 256; +constexpr int32_t kCrashedVehicleParticleNumberSprites = 12; +constexpr int32_t kCrashedVehicleParticleNumberTypes = 5; +constexpr int32_t kCrashedVehicleParticleFrameIncrement = 85; // 1/3 of 256, rounded up + struct VehicleCrashParticle : EntityBase { static constexpr auto cEntityType = EntityType::CrashedVehicleParticle; @@ -30,6 +35,8 @@ struct VehicleCrashParticle : EntityBase int32_t acceleration_y; int32_t acceleration_z; static void Create(VehicleColour& colours, const CoordsXYZ& vehiclePos); + void SetSpriteData(); + void Launch(); void Update(); void Serialise(DataSerialiser& stream); void Paint(PaintSession& session, int32_t imageDirection) const; diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj index 9bfa837ac4..493f08387d 100644 --- a/src/openrct2/libopenrct2.vcxproj +++ b/src/openrct2/libopenrct2.vcxproj @@ -541,6 +541,7 @@ + @@ -1032,6 +1033,7 @@ + @@ -1086,4 +1088,4 @@ - + \ No newline at end of file diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp index d7b1800e46..919c7157f0 100644 --- a/src/openrct2/scripting/ScriptEngine.cpp +++ b/src/openrct2/scripting/ScriptEngine.cpp @@ -27,6 +27,7 @@ # include "bindings/entity/ScEntity.hpp" # include "bindings/entity/ScGuest.hpp" # include "bindings/entity/ScLitter.hpp" +# include "bindings/entity/ScParticle.hpp" # include "bindings/entity/ScPeep.hpp" # include "bindings/entity/ScStaff.hpp" # include "bindings/entity/ScVehicle.hpp" @@ -433,6 +434,7 @@ void ScriptEngine::Initialise() ScEntity::Register(ctx); ScLitter::Register(ctx); ScVehicle::Register(ctx); + ScCrashedVehicleParticle::Register(ctx); ScPeep::Register(ctx); ScGuest::Register(ctx); ScThought::Register(ctx); diff --git a/src/openrct2/scripting/ScriptEngine.h b/src/openrct2/scripting/ScriptEngine.h index 78be06b6f9..caea02ad5e 100644 --- a/src/openrct2/scripting/ScriptEngine.h +++ b/src/openrct2/scripting/ScriptEngine.h @@ -47,7 +47,7 @@ namespace OpenRCT2 namespace OpenRCT2::Scripting { - static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 90; + static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 91; // Versions marking breaking changes. static constexpr int32_t API_VERSION_33_PEEP_DEPRECATION = 33; diff --git a/src/openrct2/scripting/bindings/entity/ScParticle.cpp b/src/openrct2/scripting/bindings/entity/ScParticle.cpp new file mode 100644 index 0000000000..5e7e0cdc87 --- /dev/null +++ b/src/openrct2/scripting/bindings/entity/ScParticle.cpp @@ -0,0 +1,225 @@ +/***************************************************************************** + * Copyright (c) 2014-2024 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#include "ScParticle.hpp" + +#include "../ride/ScRide.hpp" + +#ifdef ENABLE_SCRIPTING + +namespace OpenRCT2::Scripting +{ + static const DukEnumMap CrashParticleTypeMap({ + { "corner", 0 }, + { "rod", 1 }, + { "wheel", 2 }, + { "panel", 3 }, + { "seat", 4 }, + }); + + ScCrashedVehicleParticle::ScCrashedVehicleParticle(EntityId id) + : ScEntity(id) + { + } + + void ScCrashedVehicleParticle::Register(duk_context* ctx) + { + dukglue_set_base_class(ctx); + dukglue_register_property( + ctx, &ScCrashedVehicleParticle::acceleration_get, &ScCrashedVehicleParticle::acceleration_set, "acceleration"); + dukglue_register_property( + ctx, &ScCrashedVehicleParticle::velocity_get, &ScCrashedVehicleParticle::velocity_set, "velocity"); + dukglue_register_property( + ctx, &ScCrashedVehicleParticle::colours_get, &ScCrashedVehicleParticle::colours_set, "colours"); + dukglue_register_property( + ctx, &ScCrashedVehicleParticle::timeToLive_get, &ScCrashedVehicleParticle::timeToLive_set, "timeToLive"); + dukglue_register_property( + ctx, &ScCrashedVehicleParticle::crashedSpriteBase_get, &ScCrashedVehicleParticle::crashedSpriteBase_set, + "crashParticleType"); + dukglue_register_property(ctx, &ScCrashedVehicleParticle::frame_get, &ScCrashedVehicleParticle::frame_set, "frame"); + dukglue_register_method(ctx, &ScCrashedVehicleParticle::Launch, "launch"); + } + + VehicleCrashParticle* ScCrashedVehicleParticle::GetCrashedVehicleParticle() const + { + return ::GetEntity(_id); + } + + void ScCrashedVehicleParticle::frame_set(uint8_t value) + { + auto entity = GetCrashedVehicleParticle(); + if (entity != nullptr) + { + entity->frame = std::clamp(value, 0, kCrashedVehicleParticleNumberSprites - 1) + * kCrashedVehicleParticleFrameToSprite; + } + } + uint8_t ScCrashedVehicleParticle::frame_get() const + { + auto entity = GetCrashedVehicleParticle(); + if (entity != nullptr) + { + return entity->frame / kCrashedVehicleParticleFrameToSprite; + } + return 0; + } + + void ScCrashedVehicleParticle::crashedSpriteBase_set(const std::string& value) + { + auto entity = GetCrashedVehicleParticle(); + if (entity != nullptr) + { + entity->crashed_sprite_base = CrashParticleTypeMap[value]; + } + } + std::string ScCrashedVehicleParticle::crashedSpriteBase_get() const + { + auto entity = GetCrashedVehicleParticle(); + if (entity != nullptr) + { + return std::string(CrashParticleTypeMap[entity->crashed_sprite_base]); + } + return {}; + } + + void ScCrashedVehicleParticle::timeToLive_set(uint16_t value) + { + auto entity = GetCrashedVehicleParticle(); + if (entity != nullptr) + { + entity->time_to_live = value; + } + } + uint16_t ScCrashedVehicleParticle::timeToLive_get() const + { + auto entity = GetCrashedVehicleParticle(); + if (entity != nullptr) + { + return entity->time_to_live; + } + return 0; + } + + void ScCrashedVehicleParticle::velocity_set(const DukValue& value) + { + auto entity = GetCrashedVehicleParticle(); + if (entity != nullptr) + { + auto velocity = FromDuk(value); + entity->velocity_x = velocity.x; + entity->velocity_y = velocity.y; + entity->velocity_z = velocity.z; + } + } + DukValue ScCrashedVehicleParticle::velocity_get() const + { + auto ctx = GetContext()->GetScriptEngine().GetContext(); + auto entity = GetCrashedVehicleParticle(); + if (entity != nullptr) + { + return ToDuk(ctx, CoordsXYZ(entity->velocity_x, entity->velocity_y, entity->velocity_z)); + } + return {}; + } + + void ScCrashedVehicleParticle::acceleration_set(const DukValue& value) + { + auto entity = GetCrashedVehicleParticle(); + if (entity != nullptr) + { + auto acceleration = FromDuk(value); + entity->acceleration_x = acceleration.x; + entity->acceleration_y = acceleration.y; + entity->acceleration_z = acceleration.z; + } + } + DukValue ScCrashedVehicleParticle::acceleration_get() const + { + auto ctx = GetContext()->GetScriptEngine().GetContext(); + auto entity = GetCrashedVehicleParticle(); + if (entity != nullptr) + { + return ToDuk(ctx, CoordsXYZ(entity->acceleration_x, entity->acceleration_y, entity->acceleration_z)); + } + return {}; + } + + void ScCrashedVehicleParticle::Launch(const DukValue& value) + { + auto entity = GetCrashedVehicleParticle(); + if (entity != nullptr) + { + entity->SetSpriteData(); + entity->Launch(); + + if (value.type() == DukValue::Type::UNDEFINED) + return; + + if (value["colours"].type() == DukValue::Type::OBJECT) + { + auto coloursInt = FromDuk(value["colours"]); + entity->colour[0] = coloursInt.Body; + entity->colour[1] = coloursInt.Trim; + } + if (value["acceleration"].type() == DukValue::Type::OBJECT) + { + auto accelerationXYZ = FromDuk(value["acceleration"]); + entity->acceleration_x = accelerationXYZ.x; + entity->acceleration_y = accelerationXYZ.y; + entity->acceleration_z = accelerationXYZ.z; + } + if (value["velocity"].type() == DukValue::Type::OBJECT) + { + auto velocityXYZ = FromDuk(value["velocity"]); + entity->velocity_x = velocityXYZ.x; + entity->velocity_y = velocityXYZ.y; + entity->velocity_z = velocityXYZ.z; + } + if (value["timeToLive"].type() == DukValue::Type::NUMBER) + { + entity->time_to_live = value["timeToLive"].as_int(); + } + if (value["frame"].type() == DukValue::Type::NUMBER) + { + entity->frame = std::clamp(value["frame"].as_int(), 0, kCrashedVehicleParticleNumberSprites - 1) + * kCrashedVehicleParticleFrameToSprite; + } + if (value["crashParticleType"].type() == DukValue::Type::STRING) + { + entity->crashed_sprite_base = CrashParticleTypeMap[value["crashParticleType"].as_string()]; + } + } + } + + DukValue ScCrashedVehicleParticle::colours_get() const + { + auto ctx = GetContext()->GetScriptEngine().GetContext(); + auto entity = GetCrashedVehicleParticle(); + if (entity != nullptr) + { + DukObject dukColour(ctx); + dukColour.Set("body", entity->colour[0]); + dukColour.Set("trim", entity->colour[1]); + return dukColour.Take(); + } + return ToDuk(ctx, nullptr); + } + void ScCrashedVehicleParticle::colours_set(const DukValue& value) + { + auto entity = GetCrashedVehicleParticle(); + if (entity != nullptr) + { + auto colours = FromDuk(value); + entity->colour[0] = colours.Body; + entity->colour[1] = colours.Trim; + } + } +}; // namespace OpenRCT2::Scripting + +#endif diff --git a/src/openrct2/scripting/bindings/entity/ScParticle.hpp b/src/openrct2/scripting/bindings/entity/ScParticle.hpp new file mode 100644 index 0000000000..1dbdec41dc --- /dev/null +++ b/src/openrct2/scripting/bindings/entity/ScParticle.hpp @@ -0,0 +1,54 @@ +/***************************************************************************** + * Copyright (c) 2014-2024 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#ifdef ENABLE_SCRIPTING + +# include "../../../entity/Particle.h" +# include "../../../world/Location.hpp" +# include "ScEntity.hpp" + +# include + +namespace OpenRCT2::Scripting +{ + class ScCrashedVehicleParticle : public ScEntity + { + public: + ScCrashedVehicleParticle(EntityId id); + + static void Register(duk_context* ctx); + + private: + VehicleCrashParticle* GetCrashedVehicleParticle() const; + + DukValue colours_get() const; + void colours_set(const DukValue& value); + + DukValue acceleration_get() const; + void acceleration_set(const DukValue& value); + + DukValue velocity_get() const; + void velocity_set(const DukValue& value); + + uint8_t frame_get() const; + void frame_set(uint8_t value); + + void crashedSpriteBase_set(const std::string& value); + std::string crashedSpriteBase_get() const; + + void timeToLive_set(uint16_t value); + uint16_t timeToLive_get() const; + + void Launch(const DukValue& value); + }; +}; // namespace OpenRCT2::Scripting + +#endif diff --git a/src/openrct2/scripting/bindings/world/ScMap.cpp b/src/openrct2/scripting/bindings/world/ScMap.cpp index df3b8560d6..bd14c9bf81 100644 --- a/src/openrct2/scripting/bindings/world/ScMap.cpp +++ b/src/openrct2/scripting/bindings/world/ScMap.cpp @@ -29,6 +29,7 @@ # include "../entity/ScEntity.hpp" # include "../entity/ScGuest.hpp" # include "../entity/ScLitter.hpp" +# include "../entity/ScParticle.hpp" # include "../entity/ScStaff.hpp" # include "../entity/ScVehicle.hpp" # include "../ride/ScRide.hpp" @@ -161,6 +162,13 @@ namespace OpenRCT2::Scripting result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); } } + else if (type == "crashed_vehicle_particle") + { + for (auto sprite : EntityList()) + { + result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + } + } else { duk_error(_context, DUK_ERR_ERROR, "Invalid entity type."); @@ -221,6 +229,13 @@ namespace OpenRCT2::Scripting result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); } } + else if (type == "crashed_vehicle_particle") + { + for (auto sprite : EntityTileList(pos)) + { + result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + } + } else { // If the given type isn't valid, throw an error @@ -272,7 +287,7 @@ namespace OpenRCT2::Scripting } else if (type == "crashed_vehicle_particle") { - res = createEntityType(_context, initializer); + res = createEntityType(_context, initializer); } else if (type == "explosion_cloud") { @@ -348,6 +363,8 @@ namespace OpenRCT2::Scripting return GetObjectAsDukValue(_context, std::make_shared(spriteId)); case EntityType::Litter: return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + case EntityType::CrashedVehicleParticle: + return GetObjectAsDukValue(_context, std::make_shared(spriteId)); default: return GetObjectAsDukValue(_context, std::make_shared(spriteId)); }