Skip to content

Commit 942c4f9

Browse files
committed
Roadsign support
1 parent b27c977 commit 942c4f9

File tree

8 files changed

+208
-57
lines changed

8 files changed

+208
-57
lines changed

Client/game_sa/C2DEffectSA.cpp

+127-18
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,10 @@ void C2DEffectSA::SetRoadsignText(const std::string& text, std::uint8_t line)
198198
if (IsValidRoadsign())
199199
{
200200
if (!m_effectInterface->effect.roadsign.text)
201+
{
201202
m_effectInterface->effect.roadsign.text = static_cast<char*>(std::malloc(64));
203+
MemSetFast(m_effectInterface->effect.roadsign.text, 0, 64);
204+
}
202205

203206
if (!m_effectInterface->effect.roadsign.text)
204207
return;
@@ -207,6 +210,32 @@ void C2DEffectSA::SetRoadsignText(const std::string& text, std::uint8_t line)
207210
}
208211
}
209212

213+
RwV2d& C2DEffectSA::GetRoadsignSize()
214+
{
215+
if (IsValidRoadsign())
216+
return m_effectInterface->effect.roadsign.size;
217+
218+
static RwV2d dummySize{0, 0};
219+
return dummySize;
220+
}
221+
222+
RwV3d& C2DEffectSA::GetRoadsignRotation()
223+
{
224+
if (IsValidRoadsign())
225+
return m_effectInterface->effect.roadsign.rotation;
226+
227+
static RwV3d dummyRotation{0, 0, 0};
228+
return dummyRotation;
229+
}
230+
231+
std::string C2DEffectSA::GetRoadsignText() const
232+
{
233+
if (IsValidRoadsign() && m_effectInterface->effect.roadsign.text)
234+
return std::string(m_effectInterface->effect.roadsign.text, 64);
235+
236+
return "";
237+
}
238+
210239
void C2DEffectSA::SetEscalatorBottom(const RwV3d& bottom)
211240
{
212241
if (IsValidEscalator())
@@ -231,6 +260,99 @@ void C2DEffectSA::SetEscalatorDirection(std::uint8_t direction)
231260
m_effectInterface->effect.escalator.direction = direction;
232261
}
233262

263+
RwV3d& C2DEffectSA::GetEscalatorBottom()
264+
{
265+
if (IsValidEscalator())
266+
return m_effectInterface->effect.escalator.bottom;
267+
268+
static RwV3d dummyBottom{0, 0, 0};
269+
return dummyBottom;
270+
}
271+
272+
RwV3d& C2DEffectSA::GetEscalatorTop()
273+
{
274+
if (IsValidEscalator())
275+
return m_effectInterface->effect.escalator.top;
276+
277+
static RwV3d dummyTop{0, 0, 0};
278+
return dummyTop;
279+
}
280+
281+
RwV3d& C2DEffectSA::GetEscalatorEnd()
282+
{
283+
if (IsValidEscalator())
284+
return m_effectInterface->effect.escalator.end;
285+
286+
static RwV3d dummyEnd{0, 0, 0};
287+
return dummyEnd;
288+
}
289+
290+
RpAtomic* C2DEffectSA::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)
291+
{
292+
// Call CCustomRoadsignMgr::CreateRoadsignAtomic
293+
RpAtomic* atomic = ((RpAtomic*(__cdecl*)(float, float, std::int32_t, char*, char*, char*, char*, std::int32_t, std::uint8_t))0x6FF2D0)(sizeX, sizeY, numLines, line1, line2, line3, line4, numLetters, palleteID);
294+
RwFrame* frame = RpAtomicGetFrame(atomic);
295+
RwFrameSetIdentity(frame);
296+
297+
const RwV3d axis0{1.0f, 0.0f, 0.0f}, axis1{0.0f, 1.0f, 0.0f}, axis2{0.0f, 0.0f, 1.0f};
298+
RwFrameRotate(frame, &axis2, rotation.z, rwCOMBINEREPLACE);
299+
RwFrameRotate(frame, &axis0, rotation.x, rwCOMBINEPOSTCONCAT);
300+
RwFrameRotate(frame, &axis1, rotation.y, rwCOMBINEPOSTCONCAT);
301+
302+
RwFrameTranslate(frame, &position, TRANSFORM_AFTER);
303+
RwFrameUpdateObjects(frame);
304+
305+
return atomic;
306+
}
307+
308+
std::uint32_t C2DEffectSA::Roadsign_GetPalleteIDFromFlags(std::uint8_t flags)
309+
{
310+
std::uint32_t id = (flags >> 4) & 3;
311+
return id <= 3 ? id : 0;
312+
}
313+
314+
std::uint32_t C2DEffectSA::Roadsign_GetNumLettersFromFlags(std::uint8_t flags)
315+
{
316+
std::uint32_t letters = (flags >> 2) & 3;
317+
switch (letters)
318+
{
319+
case 1u:
320+
return 2;
321+
case 2u:
322+
return 4;
323+
case 3u:
324+
return 8;
325+
default:
326+
return 16;
327+
}
328+
}
329+
330+
std::uint32_t C2DEffectSA::Roadsign_GetNumLinesFromFlags(std::uint8_t flags)
331+
{
332+
std::uint32_t lines = flags & 3;
333+
return (lines <= 3 && lines > 0) ? lines : 4;
334+
}
335+
336+
void C2DEffectSA::Roadsign_DestroyAtomic(C2DEffectSAInterface* effect)
337+
{
338+
if (!effect)
339+
return;
340+
341+
t2dEffectRoadsign& roadsign = effect->effect.roadsign;
342+
if (roadsign.atomic)
343+
{
344+
RwFrame* frame = RpAtomicGetFrame(roadsign.atomic);
345+
if (frame)
346+
{
347+
RpAtomicSetFrame(roadsign.atomic, nullptr);
348+
RwFrameDestroy(frame);
349+
}
350+
351+
RpAtomicDestroy(roadsign.atomic);
352+
roadsign.atomic = nullptr;
353+
}
354+
}
355+
234356
C2DEffectSAInterface* C2DEffectSA::CreateCopy(C2DEffectSAInterface* effect)
235357
{
236358
C2DEffectSAInterface* copy = new C2DEffectSAInterface();
@@ -250,17 +372,16 @@ C2DEffectSAInterface* C2DEffectSA::CreateCopy(C2DEffectSAInterface* effect)
250372
copy->effect.roadsign.text = static_cast<char*>(std::malloc(64));
251373
if (copy->effect.roadsign.text)
252374
{
253-
std::memset(copy->effect.roadsign.text, 0, 64);
254-
std::strncpy(copy->effect.roadsign.text, effect->effect.roadsign.text, 64);
255-
}
375+
MemSetFast(copy->effect.roadsign.text, 0, 64);
376+
MemCpyFast(copy->effect.roadsign.text, effect->effect.roadsign.text, 64);
256377

257-
copy->effect.roadsign.atomic = RpAtomicClone(effect->effect.roadsign.atomic);
378+
copy->effect.roadsign.atomic = Roadsign_CreateAtomic(copy->position, copy->effect.roadsign.rotation, copy->effect.roadsign.size.x, copy->effect.roadsign.size.y, Roadsign_GetNumLinesFromFlags(copy->effect.roadsign.flags), &copy->effect.roadsign.text[0], &copy->effect.roadsign.text[16], &copy->effect.roadsign.text[32], &copy->effect.roadsign.text[48], Roadsign_GetNumLettersFromFlags(copy->effect.roadsign.flags), Roadsign_GetPalleteIDFromFlags(copy->effect.roadsign.flags));
379+
}
258380
}
259381

260382
return copy;
261383
}
262384

263-
// C2DEffect::Shutdown causes random unknown crash in ntdll.dll so we need own function
264385
void C2DEffectSA::Shutdown(C2DEffectSAInterface* effect)
265386
{
266387
if (!effect)
@@ -269,25 +390,13 @@ void C2DEffectSA::Shutdown(C2DEffectSAInterface* effect)
269390
if (effect->type == e2dEffectType::ROADSIGN)
270391
{
271392
t2dEffectRoadsign& roadsign = effect->effect.roadsign;
393+
Roadsign_DestroyAtomic(effect);
272394

273395
if (roadsign.text)
274396
{
275397
std::free(roadsign.text);
276398
roadsign.text = nullptr;
277399
}
278-
279-
if (roadsign.atomic)
280-
{
281-
RwFrame* frame = RpAtomicGetFrame(roadsign.atomic);
282-
if (frame)
283-
{
284-
RpAtomicSetFrame(roadsign.atomic, nullptr);
285-
RwFrameDestroy(frame);
286-
}
287-
288-
RpAtomicDestroy(roadsign.atomic);
289-
roadsign.atomic = nullptr;
290-
}
291400
}
292401
else if (effect->type == e2dEffectType::LIGHT)
293402
{

Client/game_sa/C2DEffectSA.h

+13-8
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class C2DEffectSA : public C2DEffect
4141
~C2DEffectSA() = default;
4242

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

4546
bool IsValidLight() const noexcept { return m_effectInterface && m_effectInterface->type == e2dEffectType::LIGHT; };
4647
bool IsValidRoadsign() const noexcept { return m_effectInterface && m_effectInterface->type == e2dEffectType::ROADSIGN; }
@@ -80,7 +81,7 @@ class C2DEffectSA : public C2DEffect
8081
std::uint8_t GetCoronaFlareType() const override { return IsValidLight() ? m_effectInterface->effect.light.coronaFlareType : 0; }
8182
std::uint16_t GetLightFlags() const override { return IsValidLight() ? m_effectInterface->effect.light.flags : 0; }
8283
std::int8_t GetShadowDistance() const override { return IsValidLight() ? m_effectInterface->effect.light.shadowZDistance : 0; }
83-
CVector GetCoronaOffsets() const override;
84+
CVector GetCoronaOffsets() const override;
8485
RwColor GetCoronaColor() const override { return IsValidLight() ? m_effectInterface->effect.light.color : RwColor{0,0,0,0}; }
8586
std::string GetCoronaTexture() const override { return IsValidLight() ? (m_effectInterface->effect.light.coronaTex ? m_effectInterface->effect.light.coronaTex->name : "") : ""; }
8687
std::string GetShadowTexture() const override { return IsValidLight() ? (m_effectInterface->effect.light.shadowTex ? m_effectInterface->effect.light.shadowTex->name : "") : ""; }
@@ -100,10 +101,10 @@ class C2DEffectSA : public C2DEffect
100101
void SetRoadsignText(const std::string& text, std::uint8_t line) override;
101102

102103
// Get
103-
RwV2d GetRoadsignSize() const override { return IsValidRoadsign() ? m_effectInterface->effect.roadsign.size : RwV2d{0,0}; }
104-
RwV3d GetRoadsignRotation() const override { return IsValidRoadsign() ? m_effectInterface->effect.roadsign.rotation : RwV3d{0,0,0}; }
104+
RwV2d& GetRoadsignSize() override;
105+
RwV3d& GetRoadsignRotation() override;
105106
std::uint16_t GetRoadsignFlags() const override { return IsValidRoadsign() ? m_effectInterface->effect.roadsign.flags : 0; }
106-
std::string GetRoadsignText() const override { return IsValidRoadsign() ? (m_effectInterface->effect.roadsign.text ? std::string(m_effectInterface->effect.roadsign.text, strnlen(m_effectInterface->effect.roadsign.text, 64)) : "") : ""; }
107+
std::string GetRoadsignText() const override;
107108

108109
// Escalator properties
109110
// Set
@@ -113,12 +114,16 @@ class C2DEffectSA : public C2DEffect
113114
void SetEscalatorDirection(std::uint8_t direction) override;
114115

115116
// Get
116-
RwV3d GetEscalatorBottom() const override { return IsValidEscalator() ? m_effectInterface->effect.escalator.bottom : RwV3d{0, 0, 0}; }
117-
RwV3d GetEscalatorTop() const override { return IsValidEscalator() ? m_effectInterface->effect.escalator.top : RwV3d{0, 0, 0}; }
118-
RwV3d GetEscalatorEnd() const override { return IsValidEscalator() ? m_effectInterface->effect.escalator.end : RwV3d{0, 0, 0}; }
117+
RwV3d& GetEscalatorBottom() override;
118+
RwV3d& GetEscalatorTop() override;
119+
RwV3d& GetEscalatorEnd() override;
119120
std::uint8_t GetEscalatorDirection() const override { return IsValidEscalator() ? m_effectInterface->effect.escalator.direction : 0; }
120121

121-
e2dEffectType GetEffectType() override { return m_effectInterface ? m_effectInterface->type : e2dEffectType::NONE; }
122+
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);
123+
static std::uint32_t Roadsign_GetPalleteIDFromFlags(std::uint8_t flags);
124+
static std::uint32_t Roadsign_GetNumLettersFromFlags(std::uint8_t flags);
125+
static std::uint32_t Roadsign_GetNumLinesFromFlags(std::uint8_t flags);
126+
static void Roadsign_DestroyAtomic(C2DEffectSAInterface* effect);
122127

123128
static C2DEffectSAInterface* CreateCopy(C2DEffectSAInterface* effect);
124129

Client/game_sa/CModelInfoSA.cpp

+27-3
Original file line numberDiff line numberDiff line change
@@ -2157,6 +2157,20 @@ void CModelInfoSA::Update2DFXEffect(C2DEffectSA* effect)
21572157
// Call Fx_c::CreateEntityFx
21582158
((void(__thiscall*)(CFxSAInterface*, CEntitySAInterface*, const char*, RwV3d*, RwMatrix*))FUNC_Fx_c_CreateEntityFx)(fx, entity, effectInterface->effect.particle.szName, &effectInterface->position, matrixTransform);
21592159
}
2160+
2161+
break;
2162+
}
2163+
case e2dEffectType::ROADSIGN:
2164+
{
2165+
t2dEffectRoadsign& roadsign = effectInterface->effect.roadsign;
2166+
C2DEffectSA::Roadsign_DestroyAtomic(effectInterface);
2167+
2168+
std::uint32_t numLines = C2DEffectSA::Roadsign_GetNumLinesFromFlags(roadsign.flags);
2169+
std::uint32_t numLetters = C2DEffectSA::Roadsign_GetNumLettersFromFlags(roadsign.flags);
2170+
std::uint8_t palleteID = C2DEffectSA::Roadsign_GetPalleteIDFromFlags(roadsign.flags);
2171+
roadsign.atomic = C2DEffectSA::Roadsign_CreateAtomic(effectInterface->position, roadsign.rotation, roadsign.size.x, roadsign.size.y, numLines, &roadsign.text[0], &roadsign.text[16], &roadsign.text[32], &roadsign.text[48], numLetters, palleteID);
2172+
2173+
break;
21602174
}
21612175
}
21622176
}
@@ -2301,6 +2315,7 @@ bool CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault)
23012315

23022316
// Prevent creation when stream in but keep in memory so we can restore it later
23032317
effect->type = e2dEffectType::NONE;
2318+
break;
23042319
}
23052320
case e2dEffectType::SUN_GLARE:
23062321
{
@@ -2318,8 +2333,11 @@ bool CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault)
23182333
MapGet(ms_NumOfCustom2DFXEffects, m_pInterface)--;
23192334
ms_Custom2DFXEffects.erase(it);
23202335

2321-
delete effect;
2322-
effect = nullptr;
2336+
if (effect)
2337+
{
2338+
delete effect;
2339+
effect = nullptr;
2340+
}
23232341
}
23242342
}
23252343

@@ -2432,8 +2450,14 @@ void CModelInfoSA::RestoreModified2DFXEffects()
24322450
if (tempVec && (*tempVec)[i])
24332451
{
24342452
MemCpyFast(effectInterface, (*tempVec)[i], sizeof(C2DEffectSAInterface));
2435-
C2DEffectSA::SafeDelete2DFXEffect((*tempVec)[i]);
2453+
effectInterface->effect.roadsign.text = static_cast<char*>(std::malloc(64));
2454+
if (effectInterface->effect.roadsign.text)
2455+
{
2456+
MemSetFast(effectInterface->effect.roadsign.text, 0, 64);
2457+
MemCpyFast(effectInterface->effect.roadsign.text, (*tempVec)[i]->effect.roadsign.text, 64);
2458+
}
24362459

2460+
C2DEffectSA::SafeDelete2DFXEffect((*tempVec)[i]);
24372461
Update2DFXEffect(effect);
24382462
}
24392463
}

Client/game_sa/gamesa_renderware.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ typedef int(__cdecl* RpHAnimIDGetIndex_t)(RpHAnimHierarchy*, int);
106106
typedef RwMatrix*(__cdecl* RpHAnimHierarchyGetMatrixArray_t)(RpHAnimHierarchy*);
107107
typedef RtQuat*(__cdecl* RtQuatRotate_t)(RtQuat* quat, const RwV3d* axis, float angle, RwOpCombineType combineOp);
108108
typedef RwTexture*(__cdecl* RwReadTexture_t)(const char* name, const char* mask);
109+
typedef RwFrame*(__cdecl* RwFrameRotate_t)(RwFrame* frame, const RwV3d* axis, float angle, RwOpCombineType combine);
109110

110111
/*****************************************************************************/
111112
/** Renderware function mappings **/
@@ -197,7 +198,7 @@ RWFUNC(RpHAnimIDGetIndex_t RpHAnimIDGetIndex, (RpHAnimIDGetIndex_t)0xDEAD)
197198
RWFUNC(RpHAnimHierarchyGetMatrixArray_t RpHAnimHierarchyGetMatrixArray, (RpHAnimHierarchyGetMatrixArray_t)0xDEAD)
198199
RWFUNC(RtQuatRotate_t RtQuatRotate, (RtQuatRotate_t)0xDEAD)
199200
RWFUNC(RwReadTexture_t RwReadTexture, reinterpret_cast<RwReadTexture_t>(0xDEAD))
200-
201+
RWFUNC(RwFrameRotate_t RwFrameRotate, reinterpret_cast<RwFrameRotate_t>(0xDEAD))
201202
/*****************************************************************************/
202203
/** GTA function definitions and mappings **/
203204
/*****************************************************************************/

Client/game_sa/gamesa_renderware.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ void InitRwFunctions()
9090
RpHAnimHierarchyGetMatrixArray = (RpHAnimHierarchyGetMatrixArray_t)0x7C5120;
9191
RtQuatRotate = (RtQuatRotate_t)0x7EB7C0;
9292
RwReadTexture = reinterpret_cast<RwReadTexture_t>(0x7F3AC0);
93+
RwFrameRotate = reinterpret_cast<RwFrameRotate_t>(0x7F1010);
9394

9495
SetTextureDict = (SetTextureDict_t)0x007319C0;
9596
LoadClumpFile = (LoadClumpFile_t)0x005371F0;

0 commit comments

Comments
 (0)