Portable Half-Life SDK. GoldSource and Xash3D. Crossplatform.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

475 lines
12 KiB

/***
*
* 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;
}