8 Commits

Author SHA1 Message Date
Giegue
4c7579c3e2 Merge pull request #5 from JulianR0/tier3
Tier3
2023-02-24 16:07:32 -03:00
Giegue
035e70e776 Update config files. 2023-02-24 15:29:07 -03:00
Giegue
d4ecb16bf7 Add custom model support. 2023-02-24 14:49:55 -03:00
Giegue
513cde6231 Add compatibility with Half-Life. 2023-02-23 03:29:53 -03:00
Giegue
e33eaf4d1e Full awareness of normal game entities. 2023-02-22 22:29:19 -03:00
Giegue
e29e79052d Use the game's generated graph if possible. 2023-02-21 20:16:11 -03:00
Giegue
8d28e23e16 Fixed wrong classname for Voltigore and Baby Voltigore.
Fixed unknown classnames showing as '}'.
Remove monster respawn.
Add 'monstermaker' entity.
2023-02-20 14:08:35 -03:00
Giegue
0622ccc638 Stop allied monsters from attacking the player.
Part B of making MonsterMod aware of normal HL entities.
2023-02-19 15:57:07 -03:00
44 changed files with 938 additions and 256 deletions

View File

@@ -35,17 +35,19 @@ Under no circumstances shall we allow this project to fade away and become lost
## Installation
*NOTE: Outdated. Ideally, users should be able to use the plugin "out-of-the-box" without the need to do the complicated mess explained below.*
The plugin -should- be able to be used out-of-the-box by simply downloading the binary and adding the appropiate entry in metamod's plugin list.
*TODO: Add build instructions, just using 'make' on G++ 4.8 is really vague.*
**Windows:**
`win32 addons\monstermod\monster_mm.dll`
If you are trying to use the compiled binary, you must know that it has been compiled with a mayor GCC version and it will be highly unlikely that it will run on a vanilla HLDS server. If the plugin fails to load due to libstdc++ not having CXXABI_1.X.X or similar, read on. HLDS uses it's own libstdc++ library. Any plugins compiled with GCC versions 5.x and greater will not work with the outdated library.
**Linux:**
`linux addons/monstermod/monster_mm_i386.so`
To remedy this issue you have two options:
Additional configuration files are included in the release files, each explaining it's usage and installation instructions.
You can recompile the source code under g++ 4.8 and use the newly generated binary. Make sure to edit the Makefile so it points to that version of g++. Compilation is done by simply running `make` on the `src/dlls` folder.
## Build Instructions
Alternatively, you can "remove" the outdated library to force HLDS to use the libstdc++ provided by the linux distro, which is generally more up to date. You might need to install GCC/G++ on the operating system if it doesn't work.
*TODO: Add build instructions.*
## MonsterMod and ReHLDS
@@ -55,7 +57,7 @@ Keeping track of the number of precached content will allow you to maximize the
## Using MonsterMod on Counter-Strike
Counter-Strike precaches non trivial sounds of all weapons. This means that sounds such as "clip-ins", "clip-outs" are added to the list, taking quite a bit of space in the precache count. Let it be a reminder that our good old Half-Life can only store a maximum of 512 precached resources. Since these sounds are handled client-side by the models themselves, theres is no need to be kept precached on the server. Only the weapons fire sounds are needed.
Counter-Strike precaches the sounds of all weapons. This means that sounds such as "clip-ins", "clip-outs" are added to the list, taking quite a bit of space in the precache count. Let it be a reminder that our good old Half-Life can only store a maximum of 512 precached resources. Most of these sounds are handled client-side by the models themselves, there is no need for all of them to be kept precached on the server. Only the weapons fire sounds are needed.
MonsterMod does not have an integrated "Unprecacher" to remove those sounds, but you can remove them manually with AMX Mod X, using Fakemeta. Register forward **FM_PrecacheSound** and return **FMRES_SUPERCEDE** on the following sounds:
@@ -193,8 +195,8 @@ Current milestones are separated by "Tiers", which are as follows:
- Update source code so it can compile AND run **ON WINDOWS**. **[DONE]**
- Implement *-almost-* all Opposing Force monsters. **[DONE]**
- Implement *-almost-* all default Sven Co-op monsters. **[DONE]**
- Make MonsterMod aware of normal game entities. *-For those who want to use the plugin in vanilla HL.-*
- Custom model support.
- Make MonsterMod aware of normal game entities. *-For those wanting to use this in vanilla HL.-* **[DONE]**
- Custom model support. **[DONE]**
### Tier 4

7
aux/README.md Normal file
View File

@@ -0,0 +1,7 @@
## Auxiliary Tools
This folder contains auxiliary tools used to enhance the compatibility and/or interaction of MonsterMod with other mods.
As these are auxiliary, they aren't needed to enjoy MonsterMod. If you want an extra boost, feel free to install these.
Once all milestones are completed, the possibility of removing the need for external plugins will be considered. Until then, this has to stay.

19
aux/valve/README.md Normal file
View File

@@ -0,0 +1,19 @@
## Half-Life (valve)
Auxiliary Tools to use MonsterMod in the original Half-Life.
### AMX Mod X Plugins
#### -A note about AMXX plugins-
All plugins in this section require AMXX 1.9.0 or greater, they will not work on 1.8.2 or older.
Compiled plugins are provided in the `bin` folder for your convenience. However, if you prefer to build the plugins yourself, the source code of all AMXX plugins are located in the `src` folder for compilation.
#### Half-Life <--> MonsterMod Bridge Plugin
MonsterMod monsters are hacked "func_wall"'s with simulated AI. Because of this, MonsterMod cannot interact with the Half-Life monsters. This issue also happens on the other side: The Half-Life monsters cannot interact with the MonsterMod monsters.
In simple terms, this plugin acts as a "bridge" to help Half-Life and MonsterMod communicate with each other. With this plugin, HL and MM monsters can "see" and interact with each other, and will react accordingly.
Without this plugin, HL and MM monsters will be "invisible" to each other.

Binary file not shown.

View File

@@ -0,0 +1,188 @@
#pragma semicolon 1
#include <amxmodx>
#include <engine>
#include <hamsandwich>
// (monster.h from HLSDK) monster to monster relationship types
const R_AL = -2; // (ALLY) pals. Good alternative to R_NO when applicable.
const R_FR = -1; // (FEAR) will run.
const R_NO = 0; // (NO RELATIONSHIP) disregard.
const R_DL = 1; // (DISLIKE) will attack.
const R_HT = 2; // (HATE) will attack this character instead of any visible DISLIKEd characters.
//const R_NM = 3; // (NEMESIS) a monster will ALWAYS attack its nemesis, no matter what.
new Trie:g_HLDefaultNames;
public plugin_init()
{
register_plugin( "HL-MONSTER Bridge", "1.0", "Giegue" );
RegisterHam( Ham_IRelationship, "monster_alien_controller", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_alien_grunt", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_alien_slave", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_apache", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_babycrab", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_barnacle", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_barney", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_bigmomma", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_bullchicken", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_gargantua", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_headcrab", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_houndeye", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_human_assassin", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_human_grunt", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_ichthyosaur", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_miniturret", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_scientist", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_sentry", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_snark", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_turret", "mmIRelationship" );
RegisterHam( Ham_IRelationship, "monster_zombie", "mmIRelationship" );
g_HLDefaultNames = TrieCreate();
// Better than a nest of if-else if i guess...
TrieSetString( g_HLDefaultNames, "monster_headcrab", "Head Crab" );
TrieSetString( g_HLDefaultNames, "monster_babycrab", "Baby Head Crab" );
TrieSetString( g_HLDefaultNames, "monster_bullchicken", "Bullsquid" );
TrieSetString( g_HLDefaultNames, "monster_barnacle", "Barnacle" );
TrieSetString( g_HLDefaultNames, "monster_bigmomma", "Big Momma" );
TrieSetString( g_HLDefaultNames, "monster_houndeye", "Houndeye" );
TrieSetString( g_HLDefaultNames, "monster_alien_slave", "Alien Slave" );
TrieSetString( g_HLDefaultNames, "monster_alien_controller", "Alien Controller" );
TrieSetString( g_HLDefaultNames, "monster_alien_grunt", "Alien Grunt" );
TrieSetString( g_HLDefaultNames, "monster_zombie", "Zombie" );
TrieSetString( g_HLDefaultNames, "monster_ichthyosaur", "Ichthyosaur" );
TrieSetString( g_HLDefaultNames, "monster_human_grunt", "Human Grunt" );
TrieSetString( g_HLDefaultNames, "monster_human_assassin", "Female Assassin" );
TrieSetString( g_HLDefaultNames, "monster_barney", "Barney" );
TrieSetString( g_HLDefaultNames, "monster_gman", "Goverment Man" );
TrieSetString( g_HLDefaultNames, "monster_scientist", "Scientist" );
TrieSetString( g_HLDefaultNames, "monster_sentry", "Sentry Turret" );
TrieSetString( g_HLDefaultNames, "monster_snark", "Snark" );
TrieSetString( g_HLDefaultNames, "monster_miniturret", "Mini-Turret" );
TrieSetString( g_HLDefaultNames, "monster_turret", "Turret" );
TrieSetString( g_HLDefaultNames, "monster_apache", "Apache" );
TrieSetString( g_HLDefaultNames, "monster_osprey", "Osprey Helicopter" );
TrieSetString( g_HLDefaultNames, "monster_gargantua", "Gargantua" );
TrieSetString( g_HLDefaultNames, "monster_nihilanth", "Nihilanth" );
TrieSetString( g_HLDefaultNames, "monster_tentacle"," Tentacle" );
set_task( 0.3, "hlScan", 0, "", 0, "b" );
register_srvcmd( "monster_hurt_entity", "hlTakeDamage" );
}
public plugin_end()
{
TrieDestroy( g_HLDefaultNames ); // free the handle
}
public mmIRelationship( entity, other )
{
new selfClassify, otherClassify;
selfClassify = entity_get_int( entity, EV_INT_iuser4 );
otherClassify = entity_get_int( other, EV_INT_iuser4 );
if ( entity_get_int( other, EV_INT_flags ) & FL_CLIENT )
otherClassify = 2;
// Get proper relationship
SetHamReturnInteger( IRelationshipByClass( selfClassify, otherClassify ) );
return HAM_OVERRIDE;
}
public hlScan()
{
new entity, szClassname[ 33 ], szDisplayname[ 129 ], bool:found;
for ( entity = 0; entity < get_global_int( GL_maxEntities ); entity++ )
{
// Nothing deleted me?
if ( is_valid_ent( entity ) )
{
entity_get_string( entity, EV_SZ_classname, szClassname, charsmax( szClassname ) );
if ( equal( szClassname, "monster_", 8 ) )
{
// Classify not overriden?
if ( !entity_get_int( entity, EV_INT_iuser4 ) )
{
// Set default
entity_set_int( entity, EV_INT_iuser4, ExecuteHam( Ham_Classify, entity ) );
}
// Blood color not overriden?
if ( !entity_get_int( entity, EV_INT_iuser3 ) )
{
// Set default
entity_set_int( entity, EV_INT_iuser3, ExecuteHam( Ham_BloodColor, entity ) );
}
// No name set?
entity_get_string( entity, EV_SZ_netname, szDisplayname, charsmax( szDisplayname ) );
if ( !strlen( szDisplayname ) )
{
// Find a default name
found = TrieGetString( g_HLDefaultNames, szClassname, szDisplayname, charsmax( szDisplayname ) );
// Use this name
entity_set_string( entity, EV_SZ_netname, ( found ? szDisplayname : "Monster" ) );
}
}
}
}
}
stock IRelationshipByClass( classEntity, classOther )
{
if ( classEntity == -1 )
classEntity = 0; // CLASS_FORCE_NONE
new iEnemy[16][16] =
{ // NONE MACH PLYR HPASS HMIL AMIL APASS AMONST APREY APRED INSECT PLRALY PBWPN ABWPN RXPIT RXSHK
/*NONE*/ { R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO, R_NO, R_NO, R_NO, R_NO },
/*MACHINE*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_DL, R_DL, R_DL, R_DL },
/*PLAYER*/ { R_NO ,R_DL ,R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_DL, R_DL, R_DL, R_DL },
/*HUMANPASSIVE*/{ R_NO ,R_FR ,R_AL ,R_AL ,R_HT ,R_DL ,R_DL ,R_HT ,R_DL ,R_DL ,R_NO ,R_AL, R_NO, R_NO, R_FR, R_FR },
/*HUMANMILITAR*/{ R_NO ,R_NO ,R_DL ,R_DL ,R_AL ,R_HT ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_NO, R_NO, R_HT, R_HT },
/*ALIENMILITAR*/{ R_NO ,R_DL ,R_DL ,R_DL ,R_HT ,R_AL ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO, R_DL, R_HT },
/*ALIENPASSIVE*/{ R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO, R_NO, R_NO, R_NO, R_NO },
/*ALIENMONSTER*/{ R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO, R_NO, R_NO },
/*ALIENPREY */{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_NO ,R_FR ,R_NO ,R_DL, R_NO, R_NO, R_FR, R_NO },
/*ALIENPREDATO*/{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_HT ,R_NO ,R_NO ,R_DL, R_NO, R_NO, R_DL, R_DL },
/*INSECT*/ { R_NO ,R_FR ,R_FR ,R_FR ,R_FR ,R_NO ,R_FR ,R_FR ,R_FR ,R_FR ,R_NO ,R_FR, R_NO, R_NO, R_NO, R_NO },
/*PLAYERALLY*/ { R_NO ,R_DL ,R_AL ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_NO, R_NO, R_DL, R_DL },
/*PBIOWEAPON*/ { R_NO ,R_NO ,R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_NO, R_DL, R_DL, R_DL },
/*ABIOWEAPON*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_AL ,R_NO ,R_DL ,R_DL ,R_NO ,R_NO ,R_DL, R_DL, R_NO, R_DL, R_DL },
/*RXPITDRONE*/ { R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL ,R_DL ,R_NO ,R_DL, R_NO, R_NO, R_AL, R_AL },
/*RXSHOCKTRP*/ { R_NO ,R_DL ,R_HT ,R_DL ,R_HT ,R_HT ,R_DL ,R_NO ,R_NO ,R_DL ,R_NO ,R_DL, R_NO, R_NO, R_AL, R_AL }
};
return iEnemy[ classEntity ][ classOther ];
}
public hlTakeDamage()
{
if ( read_argc() == 6 )
{
new victim, inflictor, attacker;
new Float:damage;
new damageBits;
victim = read_argv_int( 1 );
inflictor = read_argv_int( 2 );
attacker = read_argv_int( 3 );
damage = read_argv_float( 4 );
damageBits = read_argv_int( 5 );
// attacker and inflictor can be null, but never allow victim to be null
if ( !is_valid_ent( victim ) )
return;
if ( !is_valid_ent( inflictor ) )
inflictor = 0;
if ( !is_valid_ent( attacker ) )
attacker = 0;
ExecuteHamB( Ham_TakeDamage, victim, inflictor, attacker, damage, damageBits );
}
}

View File

@@ -4,11 +4,10 @@
//
// To add entries to this file, just pretend it's ripent.
//
// "delay" means monster respawndelay.
//
// You may also be interesed in these other 2 keyvalues:
// You may also be interesed in these other keyvalues:
//
// "displayname" to change the monster's name, shown in HUD when you point at it.
// "model" to change the monster's default model. If using monstermaker, use "new_model".
// "classify" to change the monster's default classification, use one of these values:
//
// CLASS_NONE -1

View File

@@ -32,6 +32,9 @@
//monster_otis
//monster_pitdrone
//monster_shocktrooper
//monster_voltigore
//monster_baby_voltigore
//monster_alien_voltigore
//monster_alien_babyvoltigore
//monster_babygarg
//monster_hwgrunt
//monster_robogrunt
//monster_stukabat

View File

@@ -139,6 +139,18 @@ sk_babygarg_dmg_slash 24
sk_babygarg_dmg_fire 4
sk_babygarg_dmg_stomp 80
// Heavy Weapons Grunt
sk_hwgrunt_health 100
// Robo Grunt
sk_rgrunt_health 50
sk_rgrunt_armor 0.75
// Stukabat
sk_stukabat_health 80
sk_stukabat_dmg_bite 11
sk_stukabat_speed 400
// MONSTER WEAPON DAMAGE
sk_9mm_bullet 5

View File

@@ -35,7 +35,7 @@ void CMApache :: Spawn( void )
pev->movetype = MOVETYPE_FLY;
pev->solid = SOLID_BBOX;
SET_MODEL(ENT(pev), "models/apache.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/apache.mdl"));
UTIL_SetSize( pev, Vector( -32, -32, -64 ), Vector( 32, 32, 0 ) );
UTIL_SetOrigin( pev, pev->origin );

View File

@@ -345,7 +345,7 @@ void CMBarney :: Spawn()
// when a level is loaded, nobody will talk (time is reset to 0)
TalkInit();
SET_MODEL(ENT(pev), "models/barney.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/barney.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;

View File

@@ -603,7 +603,7 @@ void CMBigMomma :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/big_mom.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/big_mom.mdl"));
// UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
UTIL_SetSize( pev, Vector( -64, -64, 0 ), Vector( 64, 64, 128 ) );

View File

@@ -608,7 +608,7 @@ void CMBullsquid :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/bullsquid.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/bullsquid.mdl"));
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
pev->solid = SOLID_SLIDEBOX;

View File

@@ -601,7 +601,6 @@ template <class T> T * CreateClassPtr( T *a )
// store the class pointer in the array here!!!
monsters[monster_index].monster_index = edict_index;
monsters[monster_index].monster_pent = temp_edict;
monsters[monster_index].respawn_index = -1;
monsters[monster_index].pMonster = (CMBaseMonster *)a;
// get the private data

36
src/dlls/cmbaseextra.h Normal file
View File

@@ -0,0 +1,36 @@
#ifndef BASEEXTRA_H
#define BASEEXTRA_H
// any extra entities created in this project that aren't
// monsters will go here
//=========================================================
// MonsterMaker - this ent creates monsters during the game.
//=========================================================
class CMMonsterMaker : public CMBaseMonster
{
public:
void Spawn( void );
void Precache( void );
void KeyValue( KeyValueData* pkvd);
void EXPORT ToggleUse ( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value );
void EXPORT CyclicUse ( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value );
void EXPORT MakerThink ( void );
void DeathNotice ( entvars_t *pevChild );// monster maker children use this to tell the monster maker that they have died.
void MakeMonster( void );
int m_iMonsterIndex;// index of the monster(s) that will be created.
string_t m_iszCustomModel;// custom model that the monster will use.
int m_cNumMonsters;// max number of monsters this ent can create
int m_iMaxLiveChildren;// max number of monsters that this maker may have out at one time.
int m_cLiveChildren;// how many monsters made by this monster maker that are currently alive
float m_flGround; // z coord of the ground under me, used to make sure no monsters are under the maker when it drops a new child
BOOL m_fActive;
BOOL m_fFadeChildren;// should we make the children fadeout?
};
#endif // BASEEXTRA_H

View File

@@ -618,14 +618,11 @@ void CMBaseMonster :: Killed( entvars_t *pevAttacker, int iGib )
SetConditions( bits_COND_LIGHT_DAMAGE );
// tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality.
/*jlb monstermaker
CMBaseEntity *pOwner = CMBaseEntity::Instance(pev->owner);
if ( pOwner )
{
//jlb it crashes here sometimes!!!
pOwner->DeathNotice( pev );
}
jlb*/
if ( ShouldGibMonster( iGib ) )
{
@@ -1154,11 +1151,8 @@ void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacke
}
}
}
else if (!UTIL_IsPlayer(pEntity))
else
{
// I'm doing really bad copypastes instead of making the code clean!
// Remind me to refactor this, this is not how this is supposed to be written!
// -Giegue
edict_t *pMonster = pEntity;
if ( iClassIgnore != CLASS_NONE && pMonster->v.iuser4 == iClassIgnore )
@@ -1260,7 +1254,7 @@ edict_t* CMBaseMonster :: CheckTraceHullAttack( float flDist, int iDamage, int i
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
pMonster->TakeDamage( pev, pev, iDamage, iDmgType );
}
else if (!UTIL_IsPlayer(pEntity))
else
UTIL_TakeDamageExternal( pEntity, pev, pev, iDamage, iDmgType );
}
@@ -1571,7 +1565,7 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
}
}
}
else if (!UTIL_IsPlayer(tr.pHit)) // normal game monster
else // normal game entity
{
edict_t *pMonster = tr.pHit;

View File

@@ -302,7 +302,7 @@ void CMController :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/controller.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/controller.mdl"));
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ));
pev->solid = SOLID_SLIDEBOX;

View File

@@ -42,6 +42,7 @@
#include "cmbase.h"
#include "cmbasemonster.h"
#include "cmbaseextra.h"
#include "monsters.h"
#include "weapons.h"
#include "hornet.h"
@@ -68,7 +69,6 @@ int g_DamageVictim;
edict_t *g_DamageAttacker[33];
int g_DamageBits[33];
bool g_PlayerKilled[33];
float g_flWaitTillMessage[33];
// DeathMsg
int g_DeathMsg;
@@ -158,14 +158,15 @@ monster_type_t monster_types[]=
"monster_pitdrone", FALSE,
"monster_shockroach", FALSE,
"monster_shocktrooper", FALSE,
"monster_voltigore", FALSE,
"monster_baby_voltigore", FALSE,
"monster_alien_voltigore", FALSE,
"monster_alien_babyvoltigore", FALSE,
"monster_babygarg", FALSE, // Sven Co-op Monsters
"monster_hwgrunt", FALSE,
"monster_robogrunt", FALSE,
"monster_stukabat", FALSE,
"info_node", FALSE, // Nodes
"info_node_air", FALSE,
"monstermaker", FALSE, // Extra entities
"", FALSE
};
@@ -179,6 +180,7 @@ node_spawnpoint_t node_spawnpoint[MAX_NODES];
int node_spawn_count = 0;
float check_respawn_time;
float check_graph_time;
bool process_monster_cfg(void);
bool process_monster_precache_cfg(void);
@@ -209,15 +211,6 @@ int GetMonsterIndex(void)
void FreeMonsterIndex(int index)
{
int idx = monsters[index].respawn_index;
if (idx != -1)
{
monster_spawnpoint[idx].need_to_respawn = TRUE;
monster_spawnpoint[idx].respawn_time = gpGlobals->time +
monster_spawnpoint[idx].delay;
}
delete monsters[index].pMonster;
monsters[index].monster_index = 0;
@@ -400,17 +393,36 @@ void check_player_dead( edict_t *pPlayer )
// Killed by a monster?
if ( pAttacker->v.flags & FL_MONSTER )
{
// Check the first character for 'aeiou'.
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pAttacker));
char szCheck[2];
strncpy( szCheck, STRING( pMonster->m_szMonsterName ), 1 );
// Try to get the name of the monster
char szName[129], szCheck[2];
// Make the first character lowercase
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pAttacker));
if ( pMonster != NULL )
{
// One of our monsters
strcpy(szName, STRING( pMonster->m_szMonsterName ));
}
else
{
// SOMETHING that is a monster
if ( !FStringNull( pAttacker->v.netname ) )
strcpy(szName, STRING( pAttacker->v.netname ));
else
{
// No netname, use classname
strcpy(szName, STRING( pAttacker->v.classname ));
}
}
// Now, copy the first character to check for 'aeiou'.
strncpy( szCheck, szName, 1 );
// Make this character lowercase and inspect it. Select which message.
szCheck[0] = tolower( szCheck[ 0 ] );
if ( strncmp( szCheck, "a", 1 ) == 0 || strncmp( szCheck, "e", 1 ) == 0 || strncmp( szCheck, "i", 1 ) == 0 || strncmp( szCheck, "o", 1 ) == 0 || strncmp( szCheck, "u", 1 ) == 0 )
sprintf( szMessage, "* %s was killed by an %s.\n", szPlayerName, STRING( pMonster->m_szMonsterName ) );
sprintf( szMessage, "* %s was killed by an %s.\n", szPlayerName, szName );
else
sprintf( szMessage, "* %s was killed by a %s.\n", szPlayerName, STRING( pMonster->m_szMonsterName ) );
sprintf( szMessage, "* %s was killed by a %s.\n", szPlayerName, szName );
}
else
{
@@ -469,8 +481,6 @@ void check_player_dead( edict_t *pPlayer )
sprintf( szMessage, "* %s died of hypothermia.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_MORTAR )
sprintf( szMessage, "* %s blew its missile pet.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] == (1 << 30) ) // (1 << 30) = 1073741824. For custom death messages
sprintf( szMessage, "* %s %s.\n", szPlayerName, STRING( pAttacker->v.noise ) );
else // other mods could have more DMG_ variants that aren't registered here.
sprintf( szMessage, "* %s deadly died.\n", szPlayerName );
}
@@ -519,15 +529,47 @@ void check_monster_info( edict_t *pPlayer )
// Hit an entity?
if (tr.pHit != NULL)
{
// It should be alive
if ( UTIL_IsAlive( tr.pHit ) )
{
// Must be a monster
if (tr.pHit->v.flags & FL_MONSTER)
{
char szName[129];
float monsterHealth, monsterFrags;
int classify;
BOOL isAlly = FALSE;
// Get monster info
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit));
if ( pMonster != NULL )
{
strcpy(szName, STRING( pMonster->m_szMonsterName ));
classify = pMonster->Classify();
}
else
{
// A monster that we do not recognize, use its netname
if ( !FStringNull( tr.pHit->v.netname ) )
strcpy(szName, STRING( tr.pHit->v.netname ));
else
{
// If all else fails, use classname as monster name
strcpy(szName, STRING( tr.pHit->v.classname ));
}
classify = tr.pHit->v.iuser4;
}
monsterHealth = tr.pHit->v.health;
monsterFrags = tr.pHit->v.frags;
char szInfo[512];
sprintf(szInfo, "Enemy: %s\nHealth: %.0f\nFrags: %.0f\n", STRING( pMonster->m_szMonsterName ), pMonster->pev->health, pMonster->pev->frags );
// Unless it is strictly ally to us, treat as enemy monster
if ( classify == CLASS_HUMAN_PASSIVE || classify == CLASS_PLAYER_ALLY )
isAlly = TRUE;
// Prepare the message
char szInfo[257];
sprintf(szInfo, "%s: %s\nHealth: %.0f\nFrags: %.0f\n", ( isAlly ? "Friend" : "Enemy" ), szName, monsterHealth, monsterFrags );
// Create a TE_TEXTMESSAGE and show the monster information
MESSAGE_BEGIN( MSG_ONE, SVC_TEMPENTITY, NULL, pPlayer );
@@ -536,9 +578,18 @@ void check_monster_info( edict_t *pPlayer )
WRITE_SHORT( 327 ); // X
WRITE_SHORT( 4771 ); // Y
WRITE_BYTE( 0 ); // Effect
if ( isAlly )
{
WRITE_BYTE( 9 ); // R1
WRITE_BYTE( 172 ); // G1
WRITE_BYTE( 96 ); // B1
}
else
{
WRITE_BYTE( 171 ); // R1
WRITE_BYTE( 23 ); // G1
WRITE_BYTE( 7 ); // B1
}
WRITE_BYTE( 0 ); // A1
WRITE_BYTE( 207 ); // R2
WRITE_BYTE( 23 ); // G2
@@ -551,13 +602,14 @@ void check_monster_info( edict_t *pPlayer )
MESSAGE_END();
// Delay till next scan
g_NextMessage[ ENTINDEX( pPlayer ) ] = gpGlobals->time + 0.8;
g_NextMessage[ ENTINDEX( pPlayer ) ] = gpGlobals->time + 0.30;
}
}
}
}
}
bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_index, int spawnflags, pKVD *keyvalue)
edict_t* spawn_monster(int monster_type, Vector origin, Vector angles, int spawnflags, pKVD *keyvalue)
{
int monster_index;
edict_t *monster_pent;
@@ -567,7 +619,7 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i
{
//META_CONS("[MONSTER] ERROR: No FREE Monster edicts!");
LOG_MESSAGE(PLID, "ERROR: No FREE Monster edicts!");
return TRUE;
return NULL;
}
// was this monster NOT precached?
@@ -601,11 +653,12 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i
LOG_MESSAGE(PLID, "%s", msg);
}
return TRUE;
return NULL;
}
switch (monster_type)
{
// Monsters
case 0: monsters[monster_index].pMonster = CreateClassPtr((CMAGrunt *)NULL); break;
case 1: monsters[monster_index].pMonster = CreateClassPtr((CMApache *)NULL); break;
case 2: monsters[monster_index].pMonster = CreateClassPtr((CMBarney *)NULL); break;
@@ -636,17 +689,17 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i
case 27: monsters[monster_index].pMonster = CreateClassPtr((CMHWGrunt *)NULL); break;
case 28: monsters[monster_index].pMonster = CreateClassPtr((CMRGrunt *)NULL); break;
case 29: monsters[monster_index].pMonster = CreateClassPtr((CMStukabat *)NULL); break;
// Extra entities
case 32: monsters[monster_index].pMonster = CreateClassPtr((CMMonsterMaker *)NULL); break;
}
if (monsters[monster_index].pMonster == NULL)
{
//META_CONS("[MONSTER] ERROR: Error Creating Monster!" );
LOG_MESSAGE(PLID, "ERROR: Error Creating Monster!");
return TRUE;
return NULL;
}
monsters[monster_index].respawn_index = respawn_index;
monster_pent = ENT(monsters[monster_index].pMonster->pev);
monsters[monster_index].monster_pent = monster_pent;
@@ -673,15 +726,19 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i
monsters[monster_index].pMonster->Spawn();
// Only modify starting spawnflags for monsters, not for entities!
if ( monster_index <= 29 )
{
// Reverse fadecorpse behaviour
if ( ( spawnflags & SF_MONSTER_FADECORPSE ) )
monster_pent->v.spawnflags &= ~SF_MONSTER_FADECORPSE;
else
monster_pent->v.spawnflags |= SF_MONSTER_FADECORPSE;
}
monster_pent->v.fuser4 = monster_pent->v.health; // save the original health
return FALSE;
return monster_pent;
}
@@ -698,8 +755,7 @@ void check_respawn(void)
for (int index=0; index < monster_spawn_count; index++)
{
if (monster_spawnpoint[index].need_to_respawn &&
(monster_spawnpoint[index].respawn_time <= gpGlobals->time))
if (monster_spawnpoint[index].need_to_respawn)
{
monster_spawnpoint[index].need_to_respawn = FALSE;
@@ -713,12 +769,10 @@ void check_respawn(void)
keyvalue = monster_spawnpoint[index].keyvalue;
if (spawn_monster(monster_type, origin, angles, index, spawnflags, keyvalue))
if (spawn_monster(monster_type, origin, angles, spawnflags, keyvalue) == NULL)
{
// spawn_monster failed, retry again after delay...
monster_spawnpoint[index].need_to_respawn = TRUE;
monster_spawnpoint[index].respawn_time = gpGlobals->time +
monster_spawnpoint[index].delay;
// spawn_monster failed
ALERT( at_error, "Failed to spawn %s at origin %f %f %f\n", monster_types[monster_type].name, origin.x, origin.y, origin.z );
}
}
}
@@ -752,7 +806,6 @@ void world_precache(void)
PRECACHE_MODEL ("models/w_grenade.mdl");
}
void MonsterCommand(void)
{
int index;
@@ -886,7 +939,7 @@ void MonsterCommand(void)
if (monster_angle.y < 0)
monster_angle.y += 360;
spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL);
spawn_monster(monster_type, v_src, monster_angle, spawnflags, NULL);
return;
}
@@ -914,7 +967,7 @@ void MonsterCommand(void)
if (monster_angle.y < 0)
monster_angle.y += 360;
spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL);
spawn_monster(monster_type, v_src, monster_angle, spawnflags, NULL);
return;
}
@@ -942,7 +995,7 @@ void MonsterCommand(void)
if (monster_angle.y < 0)
monster_angle.y += 360;
spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL);
spawn_monster(monster_type, v_src, monster_angle, spawnflags, NULL);
return;
}
@@ -969,7 +1022,7 @@ void MonsterCommand(void)
if (monster_angle.y < 0)
monster_angle.y += 360;
spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL);
spawn_monster(monster_type, v_src, monster_angle, spawnflags, NULL);
return;
}
@@ -996,7 +1049,7 @@ void MonsterCommand(void)
if (monster_angle.y < 0)
monster_angle.y += 360;
spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL);
spawn_monster(monster_type, v_src, monster_angle, spawnflags, NULL);
return;
}
@@ -1023,7 +1076,7 @@ void MonsterCommand(void)
if (monster_angle.y < 0)
monster_angle.y += 360;
spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL);
spawn_monster(monster_type, v_src, monster_angle, spawnflags, NULL);
return;
}
@@ -1209,29 +1262,9 @@ int mmDispatchSpawn( edict_t *pent )
// node support. -Giegue
// init the WorldGraph.
WorldGraph.InitGraph();
check_graph_time = gpGlobals->time + 2.00; // give enough gap
// make sure the .NOD file is newer than the .BSP file.
if ( !WorldGraph.CheckNODFile ( ( char * )STRING( gpGlobals->mapname ) ) )
{
// NOD file is not present, or is older than the BSP file.
WorldGraph.AllocNodes();
}
else
{
// Load the node graph for this level
if ( !WorldGraph.FLoadGraph ( (char *)STRING( gpGlobals->mapname ) ) )
{
// couldn't load, so alloc and prepare to build a graph.
ALERT ( at_console, "*Error opening .NOD file\n" );
WorldGraph.AllocNodes();
}
else
{
ALERT ( at_console, "\n*Graph Loaded!\n" );
}
}
check_respawn_time = 0.0;
check_respawn_time = gpGlobals->time + 4.00;
for (index = 0; index < MAX_MONSTER_ENTS; index++)
{
@@ -1295,7 +1328,8 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
{
int index;
CMAGrunt agrunt;
// Monsters
CMAGrunt agrunt; // 0
CMApache apache;
CMBarney barney;
CMBigMomma bigmomma;
@@ -1324,7 +1358,10 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
CMBabyGargantua babygargantua;
CMHWGrunt hwgrunt;
CMRGrunt rgrunt;
CMStukabat stukabat;
CMStukabat stukabat; // 29
// Extra entities
CMMonsterMaker monstermaker; // 32
g_psv_gravity = CVAR_GET_POINTER( "sv_gravity" );
@@ -1372,6 +1409,7 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
case 27: hwgrunt.Precache(); break;
case 28: rgrunt.Precache(); break;
case 29: stukabat.Precache(); break;
case 32: monstermaker.Precache(); break;
}
}
}
@@ -1384,9 +1422,62 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
monsters[index].pMonster = NULL;
}
for (index = 0; index < 33; index++)
{
g_DamageAttacker[index] = NULL;
g_DamageBits[index] = 0;
g_PlayerKilled[index] = false;
g_NextMessage[index] = 0.0;
}
monster_ents_used = 0;
RETURN_META(MRES_IGNORED);
}
void mmStartFrame( void )
{
// Don't generate node graph right away
if (check_graph_time != -1 && check_graph_time <= gpGlobals->time)
{
BOOL generateNodes = FALSE;
check_graph_time = -1; // only once
// it could be possible that the mod can generate the node graph
// on it's own, so we wait a bit before attempting to create ours.
// if we can use the game's generated graph, stick to that one.
// if not, then do standard node allocation and spawns. -Giegue
// make sure the .NOD file is newer than the .BSP file.
if ( !WorldGraph.CheckNODFile ( ( char * )STRING( gpGlobals->mapname ) ) )
{
// NOD file is not present, or is older than the BSP file.
generateNodes = TRUE;
WorldGraph.AllocNodes();
}
else
{
// Load the node graph for this level
if ( !WorldGraph.FLoadGraph ( (char *)STRING( gpGlobals->mapname ) ) )
{
// couldn't load, so alloc and prepare to build a graph.
generateNodes = TRUE;
ALERT ( at_console, "*Error opening .NOD file\n" );
WorldGraph.AllocNodes();
}
else
{
// node graph is OK, we can spawn the monsters instantly
check_respawn_time = 0.0;
ALERT ( at_console, "\n[MONSTER] Graph Loaded!\n" );
}
}
if ( generateNodes )
{
// spawn nodes
int index;
for (index = 0; index < node_spawn_count; index++)
{
CMBaseEntity *pNode;
@@ -1409,12 +1500,10 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
pNode->Spawn();
}
}
RETURN_META(MRES_IGNORED);
}
}
void mmStartFrame( void )
{
// Wait for node graph before spawning the monsters
if (check_respawn_time <= gpGlobals->time)
{
check_respawn_time = gpGlobals->time + 1.0;

View File

@@ -664,7 +664,7 @@ void CMGargantua :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/garg.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/garg.mdl"));
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
pev->solid = SOLID_SLIDEBOX;
@@ -1327,7 +1327,7 @@ void CMBabyGargantua::Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/babygarg.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/babygarg.mdl"));
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
pev->solid = SOLID_SLIDEBOX;

View File

@@ -616,7 +616,7 @@ void CMGonome::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/gonome.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/gonome.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;

View File

@@ -215,7 +215,7 @@ void CMHAssassin :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/hassassin.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/hassassin.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;

View File

@@ -247,7 +247,7 @@ void CMHeadCrab :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/headcrab.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/headcrab.mdl"));
UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24));
pev->solid = SOLID_SLIDEBOX;
@@ -461,7 +461,7 @@ Schedule_t* CMHeadCrab :: GetScheduleOfType ( int Type )
void CMBabyCrab :: Spawn( void )
{
CMHeadCrab::Spawn();
SET_MODEL(ENT(pev), "models/baby_headcrab.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/baby_headcrab.mdl"));
pev->rendermode = kRenderTransTexture;
pev->renderamt = 192;
UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24));

View File

@@ -831,7 +831,7 @@ void CMHGrunt :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/hgrunt.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/hgrunt.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;

View File

@@ -326,11 +326,10 @@ void CMHornet :: TrackTouch ( edict_t *pOther )
}
// is this NOT a player and IS a monster?
if (!UTIL_IsPlayer(pOther) && (pOther->v.euser4 != NULL))
if (!UTIL_IsPlayer(pOther) && (pOther->v.flags & FL_MONSTER))
{
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
if ( IRelationship( pMonster ) <= R_NO )
if ( pMonster != NULL && IRelationship( pMonster ) <= R_NO || IRelationshipByClass( pOther->v.iuser4 ) <= R_NO )
{
// hit something we don't want to hurt, so turn around.

View File

@@ -267,7 +267,7 @@ void CMHoundeye :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/houndeye.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/houndeye.mdl"));
UTIL_SetSize(pev, Vector ( -16, -16, 0 ), Vector ( 16, 16, 36 ) );
pev->solid = SOLID_SLIDEBOX;

View File

@@ -202,7 +202,7 @@ void CMHWGrunt::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/hwgrunt.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/hwgrunt.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;
@@ -816,50 +816,41 @@ Schedule_t* CMHWGrunt :: GetScheduleOfType ( int Type )
case SCHED_HWGRUNT_ELOF_FAIL:
{
// human grunt is unable to move to a position that allows him to attack the enemy.
UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_HWGRUNT_ELOF_FAIL\n" );
return &slHWGruntELOFFail[ 0 ];
}
break;
case SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE:
{
UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE\n" );
return &slHWGruntEstablishLineOfFire[ 0 ];
}
break;
case SCHED_RANGE_ATTACK1:
{
// no pistols yet, always do standing attack
UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_RANGE_ATTACK1\n" );
return &slHWGruntRangeAttack1B[ 0 ];
}
case SCHED_COMBAT_FACE:
{
UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_COMBAT_FACE\n" );
return &slHWGruntCombatFace[ 0 ];
}
case SCHED_HWGRUNT_WAIT_FACE_ENEMY:
{
UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_HWGRUNT_WAIT_FACE_ENEMY\n" );
return &slHWGruntWaitInCover[ 0 ];
}
case SCHED_HWGRUNT_SWEEP:
{
UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_HWGRUNT_SWEEP\n" );
return &slHWGruntSweep[ 0 ];
}
case SCHED_VICTORY_DANCE:
{
UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_VICTORY_DANCE\n" );
return &slHWGruntVictoryDance[ 0 ];
}
case SCHED_HWGRUNT_SUPPRESS:
{
UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_HWGRUNT_SUPPRESS\n" );
return &slHWGruntSuppress[ 0 ];
}
case SCHED_FAIL:
{
UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_FAIL\n" );
if ( m_hEnemy != NULL )
{
// grunt has an enemy, so pick a different default fail schedule most likely to help recover.

View File

@@ -416,7 +416,7 @@ void CMISlave :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/islave.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/islave.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;

View File

@@ -215,7 +215,7 @@ void CMMassn::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/massn.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/massn.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;

View File

@@ -56,8 +56,8 @@ static META_FUNCTIONS gMetaFunctionTable =
plugin_info_t Plugin_info = {
META_INTERFACE_VERSION, // interface version
"MonsterMod", // name
"2.0", // version
"03/06/2020", // date in DD/MM/YYYY format
"3.0", // version
"24/02/2023", // date in DD/MM/YYYY format
"botman, Rick90, Giegue", // original authors + recreation by...
"https://github.com/JulianR0/monstermod-redo", // url
"MONSTER", // logtag

View File

@@ -94,7 +94,7 @@ void scan_monster_cfg(FILE *fp)
// Now that I think about it this looks slow and bad code >.>
// A match is found. What is this?
if (strncmp(monster_types[mIndex].name, "monster", 7) == 0)
if (strncmp(monster_types[mIndex].name, "monster_", 8) == 0)
{
// It's a monster, add it to the list
if (monster_spawn_count == MAX_MONSTERS)
@@ -110,6 +110,22 @@ void scan_monster_cfg(FILE *fp)
monster = TRUE;
}
}
else if (strcmp(monster_types[mIndex].name, "monstermaker") == 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
@@ -142,7 +158,7 @@ void scan_monster_cfg(FILE *fp)
}
if (monster_types[mIndex].name[0] == 0)
{
LOG_MESSAGE(PLID, "ERROR: unknown classname: %s", input); // print conflictive line
LOG_MESSAGE(PLID, "ERROR: unknown classname: %s", data[kvd_index-1].value); // print conflictive line
LOG_MESSAGE(PLID, "ERROR: nothing will spawn here!");
badent = TRUE;
}
@@ -150,8 +166,8 @@ void scan_monster_cfg(FILE *fp)
else
{
// What are you doing?!
LOG_MESSAGE(PLID, "ERROR: BAD ENTITY STRUCTURE! Last line was %s", input); // print conflictive line
LOG_MESSAGE(PLID, "ERROR: nothing will spawn here!");
LOG_MESSAGE(PLID, "ERROR: BAD ENTITY STRUCTURE! Last line was %s", data[kvd_index-1].key); // print conflictive line
LOG_MESSAGE(PLID, "ERROR: classname MUST be the last entry of the entity!" );
badent = TRUE;
}
@@ -192,23 +208,6 @@ void scan_monster_cfg(FILE *fp)
node_spawnpoint[node_spawn_count].origin[2] = z;
}
}
else if (strcmp(data[i].key, "delay") == 0)
{
// ToDo: Remove this keyvalue.
// Monsters spawned directly should not respawn.
if (monster)
{
if (sscanf(data[i].value, "%f", &x) != 1)
{
LOG_MESSAGE(PLID, "ERROR: invalid delay: %s", input); // print conflictive line
// default to 30 seconds
LOG_MESSAGE(PLID, "ERROR: entity respawn frequency will be set to 30 seconds");
x = 30;
}
monster_spawnpoint[monster_spawn_count].delay = x;
}
}
else if (strcmp(data[i].key, "angles") == 0)
{
if (monster)
@@ -241,6 +240,62 @@ void scan_monster_cfg(FILE *fp)
monster_spawnpoint[monster_spawn_count].spawnflags = x;
}
}
else if (strcmp(data[i].key, "model") == 0)
{
if (monster)
{
// only applicable for normal monsters
if (strcmp(data[kvd_index-1].value, "monstermaker") != 0)
{
// precache the custom model here
PRECACHE_MODEL( data[i].value );
// the entity will need the keyvalue
strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].key, data[i].key);
strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].value, data[i].value);
}
}
}
else if (strcmp(data[i].key, "new_model") == 0)
{
if (monster)
{
// only applicable for monstermaket entity
if (strcmp(data[kvd_index-1].value, "monstermaker") == 0)
{
// precache the custom model
PRECACHE_MODEL( data[i].value );
// the entity will need the keyvalue as well
strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].key, data[i].key);
strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].value, data[i].value);
}
}
}
else if (strcmp(data[i].key, "monstertype") == 0)
{
if (monster)
{
// this keyvalue is only valid for monstermaker entity
if (strcmp(data[kvd_index-1].value, "monstermaker") == 0)
{
// process the entity precache here
int mIndex;
for (mIndex = 0; monster_types[mIndex].name[0]; mIndex++)
{
if (strcmp(data[i].value, monster_types[mIndex].name) == 0)
{
monster_types[mIndex].need_to_precache = TRUE;
break; // only one monster at a time
}
}
// pass the keyvalue to the entity
strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].key, data[i].key);
strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].value, data[i].value);
}
}
}
else
{
// We do not know this keyvalue, but an specific entity might use it.
@@ -255,8 +310,7 @@ void scan_monster_cfg(FILE *fp)
if (monster)
{
// Init monster
monster_spawnpoint[monster_spawn_count].respawn_time = gpGlobals->time + 0.1; // spawn (nearly) right away
// Spawn right away
monster_spawnpoint[monster_spawn_count].need_to_respawn = TRUE;
monster_spawn_count++;
}

View File

@@ -218,6 +218,10 @@ SOURCE=.\monster_config.cpp
# End Source File
# Begin Source File
SOURCE=.\monstermaker.cpp
# End Source File
# Begin Source File
SOURCE=.\monsters.cpp
# End Source File
# Begin Source File
@@ -274,6 +278,10 @@ SOURCE=.\strooper.cpp
# End Source File
# Begin Source File
SOURCE=.\stukabat.cpp
# End Source File
# Begin Source File
SOURCE=.\subs.cpp
# End Source File
# Begin Source File
@@ -326,6 +334,10 @@ SOURCE=.\cmbase.h
# End Source File
# Begin Source File
SOURCE=.\cmbaseextra.h
# End Source File
# Begin Source File
SOURCE=.\cmbasemonster.h
# End Source File
# Begin Source File

View File

@@ -27,7 +27,6 @@ typedef struct
int monster_index;
edict_t *monster_pent;
bool killed;
int respawn_index;
CMBaseMonster *pMonster;
} monster_t;
@@ -38,11 +37,9 @@ extern monster_t monsters[MAX_MONSTER_ENTS];
typedef struct {
Vector origin;
Vector angles;
float delay;
unsigned char monster;
int spawnflags;
pKVD *keyvalue;
float respawn_time;
bool need_to_respawn;
} monster_spawnpoint_t;

255
src/dlls/monstermaker.cpp Normal file
View File

@@ -0,0 +1,255 @@
/***
*
* Copyright (c) 1996-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.
*
****/
//=========================================================
// Monster Maker - this is an entity that creates monsters
// in the game.
//=========================================================
#include "extdll.h"
#include "util.h"
#include "cmbase.h"
#include "cmbasemonster.h"
#include "cmbaseextra.h"
#include "monsters.h"
// Monstermaker spawnflags
#define SF_MONSTERMAKER_START_ON 1 // start active ( if has targetname )
#define SF_MONSTERMAKER_CYCLIC 4 // drop one monster every time fired.
#define SF_MONSTERMAKER_MONSTERCLIP 8 // Children are blocked by monsterclip
extern monster_type_t monster_types[];
extern edict_t* spawn_monster(int monster_type, Vector origin, Vector angles, int spawnflags, pKVD *keyvalue);
// ========================================================
void CMMonsterMaker :: KeyValue( KeyValueData *pkvd )
{
if ( FStrEq(pkvd->szKeyName, "monstercount") )
{
m_cNumMonsters = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if ( FStrEq(pkvd->szKeyName, "m_imaxlivechildren") )
{
m_iMaxLiveChildren = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else 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
}
}
pkvd->fHandled = TRUE;
}
else if ( FStrEq(pkvd->szKeyName, "new_model") )
{
m_iszCustomModel = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else
CMBaseMonster::KeyValue( pkvd );
}
void CMMonsterMaker :: Spawn( )
{
pev->solid = SOLID_NOT;
m_cLiveChildren = 0;
Precache();
if ( !FStringNull ( pev->targetname ) )
{
if ( pev->spawnflags & SF_MONSTERMAKER_CYCLIC )
{
SetUse ( &CMMonsterMaker::CyclicUse );// drop one monster each time we fire
}
else
{
SetUse ( &CMMonsterMaker::ToggleUse );// so can be turned on/off
}
if ( FBitSet ( pev->spawnflags, SF_MONSTERMAKER_START_ON ) )
{// start making monsters as soon as monstermaker spawns
m_fActive = TRUE;
SetThink ( &CMMonsterMaker::MakerThink );
}
else
{// wait to be activated.
m_fActive = FALSE;
SetThink ( &CMMonsterMaker::SUB_DoNothing );
}
}
else
{// no targetname, just start.
pev->nextthink = gpGlobals->time + m_flDelay;
m_fActive = TRUE;
SetThink ( &CMMonsterMaker::MakerThink );
}
// always fade
m_fFadeChildren = TRUE;
m_flGround = 0;
}
void CMMonsterMaker :: Precache( void )
{
CMBaseMonster::Precache();
// choosen monster is auto-precached
}
//=========================================================
// MakeMonster- this is the code that drops the monster
//=========================================================
void CMMonsterMaker::MakeMonster( void )
{
edict_t *pent;
pKVD keyvalue[1]; // sometimes, i don't know what am i doing. -Giegue
int createSF = SF_MONSTER_FALL_TO_GROUND;
if ( m_iMaxLiveChildren > 0 && m_cLiveChildren >= m_iMaxLiveChildren )
{// not allowed to make a new one yet. Too many live ones out right now.
return;
}
if ( !m_flGround )
{
// set altitude. Now that I'm activated, any breakables, etc should be out from under me.
TraceResult tr;
UTIL_TraceLine ( pev->origin, pev->origin - Vector ( 0, 0, 2048 ), ignore_monsters, ENT(pev), &tr );
m_flGround = tr.vecEndPos.z;
}
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 )
{
// don't build a stack of monsters!
return;
}
// Should children hit monsterclip brushes?
if ( pev->spawnflags & SF_MONSTERMAKER_MONSTERCLIP )
createSF |= SF_MONSTER_HITMONSTERCLIP;
// Monster is to have a custom model?
if ( !FStringNull( m_iszCustomModel ) )
{
// setup model keyvalue
strcpy(keyvalue[0].key, "model");
strcpy(keyvalue[0].value, STRING( m_iszCustomModel ));
}
// Attempt to spawn monster
pent = spawn_monster(m_iMonsterIndex, pev->origin, pev->angles, createSF, keyvalue);
if ( pent == NULL )
{
ALERT ( at_console, "NULL Ent in MonsterMaker!\n" );
return;
}
// If I have a target, fire!
if ( !FStringNull ( pev->target ) )
{
// delay already overloaded for this entity, so can't call SUB_UseTargets()
FireTargets( STRING(pev->target), this->edict(), this->edict(), USE_TOGGLE, 0 );
}
pent->v.owner = edict();
if ( !FStringNull( pev->netname ) )
{
// if I have a netname (overloaded), give the child monster that name as a targetname
pent->v.targetname = pev->netname;
}
m_cLiveChildren++;// count this monster
m_cNumMonsters--;
if ( m_cNumMonsters == 0 )
{
// Disable this forever. Don't kill it because it still gets death notices
SetThink( NULL );
SetUse( NULL );
}
}
//=========================================================
// CyclicUse - drops one monster from the monstermaker
// each time we call this.
//=========================================================
void CMMonsterMaker::CyclicUse ( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value )
{
MakeMonster();
}
//=========================================================
// ToggleUse - activates/deactivates the monster maker
//=========================================================
void CMMonsterMaker :: ToggleUse ( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value )
{
if ( !ShouldToggle( useType, m_fActive ) )
return;
if ( m_fActive )
{
m_fActive = FALSE;
SetThink ( NULL );
}
else
{
m_fActive = TRUE;
SetThink ( &CMMonsterMaker::MakerThink );
}
pev->nextthink = gpGlobals->time;
}
//=========================================================
// MakerThink - creates a new monster every so often
//=========================================================
void CMMonsterMaker :: MakerThink ( void )
{
pev->nextthink = gpGlobals->time + m_flDelay;
MakeMonster();
}
//=========================================================
//=========================================================
void CMMonsterMaker :: DeathNotice ( entvars_t *pevChild )
{
// ok, we've gotten the deathnotice from our child, now clear out its owner if we don't want it to fade.
m_cLiveChildren--;
if ( !m_fFadeChildren )
{
pevChild->owner = NULL;
}
}

View File

@@ -207,7 +207,7 @@ void CMBaseMonster :: Look ( int iDistance )
}
}
}
else if (!UTIL_IsPlayer(pSightEnt))
else
{
/* MonsterMod monster looking at a NON-MonsterMod monster */
@@ -2085,15 +2085,28 @@ edict_t *CMBaseMonster :: BestVisibleEnemy ( void )
if ( UTIL_IsPlayer(pEnt) )
{
// it's a player...
if ( UTIL_IsAlive(pEnt) )
{
// repeat2
if ( IRelationshipByClass( CLASS_PLAYER ) > iBestRelationship )
{
iBestRelationship = IRelationshipByClass( CLASS_PLAYER );
iNearest = ( pEnt->v.origin - pev->origin ).Length();
pReturn = pEnt;
}
else if ( IRelationshipByClass( CLASS_PLAYER ) == iBestRelationship )
{
iDist = ( pEnt->v.origin - pev->origin ).Length();
if ( iDist <= iNearest )
{
iNearest = iDist;
iBestRelationship = R_NM; // player is always nemesis
iBestRelationship = IRelationshipByClass( CLASS_PLAYER );
pReturn = pEnt;
}
}
}
}
else if (pEnt->v.euser4 != NULL)
{
// it's a monstermod monster...
@@ -2125,12 +2138,12 @@ edict_t *CMBaseMonster :: BestVisibleEnemy ( void )
}
}
}
else if (!UTIL_IsPlayer(pEnt))
else
{
// it's a game default monster...
// it's a normal game entity...
if ( UTIL_IsAlive(pEnt) )
{
//repeat2
//repeat3
if ( IRelationship( pEnt->v.iuser4 ) > iBestRelationship )
{
iBestRelationship = IRelationship( pEnt->v.iuser4 );
@@ -2596,6 +2609,11 @@ void CMBaseMonster :: KeyValue( KeyValueData *pkvd )
m_iClassifyOverride = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "model"))
{
pev->model = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else
{
CMBaseToggle::KeyValue( pkvd );
@@ -2959,9 +2977,14 @@ BOOL CMBaseMonster :: GetEnemy ( void )
if (HasConditions(bits_COND_SEE_CLIENT) && (m_hEnemy == NULL))
{
m_hEnemy = BestVisibleEnemy();
// the player we've just seen might not always be our enemy
if ( m_hEnemy != NULL )
{
m_hTargetEnt = m_hEnemy;
m_vecEnemyLKP = m_hEnemy->v.origin;
}
}
// remember old enemies
if (m_hEnemy == NULL && PopEnemy( ))

View File

@@ -137,7 +137,7 @@ void CMOtis::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/otis.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/otis.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;

View File

@@ -563,7 +563,7 @@ void CMPitdrone::Spawn()
{
Precache();
SET_MODEL( ENT(pev), "models/pit_drone.mdl" );
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/pit_drone.mdl"));
UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 48));
pev->solid = SOLID_SLIDEBOX;

View File

@@ -312,7 +312,7 @@ void CMRGrunt::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/rgrunt.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/rgrunt.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;

View File

@@ -596,7 +596,7 @@ void CMScientist :: Spawn( void )
// when a level is loaded, nobody will talk (time is reset to 0)
TalkInit();
SET_MODEL(ENT(pev), "models/scientist.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/scientist.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;

View File

@@ -370,7 +370,7 @@ void CMStrooper::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/strooper.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/strooper.mdl"));
UTIL_SetSize( pev, Vector(-24, -24, 0), Vector(24, 24, 72) );
pev->solid = SOLID_SLIDEBOX;

View File

@@ -99,7 +99,7 @@ void CMStukabat :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/stukabat.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/stukabat.mdl"));
UTIL_SetSize( pev, Vector( -12, -12, 0 ), Vector( 12, 12, 24 ) );
pev->solid = SOLID_SLIDEBOX;

View File

@@ -121,7 +121,7 @@ void CMBaseTurret::Precache( )
void CMTurret::Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/turret.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/turret.mdl"));
pev->health = gSkillData.turretHealth;
m_HackedGunPos = Vector( 0, 0, 12.75 );
m_flMaxSpin = TURRET_MAXSPIN;
@@ -161,7 +161,7 @@ void CMTurret::Precache()
void CMMiniTurret::Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/miniturret.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/miniturret.mdl"));
pev->health = gSkillData.miniturretHealth;
m_HackedGunPos = Vector( 0, 0, 12.75 );
m_flMaxSpin = 0;
@@ -1014,7 +1014,7 @@ void CMSentry::Precache()
void CMSentry::Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/sentry.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/sentry.mdl"));
pev->health = gSkillData.sentryHealth;
m_HackedGunPos = Vector( 0, 0, 48 );
pev->view_ofs.z = 48;

View File

@@ -1520,15 +1520,13 @@ Vector VecBModelOrigin( entvars_t* pevBModel )
bool UTIL_IsAlive(entvars_t *pev)
{
return ((pev->deadflag == DEAD_NO) && (pev->health > 0) &&
((pev->flags & FL_NOTARGET) == 0) && (pev->takedamage != 0));
return ((pev->deadflag == DEAD_NO) && (pev->health > 0));
}
bool UTIL_IsAlive(edict_t *pEdict)
{
return ((pEdict->v.deadflag == DEAD_NO) && (pEdict->v.health > 0) &&
((pEdict->v.flags & FL_NOTARGET) == 0) && (pEdict->v.takedamage != 0));
return ((pEdict->v.deadflag == DEAD_NO) && (pEdict->v.health > 0));
}
@@ -1677,8 +1675,8 @@ int UTIL_TakeDamage( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAtt
flBonus = ARMOR_BONUS;
flRatio = ARMOR_RATIO;
if (!pEdict->v.takedamage)
return 0;
if (!pEdict->v.takedamage || (pEdict->v.flags & FL_GODMODE))
return 0; // refuse the damage
if ( ( bitsDamageType & DMG_BLAST ) )
{
@@ -1875,7 +1873,11 @@ void UTIL_TraceBleed( edict_t *pEdict, float flDamage, Vector vecDir, TraceResul
if ( Bloodtr.flFraction != 1.0 )
{
UTIL_BloodDecalTrace( &Bloodtr, BLOOD_COLOR_RED );
int bloodColor = pEdict->v.iuser3;
if ( !bloodColor )
bloodColor = BLOOD_COLOR_RED;
UTIL_BloodDecalTrace( &Bloodtr, bloodColor );
}
}
}
@@ -1886,9 +1888,13 @@ void UTIL_TraceAttack( edict_t *pEdict, entvars_t *pevAttacker, float flDamage,
if ( pEdict->v.takedamage )
{
int bloodColor = pEdict->v.iuser3;
if ( !bloodColor )
bloodColor = BLOOD_COLOR_RED;
AddMultiDamage( pevAttacker, pEdict, flDamage, bitsDamageType );
SpawnBlood(ptr->vecEndPos, BLOOD_COLOR_RED, flDamage);// a little surface blood.
SpawnBlood(ptr->vecEndPos, bloodColor, flDamage);// a little surface blood.
UTIL_TraceBleed( pEdict, flDamage, vecDir, ptr, bitsDamageType );
}

View File

@@ -33,9 +33,6 @@
#define VOLTIGORE_MAX_BEAMS 8
#define VOLTIGORE_CLASSNAME "monster_alien_voltigore"
#define VOLTIGORE_BABY_CLASSNAME "monster_alien_babyvoltigore"
#define VOLTIGORE_ZAP_RED 180
#define VOLTIGORE_ZAP_GREEN 16
#define VOLTIGORE_ZAP_BLUE 255
@@ -629,7 +626,7 @@ void CMVoltigore::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/voltigore.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/voltigore.mdl"));
UTIL_SetSize(pev, Vector(-80, -80, 0), Vector(80, 80, 90));
pev->solid = SOLID_SLIDEBOX;
@@ -1135,7 +1132,7 @@ void CMBabyVoltigore::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/baby_voltigore.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/baby_voltigore.mdl"));
UTIL_SetSize(pev, Vector(-32, -32, 0), Vector(32, 32, 36));
pev->solid = SOLID_SLIDEBOX;

View File

@@ -79,7 +79,7 @@ void ApplyMultiDamage(entvars_t *pevInflictor, entvars_t *pevAttacker )
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(gMultiDamage.pEntity));
pMonster->TakeDamage(pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type );
}
else if (!UTIL_IsPlayer(gMultiDamage.pEntity))
else
UTIL_TakeDamageExternal(gMultiDamage.pEntity, pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type );
}

View File

@@ -246,7 +246,7 @@ void CMZombie :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/zombie.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/zombie.mdl"));
UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX );
pev->solid = SOLID_SLIDEBOX;