void SuicideCMD(AShooterPlayerController* AttackerShooterController, FString* message, int mode)
{
if (!AttackerShooterController || !AttackerShooterController->PlayerStateField() || !AttackerShooterController->GetPlayerCharacter() || AttackerShooterController->GetPlayerCharacter()->IsDead() || !AttackerShooterController->GetPlayerCharacter()->IsConscious() || AttackerShooterController->GetPlayerCharacter()->IsSitting(false)) return;
if (AttackerShooterController && AttackerShooterController->PlayerStateField() && AttackerShooterController->GetPlayerCharacter() && AttackerShooterController->GetPlayerCharacter()->CurrentWeaponField() && AttackerShooterController->GetPlayerCharacter()->CurrentWeaponField()->AssociatedPrimalItemField())
{
FString WeaponName;
AttackerShooterController->GetPlayerCharacter()->CurrentWeaponField()->AssociatedPrimalItemField()->GetItemName(&WeaponName, false, true, nullptr);
if (WeaponName.Contains(L"Handcuffs")) return;
}
if (!ArkApi::GetApiUtils().IsRidingDino(AttackerShooterController)) AttackerShooterController->GetPlayerCharacter()->Suicide();
}
ArkApi::GetCommands().AddChatCommand("/suicide", &SuicideCMD);
to your void loadArkApi::GetCommands().RemoveChatCommand("/suicide");
to your void unloadDECLARE_HOOK(APrimalDinoCharacter_BeginPlay, void, APrimalDinoCharacter*);
void Hook_APrimalDinoCharacter_BeginPlay(APrimalDinoCharacter* dino)
{
UPrimalCharacterStatusComponent* charStatus = dino->GetCharacterStatusComponent();
FString dino_name;
dino->GetDinoDescriptiveName(&dino_name);
if (dino_name.Contains("Pteranodon") || dino_name.Contains("Argentavis") || dino_name.Contains("Quetzal")) {
const int dino_team = dino->TargetingTeamField();
float* speed5 = charStatus->CurrentStatusValuesField()() + 9;
if (dino_team == 1)
return APrimalDinoCharacter_BeginPlay_original(dino);
if (dino_team == 6)
return APrimalDinoCharacter_BeginPlay_original(dino);
if (dino_name.Contains("Pteranodon"))//&& dino_team == 1726483630
*speed5 = 0.40;
if (dino_name.Contains("Argentavis"))
*speed5 = 0.30;
if (dino_name.Contains("Quetzal"))
*speed5 = 2.00;
}
return APrimalDinoCharacter_BeginPlay_original(dino);
}
void Load()
{
Log::Get().Init("FlyerSpeed");
ArkApi::GetHooks().SetHook("APrimalDinoCharacter.BeginPlay", &Hook_APrimalDinoCharacter_BeginPlay, reinterpret_cast<LPVOID*>(&APrimalDinoCharacter_BeginPlay_original));
}
(edited)UShooterGameInstance *instance = static_cast<UShooterGameInstance*>(ArkApi::GetApiUtils().GetWorld()->OwningGameInstanceField());
if (instance)
{
auto gridinfo = instance->GridInfoField();
int grid_X = gridinfo->GetCurrentServerInfo()->gridXField();
int grid_Y = gridinfo->GetCurrentServerInfo()->gridYField();
}
(edited)void dumpCraftables() {
nlohmann::json json;
json["Craftables"] = nullptr;
// Get all blueprint crafting requirements
TArray<UObject*> types;
Globals::GetObjectsOfClass(UPrimalItem::GetPrivateStaticClass(NULL), &types, true, EObjectFlags::RF_NoFlags);
for (auto object : types) {
auto n = static_cast<UPrimalItem*> (object);
if (n->IsValidForCrafting()) {
FString name;
n->GetItemName(&name, false, false, NULL);
for (auto res : n->BaseCraftingResourceRequirementsField()) {
if (res.ResourceItemType.uClass) {
if (res.ResourceItemType.uClass->ClassDefaultObjectField()) {
auto pi = static_cast<UPrimalItem*> (res.ResourceItemType.uClass->ClassDefaultObjectField());
FString type;
pi->GetItemName(&type, false, false, NULL);
json["Craftables"][name.ToString()][type.ToString()] = res.BaseResourceRequirement;
}
}
}
}
}
std::filesystem::create_directory("resources");
std::ofstream file("resources/craftables.json");
file << json;
file.flush();
file.close();
}
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void Plugin_Unload()
{
// Stop threads here
}
#ifdef __cplusplus
}
#endif
std::pair<float, float> FVectorToCoords(FVector ActorPosition)
{
AWorldSettings* WorldSettings = ArkApi::GetApiUtils().GetWorld()->GetWorldSettings(false, true);
APrimalWorldSettings* PWorldSettings = static_cast<APrimalWorldSettings*>(WorldSettings);
float LatScale = PWorldSettings->LatitudeScaleField() != 0 ? PWorldSettings->LatitudeScaleField() : 800.0f;
float LongScale = PWorldSettings->LongitudeScaleField() != 0 ? PWorldSettings->LongitudeScaleField() : 800.0f;
float LatOrigin = PWorldSettings->LatitudeOriginField() != 0 ? PWorldSettings->LatitudeOriginField() : -400000.0f;
float LongOrigin = PWorldSettings->LongitudeOriginField() != 0 ? PWorldSettings->LongitudeOriginField() : -400000.0f;
float LatDiv = 100.f / LatScale;
float Lat = (LatDiv * ActorPosition.Y + LatDiv * abs(LatOrigin)) / 1000.f;
float LongDiv = 100.f / LongScale;
float Long = (LongDiv * ActorPosition.X + LongDiv * abs(LongOrigin)) / 1000.f;
return std::make_pair<float, float>(std::floor(Lat * 10.) / 10., std::floor(Long * 10.) / 10.);
}
static UClass* object_class = Globals::FindClass("Class /Script/CoreUObject.Class");
static UClass* script_struct_class = Globals::FindClass("Class /Script/CoreUObject.ScriptStruct");
Alternatively, you can iterate it manually like this:
for (auto i = 0; i < GUObjectArray()().ObjObjects.NumElements; i++)
{
auto obj = GUObjectArray()().ObjObjects.GetObjectPtr(i)->Object;
if (obj != nullptr)
{
// Do something with the object reference
}
}
This would mainly be used to find classes or objects which don't have a function to get the static class.
It is recommended to declare those class variables as static so they only init once.
Credits to @dougy and @substitute who made this originally (edited)void DestroyFoliageInRadius(FVector position, int radius)
{
TArray<FOverlappedFoliageElement> out;
UVictoryCore::ServerSearchFoliage(
ArkApi::GetApiUtils().GetWorld(),
&position, radius, &out,
true, true, true, false, false
);
static FString DmgTypeBpPath(FString("Blueprint'/Game/PrimalEarth/CoreBlueprints/DamageTypes/DmgType_MiningDrill_Harvest.DmgType_MiningDrill_Harvest'"));
static UClass* DmgType = UVictoryCore::BPLoadClass(&DmgTypeBpPath);
for (FOverlappedFoliageElement& elem : out)
{
if (!elem.HarvestLocation.IsNearlyZero())
{
elem.InstancedStaticMeshComponent->DealDirectDamage(nullptr, 999999, DmgType, elem.HitBodyIndex);
}
}
}
FOverlappedFoliageElement
struct FOverlappedFoliageElement
{
AActor* HarvestActor;
UInstancedStaticMeshComponent* InstancedStaticMeshComponent;
UPrimalHarvestingComponent* HarvestingComponent;
FVector HarvestLocation;
int HitBodyIndex;
float MaxHarvestHealth;
float CurrentHarvestHealth;
__int8 bIsUnharvestable : 1;
__int8 bIsVisibleAndActive : 1;
};
UInstancedStaticMeshComponent
struct UInstancedStaticMeshComponent : UStaticMeshComponent
{
void DealDirectDamage(APlayerController* ForPC, float DamageAmount, TSubclassOf<UDamageType> DamageTypeClass, int hitBodyIndex) { NativeCall<void, APlayerController*, float, TSubclassOf<UDamageType>, int>(this, "UInstancedStaticMeshComponent.DealDirectDamage", ForPC, DamageAmount, DamageTypeClass, hitBodyIndex); }
};
If not there already, add this to UVictoryCore
static void ServerSearchFoliage(UObject* WorldContextObject, FVector* Origin, float Radius, TArray<FOverlappedFoliageElement>* OutFoliage, bool bVisibleAndActiveOnly, bool bIncludeUsableFoliage, bool bIncludeMeshFoliage, bool bSortByDistance, bool bReverseSort) { NativeCall<void, UObject*, FVector*, float, TArray<FOverlappedFoliageElement>*, bool, bool, bool, bool, bool>(nullptr, "UVictoryCore.ServerSearchFoliage", WorldContextObject, Origin, Radius, OutFoliage, bVisibleAndActiveOnly, bIncludeUsableFoliage, bIncludeMeshFoliage, bSortByDistance, bReverseSort); }
(edited)void RemoveDinoBuff(APrimalDinoCharacter* dino)
{
if (dino != nullptr)
{
TArray<APrimalBuff*> dino_buffs;
dino->GetAllBuffs(&dino_buffs);
for (APrimalBuff* dino_buff : dino_buffs)
{
const FString dino_bp_buff = ArkApi::GetApiUtils().GetBlueprint(dino_buff);
if (dino_bp_buff.Contains("Insert_Buff_Path_Here"))
{
dino_buff->Destroy(true, false);
return;
}
}
}
}
(edited)AShooterPlayerController::GetPlayerCharacter
.
This method seem to crash the server if it's called on a valid controller that is just connecting/disconnecting to/from the server and the character takes any damage (turrets, dinos, etc...)
Prefer to use something like AShooterPlayerController.LastControlledPlayerCharacterField
, or the traditional AShooterPlayerController.CharacterField
, but this one can return the dino being ridden if player is currently riding a dino (edited)Fields.h
one specifically): Function to get size (in bytes) of some game structs.
GetStructSize<T>()
- For game structs (classes that start with F
, such as FTribeData
GetObjectClassSize<T>()
- For classes inherited from UObject
, such as AActor
Example of usage: Loading a tribe data pointer:
static int tribeDataStructSize = GetStructSize<FTribeData>();
FTribeData* data = static_cast<FTribeData*>(FMemory::Malloc(tribeDataStructSize));
RtlSecureZeroMemory(data, tribeDataStructSize);
ArkApi::GetApiUtils().GetShooterGameMode()->GetOrLoadTribeData(team, data);
// use the tribe data
// don't forget to free the memory!
FMemory::Free(data);
This will ensure you don't allocate a lot of memory that won't be used, since it was a common method to allocate 0x200
, around 512 bytes when some structs needed less (or even more!) than that. That function ensures the correct size is allocated. (edited)DECLARE_HOOK(FCommandLine_Get, wchar_t*);
wchar_t* Hook_FCommandLine_Get()
{
wchar_t* ret = FCommandLine_Get_original();
FString fret(ret);
if (fret.Contains(L"-NoDinos"))
fret.Replace(L"-NoDinos", L"");
else
fret.Replace(L"-NoDinos", L"");
(*ret) = **fret;
return ((wchar_t*)*fret);
}
void Load()
{
Log::Get().Init("TestPlugin");
ArkApi::GetHooks().SetHook("FCommandLine.Get", &Hook_FCommandLine_Get, &FCommandLine_Get_original);
ArkApi::GetCommands().AddConsoleCommand("dinos.enable",
[](APlayerController*, FString*, bool)
{
TArray<AActor*> out;
UGameplayStatics::GetAllActorsOfClass(ArkApi::GetApiUtils().GetWorld(), ANPCZoneManager::GetPrivateStaticClass(), &out);
for (auto* a : out)
if (a
&& a->IsA(ANPCZoneManager::GetPrivateStaticClass()))
{
((ANPCZoneManager*)a)->BeginPlay();
}
});
}
void Unload()
{
ArkApi::GetHooks().DisableHook("FCommandLine.Get", &Hook_FCommandLine_Get);
ArkApi::GetCommands().RemoveConsoleCommand("dinos.enable");
}
DECLARE_HOOK(AMissionType_CanStartMission, bool, TSubclassOf<AMissionType>, APlayerController*, FString*, AActor**);
bool Hook_AMissionType_CanStartMission(TSubclassOf<AMissionType> MissionType, APlayerController* Controller, FString* OutClientFailureReason, AActor** OutMissionDispatcher)
{
return AMissionType_CanStartMission_original(MissionType, Controller, OutClientFailureReason, OutMissionDispatcher);
}
ArkApi::GetHooks().SetHook("AMissionType.CanStartMission", &Hook_AMissionType_CanStartMission, &AMissionType_CanStartMission_original);
ArkApi::GetHooks().DisableHook("AMissionType.CanStartMission", &Hook_AMissionType_CanStartMission);
(edited)#pragma once
#include "API/ARK/Ark.h"
namespace Tools
{
class Timer
{
public:
Timer()
{
ArkApi::GetCommands().AddOnTickCallback("Plugin.HelperTimer.Update", std::bind(&Tools::Timer::Update, this, std::placeholders::_1));
}
~Timer()
{
ArkApi::GetCommands().RemoveOnTickCallback("Plugin.HelperTimer.Update");
}
template <class func, class... arguments>
void DelayExec(func&& f, int ms_after, bool async, arguments&&... args)
{
if (!async)
{
std::lock_guard<std::mutex> lg(tasks_mtx_);
tasks_.push_back(std::make_shared<Task>(std::bind(f, std::forward<arguments>(args)...), ms_after));
}
else
{
auto function = std::bind(f, std::forward<arguments>(args)...);
std::thread([&function, ms_after]()
{
std::this_thread::sleep_for(std::chrono::milliseconds(ms_after));
function();
}
).detach();
}
}
void Update(float)
{
const auto now = std::chrono::high_resolution_clock::now();
tasks_mtx_.lock();
const auto tasks = tasks_;
tasks_mtx_.unlock();
for (const auto& function : tasks)
{
if (function
&& function->execTime <= now
&& function->callback)
{
function->callback();
tasks_mtx_.lock();
tasks_.erase(std::find(tasks_.begin(), tasks_.end(), function));
tasks_mtx_.unlock();
}
}
}
static Timer& Get()
{
static Timer instance;
return instance;
}
private:
struct Task
{
Task(std::function<void()> cbk, int after)
: callback(std::move(cbk))
{
execTime = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds(after);
}
std::function<void()> callback;
std::chrono::high_resolution_clock::time_point execTime;
};
std::mutex tasks_mtx_;
std::vector<std::shared_ptr<Task>> tasks_;
};
}
TWeakObjectPtr<AActor> singleCCA;
FString MyCCA = "My_CCA_C"; //This line is just a variable incase you want to set it by config or change it later Must be the same name as your CCA class.
//This function finds your CCA (Central Control Actor) and sets it as a weak object. This is done because in some examples of CCA's from the mod community the CCA is set to update or replace iteself from time to time, using a weak object will always check to make sure it still exists in the world, if it does not, it will set a new one.
void setCCA()
{
auto Gengine = Globals::GEngine();
auto primalglobals = static_cast<UPrimalGlobals*>(Gengine()->GameSingletonField());
auto gamedata = primalglobals->PrimalGameDataOverrideField();
auto singletons = gamedata->ServerExtraWorldSingletonActorClassesField();
for (auto singleton : singletons)
{
if (singleton.uClass)
{
FString className;
singleton.uClass->GetDescription(&className);
if (className == MyCCA)
{
TArray<AActor*> SingletonActors;
UGameplayStatics::GetAllActorsOfClass(reinterpret_cast<UObject*>(ArkApi::GetApiUtils().GetWorld()), singleton, &SingletonActors);
if (!SingletonActors[0])
return;
singleCCA = GetWeakReference(SingletonActors[0]);
}
}
}
}
//This is the function you call to push information to the mod, in this example it is just a single string, but you can use whatever you want.
void SendToMod(FString info)
{
if (singleCCA.Get() == nullptr) //Check to see if the CCA is still in the world.
setCCA(); //If its not, make a new reference to it.
AActor* Function_CCA = singleCCA.Get(); //Set your weak reference to an Actor.
if (!Function_CCA) //Make sure the actor is valid.
return; //If not return.
UFunction* cca_Function = Function_CCA->FindFunctionChecked(FName("SetModInfo", EFindName::FNAME_Add)); //Looks for the function name in your CCA actor.
if (!cca_Function)
return; //If the function is not found return;
Function_CCA->ProcessEvent(cca_Function, &info); //Sends the info to the function in the CCA actor.
}
Mod: (edited)