260 lines
6.8 KiB
C++
260 lines
6.8 KiB
C++
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "Subsystems/EngineSubsystem.h"
|
|
#include "hidapi.h"
|
|
#include "HAL/Runnable.h"
|
|
#include "SkyPortalFigure.h"
|
|
|
|
|
|
#include "SkyPortalSubsystem.generated.h"
|
|
|
|
#pragma region Definitions
|
|
|
|
UENUM(BlueprintType)
|
|
enum EPortalSide {
|
|
LEFT UMETA(DisplayName = "Left side"),
|
|
RIGHT UMETA(DisplayName = "Right side"),
|
|
BOTH UMETA(DisplayName = "Both left and right"),
|
|
TRAP UMETA(DisplayName = "Trap")
|
|
};
|
|
|
|
UENUM(BlueprintType)
|
|
enum EPortalCommand {
|
|
A UMETA(DisplayName = "Activate"),
|
|
C UMETA(DisplayName = "Color"),
|
|
J UMETA(DisplayName = "Advanced color"),
|
|
L UMETA(DisplayName = "Trap color"),
|
|
M UMETA(DisplayName = "Music"),
|
|
Q UMETA(DisplayName = "Query"),
|
|
R UMETA(DisplayName = "Ready"),
|
|
S UMETA(DisplayName = "Status")
|
|
};
|
|
|
|
UENUM(BlueprintType)
|
|
enum class EFigureStatus : uint8
|
|
{
|
|
NOT_PRESENT = 0b00 UMETA(DisplayName = "Not Present"),
|
|
PRESENT = 0b01 UMETA(DisplayName = "Present"),
|
|
ADDED = 0b11 UMETA(DisplayName = "Added"),
|
|
REMOVED = 0b10 UMETA(DisplayName = "Removed")
|
|
};
|
|
|
|
USTRUCT(BlueprintType)
|
|
struct FPortalStatusData
|
|
{
|
|
GENERATED_USTRUCT_BODY()
|
|
|
|
// Array of statuses
|
|
UPROPERTY(BlueprintReadOnly, Category = "SkyPortal|Figure")
|
|
TArray<EFigureStatus> StatusArray;
|
|
|
|
// timestamp.
|
|
// only one byte long. This means that after the value 0xFF, it overflows back to 0x00.
|
|
// Since these are so far apart, it can be assumed that 0x00 is newer than anything in the range 0xF0 - 0xFF.
|
|
UPROPERTY(BlueprintReadOnly, Category = "SkyPortal|Figure")
|
|
uint8 Counter;
|
|
|
|
// Should always be true
|
|
UPROPERTY(BlueprintReadOnly, Category = "SkyPortal|Figure")
|
|
bool bIsReady;
|
|
|
|
// Overload the == operator
|
|
bool operator==(const FPortalStatusData& Other) const
|
|
{
|
|
if (bIsReady == Other.bIsReady && Counter == Other.Counter) {
|
|
if (StatusArray == Other.StatusArray) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Overload the != operator
|
|
bool operator!=(const FPortalStatusData& Other) const
|
|
{
|
|
return !(*this == Other);
|
|
}
|
|
};
|
|
|
|
/* Macro constants Definitions */
|
|
#define rw_buf_size 0x21
|
|
#define TIMEOUT 30000
|
|
#define DEBUG true
|
|
#define FIGURE_TOTAL_BLOCKS 64
|
|
#define FIGURE_BLOCK_SIZE 16
|
|
|
|
typedef struct {
|
|
unsigned char buf[rw_buf_size]; int BytesTransferred;
|
|
} RWBlock;
|
|
|
|
typedef struct {
|
|
unsigned char blockdata[FIGURE_TOTAL_BLOCKS][FIGURE_BLOCK_SIZE]; bool error;
|
|
} FigureDataBlock;
|
|
|
|
DECLARE_LOG_CATEGORY_EXTERN(LogHIDApi, Log, All);
|
|
DECLARE_LOG_CATEGORY_EXTERN(LogSkyportalIO, Log, All);
|
|
|
|
//// Delegates
|
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSkylanderAddedDelegate, int32, SkylanderID, int32, Index);
|
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSkylanderRemovedDelegate, int32, SkylanderID, int32, Index);
|
|
|
|
#pragma endregion
|
|
|
|
|
|
class FPortalStatusChecker : public FRunnable {
|
|
public:
|
|
// Constructor: pass the subsystem and desired check interval (in seconds)
|
|
FPortalStatusChecker(USkyPortalSubsystem* InSubsystem, float InCheckInterval);
|
|
|
|
// FRunnable interface
|
|
virtual bool Init() override;
|
|
virtual uint32 Run() override;
|
|
virtual void Stop() override;
|
|
virtual void Exit() override;
|
|
|
|
private:
|
|
// Pointer to the subsystem that contains PortalStatus()
|
|
USkyPortalSubsystem* SkyPortalSubsystem;
|
|
|
|
// Time interval (in seconds) for status checking
|
|
float CheckInterval;
|
|
|
|
// Thread control variables
|
|
FThreadSafeBool bShouldRun;
|
|
|
|
// Helper function to check portal status
|
|
void CheckPortalStatus();
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Handle all the portal I/O
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
UCLASS(MinimalAPI)
|
|
class USkyPortalSubsystem : public UEngineSubsystem
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
//Portal ref used in the subsystem
|
|
hid_device* PortalDevice;
|
|
|
|
public:
|
|
// Override initialization and deinitialization methods
|
|
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
|
virtual void Deinitialize() override;
|
|
|
|
/********* Portal Actions *************/
|
|
|
|
/* The first function to run, before anything else.
|
|
* It will re-init and prepare the portal for receiving/sending inputs
|
|
*
|
|
* return false if portal is not found
|
|
*/
|
|
UFUNCTION(BlueprintCallable, CallInEditor, meta = (Category = "SkyPortal"))
|
|
SKYPORTAL_API bool ConnectPortal();
|
|
|
|
/*Send a **Status** command, to see if the portal is ready to receive new commands*/
|
|
UFUNCTION(BlueprintCallable, BlueprintPure, meta = (Category = "SkyPortal|NOT FUNCTIONING"))
|
|
SKYPORTAL_API bool bIsPortalReady();
|
|
|
|
UFUNCTION(BlueprintCallable, meta = (Category = "SkyPortal|NOT FUNCTIONING"))
|
|
SKYPORTAL_API void SendPortalCommand(EPortalCommand Command);
|
|
|
|
UFUNCTION(BlueprintCallable, meta = (Category = "SkyPortal|NOT FUNCTIONING"))
|
|
SKYPORTAL_API void SendPortalSound(USoundWave* Sound);
|
|
|
|
|
|
|
|
/* Change portal color, ideally should be called just at the start.For gameplay usage, use ChangePortalColorside()*/
|
|
UFUNCTION(BlueprintCallable, CallInEditor, meta = (AutoCreateRefTerm = "Color", Category = "SkyPortal|Cosmetic"))
|
|
SKYPORTAL_API void ChangePortalColor(const FLinearColor& Color = FLinearColor::Green);
|
|
|
|
/**
|
|
* Change the color of the portal, can separate side and even trap ligth
|
|
* @param NextColor New color
|
|
* @param PortalSide The actors to record
|
|
* @param BlendTime Blend between current color and NextColor, in milliseconds
|
|
*/
|
|
UFUNCTION(BlueprintCallable, CallInEditor, meta = (AutoCreateRefTerm = "NextColor", Category = "SkyPortal|Cosmetic"))
|
|
SKYPORTAL_API void ChangePortalColorside(const FLinearColor& NextColor = FLinearColor::Green, const EPortalSide PortalSide = EPortalSide::BOTH, const float BlendTime = 500.0f);
|
|
|
|
|
|
// Blueprint-assignable event property
|
|
UPROPERTY(BlueprintAssignable, Category = "SkyPortal|Skylander")
|
|
FOnSkylanderAddedDelegate OnSkylanderAdded;
|
|
|
|
// Blueprint-assignable event property
|
|
UPROPERTY(BlueprintAssignable, Category = "SkyPortal|Skylander")
|
|
FOnSkylanderRemovedDelegate OnSkylanderRemoved;
|
|
|
|
UPROPERTY(BlueprintReadOnly)
|
|
FPortalStatusData CurrentStatusData;
|
|
|
|
UPROPERTY(BlueprintReadOnly)
|
|
FPortalStatusData OldStatusData;
|
|
|
|
EPortalCommand GetPortalCommandFromChar(unsigned char Char);
|
|
|
|
|
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
|
bool bPortalConnected = false;
|
|
|
|
FString HidError;
|
|
|
|
bool ReadBlock(unsigned int block, unsigned char data[0x10], int skylander);
|
|
bool WriteBlock(unsigned int, unsigned char[0x10], int);
|
|
bool CheckResponse(RWBlock*, char);
|
|
void CheckComplexResponse();
|
|
|
|
|
|
|
|
|
|
private:
|
|
FPortalStatusData ParsePortalStatus(const RWBlock& ResponseBlock);
|
|
|
|
|
|
|
|
|
|
static void Sleep(int sleepMs);
|
|
|
|
|
|
|
|
void ActivatePortal(int active);
|
|
|
|
void RestartPortal();
|
|
|
|
|
|
// Block/byte related data write/read functions
|
|
bool OpenPortalHandle();
|
|
void Write(RWBlock*);
|
|
FigureDataBlock ReadFigureBlocks(uint8 FigureIndex);
|
|
bool FalsePositive();
|
|
|
|
// Pointer to the status checker thread
|
|
FPortalStatusChecker* StatusChecker;
|
|
FRunnableThread* StatusCheckerThread;
|
|
|
|
|
|
protected:
|
|
|
|
//Constants
|
|
const int VendorIds[4] = { 0x12ba, 0x54c, 0x1430, 0x1430 };
|
|
const int ProductIds[4] = { 0x150, 0x967, 0x1f17 };
|
|
|
|
|
|
|
|
/////Defaults values, should not be used
|
|
//const int VendorId = 5168;
|
|
//const int ProductId = 336;
|
|
};
|
|
|
|
|