Compare commits
8 Commits
tier-3-bet
...
tier-3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c7579c3e2 | ||
|
|
035e70e776 | ||
|
|
d4ecb16bf7 | ||
|
|
513cde6231 | ||
|
|
e33eaf4d1e | ||
|
|
e29e79052d | ||
|
|
8d28e23e16 | ||
|
|
0622ccc638 |
20
README.md
20
README.md
@@ -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
7
aux/README.md
Normal 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
19
aux/valve/README.md
Normal 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.
|
||||
BIN
aux/valve/bin/hl_monsterbridge.amxx
Normal file
BIN
aux/valve/bin/hl_monsterbridge.amxx
Normal file
Binary file not shown.
188
aux/valve/src/hl_monsterbridge.sma
Normal file
188
aux/valve/src/hl_monsterbridge.sma
Normal 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 );
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 );
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 ) );
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
36
src/dlls/cmbaseextra.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
255
src/dlls/monstermaker.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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( ))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user