From c1660a0e48b923308ee479fbf2b7c4b623e407c0 Mon Sep 17 00:00:00 2001 From: Giegue Date: Fri, 6 Oct 2023 01:43:56 -0300 Subject: [PATCH] Add env_xenmaker entity. --- src/dlls/Makefile | 1 + src/dlls/cmbaseextra.h | 42 ++++ src/dlls/dllapi.cpp | 6 +- src/dlls/monster_config.cpp | 22 +- src/dlls/monster_mm.vcxproj | 4 +- src/dlls/monster_mm.vcxproj.filters | 5 +- src/dlls/xenmaker.cpp | 356 ++++++++++++++++++++++++++++ 7 files changed, 430 insertions(+), 6 deletions(-) create mode 100644 src/dlls/xenmaker.cpp diff --git a/src/dlls/Makefile b/src/dlls/Makefile index 2c5b3ed..1a1e854 100644 --- a/src/dlls/Makefile +++ b/src/dlls/Makefile @@ -60,6 +60,7 @@ OBJ = \ util.o \ voltigore.o \ weapons.o \ + xenmaker.o \ zombie.o monster_mm_i386.so: ${OBJ} diff --git a/src/dlls/cmbaseextra.h b/src/dlls/cmbaseextra.h index cabde6f..5712e82 100644 --- a/src/dlls/cmbaseextra.h +++ b/src/dlls/cmbaseextra.h @@ -48,4 +48,46 @@ public: BOOL m_fPlaying; // music is active }; +//========================================================= +// XenMaker - spawns a monster with a teleportation effect. +//========================================================= +class CMXenMaker : public CMBaseMonster +{ +public: + void Spawn(void); + void Precache(void); + void KeyValue(KeyValueData* pkvd); + void EXPORT CyclicUse(edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value); + void EXPORT RetryThink(void); + void StartEffect(void); + void EXPORT MiddleEffect(void); + void EXPORT EndEffect(void); + + int m_iMonsterIndex;// index of the monster that will be created. + + float m_flGround; // z coord of the ground under me, used to make sure no monsters are under the spawner + + float m_flBeamRadius; // Maximum beam strike radius. + int m_iBeamAlpha; + int m_iBeamCount; // Number of single beam instances. + Vector m_vBeamColor; + + float m_flLightRadius; + Vector m_vLightColor; + + float m_flStartSpriteFramerate; + float m_flStartSpriteScale; + int m_iStartSpriteAlpha; + Vector m_vStartSpriteColor; + + float m_flEndSpriteFramerate; + float m_flEndSpriteScale; + int m_iEndSpriteAlpha; + Vector m_vEndSpriteColor; + +private: + void SpawnBeam(void); + int m_iBeamIndex; +}; + #endif // BASEEXTRA_H diff --git a/src/dlls/dllapi.cpp b/src/dlls/dllapi.cpp index edbfa93..88f3ce5 100644 --- a/src/dlls/dllapi.cpp +++ b/src/dlls/dllapi.cpp @@ -166,6 +166,7 @@ monster_type_t monster_types[]= "info_node_air", FALSE, "monstermaker", FALSE, // Extra entities "ambient_music", FALSE, + "env_xenmaker", FALSE, "squadmaker", FALSE, // Aliases "", FALSE }; @@ -711,6 +712,7 @@ edict_t* spawn_monster(int monster_type, Vector origin, Vector angles, int spawn // Extra entities case 32: monsters[monster_index].pMonster = CreateClassPtr((CMMonsterMaker *)NULL); break; case 33: monsters[monster_index].pMonster = CreateClassPtr((CMAmbientMusic *)NULL); break; + case 34: monsters[monster_index].pMonster = CreateClassPtr((CMXenMaker *)NULL); break; } if (monsters[monster_index].pMonster == NULL) @@ -1435,7 +1437,8 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) // Extra entities CMMonsterMaker monstermaker; // 32 CMAmbientMusic ambientmusic; - + CMXenMaker xenmaker; + g_psv_gravity = CVAR_GET_POINTER( "sv_gravity" ); (g_engfuncs.pfnAddServerCommand)("monster", MonsterCommand); @@ -1485,6 +1488,7 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) case 29: stukabat.Precache(); break; case 32: monstermaker.Precache(); break; //case 33: ambientmusic.Precache(); break; + case 34: xenmaker.Precache(); break; } } } diff --git a/src/dlls/monster_config.cpp b/src/dlls/monster_config.cpp index 5b2950a..cc23d5e 100644 --- a/src/dlls/monster_config.cpp +++ b/src/dlls/monster_config.cpp @@ -225,6 +225,22 @@ void scan_monster_cfg(FILE *fp) monster = TRUE; } } + else if (strcmp(monster_types[mIndex].name, "env_xenmaker") == 0) + { + // A monster spawner, add it to the list + if (monster_spawn_count == MAX_MONSTERS) + { + // error.exe + LOG_MESSAGE(PLID, "ERROR: can't add monstermaker, reached MAX_MONSTERS!"); + badent = TRUE; + } + else + { + monster_spawnpoint[monster_spawn_count].monster = mIndex; + monster_types[mIndex].need_to_precache = TRUE; + monster = TRUE; + } + } else if (strcmp(monster_types[mIndex].name, "info_node") == 0) { // Normal node @@ -391,8 +407,8 @@ void scan_monster_cfg(FILE *fp) { if (monster) { - // this keyvalue is only valid for monstermaker entity - if (strcmp(data[kvd_index-1].value, "monstermaker") == 0 || strcmp(data[kvd_index-1].value, "squadmaker") == 0) + // this keyvalue is only valid for monstermaker entities + if (strcmp(data[kvd_index - 1].value, "monstermaker") == 0 || strcmp(data[kvd_index - 1].value, "squadmaker") == 0 || strcmp(data[kvd_index - 1].value, "env_xenmaker") == 0) { // process the entity precache here int mIndex; @@ -771,7 +787,7 @@ void scan_monster_bsp(void) if (monster) { // this keyvalue is only valid for monstermaker entity - if (strcmp(data[classname_kvdI].value, "monstermaker") == 0 || strcmp(data[classname_kvdI].value, "squadmaker") == 0) + if (strcmp(data[classname_kvdI].value, "monstermaker") == 0 || strcmp(data[classname_kvdI].value, "squadmaker") == 0 || strcmp(data[classname_kvdI].value, "env_xenmaker") == 0) { // process the entity precache here for (mIndex = 0; monster_types[mIndex].name[0]; mIndex++) diff --git a/src/dlls/monster_mm.vcxproj b/src/dlls/monster_mm.vcxproj index 0d74e15..18deb35 100644 --- a/src/dlls/monster_mm.vcxproj +++ b/src/dlls/monster_mm.vcxproj @@ -127,6 +127,7 @@ .\Release/monster_mm.lib + true @@ -186,6 +187,7 @@ + @@ -224,4 +226,4 @@ - + \ No newline at end of file diff --git a/src/dlls/monster_mm.vcxproj.filters b/src/dlls/monster_mm.vcxproj.filters index 399fb51..c4a95b0 100644 --- a/src/dlls/monster_mm.vcxproj.filters +++ b/src/dlls/monster_mm.vcxproj.filters @@ -186,6 +186,9 @@ Source Files + + Source Files + @@ -282,4 +285,4 @@ Header Files - + \ No newline at end of file diff --git a/src/dlls/xenmaker.cpp b/src/dlls/xenmaker.cpp new file mode 100644 index 0000000..bd2fd69 --- /dev/null +++ b/src/dlls/xenmaker.cpp @@ -0,0 +1,356 @@ +//========================================================= +// Xen Maker - Sven Co-op's env_xenmaker. +// Spawns a monster with visual/auditive teleportation effects. +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "cmbaseextra.h" +#include "monsters.h" + +// Xenmaker spawnflags +#define SF_XENMAKER_TRY_ONCE 1 // only one attempt to spawn each time it is fired +#define SF_XENMAKER_NO_SPAWN 2 // don't spawn anything, only do effects + +extern monster_type_t monster_types[]; +extern edict_t* spawn_monster(int monster_type, Vector origin, Vector angles, int spawnflags, pKVD *keyvalue); + + +// ======================================================== +void CMXenMaker::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "monstertype")) + { + // Process monster_index + int mIndex; + for (mIndex = 0; monster_types[mIndex].name[0]; mIndex++) + { + if (strcmp(pkvd->szValue, monster_types[mIndex].name) == 0) + { + m_iMonsterIndex = mIndex; + break; // grab the first entry we find + } + } + if (monster_types[mIndex].name[0] == 0) + { + ALERT(at_logged, "[MONSTER] XenMaker - %s is not a valid monster type!\n", pkvd->szValue); + m_iMonsterIndex = -1; + } + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_flBeamRadius")) + { + m_flBeamRadius = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_iBeamAlpha")) + { + m_iBeamAlpha = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_iBeamCount")) + { + m_iBeamCount = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_vBeamColor")) + { + UTIL_StringToVector(m_vBeamColor, pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_flLightRadius")) + { + m_flLightRadius = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_vLightColor")) + { + UTIL_StringToVector(m_vLightColor, pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_flStartSpriteFramerate")) + { + m_flStartSpriteFramerate = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_flStartSpriteScale")) + { + m_flStartSpriteScale = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_iStartSpriteAlpha")) + { + m_iStartSpriteAlpha = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_vStartSpriteColor")) + { + UTIL_StringToVector(m_vStartSpriteColor, pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_flEndSpriteFramerate")) + { + m_flEndSpriteFramerate = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_flEndSpriteScale")) + { + m_flEndSpriteScale = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_iEndSpriteAlpha")) + { + m_iEndSpriteAlpha = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_vEndSpriteColor")) + { + UTIL_StringToVector(m_vEndSpriteColor, pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CMBaseMonster::KeyValue(pkvd); +} + + +void CMXenMaker::Spawn() +{ + // likely omitted keyvalue, but it could truly be an alien grunt spawn + if (m_iMonsterIndex == 0) + { + if (!monster_types[0].need_to_precache) + { + // monstertype was not defined, it may be intentional if nothing is to spawn here + if (!FBitSet(pev->spawnflags, SF_XENMAKER_NO_SPAWN)) + ALERT(at_logged, "[MONSTER] Spawned a env_xenmaker entity without a monstertype! targetname: \"%s\"\n", STRING(pev->targetname)); + m_iMonsterIndex = -1; + } + } + + pev->solid = SOLID_NOT; + + Precache(); + + SetUse(&CMXenMaker::CyclicUse); // drop one monster each time we fire + SetThink(&CMXenMaker::SUB_DoNothing); + + m_flGround = 0; + pev->classname = MAKE_STRING("env_xenmaker"); +} + +void CMXenMaker::Precache(void) +{ + m_iBeamIndex = PRECACHE_MODELINDEX("sprites/lgtning.spr"); + PRECACHE_MODEL("sprites/fexplo1.spr"); + PRECACHE_MODEL("sprites/xflare1.spr"); + + PRECACHE_SOUND("debris/beamstart7.wav"); + PRECACHE_SOUND("debris/beamstart2.wav"); + + CMBaseMonster::Precache(); + // choosen monster is auto-precached +} + +//========================================================= +// StartEffect - spawns the monster and starts the effects +//========================================================= +void CMXenMaker::StartEffect(void) +{ + if (!m_flGround) + { + // setup altitude + TraceResult tr; + + UTIL_TraceLine(pev->origin, pev->origin - Vector(0, 0, 2048), ignore_monsters, ENT(pev), &tr); + m_flGround = tr.vecEndPos.z; + } + + if (!FBitSet(pev->spawnflags, SF_XENMAKER_NO_SPAWN)) + { + // monstermaker incorrectly setup + if (m_iMonsterIndex == -1) + { + ALERT(at_console, "[MONSTER] NULL Ent in XenMaker!\n"); + return; + } + + edict_t *pent; + + Vector mins = pev->origin - Vector(34, 34, 0); + Vector maxs = pev->origin + Vector(34, 34, 0); + maxs.z = pev->origin.z; + mins.z = m_flGround; + + edict_t *pList[2]; + int count = UTIL_EntitiesInBox(pList, 2, mins, maxs, FL_CLIENT | FL_MONSTER); + if (!count) + { + // Attempt to spawn monster + pent = spawn_monster(m_iMonsterIndex, pev->origin, pev->angles, SF_MONSTER_FALL_TO_GROUND, NULL); + if (pent == NULL) + { + ALERT(at_console, "[MONSTER] XenMaker - failed to spawn monster! targetname: \"%s\"\n", STRING(pev->targetname)); + } + } + else if (!FBitSet(pev->spawnflags, SF_XENMAKER_TRY_ONCE)) + { + // wait until spawnpoint is clear + pev->nextthink = gpGlobals->time + 1; + SetUse(NULL); + SetThink(&CMXenMaker::RetryThink); + return; // don't do effects + } + } + + // BEAM EFFECT + for (int beam = 0; beam < m_iBeamCount; beam++) + { + SpawnBeam(); + } + + // LIGHT EFFECT + MESSAGE_BEGIN(MSG_BROADCAST, SVC_TEMPENTITY); + WRITE_BYTE(TE_DLIGHT); + WRITE_COORD(pev->origin.x); + WRITE_COORD(pev->origin.y); + WRITE_COORD(pev->origin.z); + WRITE_BYTE((int)(m_flLightRadius / 10)); + WRITE_BYTE((int)m_vLightColor.x); + WRITE_BYTE((int)m_vLightColor.y); + WRITE_BYTE((int)m_vLightColor.z); + WRITE_BYTE(10); // life + WRITE_BYTE(0); // decay rate + MESSAGE_END(); + + // SPRITE EFFECT + CMSprite *pSprite = CMSprite::SpriteCreate("sprites/fexplo1.spr", pev->origin, FALSE); + if (pSprite) + { + pSprite->SetScale(m_flStartSpriteScale); + pSprite->SetTransparency(kRenderGlow, (int)m_vStartSpriteColor.x, (int)m_vStartSpriteColor.y, (int)m_vStartSpriteColor.z, m_iStartSpriteAlpha, kRenderFxNoDissipation); + pSprite->AnimateAndDie(m_flStartSpriteFramerate); + } + + // SOUND EFFECT + EMIT_SOUND_DYN(ENT(pev), CHAN_AUTO, "debris/beamstart7.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); + + pev->nextthink = gpGlobals->time + 0.5; + SetUse(NULL); + SetThink(&CMXenMaker::MiddleEffect); +} + +//========================================================= +// MiddleEffect - second set of effects +//========================================================= +void CMXenMaker::MiddleEffect(void) +{ + // SPRITE EFFECT + CMSprite *pSprite = CMSprite::SpriteCreate("sprites/xflare1.spr", pev->origin, FALSE); + if (pSprite) + { + pSprite->SetScale(m_flEndSpriteScale); + pSprite->SetTransparency(kRenderGlow, (int)m_vEndSpriteColor.x, (int)m_vEndSpriteColor.y, (int)m_vEndSpriteColor.z, m_iEndSpriteAlpha, kRenderFxNoDissipation); + pSprite->AnimateAndDie(m_flEndSpriteFramerate); + } + + pev->nextthink = gpGlobals->time + 0.5; + SetThink(&CMXenMaker::EndEffect); +} + +//========================================================= +// EndEffect - final set of effects +//========================================================= +void CMXenMaker::EndEffect(void) +{ + // SOUND EFFECT + EMIT_SOUND_DYN(ENT(pev), CHAN_AUTO, "debris/beamstart2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); + + SetUse(&CMXenMaker::CyclicUse); + SetThink(&CMXenMaker::SUB_DoNothing); +} + +//========================================================= +// CyclicUse - drops one monster from the xen maker +// each time we call this. +//========================================================= +void CMXenMaker::CyclicUse(edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value) +{ + StartEffect(); +} + +//========================================================= +// RetryThink - try spawning again if spawn was obstructed +//========================================================= +void CMXenMaker::RetryThink(void) +{ + SetUse(&CMXenMaker::CyclicUse); + SetThink(&CMXenMaker::SUB_DoNothing); + + StartEffect(); +} + +//========================================================= +// SpawnBeam - calculates beam end position and creates it. +// starting position is the origin of the xenmaker itself. +//========================================================= +void CMXenMaker::SpawnBeam(void) +{ + // CLightning::RandomArea + for (int iLoops = 0; iLoops < 10; iLoops++) + { + Vector vecSrc = pev->origin; + + Vector vecDir1 = Vector(RANDOM_FLOAT(-1.0, 1.0), RANDOM_FLOAT(-1.0, 1.0), RANDOM_FLOAT(-1.0, 1.0)); + vecDir1 = vecDir1.Normalize(); + TraceResult tr1; + UTIL_TraceLine(vecSrc, vecSrc + vecDir1 * m_flBeamRadius, ignore_monsters, ENT(pev), &tr1); + + if (tr1.flFraction == 1.0) + continue; + + Vector vecDir2; + do + { + vecDir2 = Vector(RANDOM_FLOAT(-1.0, 1.0), RANDOM_FLOAT(-1.0, 1.0), RANDOM_FLOAT(-1.0, 1.0)); + } while (DotProduct(vecDir1, vecDir2) > 0); + vecDir2 = vecDir2.Normalize(); + TraceResult tr2; + UTIL_TraceLine(vecSrc, vecSrc + vecDir2 * m_flBeamRadius, ignore_monsters, ENT(pev), &tr2); + + if (tr2.flFraction == 1.0) + continue; + + if ((tr1.vecEndPos - tr2.vecEndPos).Length() < m_flBeamRadius * 0.1) + continue; + + UTIL_TraceLine(tr1.vecEndPos, tr2.vecEndPos, ignore_monsters, ENT(pev), &tr2); + + if (tr2.flFraction != 1.0) + continue; + + // CLightning::Zap + MESSAGE_BEGIN(MSG_BROADCAST, SVC_TEMPENTITY); + WRITE_BYTE(TE_BEAMPOINTS); + WRITE_COORD(tr1.vecEndPos.x); + WRITE_COORD(tr1.vecEndPos.y); + WRITE_COORD(tr1.vecEndPos.z); + WRITE_COORD(tr2.vecEndPos.x); + WRITE_COORD(tr2.vecEndPos.y); + WRITE_COORD(tr2.vecEndPos.z); + WRITE_SHORT(m_iBeamIndex); + WRITE_BYTE(0); // starting frame + WRITE_BYTE(10); // framerate + WRITE_BYTE(10); // life + WRITE_BYTE(16); // width + WRITE_BYTE(64); // noise + WRITE_BYTE((int)m_vBeamColor.x); + WRITE_BYTE((int)m_vBeamColor.y); + WRITE_BYTE((int)m_vBeamColor.z); + WRITE_BYTE(m_iBeamAlpha); + WRITE_BYTE(15); // speed + MESSAGE_END(); + break; + } +}