/*** * * Copyright (c) 2001, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. * All Rights Reserved. * * Use, distribution, and modification of this source code and/or resulting * object code is restricted to non-commercial enhancements to products from * Valve LLC. All other use, distribution, or modification is prohibited * without written permission from Valve LLC. * ****/ /* ===== ctf_items.cpp ======================================================== This contains the Flag entity information for the Half-Life : Opposing force CTF Gamemode. */ #include "extdll.h" #include "util.h" #include "cbase.h" #include "player.h" #include "weapons.h" #include "gamerules.h" #include "skill.h" #include "game.h" #include "items.h" #include "ctf_gamerules.h" #include "ctf_powerups.h" extern int gmsgRuneStatus; extern bool g_bSpawnedRunes; edict_t *RuneSelectSpawnPoint(void); static const char* g_pszRuneClassName[] = { IT_CTF_ACCELERATOR_CLASSNAME, IT_CTF_BACKPACK_CLASSNAME, IT_CTF_LONGJUMP_CLASSNAME, IT_CTF_PORTABLEHEV_CLASSNAME, IT_CTF_REGEN_CLASSNAME, }; const char* GetRuneClass(int index) { return g_pszRuneClassName[index]; } LINK_ENTITY_TO_CLASS(info_ctfspawn_powerup, CPowerupCTFBase); LINK_ENTITY_TO_CLASS(item_ctfaccelerator, CPowerupAccelerator); LINK_ENTITY_TO_CLASS(item_ctfbackpack, CPowerupBackpack); LINK_ENTITY_TO_CLASS(item_ctflongjump, CPowerupJumppack); LINK_ENTITY_TO_CLASS(item_ctfportablehev, CPowerupPorthev); LINK_ENTITY_TO_CLASS(item_ctfregeneration, CPowerupRegen); /*************************************** **************************************** RUNES **************************************** ***************************************/ void CPowerupCTFBase::KeyValue(KeyValueData* pkvd) { if (FStrEq(pkvd->szKeyName, "team_no")) { m_teamNo = atoi(pkvd->szValue); pkvd->fHandled = TRUE; } else CBaseEntity::KeyValue(pkvd); } static const char* SelectRuneRandom( int* pRuneFlag ) { switch (RANDOM_LONG(0, ARRAYSIZE(g_pszRuneClassName) - 1)) { case 0: *pRuneFlag = IT_CTF_ACCELERATOR_FLAG; return "models/w_accelerator.mdl"; case 1: *pRuneFlag = IT_CTF_BACKPACK_FLAG; return "models/w_backpack.mdl"; case 2: *pRuneFlag = IT_CTF_REGEN_FLAG; return "models/w_health.mdl"; case 3: *pRuneFlag = IT_CTF_LONGJUMP_FLAG; return "models/w_jumppack.mdl"; case 4: *pRuneFlag = IT_CTF_PORTABLEHEV_FLAG; return "models/w_porthev.mdl"; default: *pRuneFlag = -1; return NULL; } } void CPowerupCTFBase::Spawn(void) { if (GetRuneFlag() < 0) { int flag; const char* modelname = SelectRuneRandom(&flag); if (modelname != NULL && flag > 0) { const char* printname = NULL; if (flag & IT_CTF_ACCELERATOR_FLAG) printname = "Accelerator"; else if (flag & IT_CTF_BACKPACK_FLAG) printname = "Backpack"; else if (flag & IT_CTF_LONGJUMP_FLAG) printname = "Long jump"; else if (flag & IT_CTF_PORTABLEHEV_FLAG) printname = "Portable HEV"; else printname = "Regeneration"; m_iszPrintName = ALLOC_STRING(printname); } SET_MODEL(ENT(pev), SelectRuneRandom(&m_iRuneFlag) ); } else { SET_MODEL(ENT(pev), GetRuneModel()); m_iRuneFlag = GetRuneFlag(); } if (m_iRuneFlag < 0 || !STRING(pev->model)) { ALERT(at_error, "Could not properly setup rune entity. Removing...\n"); UTIL_Remove(this); } #if 1 ALERT(at_console, "Spawning rune %d with model %s.\n", m_iRuneFlag, STRING(pev->model)); #endif m_iszPrintName = MAKE_STRING("Unknown"); m_bTouchable = FALSE; dropped = false; pev->movetype = MOVETYPE_TOSS; pev->solid = SOLID_TRIGGER; vec3_t forward, right, up; UTIL_SetSize(pev, Vector(-15, -15, -15), Vector(15, 15, 15)); pev->angles.z = pev->angles.x = 0; pev->angles.y = RANDOM_LONG(0, 360); //If we got an owner, it means we are either dropping the flag or diying and letting it go. if (pev->owner) g_engfuncs.pfnAngleVectors(pev->owner->v.angles, forward, right, up); else g_engfuncs.pfnAngleVectors(pev->angles, forward, right, up); UTIL_SetOrigin(pev, pev->origin); pev->velocity = (forward * 400) + (up * 200); if (pev->owner == NULL) { pev->origin.z += 16; pev->velocity.z = 300; } pev->owner = NULL; SetTouch(&CPowerupCTFBase::RuneTouch); pev->nextthink = gpGlobals->time + 1; SetThink(&CPowerupCTFBase::MakeTouchable); } void CPowerupCTFBase::RuneRespawn(void) { edict_t *pentSpawnSpot; vec3_t vOrigin; pentSpawnSpot = RuneSelectSpawnPoint(); vOrigin = VARS(pentSpawnSpot)->origin; UTIL_SetOrigin(pev, vOrigin); if (dropped) UTIL_LogPrintf("\"<-1><><>\" triggered triggered \"Respawn_ResistRune\"\n"); Spawn(); } void CPowerupCTFBase::MakeTouchable(void) { m_bTouchable = TRUE; pev->nextthink = gpGlobals->time + 120; // if no one touches it in two minutes, // respawn it somewhere else, so inaccessible // ones will come 'back' SetThink(&CPowerupCTFBase::RuneRespawn); } void CPowerupCTFBase::RuneTouch(CBaseEntity *pOther) { //No toucher? if (!pOther) return; //Not a player? if (!pOther->IsPlayer()) return; //DEAD?! if (pOther->pev->health <= 0) return; //Spectating? if (pOther->pev->movetype == MOVETYPE_NOCLIP) return; // If only a specific team can use this power up, // ensure toucher is allowed to. if (m_teamNo > 0) { // Not in the same team, return. if (pOther->pev->team != m_teamNo - 1) return; } /* //Only one per customer if (pPlayer->m_iRuneStatus) { ClientPrint(pOther->pev, HUD_PRINTCENTER, "You already have a rune!\n"); return; } */ if (!m_bTouchable) return; //pPlayer->m_iRuneStatus = m_iRuneFlag; //Add me the rune flag ClientPrint(pOther->pev, HUD_PRINTCENTER, "You got the rune of %s!\n", STRING(m_iszPrintName)); #if 0 UTIL_LogPrintf("\"%s<%i><%s><%s>\" triggered \"Found_Rune\"\n", STRING(pOther->pev->netname), GETPLAYERUSERID(pOther->edict()), GETPLAYERAUTHID(pOther->edict()), pPlayer->m_szTeamName); #endif if (RANDOM_LONG(0, 1) == 1) EMIT_SOUND(ENT(pev), CHAN_ITEM, "weapons/ammopickup1.wav", 1, ATTN_NORM); else EMIT_SOUND(ENT(pev), CHAN_ITEM, "weapons/ammopickup2.wav", 1, ATTN_NORM); //Update my client side rune hud thingy. /* MESSAGE_BEGIN(MSG_ONE, gmsgRuneStatus, NULL, pOther->pev); WRITE_BYTE(pPlayer->m_iRuneStatus); MESSAGE_END(); */ //And Remove this entity UTIL_Remove(this); } /*************************************** **************************************** RUNES **************************************** ***************************************/ /*---------------------------------------------------------------------- The Rune Game modes Rune 1 - Earth Magic resistance Rune 2 - Black Magic strength Rune 3 - Hell Magic haste Rune 4 - Elder Magic regeneration ----------------------------------------------------------------------*/ BOOL IsRuneSpawnPointValid(CBaseEntity *pSpot) { #if 0 CBaseEntity *ent = NULL; while ((ent = UTIL_FindEntityInSphere(ent, pSpot->pev->origin, 128)) != NULL) { //Try not to spawn it near other runes. if (!strcmp(STRING(ent->pev->classname), "item_rune1") || !strcmp(STRING(ent->pev->classname), "item_rune2") || !strcmp(STRING(ent->pev->classname), "item_rune3") || !strcmp(STRING(ent->pev->classname), "item_rune4")) return FALSE; } #endif return TRUE; } edict_t *RuneSelectSpawnPoint(void) { CBaseEntity *pSpot; pSpot = NULL; // Randomize the start spot for (int i = RANDOM_LONG(1, 5); i > 0; i--) pSpot = UTIL_FindEntityByClassname(pSpot, CTF_POWERUP_SPAWN_ENTITY_CLASSNAME); if (!pSpot) // skip over the null point pSpot = UTIL_FindEntityByClassname(pSpot, CTF_POWERUP_SPAWN_ENTITY_CLASSNAME); CBaseEntity *pFirstSpot = pSpot; do { if (pSpot) { if (IsRuneSpawnPointValid(pSpot)) { if (pSpot->pev->origin == Vector(0, 0, 0)) { pSpot = UTIL_FindEntityByClassname(pSpot, CTF_POWERUP_SPAWN_ENTITY_CLASSNAME); continue; } // if so, go to pSpot goto ReturnSpot; } } // increment pSpot pSpot = UTIL_FindEntityByClassname(pSpot, CTF_POWERUP_SPAWN_ENTITY_CLASSNAME); } while (pSpot != pFirstSpot); // loop if we're not back to the start // we haven't found a place to spawn yet, so kill any guy at the first spawn point and spawn there if (pSpot) goto ReturnSpot; ReturnSpot: if (!pSpot) { ALERT(at_error, "RuneSelectSpawnPoint: no %s on level\n", CTF_POWERUP_SPAWN_ENTITY_CLASSNAME); return INDEXENT(0); } return pSpot->edict(); } void VectorScale(const float *in, float scale, float *out) { out[0] = in[0] * scale; out[1] = in[1] * scale; out[2] = in[2] * scale; } void G_ProjectSource(vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result) { result[0] = point[0] + forward[0] * distance[0] + right[0] * distance[1]; result[1] = point[1] + forward[1] * distance[0] + right[1] * distance[1]; result[2] = point[2] + forward[2] * distance[0] + right[2] * distance[1] + distance[2]; } #define VectorSet(v, x, y, z) (v[0]=(x), v[1]=(y), v[2]=(z)) void DropRune(CBasePlayer *pPlayer) { TraceResult tr; // do they even have a rune? if (pPlayer->m_iRuneStatus == 0) return; // Make Sure there's enough room to drop the rune here // This is so hacky ( the reason why we are doing this), and I hate it to death. UTIL_MakeVectors(pPlayer->pev->v_angle); Vector vecSrc = pPlayer->GetGunPosition(); Vector vecEnd = vecSrc + gpGlobals->v_forward * 32; UTIL_TraceHull(vecSrc, vecEnd, dont_ignore_monsters, human_hull, ENT(pPlayer->pev), &tr); if (tr.flFraction != 1) { ClientPrint(pPlayer->pev, HUD_PRINTCENTER, "Not enough room to drop the rune here."); return; } CBaseEntity *pRune = NULL; const char * runeName; if (pPlayer->m_iRuneStatus == IT_CTF_ACCELERATOR_FLAG) { pRune = CBaseEntity::Create(IT_CTF_ACCELERATOR_CLASSNAME, pPlayer->pev->origin, pPlayer->pev->angles, pPlayer->edict()); runeName = "AcceleratorRune"; if (pRune) ((CPowerupAccelerator*)pRune)->dropped = true; } else if (pPlayer->m_iRuneStatus == IT_CTF_BACKPACK_FLAG) { pRune = CBaseEntity::Create(IT_CTF_BACKPACK_CLASSNAME, pPlayer->pev->origin, pPlayer->pev->angles, pPlayer->edict()); runeName = "BackpackRune"; if (pRune) ((CPowerupBackpack*)pRune)->dropped = true; } else if (pPlayer->m_iRuneStatus == IT_CTF_REGEN_FLAG) { pRune = CBaseEntity::Create(IT_CTF_REGEN_CLASSNAME, pPlayer->pev->origin, pPlayer->pev->angles, pPlayer->edict()); runeName = "RegenRune"; if (pRune) ((CPowerupRegen*)pRune)->dropped = true; } else if (pPlayer->m_iRuneStatus == IT_CTF_LONGJUMP_FLAG) { pRune = CBaseEntity::Create(IT_CTF_LONGJUMP_CLASSNAME, pPlayer->pev->origin, pPlayer->pev->angles, pPlayer->edict()); runeName = "JumppackRune"; if (pRune) ((CPowerupJumppack*)pRune)->dropped = true; } else if (pPlayer->m_iRuneStatus == IT_CTF_PORTABLEHEV_FLAG) { pRune = CBaseEntity::Create(IT_CTF_PORTABLEHEV_CLASSNAME, pPlayer->pev->origin, pPlayer->pev->angles, pPlayer->edict()); runeName = "PorthevRune"; if (pRune) ((CPowerupPorthev*)pRune)->dropped = true; } else { runeName = "Unknown"; } pPlayer->m_iRuneStatus = 0; UTIL_LogPrintf("\"%s<%i><%s><%s>\" triggered \"Dropped_%s\"\n", STRING(pPlayer->pev->netname), GETPLAYERUSERID(pPlayer->edict()), GETPLAYERAUTHID(pPlayer->edict()), pPlayer->m_szTeamName, runeName); MESSAGE_BEGIN(MSG_ONE, gmsgRuneStatus, NULL, pPlayer->pev); WRITE_BYTE(pPlayer->m_iRuneStatus); MESSAGE_END(); } /* ================ SpawnRunes spawn all the runes self is the entity that was created for us, we remove it ================ */ void SpawnRunes(void) { if (g_bSpawnedRunes) return; edict_t *pentSpawnSpot = NULL; for (int i = 0; i < ARRAYSIZE(g_pszRuneClassName); i++) { pentSpawnSpot = RuneSelectSpawnPoint(); CBaseEntity::Create(g_pszRuneClassName[i], VARS(pentSpawnSpot)->origin, VARS(pentSpawnSpot)->angles, NULL); } g_bSpawnedRunes = TRUE; }