Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2DFX Effects API #3741

Draft
wants to merge 19 commits into
base: master
Choose a base branch
from
456 changes: 456 additions & 0 deletions Client/game_sa/C2DEffectSA.cpp

Large diffs are not rendered by default.

141 changes: 141 additions & 0 deletions Client/game_sa/C2DEffectSA.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*****************************************************************************
*
* PROJECT: Multi Theft Auto
* LICENSE: See LICENSE in the top level directory
* FILE: game_sa/C2DEffectSA.h
* PURPOSE: Header file for 2dfx class
*
* Multi Theft Auto is available from https://www.multitheftauto.com/
*
*****************************************************************************/

#pragma once
#include "game/C2DEffect.h"
#include "C2DEffectSAInterface.h"
#include <variant>

#define ARRAY_2DFXInfoStore 0xB4C2D8 // C2dfxInfoStore d2fxModels

#define FUNC_C2DEffect_Shutdown 0x4C57D0
#define FUNC_PushCurrentTxd 0x7316A0
#define FUNC_FindTxdSlot 0x731850
#define FUNC_SetCurrentTxd 0x7319C0
#define FUNC_PopCurrentTxd 0x7316B0

// Escalators stuff
#define ARRAY_CEscalators 0xC6E9A8
#define NUM_MAX_ESCALATORS 32
#define FUNC_CEscalator_SwitchOff 0x717860

// fx stuff
#define FUNC_Fx_c_DestroyEntityFx 0x4A1280
#define FUNC_Fx_c_CreateEntityFx 0x4A11E0
#define VAR_G_Fx 0xA9AE00
#define OFFSET_FxSystem_Entities 0xC
#define OFFSET_FxSystem_Link_Prev 0x4

class C2DEffectSA : public C2DEffect
{
public:
C2DEffectSA(C2DEffectSAInterface* effectInterface, std::uint32_t modelID);
~C2DEffectSA() = default;

C2DEffectSAInterface* GetInterface() noexcept { return m_effectInterface; }
e2dEffectType GetEffectType() override { return m_effectInterface ? m_effectInterface->type : e2dEffectType::NONE; }

bool IsValidLight() const noexcept { return m_effectInterface && m_effectInterface->type == e2dEffectType::LIGHT; };
bool IsValidRoadsign() const noexcept { return m_effectInterface && m_effectInterface->type == e2dEffectType::ROADSIGN; }
bool IsValidEscalator() const noexcept { return m_effectInterface && m_effectInterface->type == e2dEffectType::ESCALATOR; }
bool IsValidParticle() const noexcept { return m_effectInterface && m_effectInterface->type == e2dEffectType::PARTICLE; }

void Destroy() const;

void SetPosition(const CVector& position) override;
CVector& GetPosition() override;

// Light properties
// Set
void SetCoronaFarClip(float clip) override;
void SetCoronaPointLightRange(float range) override;
void SetCoronaSize(float size) override;
void SetShadowSize(float size) override;
void SetShadowMultiplier(std::uint8_t multiplier) override;
void SetCoronaShowMode(e2dCoronaFlashType showMode) override;
void SetCoronaReflectionsEnabled(bool enable) override;
void SetCoronaFlareType(std::uint8_t flareType) override;
void SetLightFlags(std::uint16_t flags) override;
void SetShadowDistance(std::int8_t distance) override;
void SetCoronaOffsets(const CVector& offsets) override;
void SetCoronaColor(const RwColor& color) override;
void SetCoronaTexture(const std::string& name) override;
void SetShadowTexture(const std::string& name) override;

// Get
float GetCoronaFarClip() const override { return IsValidLight() ? m_effectInterface->effect.light.coronaFarClip : 0.0f; }
float GetCoronaPointLightRange() const override { return IsValidLight() ? m_effectInterface->effect.light.pointLightRange : 0.0f; }
float GetCoronaSize() const override { return IsValidLight() ? m_effectInterface->effect.light.coronaSize : 0.0f; }
float GetShadowSize() const override { return IsValidLight() ? m_effectInterface->effect.light.shadowSize : 0.0f; }
std::uint8_t GetShadowMultiplier() const override { return IsValidLight() ? m_effectInterface->effect.light.shadowColorMultiplier : 0; }
e2dCoronaFlashType GetCoronaShowMode() const override { return IsValidLight() ? m_effectInterface->effect.light.coronaFlashType : e2dCoronaFlashType::UNUSED; }
bool GetCoronaReflectionsEnabled() const override { return IsValidLight() ? m_effectInterface->effect.light.coronaEnableReflection : false; }
std::uint8_t GetCoronaFlareType() const override { return IsValidLight() ? m_effectInterface->effect.light.coronaFlareType : 0; }
std::uint16_t GetLightFlags() const override { return IsValidLight() ? m_effectInterface->effect.light.flags : 0; }
std::int8_t GetShadowDistance() const override { return IsValidLight() ? m_effectInterface->effect.light.shadowZDistance : 0; }
CVector GetCoronaOffsets() const override;
RwColor GetCoronaColor() const override { return IsValidLight() ? m_effectInterface->effect.light.color : RwColor{0,0,0,0}; }
std::string GetCoronaTexture() const override { return IsValidLight() ? (m_effectInterface->effect.light.coronaTex ? m_effectInterface->effect.light.coronaTex->name : "") : ""; }
std::string GetShadowTexture() const override { return IsValidLight() ? (m_effectInterface->effect.light.shadowTex ? m_effectInterface->effect.light.shadowTex->name : "") : ""; }

// Particle properties
// Set
void SetParticleName(const std::string& name) override;

// Get
std::string GetParticleName() const override { return IsValidParticle() ? (m_effectInterface->effect.particle.szName ? m_effectInterface->effect.particle.szName : "") : ""; }

// Roadsign properties
// Set
void SetRoadsignSize(const RwV2d& size) override;
void SetRoadsignRotation(const RwV3d& rotation) override;
void SetRoadsignFlags(std::uint8_t flags) override;
void SetRoadsignText(const std::string& text, std::uint8_t line) override;

// Get
RwV2d& GetRoadsignSize() override;
RwV3d& GetRoadsignRotation() override;
std::uint16_t GetRoadsignFlags() const override { return IsValidRoadsign() ? m_effectInterface->effect.roadsign.flags : 0; }
std::string GetRoadsignText() const override;

// Escalator properties
// Set
void SetEscalatorBottom(const RwV3d& bottom) override;
void SetEscalatorTop(const RwV3d& top) override;
void SetEscalatorEnd(const RwV3d& end) override;
void SetEscalatorDirection(std::uint8_t direction) override;

// Get
RwV3d& GetEscalatorBottom() override;
RwV3d& GetEscalatorTop() override;
RwV3d& GetEscalatorEnd() override;
std::uint8_t GetEscalatorDirection() const override { return IsValidEscalator() ? m_effectInterface->effect.escalator.direction : 0; }

static RpAtomic* Roadsign_CreateAtomic(const RwV3d& position, const RwV3d& rotation, float sizeX, float sizeY, std::uint32_t numLines, char* line1, char* line2, char* line3, char* line4, std::uint32_t numLetters, std::uint8_t palleteID);
static std::uint32_t Roadsign_GetPalleteIDFromFlags(std::uint8_t flags);
static std::uint32_t Roadsign_GetNumLettersFromFlags(std::uint8_t flags);
static std::uint32_t Roadsign_GetNumLinesFromFlags(std::uint8_t flags);
static void Roadsign_DestroyAtomic(C2DEffectSAInterface* effect);

static C2DEffectSAInterface* CreateCopy(C2DEffectSAInterface* effect);

static void Shutdown(C2DEffectSAInterface* effect);
static void SafeDelete2DFXEffect(C2DEffectSAInterface* effect);
static void PrepareTexturesForLightEffect(RwTexture*& coronaTex, RwTexture*& shadowTex, const char* coronaName, const char* shadowName, bool removeIfExist);

public:
static int effect2dPluginOffset;

private:
C2DEffectSAInterface* m_effectInterface;
std::uint32_t m_model;

};
250 changes: 250 additions & 0 deletions Client/game_sa/C2DEffectSAInterface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
/*****************************************************************************
*
* PROJECT: Multi Theft Auto
* LICENSE: See LICENSE in the top level directory
* FILE: game_sa/C2DEffectSAInterface.h
* PURPOSE: Header file for 2dfx game interface layer
*
* Multi Theft Auto is available from https://www.multitheftauto.com/
*
*****************************************************************************/
#pragma once

#include "game/RenderWare.h"
#include "game/CModelInfo.h"
#include "game/C2DEffects.h"
#include "CObjectSA.h"

struct t2dEffectLight
{
RwColor color;
float coronaFarClip;
float pointLightRange;
float coronaSize;
float shadowSize;

// Flags
union
{
struct
{
// Flags 1
std::uint16_t checkObstacles : 1;
std::uint16_t fogType : 1;
std::uint16_t fogType2 : 1;
std::uint16_t withoutCorona : 1;
std::uint16_t onlyLongDistance : 1;
std::uint16_t atDay : 1;
std::uint16_t atNight : 1;
std::uint16_t blinking1 : 1;

// Flags 2
std::uint16_t onlyFromBelow : 1;
std::uint16_t blinking2 : 1;
std::uint16_t updateHeightAboveGround : 1;
std::uint16_t checkDirection : 1;
std::uint16_t blinking3 : 1;
};

std::uint16_t flags;
};

e2dCoronaFlashType coronaFlashType; // show mode
bool coronaEnableReflection;
std::uint8_t coronaFlareType; // lens flare effect 0 - off, 1 - on
std::uint8_t shadowColorMultiplier;
std::int8_t shadowZDistance;
std::int8_t offsetX;
std::int8_t offsetY;
std::int8_t offsetZ;
std::uint8_t field_1E[2];
RwTexture* coronaTex;
RwTexture* shadowTex;
std::int32_t field_28;
std::int32_t field_2C;
};

// The particle effect name is an entry in effects.fxp
struct t2dEffectParticle
{
char szName[24];
};

// It`s used for spawning peds (Like on ticketbooth, Windows of shops, Blackjack-tables)
// It includes information about the External Script ped is going to use when spawned, it`s facing angle and it`s behaviour
struct t2dEffectAttractor
{
RwV3d queueDirection;
RwV3d useDirection;
RwV3d forwardDirection;
e2dAttractorType attractorType;
std::uint8_t pedExistingProbability;
std::uint8_t field_26;
std::uint8_t flags;
char szScriptName[8];
};

// entry-exit markers similar to ipl version
struct t2dEffectEnex
{
float enterAngle; // Rotation angle enter-marker (relative to the object)
RwV3d size; // The radius of the approximation to the marker
RwV3d exitPosn; // The position of exit-marker (offset relative to enter position)
float exitAngle; // angle of rotation exit-marker (relative to the object)
std::int16_t interiorId;
std::uint8_t flags1; // Unknown flags
std::uint8_t skyColor;
char szInteriorName[8];
std::uint8_t timeOn;
std::uint8_t timeOff;

// Flags 2
union
{
struct
{
std::uint8_t unknown1 : 1;
std::uint8_t unknown2 : 1;
std::uint8_t timedEffect : 1;
};
std::uint8_t flags2;
};

std::uint8_t field_2F;
};

struct t2dEffectRoadsign
{
RwV2d size;
RwV3d rotation;

// Flags
union
{
struct
{
std::uint8_t numOfLines : 2;
std::uint8_t symbolsPerLine : 2;
std::uint8_t textColor : 2;
};

std::uint8_t flags;
};

std::uint8_t field_16[2];
char* text; // size 64
RpAtomic* atomic;
};

// Section defines a place where peds can cover during firefights
struct t2dEffectCoverPoint
{
RwV2d direction;
std::uint8_t type;
std::uint8_t field_9[3];
};

// Example in vgseesc01.dff
struct t2dEffectEscalator
{
RwV3d bottom;
RwV3d top;
RwV3d end; // Z pos, matches top Z if escalator goes up, bottom Z if it goes down
std::uint8_t direction; // 0 - down, 1 - up
std::uint8_t field_25[3];
};

// Example in kb_bandit_u.dff
// Used to determine additional coordinates that can be used in scripts
struct t2dEffectTriggerPoint
{
std::int32_t id;
};

// Some interiors stuff, probably unused?
struct t2dEffectFurniture
{
std::uint8_t type;
std::int8_t groupId;
// size
std::uint8_t width;
std::uint8_t depth;
std::uint8_t height;
// doors
std::int8_t door;
std::int8_t l_door[2]; // start, end
std::int8_t r_door[2]; // start, end
std::int8_t t_door[2]; // start, end
// windows
std::int8_t l_window[2]; // start, end
std::int8_t r_window[2]; // start, end
std::int8_t t_window[2]; // start, end
// something like offsets?
std::int8_t goLeft[3]; // x,y,z?
std::int8_t goBottom[3]; // x,y,z?
std::int8_t goWidth[3]; // x,y,z?
std::int8_t goDepth[3]; // x,y,z?

std::uint8_t seed;
std::uint8_t status;
float rotation;
};

union t2dEffectUnion
{
t2dEffectLight light;
t2dEffectParticle particle;
t2dEffectAttractor attractor;
t2dEffectEnex enex;
t2dEffectRoadsign roadsign;
t2dEffectCoverPoint coverPoint;
t2dEffectEscalator escalator;
t2dEffectTriggerPoint triggerPoint; // slot machine, k.a.a.c gates, basketball hoop
t2dEffectFurniture furniture;
};

class C2DEffectSAInterface
{
public:
RwV3d position;
e2dEffectType type;
std::uint8_t field_D[3];

t2dEffectUnion effect;
};

class C2DEffectInfoStoreSAInterface
{
public:
std::uint32_t objCount;
C2DEffectSAInterface objects[100];
};

class C2DEffectPluginDataSAInterface
{
public:
std::uint32_t count;
C2DEffectSAInterface objects[];
};

class CEscalatorSAInterface
{
public:
RwV3d startPos;
RwV3d bottomPos;
RwV3d topPos;
RwV3d endPos;
std::uint8_t rotation[72]; // CMatrixSAInterface
bool exist;
bool objectCreated;
bool moveDown;
std::uint8_t field_7B; // pad
std::int32_t numIntermediatePlanes;
std::uint32_t numBottomPlanes;
std::uint32_t numTopPlanes;
std::uint8_t field_88[8]; // unused field
RwSphere bounding;
float currentPosition;
CEntitySAInterface* entity;
CObjectSAInterface* objects[42];
};
Loading
Loading