namespace FuncPayload { mixin class MXCMovementManager { CPathTrack@ pCurrentPath; float flMaxSpeed = m_flMaxSpeed; float flSpeedMultiplier = m_flSpeedMultiplier; // Variables para el HUD float m_flTotalTrackLength = 0.0f; float m_flDistanceTraveled = 0.0f; // Calcula la longitud total del recorrido void CalculateTotalTrackLength() { m_flTotalTrackLength = 0.0f; string szNextTarget = string(pev.target); int safetyLoop = 0; CBaseEntity@ pCurrNode = null; @pCurrNode = g_EntityFuncs.FindEntityByTargetname(null, szNextTarget); if(pCurrNode is null) { g_Game.AlertMessage(at_console, "[Payload Error] No se pudo encontrar el primer nodo.\n"); } while(pCurrNode !is null && safetyLoop < 500) { string szNext = pCurrNode.pev.target; if(szNext == "" || szNext.IsEmpty()) break; CBaseEntity@ pNextNode = g_EntityFuncs.FindEntityByTargetname(null, szNext); if(pNextNode !is null) { float flSegLen = (pNextNode.pev.origin - pCurrNode.pev.origin).Length(); m_flTotalTrackLength += flSegLen; @pCurrNode = pNextNode; } else break; safetyLoop++; } if(m_flTotalTrackLength <= 10.0f) m_flTotalTrackLength = 1000.0f; } void MovePayloadToFirstTarget() { string szTargetName = string(pev.target); if(szTargetName.IsEmpty()) return; @pCurrentPath = GetPathTrackByTarget(szTargetName); if (pCurrentPath is null) return; float flSafeHeight = m_flHeight; if(flSafeHeight == 0) flSafeHeight = 16.0f; Vector vAdjustedDestination = pCurrentPath.pev.origin; vAdjustedDestination.z += flSafeHeight; g_EntityFuncs.SetOrigin(self, vAdjustedDestination); pev.angles.y += 180; m_flDistanceTraveled = 0.0f; } void Next() { if (pCurrentPath is null) { MovePayloadToFirstTarget(); if(pCurrentPath is null) { StopMoving(); return; } } float flSafeHeight = m_flHeight; if(flSafeHeight == 0) flSafeHeight = 16.0f; Vector vTargetOrigin = pCurrentPath.pev.origin; vTargetOrigin.z += flSafeHeight; Vector vDelta = vTargetOrigin - pev.origin; float flDistanceToNode = vDelta.Length(); float flCurrentSpeed = GetCurrentAdjustedSpeed(); if(flCurrentSpeed < 10.0f) flCurrentSpeed = 10.0f; float flStep = flCurrentSpeed * 0.05f; // CHEQUEO DE LLEGADA AL NODO if (flDistanceToNode <= flStep) { m_flDistanceTraveled += flDistanceToNode; // --- LOGICA DE CHECKPOINT (Usando 'health') --- // 'health' es una propiedad universal que path_track no usa, // así que es perfecta para guardar el tiempo extra. float flBonus = pCurrentPath.pev.health; if(flBonus > 0) { // Sumar tiempo al final del juego (fuser2 es el timestamp de fin) pev.fuser2 += flBonus; // Feedback g_PlayerFuncs.ClientPrintAll(HUD_PRINTCENTER, "CHECKPOINT!\nTIEMPO AGREGADO: +" + int(flBonus) + " SEGUNDOS!"); // Sonido g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_ITEM, "items/smallmedkit1.wav", 1.0, ATTN_NORM, 0, PITCH_NORM); // IMPORTANTE: Poner la vida a 0 para no dar el bonus infinitas veces pCurrentPath.pev.health = 0; } FirePathTarget(pCurrentPath); UpdateSpeedsFromPath(pCurrentPath); string szNextTarget = pCurrentPath.pev.target; if (szNextTarget == "" || szNextTarget.IsEmpty()) { StopMoving(); return; } CPathTrack@ pNextPath = GetPathTrackByTarget(szNextTarget); if (pNextPath !is null) { g_EntityFuncs.SetOrigin(self, vTargetOrigin); @pCurrentPath = pNextPath; } else { StopMoving(); } } else { Vector vDir = vDelta.Normalize(); Vector vNextPos = pev.origin + (vDir * flStep); g_EntityFuncs.SetOrigin(self, vNextPos); m_flDistanceTraveled += flStep; pev.velocity = vDir * flCurrentSpeed; if (pev.velocity != g_vecZero) { Vector vAngles = Math.VecToAngles(vDir); vAngles.y += 180; if(pev.SpawnFlagBitSet(SF_PAYLOAD_NOPITCH)) { vAngles.x = 0; vAngles.z = 0; } pev.angles = vAngles; pev.avelocity = g_vecZero; } } } void Blocked(CBaseEntity@ pBlockingEnt) { pBlockingEnt.TakeDamage(pev, pev, pev.dmg > 0 ? pev.dmg : 50.0f, DMG_CRUSH); } void ForceSetSpeed(float flNewSpeed) { pev.speed = flNewSpeed; } void SetSpeed(float flNewSpeed) { pev.speed = Math.min(flNewSpeed, flMaxSpeed); } void SetMaxSpeed(float flNewMaxSpeed) { flMaxSpeed = flNewMaxSpeed; pev.impulse = int(flMaxSpeed); SetSpeed(pev.speed); } void SetSpeedMultiplier(float flMultiplier) { flSpeedMultiplier = flMultiplier; } float GetCurrentAdjustedSpeed() { return Math.min(pev.speed * flSpeedMultiplier, flMaxSpeed); } void StopMoving() { pev.velocity = g_vecZero; pev.avelocity = g_vecZero; } private void FirePathTarget(CPathTrack@ pFire) { if (string(pFire.pev.message).IsEmpty()) return; g_EntityFuncs.FireTargets(string(pFire.pev.message), self, self, USE_TOGGLE, 0); if (pFire.pev.SpawnFlagBitSet(SF_PATH_FIREONCE)) pFire.pev.message = 0; } private void UpdateSpeedsFromPath(CPathTrack@ pFire) { if (pFire.pev.speed != 0) ForceSetSpeed(pFire.pev.speed); if (pFire.m_flMaxSpeed >= 0) SetMaxSpeed(pFire.m_flMaxSpeed); if (pFire.m_flMaxSpeed == -1) SetMaxSpeed(m_flMaxSpeed); if (pFire.m_flNewSpeed >= 0) SetSpeed(pFire.m_flNewSpeed); if (pFire.m_flNewSpeed == -1) SetSpeed(m_flStartSpeed); } CPathTrack@ GetPathTrackByTarget(string target) { CPathTrack@ pPath = cast(g_EntityFuncs.FindEntityByTargetname(null, target)); if (pPath is null) return null; if (!pPath.pev.ClassNameIs("path_track")) return null; return pPath; } } }