268 lines
7.5 KiB
C++
268 lines
7.5 KiB
C++
#include "PapyrusSpawnerTask.h"
|
|
|
|
#include "PapyrusVM.h"
|
|
#include "PapyrusNativeFunctions.h"
|
|
|
|
#include "GameForms.h"
|
|
#include "GameReferences.h"
|
|
#include "NiTypes.h"
|
|
|
|
#include "PapyrusObjects.h"
|
|
#include "PapyrusDelayFunctors.h"
|
|
#include "Serialization.h"
|
|
|
|
#include <vector>
|
|
|
|
|
|
namespace
|
|
{
|
|
///
|
|
/// SpawnerTaskFunctor
|
|
///
|
|
|
|
class SpawnerTaskFunctor : public LatentSKSEDelayFunctor
|
|
{
|
|
struct SpawnData;
|
|
|
|
public:
|
|
explicit SpawnerTaskFunctor(UInt32 stackId) :
|
|
LatentSKSEDelayFunctor( stackId )
|
|
{}
|
|
|
|
explicit SpawnerTaskFunctor(SerializationTag tag) :
|
|
LatentSKSEDelayFunctor( tag )
|
|
{}
|
|
|
|
virtual const char* ClassName() const { return "SpawnerTaskFunctor"; }
|
|
virtual UInt32 ClassVersion() const { return 1; }
|
|
|
|
virtual bool Save(SKSESerializationInterface* intfc);
|
|
virtual bool Load(SKSESerializationInterface* intfc, UInt32 version);
|
|
|
|
virtual void Run(VMValue& resultValue)
|
|
{
|
|
VMClassRegistry* registry = (*g_skyrimVM)->GetClassRegistry();
|
|
|
|
UInt32 nullHandle = *g_invalidRefHandle;
|
|
|
|
VMResultArray<TESObjectREFR*> allSpawned;
|
|
|
|
for (size_t i=0; i<data_.size(); i++)
|
|
{
|
|
SpawnData& e = data_[i];
|
|
|
|
// Lookup form
|
|
TESForm* spawnForm = LookupFormByID(e.spawnFormId);
|
|
if (spawnForm == NULL)
|
|
continue;
|
|
|
|
// Lookup target
|
|
NiPointer<TESObjectREFR> target;
|
|
LookupREFRByHandle(e.targetRefrHandle, target);
|
|
if (target == NULL)
|
|
continue;
|
|
|
|
// Spawn
|
|
TESObjectREFR* spawned = PlaceAtMe_Native(registry, this->stackId_, target, spawnForm, e.count, e.bForcePersist, e.bInitiallyDisabled);
|
|
if (spawned == NULL)
|
|
continue;
|
|
|
|
// Position
|
|
TESObjectCELL* parentCell = spawned->parentCell;
|
|
TESWorldSpace* worldspace = CALL_MEMBER_FN(spawned,GetWorldspace)();
|
|
|
|
NiPoint3 finalPos( e.pos );
|
|
finalPos += target->pos;
|
|
|
|
MoveRefrToPosition(spawned, &nullHandle, parentCell, worldspace, &finalPos, &e.rot);
|
|
|
|
// Save spawned for result
|
|
allSpawned.push_back(spawned);
|
|
}
|
|
|
|
PackValue(&resultValue, &allSpawned, registry);
|
|
}
|
|
|
|
void AddSpawn(TESForm* form, TESObjectREFR* target, const NiPoint3& pos, const NiPoint3& rot,
|
|
UInt32 count, bool bForcePersist, bool bInitiallyDisabled)
|
|
{
|
|
UInt32 formId = form->formID;
|
|
UInt32 targetRefrHandle = GetOrCreateRefrHandle(target);
|
|
|
|
if (targetRefrHandle == *g_invalidRefHandle)
|
|
return;
|
|
|
|
SpawnData t = { formId, targetRefrHandle, pos, rot, count, bForcePersist, bInitiallyDisabled };
|
|
data_.push_back(t);
|
|
}
|
|
|
|
private:
|
|
struct SpawnData
|
|
{
|
|
UInt32 spawnFormId;
|
|
UInt32 targetRefrHandle;
|
|
NiPoint3 pos;
|
|
NiPoint3 rot;
|
|
UInt32 count;
|
|
bool bForcePersist;
|
|
bool bInitiallyDisabled;
|
|
};
|
|
|
|
std::vector<SpawnData> data_;
|
|
};
|
|
|
|
bool SpawnerTaskFunctor::Save(SKSESerializationInterface* intfc)
|
|
{
|
|
using namespace Serialization;
|
|
|
|
if (! LatentSKSEDelayFunctor::Save(intfc))
|
|
return false;
|
|
|
|
UInt32 dataSize = data_.size();
|
|
|
|
if (! WriteData(intfc, &dataSize))
|
|
return false;
|
|
|
|
for (UInt32 i=0; i<dataSize; i++)
|
|
if (! WriteData(intfc, &data_[i]))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SpawnerTaskFunctor::Load(SKSESerializationInterface* intfc, UInt32 version)
|
|
{
|
|
using namespace Serialization;
|
|
|
|
if (! LatentSKSEDelayFunctor::Load(intfc, version))
|
|
return false;
|
|
|
|
UInt32 dataSize;;
|
|
|
|
if (! ReadData(intfc, &dataSize))
|
|
return false;
|
|
|
|
// Note: Reserve, not resize.
|
|
// If formIds fail to resolve, we may skip entries.
|
|
data_.reserve(dataSize);
|
|
|
|
for (UInt32 i=0; i<dataSize; i++)
|
|
{
|
|
SpawnData e;
|
|
|
|
if (! ReadData(intfc, &e))
|
|
return false;
|
|
|
|
// Fixup formid of spawn. Might change if load order was changed.
|
|
UInt32 fixedFormId;
|
|
if (! intfc->ResolveFormId(e.spawnFormId, &fixedFormId))
|
|
{
|
|
_MESSAGE("SpawnerTaskFunctor::Load: FormID of spawn %i could not be resolved, skipping.");
|
|
continue;
|
|
}
|
|
|
|
e.spawnFormId = fixedFormId;
|
|
|
|
data_.push_back(e);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
///
|
|
/// Native functions
|
|
///
|
|
|
|
namespace papyrusSpawnerTask
|
|
{
|
|
SInt32 Create(VMClassRegistry* registry, UInt32 stackId, StaticFunctionTag*)
|
|
{
|
|
SpawnerTaskFunctor* func = new SpawnerTaskFunctor(stackId);
|
|
|
|
UInt32 handle = SKSEObjectStorageInstance().StoreObject(func, stackId);
|
|
return handle;
|
|
}
|
|
|
|
void AddSpawn(VMClassRegistry* registry, UInt32 stackId, StaticFunctionTag*, SInt32 handle, TESForm* form, TESObjectREFR* target,
|
|
VMArray<float> position, VMArray<float> rotation, SInt32 count, bool bForcePersist, bool bInitiallyDisabled)
|
|
{
|
|
ERROR_AND_RETURN_IF(handle <= 0, "Invalid handle.", registry, stackId)
|
|
ERROR_AND_RETURN_IF(form == NULL, "FormToPlace parameter must not be none.", registry, stackId)
|
|
ERROR_AND_RETURN_IF(target == NULL, "Target parameter must not be none.", registry, stackId)
|
|
ERROR_AND_RETURN_IF(count <= 0, "Count parameter must be greater zero.", registry, stackId)
|
|
ERROR_AND_RETURN_IF(position.Length() != 3, "Position parameter must be an array of length 3.", registry, stackId)
|
|
ERROR_AND_RETURN_IF(rotation.Length() != 3, "Rotation parameter must be an array of length 3.", registry, stackId)
|
|
|
|
SpawnerTaskFunctor* func = SKSEObjectStorageInstance().AccessObject<SpawnerTaskFunctor>(handle);
|
|
|
|
ERROR_AND_RETURN_IF(func == NULL, "Failed to lookup object for given handle.", registry, stackId)
|
|
|
|
NiPoint3 pos;
|
|
position.Get(&pos.x, 0);
|
|
position.Get(&pos.y, 1);
|
|
position.Get(&pos.z, 2);
|
|
|
|
NiPoint3 rot;
|
|
rotation.Get(&rot.x, 0);
|
|
rotation.Get(&rot.y, 1);
|
|
rotation.Get(&rot.z, 2);
|
|
|
|
func->AddSpawn(form, target, pos, rot, count, bForcePersist, bInitiallyDisabled);
|
|
}
|
|
|
|
bool Run(VMClassRegistry* registry, UInt32 stackId, StaticFunctionTag*, SInt32 handle)
|
|
{
|
|
ERROR_AND_RETURN_0_IF(handle <= 0, "Invalid handle.", registry, stackId)
|
|
|
|
SpawnerTaskFunctor* func = SKSEObjectStorageInstance().TakeObject<SpawnerTaskFunctor>(handle);
|
|
|
|
ERROR_AND_RETURN_0_IF(func == NULL, "Failed to lookup object for given handle.", registry, stackId)
|
|
|
|
if (stackId != func->StackId())
|
|
{
|
|
registry->LogError("Tried to run SpawnerTaskFunctor from non-owning stack.", stackId);
|
|
|
|
delete func;
|
|
return false;
|
|
}
|
|
|
|
SKSEDelayFunctorManagerInstance().Enqueue(func);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Cancel(VMClassRegistry* registry, UInt32 stackId, StaticFunctionTag*, SInt32 handle)
|
|
{
|
|
ERROR_AND_RETURN_IF(handle <= 0, "Invalid handle.", registry, stackId)
|
|
|
|
SpawnerTaskFunctor* func = SKSEObjectStorageInstance().TakeObject<SpawnerTaskFunctor>(handle);
|
|
|
|
ERROR_AND_RETURN_IF(func == NULL, "Failed to lookup object for given handle.", registry, stackId)
|
|
|
|
delete func;
|
|
}
|
|
}
|
|
|
|
void papyrusSpawnerTask::RegisterFuncs(VMClassRegistry* registry)
|
|
{
|
|
SKSEObjectRegistry& skseObjRegistry = SKSEObjectRegistryInstance();
|
|
skseObjRegistry.RegisterClass<SpawnerTaskFunctor>();
|
|
|
|
registry->RegisterFunction(
|
|
new NativeFunction0 <StaticFunctionTag, SInt32>("Create", "SpawnerTask", papyrusSpawnerTask::Create, registry));
|
|
|
|
registry->RegisterFunction(
|
|
new NativeFunction8 <StaticFunctionTag, void, SInt32, TESForm*, TESObjectREFR*, VMArray<float>, VMArray<float>, SInt32, bool, bool>("AddSpawn", "SpawnerTask", papyrusSpawnerTask::AddSpawn, registry));
|
|
|
|
registry->RegisterFunction(
|
|
new LatentNativeFunction1 <StaticFunctionTag, VMResultArray<TESObjectREFR*>, SInt32>("Run", "SpawnerTask", papyrusSpawnerTask::Run, registry));
|
|
|
|
registry->RegisterFunction(
|
|
new NativeFunction1 <StaticFunctionTag, void, SInt32>("Cancel", "SpawnerTask", papyrusSpawnerTask::Cancel, registry));
|
|
|
|
registry->SetFunctionFlags("SpawnerTask", "Create", VMClassRegistry::kFunctionFlag_NoWait);
|
|
registry->SetFunctionFlags("SpawnerTask", "AddSpawn", VMClassRegistry::kFunctionFlag_NoWait);
|
|
registry->SetFunctionFlags("SpawnerTask", "Cancel", VMClassRegistry::kFunctionFlag_NoWait);
|
|
|
|
} |