enderalse/source/fs.dll/skse64/skse64/PapyrusSpawnerTask.cpp

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);
}