ReGameDLL_CS/regamedll/dlls/career_tasks.cpp
Francisco Muñoz d4deabfe59
CZero: Fix broken Career Tasks (#836)
* Fix HandleEnemyKill params and Hostage target detection

* Var name mistake

* Grouping more code on FIXES macro
2023-07-10 15:44:31 +03:00

594 lines
14 KiB
C++

#include "precompiled.h"
CCareerTaskManager *TheCareerTasks = nullptr;
CCareerTask *CPreventDefuseTask::NewTask(const char *taskName, GameEventType event, const char *weaponName, int n, bool mustLive, bool crossRounds, int id, bool isComplete)
{
CPreventDefuseTask *pNewTask = new CPreventDefuseTask(taskName, event, weaponName, n, mustLive, crossRounds, id, isComplete);
pNewTask->m_bombPlantedThisRound = false;
pNewTask->m_defuseStartedThisRound = false;
return reinterpret_cast<CCareerTask *>(pNewTask);
}
CPreventDefuseTask::CPreventDefuseTask(const char *taskName, GameEventType event, const char *weaponName, int n, bool mustLive, bool crossRounds, int id, bool isComplete) :
CCareerTask(taskName, event, weaponName, n, mustLive, crossRounds, id, isComplete)
{
m_bombPlantedThisRound = false;
m_defuseStartedThisRound = false;
}
void CPreventDefuseTask::Reset()
{
m_bombPlantedThisRound = false;
m_defuseStartedThisRound = false;
CCareerTask::Reset();
}
void CPreventDefuseTask::OnEvent(GameEventType event, CBasePlayer *pAttacker, CBasePlayer *pVictim)
{
if (IsComplete())
return;
if (event == EVENT_BOMB_PLANTED)
{
m_bombPlantedThisRound = true;
}
else if (event == EVENT_BOMB_DEFUSING)
{
m_defuseStartedThisRound = true;
}
CCareerTask::OnEvent(event, pAttacker, pVictim);
if (event >= EVENT_ROUND_DRAW && event <= EVENT_ROUND_LOSS)
{
m_bombPlantedThisRound = false;
m_defuseStartedThisRound = false;
}
}
CCareerTask *CCareerTask::NewTask(const char *taskName, GameEventType event, const char *weaponName, int n, bool mustLive, bool crossRounds, int id, bool isComplete)
{
CCareerTask *pTask = new CCareerTask(taskName, event, weaponName, n, mustLive, crossRounds, id, isComplete);
return pTask;
}
CCareerTask::CCareerTask(const char *taskName, GameEventType event, const char *weaponName, int n, bool mustLive, bool crossRounds, int id, bool isComplete)
{
m_isComplete = isComplete;
m_event = event;
m_eventsNeeded = n;
m_name = taskName;
m_eventsSeen = 0;
m_mustLive = mustLive;
m_crossRounds = crossRounds;
m_diedThisRound = false;
m_id = id;
m_weaponId = AliasToWeaponID(weaponName);
m_weaponClassId = AliasToWeaponClass(weaponName);
m_rescuer = (Q_stricmp(taskName, "stoprescue") == 0);
m_defuser = (Q_stricmp(taskName, "killdefuser") == 0);
m_vip = (Q_stricmp(taskName, "killvip") == 0);
if (event == EVENT_ALL_HOSTAGES_RESCUED)
{
m_mustLive = true;
m_crossRounds = false;
}
if (m_isComplete)
{
MESSAGE_BEGIN(MSG_ALL, gmsgCZCareer);
WRITE_STRING("TASKDONE");
WRITE_BYTE(m_id);
MESSAGE_END();
}
}
void CCareerTask::Reset()
{
m_eventsSeen = 0;
m_isComplete = false;
MESSAGE_BEGIN(MSG_ALL, gmsgCZCareer);
WRITE_STRING("TASKUNDONE");
WRITE_BYTE(m_id);
MESSAGE_END();
MESSAGE_BEGIN(MSG_ALL, gmsgCZCareer);
WRITE_STRING("TASKPART");
WRITE_BYTE(m_id);
WRITE_SHORT(m_eventsSeen);
MESSAGE_END();
}
void CCareerTask::SendPartialNotification()
{
MESSAGE_BEGIN(MSG_ALL, gmsgCZCareer);
WRITE_STRING("TASKPART");
WRITE_BYTE(m_id);
WRITE_SHORT(m_eventsSeen);
MESSAGE_END();
UTIL_LogPrintf("Career Task Partial %d %d\n", m_id, m_eventsSeen);
}
void CCareerTask::OnWeaponKill(int weaponId, int weaponClassId, bool headshot, bool killerHasShield, CBasePlayer *pAttacker, CBasePlayer *pVictim)
{
if (m_isComplete || (m_event != EVENT_KILL && (m_event != EVENT_HEADSHOT || !headshot)))
return;
if (!pVictim || (m_defuser && !pVictim->m_bIsDefusing) || (m_vip && !pVictim->m_bIsVIP))
return;
if (m_rescuer)
{
int hostagesCount = 0;
CHostage *pHostage = nullptr;
while ((pHostage = UTIL_FindEntityByClassname(pHostage, "hostage_entity")))
{
if (!pHostage->IsAlive())
continue;
#ifndef REGAMEDLL_FIXES
if (!pHostage->IsFollowingSomeone())
continue;
if (pHostage->m_target == pVictim)
#else
if (pHostage->IsFollowing(pVictim))
#endif
hostagesCount++;
}
if (!hostagesCount)
return;
}
if (m_weaponId == WEAPON_SHIELDGUN)
{
if (!killerHasShield)
return;
}
else if (m_weaponId)
{
if (m_weaponId != weaponId)
return;
}
else
{
if (!m_weaponClassId || m_weaponClassId != weaponClassId)
return;
}
m_eventsSeen++;
SendPartialNotification();
}
void CCareerTask::OnWeaponInjury(int weaponId, int weaponClassId, bool attackerHasShield, CBasePlayer *pAttacker)
{
if (m_isComplete || m_event != EVENT_PLAYER_TOOK_DAMAGE)
return;
if (m_weaponId == WEAPON_SHIELDGUN)
{
if (!attackerHasShield)
return;
}
else if (m_weaponId)
{
if (m_weaponId != weaponId)
return;
}
else
{
if (!m_weaponClassId || m_weaponClassId != weaponClassId)
return;
}
m_eventsSeen++;
SendPartialNotification();
}
void CCareerTask::OnEvent(GameEventType event, CBasePlayer *pVictim, CBasePlayer *pAttacker)
{
if (m_isComplete)
return;
if (event == m_event)
{
if ((m_defuser && !pAttacker->m_bIsDefusing) || (m_vip && !pAttacker->m_bIsVIP))
return;
if (m_rescuer)
{
int hostagesCount = 0;
CHostage *pHostage = nullptr;
while ((pHostage = UTIL_FindEntityByClassname(pHostage, "hostage_entity")))
{
if (!pHostage->IsAlive())
continue;
#ifndef REGAMEDLL_FIXES
if (!pHostage->IsFollowingSomeone())
continue;
if (pHostage->m_target == pAttacker)
#else
if (pHostage->IsFollowing(pAttacker))
#endif
hostagesCount++;
}
if (!hostagesCount)
return;
}
if ((m_event != EVENT_KILL || (!m_weaponId && !m_weaponClassId))
&& (m_event != EVENT_HEADSHOT || (!m_weaponId && !m_weaponClassId))
&& (m_event != EVENT_PLAYER_TOOK_DAMAGE || (!m_weaponId && !m_weaponClassId)))
{
if (m_event == EVENT_ROUND_WIN)
{
if (!Q_strcmp(m_name, "defendhostages"))
{
int hostagesCount = 0;
CHostage *pHostage = nullptr;
while ((pHostage = UTIL_FindEntityByClassname(pHostage, "hostage_entity")))
{
if (pHostage->pev->takedamage != DAMAGE_YES && pHostage->pev->deadflag != DEAD_DEAD)
hostagesCount++;
}
if (!hostagesCount)
{
m_eventsSeen++;
SendPartialNotification();
}
}
else if (!Q_strcmp(m_name, "hostagessurvive"))
{
int hostagesCount = 0;
CHostage *pHostage = nullptr;
while ((pHostage = UTIL_FindEntityByClassname(pHostage, "hostage_entity")))
{
if (pHostage && pHostage->IsDead())
hostagesCount++;
}
if (!hostagesCount)
{
m_eventsSeen++;
SendPartialNotification();
}
}
else if (!Q_strcmp(m_name, "winfast"))
{
if (m_eventsNeeded >= TheCareerTasks->GetRoundElapsedTime())
{
m_eventsSeen = m_eventsNeeded;
SendPartialNotification();
}
}
else if (IsTaskCompletableThisRound())
{
m_eventsSeen++;
SendPartialNotification();
}
}
else
{
m_eventsSeen++;
SendPartialNotification();
}
}
}
CBasePlayer *pLocalPlayer = UTIL_GetLocalPlayer();
if (event == m_event && !m_mustLive && m_eventsSeen >= m_eventsNeeded && IsTaskCompletableThisRound())
{
EMIT_SOUND(ENT(pLocalPlayer->pev), CHAN_VOICE, "events/task_complete.wav", VOL_NORM, ATTN_NORM);
m_isComplete = true;
MESSAGE_BEGIN(MSG_ALL, gmsgCZCareer);
WRITE_STRING("TASKDONE");
WRITE_BYTE(m_id);
MESSAGE_END();
if (TheTutor)
{
TheTutor->OnEvent(EVENT_CAREER_TASK_DONE);
}
UTIL_LogPrintf("Career Task Done %d\n", m_id);
if (m_event == EVENT_ROUND_WIN && !Q_strcmp(m_name, "winfast"))
{
TheCareerTasks->SetFinishedTaskTime(int(TheCareerTasks->GetRoundElapsedTime()));
pLocalPlayer->SyncRoundTimer();
}
}
else if (event >= EVENT_ROUND_DRAW)
{
if (event > EVENT_ROUND_LOSS)
{
if (event == EVENT_DIE && (m_mustLive || m_crossRounds))
{
m_eventsSeen = 0;
SendPartialNotification();
m_diedThisRound = true;
}
}
else if (m_mustLive)
{
if (m_eventsSeen >= m_eventsNeeded && !m_diedThisRound && IsTaskCompletableThisRound())
{
EMIT_SOUND(ENT(pLocalPlayer->pev), CHAN_VOICE, "events/task_complete.wav", VOL_NORM, ATTN_NORM);
m_isComplete = true;
MESSAGE_BEGIN(MSG_ALL, gmsgCZCareer);
WRITE_STRING("TASKDONE");
WRITE_BYTE(m_id);
MESSAGE_END();
UTIL_LogPrintf("Career Task Done %d\n", m_id);
if (m_event == EVENT_ROUND_WIN && !Q_strcmp(m_name, "winfast"))
{
TheCareerTasks->SetFinishedTaskTime(int(TheCareerTasks->GetRoundElapsedTime()));
pLocalPlayer->SyncRoundTimer();
}
if (TheTutor)
{
TheTutor->OnEvent(EVENT_CAREER_TASK_DONE);
}
}
m_diedThisRound = false;
if (m_mustLive)
{
m_eventsSeen = 0;
SendPartialNotification();
}
}
}
}
void CCareerTaskManager::Create()
{
if (TheCareerTasks)
{
TheCareerTasks->Reset();
return;
}
TheCareerTasks = new CCareerTaskManager;
}
CCareerTaskManager::CCareerTaskManager()
{
m_taskTime = 0;
Reset();
}
void CCareerTaskManager::Reset(bool deleteTasks)
{
if (deleteTasks)
{
for (auto task : m_tasks)
delete task;
m_tasks.clear();
m_nextId = 0;
}
else
{
for (auto task : m_tasks)
task->Reset();
}
m_finishedTaskTime = 0;
m_finishedTaskRound = 0;
m_shouldLatchRoundEndMessage = false;
m_roundStartTime = gpGlobals->time + freezetime.value;
}
void CCareerTaskManager::SetFinishedTaskTime(int val)
{
m_finishedTaskTime = val;
m_finishedTaskRound = CSGameRules()->m_iTotalRoundsPlayed;
}
const TaskInfo CCareerTaskManager::m_taskInfo[] =
{
{ "defuse", EVENT_BOMB_DEFUSED, &CCareerTask::NewTask },
{ "plant", EVENT_BOMB_PLANTED, &CCareerTask::NewTask },
{ "rescue", EVENT_HOSTAGE_RESCUED, &CCareerTask::NewTask },
{ "killall", EVENT_KILL_ALL, &CCareerTask::NewTask },
{ "kill", EVENT_KILL, &CCareerTask::NewTask },
{ "killwith", EVENT_KILL, &CCareerTask::NewTask },
{ "killblind", EVENT_KILL_FLASHBANGED, &CCareerTask::NewTask },
{ "killvip", EVENT_KILL, &CCareerTask::NewTask },
{ "headshot", EVENT_HEADSHOT, &CCareerTask::NewTask },
{ "headshotwith", EVENT_HEADSHOT, &CCareerTask::NewTask },
{ "winfast", EVENT_ROUND_WIN, &CCareerTask::NewTask },
{ "rescue", EVENT_HOSTAGE_RESCUED, &CCareerTask::NewTask },
{ "rescueall", EVENT_ALL_HOSTAGES_RESCUED, &CCareerTask::NewTask },
{ "injure", EVENT_PLAYER_TOOK_DAMAGE, &CCareerTask::NewTask },
{ "injurewith", EVENT_PLAYER_TOOK_DAMAGE, &CCareerTask::NewTask },
{ "killdefuser", EVENT_KILL, &CCareerTask::NewTask },
{ "stoprescue", EVENT_KILL, &CCareerTask::NewTask },
{ "defendhostages", EVENT_ROUND_WIN, &CCareerTask::NewTask },
{ "hostagessurvive", EVENT_ROUND_WIN, &CCareerTask::NewTask },
{ "preventdefuse", EVENT_ROUND_WIN, &CPreventDefuseTask::NewTask },
{ nullptr, EVENT_INVALID, &CCareerTask::NewTask },
};
void CCareerTaskManager::AddTask(const char *taskName, const char *weaponName, int eventCount, bool mustLive, bool crossRounds, bool isComplete)
{
m_nextId++;
for (auto &taskInfo : m_taskInfo)
{
if (taskInfo.taskName)
{
if (!Q_stricmp(taskInfo.taskName, taskName))
{
CCareerTask *newTask = taskInfo.factory
(
taskInfo.taskName,
taskInfo.event,
weaponName,
eventCount,
mustLive,
crossRounds,
m_nextId,
isComplete
);
m_tasks.push_back(newTask);
if (taskInfo.event == EVENT_ROUND_WIN && !Q_strcmp(taskName, "winfast"))
{
m_taskTime = eventCount;
if (isComplete)
{
m_finishedTaskTime = eventCount;
}
}
return;
}
}
}
MESSAGE_BEGIN(MSG_ALL, gmsgCZCareer);
WRITE_STRING("TASKDONE");
WRITE_BYTE(m_nextId);
MESSAGE_END();
}
void CCareerTaskManager::HandleEvent(GameEventType event, CBasePlayer *pAttacker, CBasePlayer *pVictim)
{
if (event == EVENT_ROUND_START)
{
m_roundStartTime = gpGlobals->time;
return;
}
if ((event <= EVENT_ROUND_LOSS && event >= EVENT_ROUND_DRAW) && m_shouldLatchRoundEndMessage)
{
m_roundEndMessage = event;
return;
}
for (auto task : m_tasks) {
task->OnEvent(event, pAttacker, pVictim);
}
}
void CCareerTaskManager::HandleWeaponKill(int weaponId, int weaponClassId, bool headshot, bool killerHasShield, CBasePlayer *pAttacker, CBasePlayer *pVictim)
{
for (auto task : m_tasks) {
task->OnWeaponKill(weaponId, weaponClassId, headshot, killerHasShield, pAttacker, pVictim);
}
}
void CCareerTaskManager::HandleEnemyKill(bool wasBlind, const char *weaponName, bool headshot, bool killerHasShield, CBasePlayer *pAttacker, CBasePlayer *pVictim)
{
HandleWeaponKill(AliasToWeaponID(weaponName), AliasToWeaponClass(weaponName), headshot, killerHasShield, pAttacker, pVictim);
HandleEvent(EVENT_KILL, pAttacker, pVictim);
if (headshot)
{
HandleEvent(EVENT_HEADSHOT, pAttacker, pVictim);
}
if (wasBlind)
{
HandleEvent(EVENT_KILL_FLASHBANGED, pAttacker, pVictim);
}
}
void CCareerTaskManager::HandleWeaponInjury(int weaponId, int weaponClassId, bool attackerHasShield, CBasePlayer *pAttacker)
{
for (auto task : m_tasks) {
task->OnWeaponInjury(weaponId, weaponClassId, attackerHasShield, pAttacker);
}
}
void CCareerTaskManager::HandleEnemyInjury(const char *weaponName, bool attackerHasShield, CBasePlayer *pAttacker)
{
HandleWeaponInjury(AliasToWeaponID(weaponName), AliasToWeaponClass(weaponName), attackerHasShield, pAttacker);
HandleEvent(EVENT_PLAYER_TOOK_DAMAGE);
}
void CCareerTaskManager::HandleDeath(int team, CBasePlayer *pAttacker)
{
int enemyTeam = (Q_strcmp(humans_join_team.string, "CT") != 0) ? CT : TERRORIST;
int numEnemies = 0;
if (enemyTeam != team)
return;
for (int i = 1; i <= gpGlobals->maxClients; i++)
{
CBasePlayer *pPlayer = UTIL_PlayerByIndex(i);
if (pPlayer && pPlayer->m_iTeam == enemyTeam && pPlayer->IsAlive())
numEnemies++;
}
if (!numEnemies)
{
HandleEvent(EVENT_KILL_ALL);
}
}
bool CCareerTaskManager::AreAllTasksComplete()
{
for (auto task : m_tasks) {
if (!task->IsComplete())
return false;
}
return true;
}
int CCareerTaskManager::GetNumRemainingTasks()
{
int nTasksCount = 0;
for (auto task : m_tasks) {
if (task->IsComplete())
nTasksCount++;
}
return nTasksCount;
}
float CCareerTaskManager::GetRoundElapsedTime()
{
return (gpGlobals->time - m_roundStartTime);
}
void CCareerTaskManager::LatchRoundEndMessage()
{
m_shouldLatchRoundEndMessage = true;
}
void CCareerTaskManager::UnlatchRoundEndMessage()
{
m_shouldLatchRoundEndMessage = false;
HandleEvent(m_roundEndMessage);
}