diff --git a/docs/release/dev/sp-somefunctions.md b/docs/release/dev/sp-somefunctions.md new file mode 100644 index 0000000000..54ed0c475a --- /dev/null +++ b/docs/release/dev/sp-somefunctions.md @@ -0,0 +1 @@ +Added some useful functions: ActorSit/GetUp, Raycast, CalculateAnticipatedLocation. diff --git a/skyrim-platform/src/platform_se/codegen/convert-files/Definitions.txt b/skyrim-platform/src/platform_se/codegen/convert-files/Definitions.txt index 2ecb50d579..e32338b15f 100644 --- a/skyrim-platform/src/platform_se/codegen/convert-files/Definitions.txt +++ b/skyrim-platform/src/platform_se/codegen/convert-files/Definitions.txt @@ -1586,3 +1586,14 @@ export interface Inventory { } export declare function setInventory(formId: number, inventory: Inventory): void; + +export interface RayCastResult +{ + pos: number[]; + normal: number[]; +}; + +export declare function actorSit(formId: number): void; +export declare function actorGetUp(actorId: number): void; +export declare function actorRaycast(actorId: number, r: number): RayCastResult; +export declare function calculateAnticipatedLocation(actorId: number): number[]; diff --git a/skyrim-platform/src/platform_se/codegen/convert-files/skyrimPlatform.ts b/skyrim-platform/src/platform_se/codegen/convert-files/skyrimPlatform.ts index dde7cb3ae7..78f4a7ec1e 100644 --- a/skyrim-platform/src/platform_se/codegen/convert-files/skyrimPlatform.ts +++ b/skyrim-platform/src/platform_se/codegen/convert-files/skyrimPlatform.ts @@ -1592,6 +1592,17 @@ export interface Inventory { export declare function setInventory(formId: number, inventory: Inventory): void; +export interface RayCastResult +{ + pos: number[]; + normal: number[]; +}; + +export declare function actorSit(formId: number): void; +export declare function actorGetUp(actorId: number): void; +export declare function actorRaycast(actorId: number, r: number): RayCastResult; +export declare function calculateAnticipatedLocation(actorId: number): number[]; + // Based on Form.pex export declare class Form extends PapyrusObject { static from(papyrusObject: PapyrusObject | null): Form | null diff --git a/skyrim-platform/src/platform_se/skyrim_platform/FenixFunctions.cpp b/skyrim-platform/src/platform_se/skyrim_platform/FenixFunctions.cpp new file mode 100644 index 0000000000..8bbaa1898f --- /dev/null +++ b/skyrim-platform/src/platform_se/skyrim_platform/FenixFunctions.cpp @@ -0,0 +1,160 @@ +#include "NullPointerException.h" + +namespace FenixFunctions { + +namespace Impl { + +void Sit(RE::Actor* a) +{ + a->actorState1.sitSleepState = RE::SIT_SLEEP_STATE::kIsSitting; +} + +void GetUp(RE::Actor* a) +{ + a->actorState1.sitSleepState = RE::SIT_SLEEP_STATE::kNormal; +} + +enum class LineOfSightLocation : uint32_t +{ + kNone, + kEyes, + kHead, + kTorso, + kFeet +}; + +RE::NiPoint3 CalculateLOSLocation(RE::TESObjectREFR* refr, + LineOfSightLocation los_loc) +{ + using func_t = decltype(CalculateLOSLocation); + REL::Relocation func{ RELOCATION_ID(46021, 44877) }; + return func(refr, los_loc); +} + +RE::NiPoint3 ConvertAnglesToDir(const RE::NiPoint3& angles) +{ + RE::NiPoint3 ans; + + float sinx = sinf(angles.x); + float cosx = cosf(angles.x); + float sinz = sinf(angles.z); + float cosz = cosf(angles.z); + + ans.x = cosx * sinz; + ans.y = cosx * cosz; + ans.z = -sinx; + + return ans; +} + +RE::NiPoint3 Rotate(float r, const RE::NiPoint3& angles) +{ + return ConvertAnglesToDir(angles) * r; +} + +std::pair RaycastActor(RE::Actor* caster, float R) +{ + auto havokWorldScale = RE::bhkWorld::GetWorldScale(); + RE::bhkPickData pickData; + RE::NiPoint3 rayStart, rayEnd; + + rayStart = CalculateLOSLocation(caster, LineOfSightLocation::kHead); + rayEnd = rayStart + Rotate(R, caster->data.angle); + pickData.rayInput.from = rayStart * havokWorldScale; + pickData.rayInput.to = rayEnd * havokWorldScale; + + uint32_t collisionFilterInfo = 0; + caster->GetCollisionFilterInfo(collisionFilterInfo); + pickData.rayInput.filterInfo = + (static_cast(collisionFilterInfo >> 16) << 16) | + static_cast(RE::COL_LAYER::kCharController); + + caster->GetParentCell()->GetbhkWorld()->PickObject(pickData); + RE::NiPoint3 hitPos; + if (pickData.rayOutput.HasHit()) { + hitPos = rayStart + (rayEnd - rayStart) * pickData.rayOutput.hitFraction; + // pick_data.rayOutput.normal; + RE::NiPoint3 normal = { pickData.rayOutput.normal.quad.m128_f32[0], + pickData.rayOutput.normal.quad.m128_f32[1], + pickData.rayOutput.normal.quad.m128_f32[2] }; + return { hitPos, normal }; + } else { + return { rayEnd, {} }; + } +} + +void CalculateAnticipatedLocation(RE::TESObjectREFR* refr, float dtime, + RE::NiPoint3& ans) +{ + using func_t = decltype(CalculateAnticipatedLocation); + REL::Relocation func{ RELOCATION_ID(46045, 47309) }; + return func(refr, dtime, ans); +} + +} + +RE::Actor* GetArgActor(const JsValue& arg) +{ + auto formId = static_cast(static_cast(arg)); + auto a = RE::TESForm::LookupByID(formId); + + if (!a) { + throw NullPointerException("pActor"); + } + + return a; +} + +JsValue ConvertPointToJS(const RE::NiPoint3& P) +{ + std::vector p = { P.x, P.y, P.z }; + return p; +} + +JsValue ActorSit(const JsFunctionArguments& args) +{ + auto a = GetArgActor(args[1]); + Impl::Sit(a); + return JsValue::Undefined(); +} + +JsValue ActorGetUp(const JsFunctionArguments& args) +{ + auto a = GetArgActor(args[1]); + Impl::GetUp(a); + return JsValue::Undefined(); +} + +JsValue ActorRaycast(const JsFunctionArguments& args) +{ + auto a = GetArgActor(args[1]); + float R = static_cast(static_cast(args[2])); + auto [P, N] = Impl::RaycastActor(a, R); + + auto ans = JsValue::Object(); + ans.SetProperty("pos", ConvertPointToJS(P)); + ans.SetProperty("normal", ConvertPointToJS(N)); + return ans; +} + +JsValue CalculateAnticipatedLocation(const JsFunctionArguments& args) +{ + auto a = GetArgActor(args[1]); + float dTime = static_cast(static_cast(args[2])); + + RE::NiPoint3 ans; + Impl::CalculateAnticipatedLocation(a, dTime, ans); + + return ConvertPointToJS(ans); +} + +void Register(JsValue& exports) +{ + exports.SetProperty("actorSit", JsValue::Function(ActorSit)); + exports.SetProperty("actorGetUp", JsValue::Function(ActorGetUp)); + exports.SetProperty("actorRaycast", JsValue::Function(ActorRaycast)); + exports.SetProperty("calculateAnticipatedLocation", + JsValue::Function(CalculateAnticipatedLocation)); +} + +} diff --git a/skyrim-platform/src/platform_se/skyrim_platform/FenixFunctions.h b/skyrim-platform/src/platform_se/skyrim_platform/FenixFunctions.h new file mode 100644 index 0000000000..e19fb5606c --- /dev/null +++ b/skyrim-platform/src/platform_se/skyrim_platform/FenixFunctions.h @@ -0,0 +1,8 @@ +#pragma once + +namespace FenixFunctions { +JsValue ActorSit(const JsFunctionArguments& args); +JsValue ActorGetUp(const JsFunctionArguments& args); + +void Register(JsValue& exports); +} diff --git a/skyrim-platform/src/platform_se/skyrim_platform/SkyrimPlatform.cpp b/skyrim-platform/src/platform_se/skyrim_platform/SkyrimPlatform.cpp index 56bc68c8ee..6d4fdd9fb3 100644 --- a/skyrim-platform/src/platform_se/skyrim_platform/SkyrimPlatform.cpp +++ b/skyrim-platform/src/platform_se/skyrim_platform/SkyrimPlatform.cpp @@ -14,6 +14,7 @@ #include "HttpClient.h" #include "HttpClientApi.h" #include "InventoryApi.h" +#include "FenixFunctions.h" #include "LoadGameApi.h" #include "MpClientPluginApi.h" #include "SkyrimPlatformProxy.h" @@ -230,6 +231,7 @@ class CommonExecutionListener : public TickListener FileInfoApi::Register(e); TextApi::Register(e); InventoryApi::Register(e); + FenixFunctions::Register(e); ConstEnumApi::Register(e, engine); CallNativeApi::Register( e, [this] { return nativeCallRequirements; });