#include "common.h" #include "patcher.h" #include "SpecialFX.h" #include "RenderBuffer.h" #include "Timer.h" #include "Sprite.h" #include "Font.h" #include "Text.h" #include "TxdStore.h" #include "FileMgr.h" #include "FileLoader.h" #include "Lights.h" #include "VisibilityPlugins.h" #include "World.h" #include "Particle.h" #include "Shadows.h" #include "General.h" #include "Camera.h" #include "Shadows.h" #include "main.h" WRAPPER void CSpecialFX::Render(void) { EAXJMP(0x518DC0); } WRAPPER void CSpecialFX::Update(void) { EAXJMP(0x518D40); } WRAPPER void CSpecialFX::Init(void) { EAXJMP(0x5189E0); } WRAPPER void CSpecialFX::Shutdown(void) { EAXJMP(0x518BE0); } WRAPPER void CMotionBlurStreaks::RegisterStreak(int32 id, uint8 r, uint8 g, uint8 b, CVector p1, CVector p2) { EAXJMP(0x519460); } CBulletTrace (&CBulletTraces::aTraces)[NUMBULLETTRACES] = *(CBulletTrace(*)[NUMBULLETTRACES])*(uintptr*)0x72B1B8; RxObjSpace3DVertex (&TraceVertices)[6] = *(RxObjSpace3DVertex(*)[6])*(uintptr*)0x649884; RwImVertexIndex (&TraceIndexList)[12] = *(RwImVertexIndex(*)[12])*(uintptr*)0x64986C; void CBulletTraces::Init(void) { for (int i = 0; i < NUMBULLETTRACES; i++) aTraces[i].m_bInUse = false; } void CBulletTraces::AddTrace(CVector* vecStart, CVector* vecTarget) { int index; for (index = 0; index < NUMBULLETTRACES; index++) { if (!aTraces[index].m_bInUse) break; } if (index == NUMBULLETTRACES) return; aTraces[index].m_vecCurrentPos = *vecStart; aTraces[index].m_vecTargetPos = *vecTarget; aTraces[index].m_bInUse = true; aTraces[index].m_framesInUse = 0; aTraces[index].m_lifeTime = 25 + CGeneral::GetRandomNumber() % 32; } void CBulletTraces::Render(void) { for (int i = 0; i < NUMBULLETTRACES; i++) { if (!aTraces[i].m_bInUse) continue; RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)0); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)2); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)2); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpShadowExplosionTex->raster); CVector inf = aTraces[i].m_vecCurrentPos; CVector sup = aTraces[i].m_vecTargetPos; CVector center = (inf + sup) / 2; CVector width = CrossProduct(TheCamera.GetForward(), (sup - inf)); width.Normalise(); width /= 20; uint8 intensity = aTraces[i].m_lifeTime; for (int i = 0; i < ARRAY_SIZE(TraceVertices); i++) RwIm3DVertexSetRGBA(&TraceVertices[i], intensity, intensity, intensity, 0xFF); RwIm3DVertexSetPos(&TraceVertices[0], inf.x + width.x, inf.y + width.y, inf.z + width.z); RwIm3DVertexSetPos(&TraceVertices[1], inf.x - width.x, inf.y - width.y, inf.z - width.z); RwIm3DVertexSetPos(&TraceVertices[2], center.x + width.x, center.y + width.y, center.z + width.z); RwIm3DVertexSetPos(&TraceVertices[3], center.x - width.x, center.y - width.y, center.z - width.z); RwIm3DVertexSetPos(&TraceVertices[4], sup.x + width.x, sup.y + width.y, sup.z + width.z); RwIm3DVertexSetPos(&TraceVertices[5], sup.x - width.x, sup.y - width.y, sup.z - width.z); LittleTest(); if (RwIm3DTransform(TraceVertices, ARRAY_SIZE(TraceVertices), nil, 1)) { RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TraceIndexList, ARRAY_SIZE(TraceIndexList)); RwIm3DEnd(); } } RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)1); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)5); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)6); } void CBulletTraces::Update(void) { for (int i = 0; i < NUMBULLETTRACES; i++) { if (aTraces[i].m_bInUse) aTraces[i].Update(); } } void CBulletTrace::Update(void) { if (m_framesInUse == 0) { m_framesInUse++; return; } if (m_framesInUse > 60) { m_bInUse = false; return; } CVector diff = m_vecCurrentPos - m_vecTargetPos; float remaining = diff.Magnitude(); if (remaining > 0.8f) m_vecCurrentPos = m_vecTargetPos + (remaining - 0.8f) / remaining * diff; else m_bInUse = false; if (--m_lifeTime == 0) m_bInUse = false; m_framesInUse++; } WRAPPER void CBrightLights::RegisterOne(CVector pos, CVector up, CVector right, CVector fwd, uint8 type, uint8 unk1, uint8 unk2, uint8 unk3) { EAXJMP(0x51A410); } RpAtomic * MarkerAtomicCB(RpAtomic *atomic, void *data) { *(RpAtomic**)data = atomic; return atomic; } bool C3dMarker::AddMarker(uint32 identifier, uint16 type, float fSize, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate) { m_nIdentifier = identifier; m_Matrix.SetUnity(); RpAtomic *origAtomic; origAtomic = nil; RpClumpForAllAtomics(C3dMarkers::m_pRpClumpArray[type], MarkerAtomicCB, &origAtomic); RpAtomic *atomic = RpAtomicClone(origAtomic); RwFrame *frame = RwFrameCreate(); RpAtomicSetFrame(atomic, frame); CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); RpGeometry *geometry = RpAtomicGetGeometry(atomic); RpGeometrySetFlags(geometry, RpGeometryGetFlags(geometry) | rpGEOMETRYMODULATEMATERIALCOLOR); m_pAtomic = atomic; m_Matrix.Attach(RwFrameGetMatrix(RpAtomicGetFrame(m_pAtomic))); m_pMaterial = RpGeometryGetMaterial(geometry, 0); m_fSize = fSize; m_fStdSize = m_fSize; m_Color.red = r; m_Color.green = g; m_Color.blue = b; m_Color.alpha = a; m_nPulsePeriod = pulsePeriod; m_fPulseFraction = pulseFraction; m_nRotateRate = rotateRate; m_nStartTime = CTimer::GetTimeInMilliseconds(); m_nType = type; return m_pAtomic != nil; } void C3dMarker::DeleteMarkerObject() { RwFrame *frame; m_nIdentifier = 0; m_nStartTime = 0; m_bIsUsed = false; m_nType = MARKERTYPE_INVALID; frame = RpAtomicGetFrame(m_pAtomic); RpAtomicDestroy(m_pAtomic); RwFrameDestroy(frame); m_pAtomic = nil; } void C3dMarker::Render() { if (m_pAtomic == nil) return; RwRGBA *color = RpMaterialGetColor(m_pMaterial); *color = m_Color; m_Matrix.UpdateRW(); CMatrix matrix; matrix.Attach(m_Matrix.m_attachment); matrix.Scale(m_fSize); matrix.UpdateRW(); RwFrameUpdateObjects(RpAtomicGetFrame(m_pAtomic)); SetBrightMarkerColours(m_fBrightness); if (m_nType != MARKERTYPE_ARROW) RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RpAtomicRender(m_pAtomic); if (m_nType != MARKERTYPE_ARROW) RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); ReSetAmbientAndDirectionalColours(); } C3dMarker(&C3dMarkers::m_aMarkerArray)[NUM3DMARKERS] = *(C3dMarker(*)[NUM3DMARKERS])*(uintptr*)0x72D408; int32 &C3dMarkers::NumActiveMarkers = *(int32*)0x8F2A08; RpClump* (&C3dMarkers::m_pRpClumpArray)[NUMMARKERTYPES] = *(RpClump*(*)[NUMMARKERTYPES])*(uintptr*)0x8E2888; void C3dMarkers::Init() { for (int i = 0; i < NUM3DMARKERS; i++) { m_aMarkerArray[i].m_pAtomic = nil; m_aMarkerArray[i].m_nType = MARKERTYPE_INVALID; m_aMarkerArray[i].m_bIsUsed = false; m_aMarkerArray[i].m_nIdentifier = 0; m_aMarkerArray[i].m_Color.red = 255; m_aMarkerArray[i].m_Color.green = 255; m_aMarkerArray[i].m_Color.blue = 255; m_aMarkerArray[i].m_Color.alpha = 255; m_aMarkerArray[i].m_nPulsePeriod = 1024; m_aMarkerArray[i].m_nRotateRate = 5; m_aMarkerArray[i].m_nStartTime = 0; m_aMarkerArray[i].m_fPulseFraction = 0.25f; m_aMarkerArray[i].m_fStdSize = 1.0f; m_aMarkerArray[i].m_fSize = 1.0f; m_aMarkerArray[i].m_fBrightness = 1.0f; m_aMarkerArray[i].m_fCameraRange = 0.0f; } NumActiveMarkers = 0; int txdSlot = CTxdStore::FindTxdSlot("particle"); CTxdStore::PushCurrentTxd(); CTxdStore::SetCurrentTxd(txdSlot); CFileMgr::ChangeDir("\\"); m_pRpClumpArray[MARKERTYPE_ARROW] = CFileLoader::LoadAtomicFile2Return("models/generic/arrow.dff"); m_pRpClumpArray[MARKERTYPE_CYLINDER] = CFileLoader::LoadAtomicFile2Return("models/generic/zonecylb.dff"); CTxdStore::PopCurrentTxd(); } void C3dMarkers::Shutdown() { for (int i = 0; i < NUM3DMARKERS; i++) { if (m_aMarkerArray[i].m_pAtomic != nil) m_aMarkerArray[i].DeleteMarkerObject(); } for (int i = 0; i < NUMMARKERTYPES; i++) { if (m_pRpClumpArray[i] != nil) RpClumpDestroy(m_pRpClumpArray[i]); } } void C3dMarkers::Render() { NumActiveMarkers = 0; ActivateDirectional(); for (int i = 0; i < NUM3DMARKERS; i++) { if (m_aMarkerArray[i].m_bIsUsed) { if (m_aMarkerArray[i].m_fCameraRange < 120.0f) m_aMarkerArray[i].Render(); NumActiveMarkers++; m_aMarkerArray[i].m_bIsUsed = false; } else if (m_aMarkerArray[i].m_pAtomic != nil) { m_aMarkerArray[i].DeleteMarkerObject(); } } } C3dMarker * C3dMarkers::PlaceMarker(uint32 identifier, uint16 type, CVector &pos, float size, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate) { C3dMarker *pMarker; pMarker = nil; float dist = Sqrt((pos.x - FindPlayerCentreOfWorld(0).x) * (pos.x - FindPlayerCentreOfWorld(0).x) + (pos.y - FindPlayerCentreOfWorld(0).y) * (pos.y - FindPlayerCentreOfWorld(0).y)); if (type != MARKERTYPE_ARROW && type != MARKERTYPE_CYLINDER) return nil; for (int i = 0; i < NUM3DMARKERS; i++) { if (!m_aMarkerArray[i].m_bIsUsed && m_aMarkerArray[i].m_nIdentifier == identifier) { pMarker = &m_aMarkerArray[i]; break; } } if (pMarker == nil) { for (int i = 0; i < NUM3DMARKERS; i++) { if (m_aMarkerArray[i].m_nType == MARKERTYPE_INVALID) { pMarker = &m_aMarkerArray[i]; break; } } } if (pMarker == nil && type == MARKERTYPE_ARROW) { for (int i = 0; i < NUM3DMARKERS; i++) { if (dist < m_aMarkerArray[i].m_fCameraRange && m_aMarkerArray[i].m_nType == MARKERTYPE_ARROW && (pMarker == nil || m_aMarkerArray[i].m_fCameraRange > pMarker->m_fCameraRange)) { pMarker = &m_aMarkerArray[i]; break; } } if (pMarker != nil) pMarker->m_nType = MARKERTYPE_INVALID; } if (pMarker == nil) return pMarker; pMarker->m_fCameraRange = dist; if (pMarker->m_nIdentifier == identifier && pMarker->m_nType == type) { if (type == MARKERTYPE_ARROW) { if (dist < 25.0f) { if (dist > 5.0f) pMarker->m_fStdSize = size - (25.0f - dist) * (0.3f * size) / 20.0f; else pMarker->m_fStdSize = size - 0.3f * size; } else { pMarker->m_fStdSize = size; } } else if (type == MARKERTYPE_CYLINDER) { if (dist < size + 12.0f) { if (dist > size + 1.0f) pMarker->m_Color.alpha = (1.0f - (size + 12.0f - dist) * 0.7f / 11.0f) * (float)a; else pMarker->m_Color.alpha = (float)a * 0.3f; } else { pMarker->m_Color.alpha = a; } } float someSin = Sin(TWOPI * (float)((pMarker->m_nPulsePeriod - 1) & (CTimer::GetTimeInMilliseconds() - pMarker->m_nStartTime)) / (float)pMarker->m_nPulsePeriod); pMarker->m_fSize = pMarker->m_fStdSize - pulseFraction * pMarker->m_fStdSize * someSin; if (type == MARKERTYPE_ARROW) { pos.z += 0.25f * pMarker->m_fStdSize * someSin; } else if (type == MARKERTYPE_0) { if (someSin > 0.0f) pMarker->m_Color.alpha = (float)a * 0.7f * someSin + a; else pMarker->m_Color.alpha = (float)a * 0.4f * someSin + a; } if (pMarker->m_nRotateRate) { RwV3d pos = pMarker->m_Matrix.m_matrix.pos; pMarker->m_Matrix.RotateZ(DEGTORAD(pMarker->m_nRotateRate * CTimer::GetTimeStep())); pMarker->m_Matrix.GetPosition() = pos; } if (type == MARKERTYPE_ARROW) pMarker->m_Matrix.GetPosition() = pos; pMarker->m_bIsUsed = true; return pMarker; } if (pMarker->m_nIdentifier != 0) pMarker->DeleteMarkerObject(); pMarker->AddMarker(identifier, type, size, r, g, b, a, pulsePeriod, pulseFraction, rotateRate); if (type == MARKERTYPE_CYLINDER || type == MARKERTYPE_0 || type == MARKERTYPE_2) { float z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z + 1.0f, nil); if (z != 0.0f) pos.z = z - 0.05f * size; } pMarker->m_Matrix.SetTranslate(pos.x, pos.y, pos.z); if (type == MARKERTYPE_2) { pMarker->m_Matrix.RotateX(PI); pMarker->m_Matrix.GetPosition() = pos; } pMarker->m_Matrix.UpdateRW(); if (type == MARKERTYPE_ARROW) { if (dist < 25.0f) { if (dist > 5.0f) pMarker->m_fStdSize = size - (25.0f - dist) * (0.3f * size) / 20.0f; else pMarker->m_fStdSize = size - 0.3f * size; } else { pMarker->m_fStdSize = size; } } else if (type == MARKERTYPE_CYLINDER) { if (dist < size + 12.0f) { if (dist > size + 1.0f) pMarker->m_Color.alpha = (1.0f - (size + 12.0f - dist) * 0.7f / 11.0f) * (float)a; else pMarker->m_Color.alpha = (float)a * 0.3f; } else { pMarker->m_Color.alpha = a; } } pMarker->m_bIsUsed = true; return pMarker; } void C3dMarkers::PlaceMarkerSet(uint32 id, uint16 type, CVector &pos, float size, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate) { PlaceMarker(id, type, pos, size, r, g, b, a, pulsePeriod, pulseFraction, 1); PlaceMarker(id, type, pos, size * 0.93f, r, g, b, a, pulsePeriod, pulseFraction, 2); PlaceMarker(id, type, pos, size * 0.86f, r, g, b, a, pulsePeriod, pulseFraction, -1); } void C3dMarkers::Update() { } int CShinyTexts::NumShinyTexts; CShinyText CShinyTexts::aShinyTexts[NUMSHINYTEXTS]; void CShinyTexts::Init(void) { NumShinyTexts = 0; } void CShinyTexts::RegisterOne(CVector p0, CVector p1, CVector p2, CVector p3, float u0, float v0, float u1, float v1, float u2, float v2, float u3, float v3, uint8 type, uint8 red, uint8 green, uint8 blue, float maxDist) { if(NumShinyTexts >= NUMSHINYTEXTS) return; aShinyTexts[NumShinyTexts].m_camDist = (p0 - TheCamera.GetPosition()).Magnitude(); if(aShinyTexts[NumShinyTexts].m_camDist > maxDist) return; aShinyTexts[NumShinyTexts].m_verts[0] = p0; aShinyTexts[NumShinyTexts].m_verts[1] = p1; aShinyTexts[NumShinyTexts].m_verts[2] = p2; aShinyTexts[NumShinyTexts].m_verts[3] = p3; aShinyTexts[NumShinyTexts].m_texCoords[0].x = u0; aShinyTexts[NumShinyTexts].m_texCoords[0].y = v0; aShinyTexts[NumShinyTexts].m_texCoords[1].x = u1; aShinyTexts[NumShinyTexts].m_texCoords[1].y = v1; aShinyTexts[NumShinyTexts].m_texCoords[2].x = u2; aShinyTexts[NumShinyTexts].m_texCoords[2].y = v2; aShinyTexts[NumShinyTexts].m_texCoords[3].x = u3; aShinyTexts[NumShinyTexts].m_texCoords[3].y = v3; aShinyTexts[NumShinyTexts].m_type = type; aShinyTexts[NumShinyTexts].m_red = red; aShinyTexts[NumShinyTexts].m_green = green; aShinyTexts[NumShinyTexts].m_blue = blue; // Fade out at half the max dist float halfDist = maxDist*0.5f; if(aShinyTexts[NumShinyTexts].m_camDist > halfDist){ float f = 1.0f - (aShinyTexts[NumShinyTexts].m_camDist - halfDist)/halfDist; aShinyTexts[NumShinyTexts].m_red *= f; aShinyTexts[NumShinyTexts].m_green *= f; aShinyTexts[NumShinyTexts].m_blue *= f; } NumShinyTexts++; } void CShinyTexts::Render(void) { int i, ix, v; RwTexture *lastTex = nil; if(NumShinyTexts == 0) return; RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); TempBufferVerticesStored = 0; TempBufferIndicesStored = 0; for(i = 0; i < NumShinyTexts; i++){ if(TempBufferIndicesStored > TEMPBUFFERINDEXSIZE-64 || TempBufferVerticesStored > TEMPBUFFERVERTSIZE-62) RenderOutGeometryBuffer(); uint8 r = aShinyTexts[i].m_red; uint8 g = aShinyTexts[i].m_green; uint8 b = aShinyTexts[i].m_blue; switch(aShinyTexts[i].m_type){ case SHINYTEXT_WALK: if(lastTex != gpWalkDontTex){ RenderOutGeometryBuffer(); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpWalkDontTex)); lastTex = gpWalkDontTex; } quad: v = TempBufferVerticesStored; RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+0], r, g, b, 255); RwIm3DVertexSetPos(&TempBufferRenderVertices[v+0], aShinyTexts[i].m_verts[0].x, aShinyTexts[i].m_verts[0].y, aShinyTexts[i].m_verts[0].z); RwIm3DVertexSetU(&TempBufferRenderVertices[v+0], aShinyTexts[i].m_texCoords[0].x); RwIm3DVertexSetV(&TempBufferRenderVertices[v+0], aShinyTexts[i].m_texCoords[0].y); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+1], r, g, b, 255); RwIm3DVertexSetPos(&TempBufferRenderVertices[v+1], aShinyTexts[i].m_verts[1].x, aShinyTexts[i].m_verts[1].y, aShinyTexts[i].m_verts[1].z); RwIm3DVertexSetU(&TempBufferRenderVertices[v+1], aShinyTexts[i].m_texCoords[1].x); RwIm3DVertexSetV(&TempBufferRenderVertices[v+1], aShinyTexts[i].m_texCoords[1].y); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+2], r, g, b, 255); RwIm3DVertexSetPos(&TempBufferRenderVertices[v+2], aShinyTexts[i].m_verts[2].x, aShinyTexts[i].m_verts[2].y, aShinyTexts[i].m_verts[2].z); RwIm3DVertexSetU(&TempBufferRenderVertices[v+2], aShinyTexts[i].m_texCoords[2].x); RwIm3DVertexSetV(&TempBufferRenderVertices[v+2], aShinyTexts[i].m_texCoords[2].y); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+3], r, g, b, 255); RwIm3DVertexSetPos(&TempBufferRenderVertices[v+3], aShinyTexts[i].m_verts[3].x, aShinyTexts[i].m_verts[3].y, aShinyTexts[i].m_verts[3].z); RwIm3DVertexSetU(&TempBufferRenderVertices[v+3], aShinyTexts[i].m_texCoords[3].x); RwIm3DVertexSetV(&TempBufferRenderVertices[v+3], aShinyTexts[i].m_texCoords[3].y); ix = TempBufferIndicesStored; TempBufferRenderIndexList[ix+0] = 0 + TempBufferVerticesStored; TempBufferRenderIndexList[ix+1] = 1 + TempBufferVerticesStored; TempBufferRenderIndexList[ix+2] = 2 + TempBufferVerticesStored; TempBufferRenderIndexList[ix+3] = 2 + TempBufferVerticesStored; TempBufferRenderIndexList[ix+4] = 1 + TempBufferVerticesStored; TempBufferRenderIndexList[ix+5] = 3 + TempBufferVerticesStored; TempBufferVerticesStored += 4; TempBufferIndicesStored += 6; break; case SHINYTEXT_FLAT: if(lastTex != nil){ RenderOutGeometryBuffer(); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); lastTex = nil; } goto quad; } } RenderOutGeometryBuffer(); NumShinyTexts = 0; RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); } void CShinyTexts::RenderOutGeometryBuffer(void) { if(TempBufferIndicesStored != 0){ LittleTest(); if(RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, rwIM3D_VERTEXUV)){ RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored); RwIm3DEnd(); } TempBufferVerticesStored = 0; TempBufferIndicesStored = 0; } } #define MONEY_MESSAGE_LIFETIME_MS 2000 CMoneyMessage CMoneyMessages::aMoneyMessages[NUMMONEYMESSAGES]; void CMoneyMessage::Render() { const float MAX_SCALE = 4.0f; uint32 nLifeTime = CTimer::GetTimeInMilliseconds() - m_nTimeRegistered; if (nLifeTime >= MONEY_MESSAGE_LIFETIME_MS) m_nTimeRegistered = 0; else { float fLifeTime = (float)nLifeTime / MONEY_MESSAGE_LIFETIME_MS; RwV3d vecOut; float fDistX, fDistY; if (CSprite::CalcScreenCoors(m_vecPosition + CVector(0.0f, 0.0f, fLifeTime), &vecOut, &fDistX, &fDistY, true)) { fDistX *= (0.7 * fLifeTime + 2.0) * m_fSize; fDistY *= (0.7 * fLifeTime + 2.0) * m_fSize; CFont::SetPropOn(); CFont::SetBackgroundOff(); float fScaleY = fDistY / 100.0f; if (fScaleY > MAX_SCALE) fScaleY = MAX_SCALE; float fScaleX = fDistX / 100.0f; if (fScaleX > MAX_SCALE) fScaleX = MAX_SCALE; CFont::SetScale(fScaleX, fScaleY); // maybe use SCREEN_SCALE_X and SCREEN_SCALE_Y here? CFont::SetCentreOn(); CFont::SetCentreSize(SCREEN_WIDTH); CFont::SetJustifyOff(); CFont::SetColor(CRGBA(m_Colour.r, m_Colour.g, m_Colour.b, (255.0f - 255.0f * fLifeTime) * m_fOpacity)); CFont::SetBackGroundOnlyTextOff(); CFont::SetFontStyle(FONT_BANK); CFont::PrintString(vecOut.x, vecOut.y, m_aText); } } } void CMoneyMessages::Init() { for (int32 i = 0; i < NUMMONEYMESSAGES; i++) aMoneyMessages[i].m_nTimeRegistered = 0; } void CMoneyMessages::Render() { for (int32 i = 0; i < NUMMONEYMESSAGES; i++) { if (aMoneyMessages[i].m_nTimeRegistered != 0) aMoneyMessages[i].Render(); } } void CMoneyMessages::RegisterOne(CVector vecPos, const char *pText, uint8 bRed, uint8 bGreen, uint8 bBlue, float fSize, float fOpacity) { uint32 nIndex = 0; while (aMoneyMessages[nIndex].m_nTimeRegistered != 0) { if (++nIndex >= NUMMONEYMESSAGES) return; } // Add data of this money message to the array AsciiToUnicode(pText, aMoneyMessages[nIndex].m_aText); aMoneyMessages[nIndex].m_nTimeRegistered = CTimer::GetTimeInMilliseconds(); aMoneyMessages[nIndex].m_vecPosition = vecPos; aMoneyMessages[nIndex].m_Colour.red = bRed; aMoneyMessages[nIndex].m_Colour.green = bGreen; aMoneyMessages[nIndex].m_Colour.blue = bBlue; aMoneyMessages[nIndex].m_fSize = fSize; aMoneyMessages[nIndex].m_fOpacity = fOpacity; } CRGBA FoamColour(255, 255, 255, 255); unsigned int CSpecialParticleStuff::BoatFromStart; void CSpecialParticleStuff::CreateFoamAroundObject(CMatrix* pMatrix, float innerFw, float innerRg, float innerUp, int32 particles) { float outerFw = innerFw + 5.0f; float outerRg = innerRg + 5.0f; float outerUp = innerUp + 5.0f; for (int attempts = 0; particles > 0 && attempts < 1000; attempts++) { CVector pos; int rnd = CGeneral::GetRandomNumber(); pos.x = (int8)(rnd - 128) * innerFw / 110.0f; pos.y = (int8)((rnd >> 8) - 128) * innerFw / 110.0f; pos.z = 0.0f; if (DotProduct2D(pos, TheCamera.GetForward()) >= 0) continue; // was there any point in adding it here? pos += pMatrix->GetPosition(); pos.z = 2.0f; float fw = Abs(DotProduct(pMatrix->GetForward(), pos - pMatrix->GetPosition())); if (fw >= outerFw) continue; float rg = Abs(DotProduct(pMatrix->GetRight(), pos - pMatrix->GetPosition())); if (rg >= outerRg) continue; float up = Abs(DotProduct(pMatrix->GetUp(), pos - pMatrix->GetPosition())); if (up >= outerUp) continue; if (fw > innerFw || rg > innerRg || up > innerUp) { CParticle::AddParticle(PARTICLE_STEAM2, pos, CVector(0.0f, 0.0f, 0.0f), nil, 4.0f, FoamColour, 1, 0, 0, 0); particles--; } } } void CSpecialParticleStuff::StartBoatFoamAnimation() { BoatFromStart = CTimer::GetTimeInMilliseconds(); } void CSpecialParticleStuff::UpdateBoatFoamAnimation(CMatrix* pMatrix) { static int32 FrameInAnimation = 0; static float X, Y, Z, dX, dY, dZ; CreateFoamAroundObject(pMatrix, 107.0f, 24.1f, 30.5f, 2); uint32 prev = CTimer::GetPreviousTimeInMilliseconds(); uint32 cur = CTimer::GetTimeInMilliseconds(); if (FrameInAnimation != 0) { X += dX; Y += dY; Z += dZ; CVector pos = *pMatrix * CVector(X, Y, Z); CParticle::AddParticle(PARTICLE_STEAM_NY, pos, CVector(0.0f, 0.0f, 0.0f), nil, FrameInAnimation * 0.5f + 2.0f, FoamColour, 1, 0, 0, 0); if (++FrameInAnimation > 15) FrameInAnimation = 0; } if ((cur & 0x3FF) < (prev & 0x3FF)) { FrameInAnimation = 1; int rnd = CGeneral::GetRandomNumber(); X = (int8)(rnd - 128) * 0.2f; Y = (int8)((rnd >> 8) - 128) * 0.2f; Z = 10.0f; rnd = CGeneral::GetRandomNumber(); dX = (int8)(rnd - 128) * 0.02f; dY = (int8)((rnd >> 8) - 128) * 0.02f; dZ = 2.0f; } } STARTPATCHES InjectHook(0x518DE0, &CBulletTraces::Init, PATCH_JUMP); InjectHook(0x518E90, &CBulletTraces::AddTrace, PATCH_JUMP); InjectHook(0x518F20, &CBulletTraces::Render, PATCH_JUMP); InjectHook(0x519240, &CBulletTraces::Update, PATCH_JUMP); InjectHook(0x51B070, &C3dMarker::AddMarker, PATCH_JUMP); InjectHook(0x51B170, &C3dMarker::DeleteMarkerObject, PATCH_JUMP); InjectHook(0x51B1B0, &C3dMarker::Render, PATCH_JUMP); InjectHook(0x51B2B0, C3dMarkers::Init, PATCH_JUMP); InjectHook(0x51B480, C3dMarkers::PlaceMarker, PATCH_JUMP); InjectHook(0x51BB80, C3dMarkers::PlaceMarkerSet, PATCH_JUMP); InjectHook(0x51B400, C3dMarkers::Render, PATCH_JUMP); InjectHook(0x51B3B0, C3dMarkers::Shutdown, PATCH_JUMP); InjectHook(0x51A5A0, CShinyTexts::Init, PATCH_JUMP); InjectHook(0x51AAB0, CShinyTexts::RegisterOne, PATCH_JUMP); InjectHook(0x51A5B0, CShinyTexts::Render, PATCH_JUMP); InjectHook(0x51AA50, CShinyTexts::RenderOutGeometryBuffer, PATCH_JUMP); InjectHook(0x51AF70, CMoneyMessages::Init, PATCH_JUMP); InjectHook(0x51B030, CMoneyMessages::Render, PATCH_JUMP); ENDPATCHES