Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c7579c3e2 | ||
|
|
035e70e776 | ||
|
|
d4ecb16bf7 | ||
|
|
513cde6231 | ||
|
|
e33eaf4d1e | ||
|
|
e29e79052d | ||
|
|
8d28e23e16 | ||
|
|
0622ccc638 | ||
|
|
6e1081d793 | ||
|
|
5830ac7908 | ||
|
|
2f80da1e15 | ||
|
|
c32827b4cb | ||
|
|
5f7abc2df0 | ||
|
|
875d6610ae | ||
|
|
94fc64226c | ||
|
|
19b7c69b14 | ||
|
|
0fd2d27b6f | ||
|
|
87210e5f6c | ||
|
|
9df28b9d3f | ||
|
|
9602f862c9 | ||
|
|
5b0ced8e7d | ||
|
|
63779c0471 | ||
|
|
433bb24dcd | ||
|
|
ba25f7710c | ||
|
|
bf607621b3 | ||
|
|
769ec8852f | ||
|
|
7fcf39b6d3 | ||
|
|
2762dbc0b2 |
164
README.md
164
README.md
@@ -21,26 +21,155 @@ A small light is seen at the distance...
|
|||||||
|
|
||||||
## What is this?
|
## What is this?
|
||||||
|
|
||||||
MonsterMod is a MetaMod plugin. It's purpose was to allow support for monsters to spawn in multiplayer games where they are not. The project updates became incredibly obscure, getting up-to-date versions and the new additions was very difficult. And the only one who had even futher progress kept the plugin private.
|
MonsterMod is a MetaMod plugin. Its purpose was to allow multiplayer games to add monsters, where it wasn't possible to do so by normal means. The updates of the project became incredibly obscure: Getting the "up-to-date" versions containing the new additions (opposing force monsters, for example) were very difficult. And the only one who managed to bring the plugin even futher kept the progress of the plugin private.
|
||||||
|
|
||||||
Nearly 18 years after botman's original plugin was released, the future of the project became nothing but a forgotten, ancient relic of the past.
|
After 20 years (and a half) since botman's original plugin was released, the future of the project became nothing but a forgotten, ancient relic of the past.
|
||||||
|
|
||||||
Not anymore...
|
Not anymore...
|
||||||
|
|
||||||
This project aims towards the recreation of the new features of the "obscured and updated" Monster Mod plugin. Taking botman's original 2002 plugin and working up from the ground, and to rebuild it with the new features and monsters that only few were able to see.
|
The first goal of this project aims towards the recreation of the new features of the "obscured and updated" Monster Mod plugin. Taking botman's original 2002 plugin and working from the ground up, the mission is to rebuild it with the new features and monsters that only few were able to see.
|
||||||
|
|
||||||
The source code is completely free to use for everyone: In the event that the development of this new project falls and becomes stagnant again, the plugin will live on, as the project's second goal is it's preservation. The original botman's page where you can download the 2002 plugin will not stay up forever.
|
The source code is completely free for everyone to use: In the event that the development of this project falls and becomes stagnant again, the plugin will live on, as the project's second goal is its preservation. The original botman's page where you can download the 2002 plugin will not stay up forever.
|
||||||
|
|
||||||
Under no circumstances we shall allow this project to fade away and become lost amidst the gears of time.
|
Under no circumstances shall we allow this project to fade away and become lost amidst the gears of time.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
**Windows:**
|
||||||
|
`win32 addons\monstermod\monster_mm.dll`
|
||||||
|
|
||||||
|
**Linux:**
|
||||||
|
`linux addons/monstermod/monster_mm_i386.so`
|
||||||
|
|
||||||
|
Additional configuration files are included in the release files, each explaining it's usage and installation instructions.
|
||||||
|
|
||||||
|
## Build Instructions
|
||||||
|
|
||||||
|
*TODO: Add build instructions.*
|
||||||
|
|
||||||
|
## MonsterMod and ReHLDS
|
||||||
|
|
||||||
|
Usage of ReHLDS is highly recommended, as you can use the command `rescount` which will reveal the current number of precached models and sounds. You can also use `reslist model` and `reslist sound` to see the entire list of precached content.
|
||||||
|
|
||||||
|
Keeping track of the number of precached content will allow you to maximize the number of monsters you can use without risking going over the limits.
|
||||||
|
|
||||||
|
## Using MonsterMod on Counter-Strike
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
```
|
||||||
|
"weapons/ak47_boltpull.wav"
|
||||||
|
"weapons/ak47_clipin.wav"
|
||||||
|
"weapons/ak47_clipout.wav"
|
||||||
|
"weapons/aug_boltpull.wav"
|
||||||
|
"weapons/aug_boltslap.wav"
|
||||||
|
"weapons/aug_clipin.wav"
|
||||||
|
"weapons/aug_clipout.wav"
|
||||||
|
"weapons/aug_forearm.wav"
|
||||||
|
"weapons/awp_clipin.wav"
|
||||||
|
"weapons/awp_clipout.wav"
|
||||||
|
"weapons/awp_deploy.wav"
|
||||||
|
"weapons/boltdown.wav"
|
||||||
|
"weapons/boltpull1.wav"
|
||||||
|
"weapons/boltup.wav"
|
||||||
|
"weapons/clipin1.wav"
|
||||||
|
"weapons/clipout1.wav"
|
||||||
|
"weapons/de_clipin.wav"
|
||||||
|
"weapons/de_clipout.wav"
|
||||||
|
"weapons/de_deploy.wav"
|
||||||
|
"weapons/elite_clipout.wav"
|
||||||
|
"weapons/elite_deploy.wav"
|
||||||
|
"weapons/elite_leftclipin.wav"
|
||||||
|
"weapons/elite_reloadstart.wav"
|
||||||
|
"weapons/elite_rightclipin.wav"
|
||||||
|
"weapons/elite_sliderelease.wav"
|
||||||
|
"weapons/elite_twirl.wav"
|
||||||
|
"weapons/famas_boltpull.wav"
|
||||||
|
"weapons/famas_boltslap.wav"
|
||||||
|
"weapons/famas_clipin.wav"
|
||||||
|
"weapons/famas_clipout.wav"
|
||||||
|
"weapons/famas_forearm.wav"
|
||||||
|
"weapons/fiveseven_clipin.wav"
|
||||||
|
"weapons/fiveseven_clipout.wav"
|
||||||
|
"weapons/fiveseven_slidepull.wav"
|
||||||
|
"weapons/fiveseven_sliderelease.wav"
|
||||||
|
"weapons/g3sg1_clipin.wav"
|
||||||
|
"weapons/g3sg1_clipout.wav"
|
||||||
|
"weapons/g3sg1_slide.wav"
|
||||||
|
"weapons/galil_boltpull.wav"
|
||||||
|
"weapons/galil_clipin.wav"
|
||||||
|
"weapons/galil_clipout.wav"
|
||||||
|
"weapons/m4a1_boltpull.wav"
|
||||||
|
"weapons/m4a1_clipin.wav"
|
||||||
|
"weapons/m4a1_clipout.wav"
|
||||||
|
"weapons/m4a1_deploy.wav"
|
||||||
|
"weapons/m4a1_silencer_off.wav"
|
||||||
|
"weapons/m4a1_silencer_on.wav"
|
||||||
|
"weapons/m249_boxin.wav"
|
||||||
|
"weapons/m249_boxout.wav"
|
||||||
|
"weapons/m249_chain.wav"
|
||||||
|
"weapons/m249_coverdown.wav"
|
||||||
|
"weapons/m249_coverup.wav"
|
||||||
|
"weapons/mac10_boltpull.wav"
|
||||||
|
"weapons/mac10_clipin.wav"
|
||||||
|
"weapons/mac10_clipout.wav"
|
||||||
|
"weapons/mp5_clipin.wav"
|
||||||
|
"weapons/mp5_clipout.wav"
|
||||||
|
"weapons/mp5_slideback.wav"
|
||||||
|
"weapons/p90_boltpull.wav"
|
||||||
|
"weapons/p90_clipin.wav"
|
||||||
|
"weapons/p90_clipout.wav"
|
||||||
|
"weapons/p90_cliprelease.wav"
|
||||||
|
"weapons/p228_clipin.wav"
|
||||||
|
"weapons/p228_clipout.wav"
|
||||||
|
"weapons/p228_slidepull.wav"
|
||||||
|
"weapons/p228_sliderelease.wav"
|
||||||
|
"weapons/scout_bolt.wav"
|
||||||
|
"weapons/scout_clipin.wav"
|
||||||
|
"weapons/scout_clipout.wav"
|
||||||
|
"weapons/sg550_boltpull.wav"
|
||||||
|
"weapons/sg550_clipin.wav"
|
||||||
|
"weapons/sg550_clipout.wav"
|
||||||
|
"weapons/sg552_boltpull.wav"
|
||||||
|
"weapons/sg552_clipin.wav"
|
||||||
|
"weapons/sg552_clipout.wav"
|
||||||
|
"weapons/slideback1.wav"
|
||||||
|
"weapons/sliderelease1.wav"
|
||||||
|
"weapons/ump45_boltslap.wav"
|
||||||
|
"weapons/ump45_clipin.wav"
|
||||||
|
"weapons/ump45_clipout.wav"
|
||||||
|
"weapons/usp_clipin.wav"
|
||||||
|
"weapons/usp_clipout.wav"
|
||||||
|
"weapons/usp_silencer_off.wav"
|
||||||
|
"weapons/usp_silencer_on.wav"
|
||||||
|
"weapons/usp_slideback.wav"
|
||||||
|
"weapons/usp_sliderelease.wav"
|
||||||
|
```
|
||||||
|
|
||||||
|
Doing this will free **85** sounds from the precache list that you can now use for additional monsters.
|
||||||
|
|
||||||
|
## Known Bugs
|
||||||
|
|
||||||
|
I'm aware that the plugin is far from perfect, and there are a few things that need polishing *-especially the AI-*. I'll try to fix/will be fixing as the project evolves:
|
||||||
|
|
||||||
|
- Human Grunts are unable to reload their weapons. As a workaround, infinite ammo has been given to them.
|
||||||
|
|
||||||
|
- Male Assassins share the same AI as HGrunts, so their ammo problem is still a thing, and the same workaround is used.
|
||||||
|
|
||||||
|
- Shock Troopers seems to be broken despite sharing HGrunts AI code. Despite having infinite ammo, they eventually stop firing. Worse, taking cover is absolutely broken, and they remain completely frozen in place when it happens.
|
||||||
|
|
||||||
|
- If a Heavy Weapons Grunt is to lose their target while his minigun is still spinning, the next time it targets an enemy it will instantly fire instead of spinning up the minigun again.
|
||||||
|
|
||||||
## Milestones
|
## Milestones
|
||||||
|
|
||||||
Attempting to recreate everything in one go is a daunting task.
|
Attempting to recreate everything in one go is a daunting task.
|
||||||
Let it be known that the original 2002 source code will NOT compile on today's compilers, and does NOT contain all the necessary files for compilation. The preliminary was to rewrite and provide as many files or lines of code to ensure it can compile again, and be usable on an actual HLDS installation.
|
Let it be known that the original 2002 source code will NOT compile on today's compilers, and does NOT contain all the necessary files for compilation. The preliminary was to rewrite and provide as many files or lines of code to ensure it can compile again, and be usable on an actual HLDS installation.
|
||||||
|
|
||||||
Currently, I aim working this for linux only. While the original 2002 Visual C++ DSP file exists in the repository, it has not been updated to newer formats. Who knows if it can even open on newer Visual Studio versions, let alone, to compile?
|
The original Visual C++ 6.0 DSP file exists in the repository but neither the file nor the code has been updated to newer formats. Don't expect it to compile on modern Visual Studio versions.
|
||||||
|
|
||||||
Even so, in linux, I had to resort to compile under GCC 4.8 for the plugin to work. While I could use a newer compiler (GCC 9 at the time of this writing), whenever or not the plugin will run is dependant on the HLDS installation and the system libraries is it hosted with. If you compile with newer GCC compilers, be warned that it may refuse to load, as it will complain about libstdc++ not having CXXABI_1.X.X or similar.
|
|
||||||
|
|
||||||
Current milestones are separated by "Tiers", which are as follows:
|
Current milestones are separated by "Tiers", which are as follows:
|
||||||
|
|
||||||
@@ -63,19 +192,24 @@ Current milestones are separated by "Tiers", which are as follows:
|
|||||||
|
|
||||||
### Tier 3
|
### Tier 3
|
||||||
|
|
||||||
- Implement *-almost-* all Opposing Force monsters.
|
- Update source code so it can compile AND run **ON WINDOWS**. **[DONE]**
|
||||||
- Implement *-almost-* all default Sven Co-op monsters.
|
- Implement *-almost-* all Opposing Force monsters. **[DONE]**
|
||||||
- Add configurations to change AI behaviour.
|
- Implement *-almost-* all default Sven Co-op monsters. **[DONE]**
|
||||||
|
- Make MonsterMod aware of normal game entities. *-For those wanting to use this in vanilla HL.-* **[DONE]**
|
||||||
|
- Custom model support. **[DONE]**
|
||||||
|
|
||||||
### Tier 4
|
### Tier 4
|
||||||
|
|
||||||
- Custom model support, along with scaling.
|
- Implement reading entities within the BSP itself.
|
||||||
- Custom sound support.
|
- Model scaling. *-If possible.-*
|
||||||
- Implement extra entities to enhance map gameplay.
|
- Custom sound support, along with sentences.
|
||||||
|
- Fix all pending bugs.
|
||||||
|
|
||||||
### Tier 5
|
### Tier 5
|
||||||
|
|
||||||
- Update source code so it can compile AND run **ON WINDOWS**.
|
- Add configurations to change AI behaviour.
|
||||||
|
- Optimize code and enhance the AI.
|
||||||
|
- Create "tool" entities for easier map customization.
|
||||||
|
|
||||||
|
|
||||||
What will the future hold after all Tiers has been completed?
|
What will the future hold after all Tiers has been completed?
|
||||||
|
|||||||
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,14 +4,13 @@
|
|||||||
//
|
//
|
||||||
// To add entries to this file, just pretend it's ripent.
|
// To add entries to this file, just pretend it's ripent.
|
||||||
//
|
//
|
||||||
// "delay" means monster respawndelay.
|
// You may also be interesed in these other keyvalues:
|
||||||
//
|
|
||||||
// You may also be interesed in these other 2 keyvalues:
|
|
||||||
//
|
//
|
||||||
// "displayname" to change the monster's name, shown in HUD when you point at it.
|
// "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:
|
// "classify" to change the monster's default classification, use one of these values:
|
||||||
//
|
//
|
||||||
// CLASS_NONE 0
|
// CLASS_NONE -1
|
||||||
// CLASS_MACHINE 1
|
// CLASS_MACHINE 1
|
||||||
// CLASS_PLAYER 2
|
// CLASS_PLAYER 2
|
||||||
// CLASS_HUMAN_PASSIVE 3
|
// CLASS_HUMAN_PASSIVE 3
|
||||||
@@ -25,6 +24,8 @@
|
|||||||
// CLASS_PLAYER_ALLY 11
|
// CLASS_PLAYER_ALLY 11
|
||||||
// CLASS_PLAYER_BIOWEAPON 12
|
// CLASS_PLAYER_BIOWEAPON 12
|
||||||
// CLASS_ALIEN_BIOWEAPON 13
|
// CLASS_ALIEN_BIOWEAPON 13
|
||||||
|
// CLASS_RACEX_PITDRONE 14
|
||||||
|
// CLASS_RACEX_SHOCK 15
|
||||||
|
|
||||||
{
|
{
|
||||||
"origin" "90 -180 150"
|
"origin" "90 -180 150"
|
||||||
|
|||||||
@@ -3,8 +3,11 @@
|
|||||||
//
|
//
|
||||||
// Install this file to your mod's base directory.
|
// Install this file to your mod's base directory.
|
||||||
//
|
//
|
||||||
// (just remove the comment characters at the beginning of the line for the
|
// Just remove the comment characters at the beginning of the line for the
|
||||||
// monsters that you always want to precache.)
|
// monsters that you always want to precache.
|
||||||
|
//
|
||||||
|
// Be wary if you precache too many monsters, it may easily crash your
|
||||||
|
// server due to the age-old 512 limit.
|
||||||
|
|
||||||
//monster_alien_grunt
|
//monster_alien_grunt
|
||||||
//monster_apache
|
//monster_apache
|
||||||
@@ -24,3 +27,14 @@
|
|||||||
//monster_turret
|
//monster_turret
|
||||||
//monster_miniturret
|
//monster_miniturret
|
||||||
//monster_sentry
|
//monster_sentry
|
||||||
|
//monster_gonome
|
||||||
|
//monster_male_assassin
|
||||||
|
//monster_otis
|
||||||
|
//monster_pitdrone
|
||||||
|
//monster_shocktrooper
|
||||||
|
//monster_alien_voltigore
|
||||||
|
//monster_alien_babyvoltigore
|
||||||
|
//monster_babygarg
|
||||||
|
//monster_hwgrunt
|
||||||
|
//monster_robogrunt
|
||||||
|
//monster_stukabat
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
//
|
//
|
||||||
// Install this file to your mod's base directory.
|
// Install this file to your mod's base directory.
|
||||||
|
|
||||||
|
|
||||||
|
// ORIGINAL HALF-LIFE MONSTERS
|
||||||
|
|
||||||
// Alien Grunt
|
// Alien Grunt
|
||||||
sk_agrunt_health 90
|
sk_agrunt_health 90
|
||||||
sk_agrunt_dmg_punch 20
|
sk_agrunt_dmg_punch 20
|
||||||
@@ -55,10 +58,6 @@ sk_islave_dmg_claw 10
|
|||||||
sk_islave_dmg_clawrake 25
|
sk_islave_dmg_clawrake 25
|
||||||
sk_islave_dmg_zap 10
|
sk_islave_dmg_zap 10
|
||||||
|
|
||||||
// Icthyosaur
|
|
||||||
sk_ichthyosaur_health 200
|
|
||||||
sk_ichthyosaur_shake 35
|
|
||||||
|
|
||||||
// Controller
|
// Controller
|
||||||
sk_controller_health 60
|
sk_controller_health 60
|
||||||
sk_controller_dmgzap 25
|
sk_controller_dmgzap 25
|
||||||
@@ -88,8 +87,75 @@ sk_miniturret_health 40
|
|||||||
// Sentry Turret
|
// Sentry Turret
|
||||||
sk_sentry_health 40
|
sk_sentry_health 40
|
||||||
|
|
||||||
|
|
||||||
|
// OPPOSING FORCE MONSTERS
|
||||||
|
|
||||||
|
// Gonome
|
||||||
|
sk_gonome_health 85
|
||||||
|
sk_gonome_dmg_one_slash 20
|
||||||
|
sk_gonome_dmg_guts 10
|
||||||
|
sk_gonome_dmg_one_bite 14
|
||||||
|
|
||||||
|
// Male Assassin
|
||||||
|
sk_massassin_health 50
|
||||||
|
sk_massassin_kick 25
|
||||||
|
|
||||||
|
// Otis
|
||||||
|
sk_otis_health 35
|
||||||
|
|
||||||
|
// Pit Drone
|
||||||
|
sk_pitdrone_health 40
|
||||||
|
sk_pitdrone_dmg_bite 25
|
||||||
|
sk_pitdrone_dmg_whip 35
|
||||||
|
sk_pitdrone_dmg_spit 10
|
||||||
|
|
||||||
|
// Shock Roach
|
||||||
|
sk_shockroach_health 10
|
||||||
|
sk_shockroach_lifespan 10
|
||||||
|
|
||||||
|
// ShockTrooper
|
||||||
|
sk_shocktrooper_health 50
|
||||||
|
sk_shocktrooper_kick 10
|
||||||
|
sk_shocktrooper_maxcharge 8
|
||||||
|
sk_shocktrooper_rchgspeed 1
|
||||||
|
sk_shock_dmg 15
|
||||||
|
sk_spore_dmg 50
|
||||||
|
|
||||||
|
// Voltigore
|
||||||
|
sk_voltigore_health 320
|
||||||
|
sk_voltigore_dmg_beam 50
|
||||||
|
sk_voltigore_dmg_punch 40
|
||||||
|
|
||||||
|
// Baby Voltigore
|
||||||
|
sk_babyvoltigore_health 60
|
||||||
|
sk_babyvoltigore_dmg_punch 15
|
||||||
|
|
||||||
|
|
||||||
|
// SVEN CO-OP MONSTERS
|
||||||
|
|
||||||
|
// Baby Gargantua
|
||||||
|
sk_babygarg_health 640
|
||||||
|
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
|
// MONSTER WEAPON DAMAGE
|
||||||
sk_9mm_bullet 5
|
sk_9mm_bullet 5
|
||||||
sk_9mmAR_bullet 4
|
sk_9mmAR_bullet 4
|
||||||
sk_9mmAR_grenade 100
|
sk_9mmAR_grenade 100
|
||||||
sk_12mm_bullet 10
|
sk_12mm_bullet 10
|
||||||
|
sk_762_bullet 100
|
||||||
|
sk_357_bullet 40
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
CPP = g++-7
|
CPP = g++
|
||||||
BASEFLAGS = -Dstricmp=strcasecmp -Dstrcmpi=strcasecmp -m32
|
BASEFLAGS = -Dstricmp=strcasecmp -Dstrcmpi=strcasecmp -m32
|
||||||
OPTFLAGS = -O2
|
OPTFLAGS = -O2
|
||||||
CPPFLAGS = ${BASEFLAGS} ${OPTFLAGS} -w -I. -I../engine -I../common -I../pm_shared -I../metamod
|
CPPFLAGS = ${BASEFLAGS} ${OPTFLAGS} -w -I. -I../engine -I../common -I../pm_shared -I../metamod
|
||||||
@@ -17,31 +17,44 @@ OBJ = \
|
|||||||
controller.o \
|
controller.o \
|
||||||
defaultai.o \
|
defaultai.o \
|
||||||
dllapi.o \
|
dllapi.o \
|
||||||
explode.o \
|
|
||||||
effects.o \
|
effects.o \
|
||||||
|
explode.o \
|
||||||
|
flyingmonster.o \
|
||||||
gargantua.o \
|
gargantua.o \
|
||||||
ggrenade.o \
|
ggrenade.o \
|
||||||
|
gonome.o \
|
||||||
h_ai.o \
|
h_ai.o \
|
||||||
h_export.o \
|
|
||||||
hassassin.o \
|
hassassin.o \
|
||||||
headcrab.o \
|
headcrab.o \
|
||||||
|
h_export.o \
|
||||||
hgrunt.o \
|
hgrunt.o \
|
||||||
hornet.o \
|
hornet.o \
|
||||||
houndeye.o \
|
houndeye.o \
|
||||||
|
hwgrunt.o \
|
||||||
islave.o \
|
islave.o \
|
||||||
|
massn.o \
|
||||||
monster_api.o \
|
monster_api.o \
|
||||||
monster_config.o \
|
monster_config.o \
|
||||||
monsters.o \
|
monsters.o \
|
||||||
monsterstate.o \
|
monsterstate.o \
|
||||||
nodes.o \
|
nodes.o \
|
||||||
|
otis.o \
|
||||||
|
pitdrone.o \
|
||||||
|
rgrunt.o \
|
||||||
scientist.o \
|
scientist.o \
|
||||||
|
shock.o \
|
||||||
|
shockroach.o \
|
||||||
skill.o \
|
skill.o \
|
||||||
sound.o \
|
sound.o \
|
||||||
|
sporegrenade.o \
|
||||||
squeakgrenade.o \
|
squeakgrenade.o \
|
||||||
|
strooper.o \
|
||||||
|
stukabat.o \
|
||||||
subs.o \
|
subs.o \
|
||||||
talkmonster.o \
|
talkmonster.o \
|
||||||
turret.o \
|
turret.o \
|
||||||
util.o \
|
util.o \
|
||||||
|
voltigore.o \
|
||||||
weapons.o \
|
weapons.o \
|
||||||
zombie.o
|
zombie.o
|
||||||
|
|
||||||
|
|||||||
@@ -297,9 +297,14 @@ int GetAnimationEvent( void *pmodel, entvars_t *pev, MonsterEvent_t *pMonsterEve
|
|||||||
|
|
||||||
for (; index < pseqdesc->numevents; index++)
|
for (; index < pseqdesc->numevents; index++)
|
||||||
{
|
{
|
||||||
|
// Some Sven Co-op monsters rely on client-side events.
|
||||||
|
// Let those be sent to the server AI, even if it's wrong. -Giegue
|
||||||
|
|
||||||
|
/*
|
||||||
// Don't send client-side events to the server AI
|
// Don't send client-side events to the server AI
|
||||||
if ( pevent[index].event >= EVENT_CLIENT )
|
if ( pevent[index].event >= EVENT_CLIENT )
|
||||||
continue;
|
continue;
|
||||||
|
*/
|
||||||
|
|
||||||
if ( (pevent[index].frame >= flStart && pevent[index].frame < flEnd) ||
|
if ( (pevent[index].frame >= flStart && pevent[index].frame < flEnd) ||
|
||||||
((pseqdesc->flags & STUDIO_LOOPING) && flEnd >= pseqdesc->numframes - 1 && pevent[index].frame < flEnd - pseqdesc->numframes + 1) )
|
((pseqdesc->flags & STUDIO_LOOPING) && flEnd >= pseqdesc->numframes - 1 && pevent[index].frame < flEnd - pseqdesc->numframes + 1) )
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ void CMApache :: Spawn( void )
|
|||||||
pev->movetype = MOVETYPE_FLY;
|
pev->movetype = MOVETYPE_FLY;
|
||||||
pev->solid = SOLID_BBOX;
|
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_SetSize( pev, Vector( -32, -32, -64 ), Vector( 32, 32, 0 ) );
|
||||||
UTIL_SetOrigin( pev, pev->origin );
|
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)
|
// when a level is loaded, nobody will talk (time is reset to 0)
|
||||||
TalkInit();
|
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);
|
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
|
||||||
|
|
||||||
pev->solid = SOLID_SLIDEBOX;
|
pev->solid = SOLID_SLIDEBOX;
|
||||||
|
|||||||
@@ -603,7 +603,7 @@ void CMBigMomma :: Spawn()
|
|||||||
{
|
{
|
||||||
Precache( );
|
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( -32, -32, 0 ), Vector( 32, 32, 64 ) );
|
||||||
UTIL_SetSize( pev, Vector( -64, -64, 0 ), Vector( 64, 64, 128 ) );
|
UTIL_SetSize( pev, Vector( -64, -64, 0 ), Vector( 64, 64, 128 ) );
|
||||||
|
|
||||||
|
|||||||
@@ -608,7 +608,7 @@ void CMBullsquid :: Spawn()
|
|||||||
{
|
{
|
||||||
Precache( );
|
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 ) );
|
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
|
||||||
|
|
||||||
pev->solid = SOLID_SLIDEBOX;
|
pev->solid = SOLID_SLIDEBOX;
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ CMBaseEntity
|
|||||||
// C functions for external declarations that call the appropriate C++ methods
|
// C functions for external declarations that call the appropriate C++ methods
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define EXPORT _declspec( dllexport )
|
#define EXPORT __declspec( dllexport )
|
||||||
#else
|
#else
|
||||||
#define EXPORT /* */
|
#define EXPORT /* */
|
||||||
#endif
|
#endif
|
||||||
@@ -80,6 +80,8 @@ typedef void (CMBaseEntity::*USEPTR)( edict_t *pActivator, edict_t *pCaller, USE
|
|||||||
#define CLASS_PLAYER_ALLY 11
|
#define CLASS_PLAYER_ALLY 11
|
||||||
#define CLASS_PLAYER_BIOWEAPON 12 // hornets and snarks.launched by players
|
#define CLASS_PLAYER_BIOWEAPON 12 // hornets and snarks.launched by players
|
||||||
#define CLASS_ALIEN_BIOWEAPON 13 // hornets and snarks.launched by the alien menace
|
#define CLASS_ALIEN_BIOWEAPON 13 // hornets and snarks.launched by the alien menace
|
||||||
|
#define CLASS_RACEX_PITDRONE 14
|
||||||
|
#define CLASS_RACEX_SHOCK 15
|
||||||
#define CLASS_BARNACLE 99 // special because no one pays attention to it, and it eats a wide cross-section of creatures.
|
#define CLASS_BARNACLE 99 // special because no one pays attention to it, and it eats a wide cross-section of creatures.
|
||||||
|
|
||||||
class CMBaseEntity;
|
class CMBaseEntity;
|
||||||
@@ -599,7 +601,6 @@ template <class T> T * CreateClassPtr( T *a )
|
|||||||
// store the class pointer in the array here!!!
|
// store the class pointer in the array here!!!
|
||||||
monsters[monster_index].monster_index = edict_index;
|
monsters[monster_index].monster_index = edict_index;
|
||||||
monsters[monster_index].monster_pent = temp_edict;
|
monsters[monster_index].monster_pent = temp_edict;
|
||||||
monsters[monster_index].respawn_index = -1;
|
|
||||||
monsters[monster_index].pMonster = (CMBaseMonster *)a;
|
monsters[monster_index].pMonster = (CMBaseMonster *)a;
|
||||||
|
|
||||||
// get the private data
|
// 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
|
||||||
@@ -134,6 +134,8 @@ public:
|
|||||||
virtual void MonsterThink( void );
|
virtual void MonsterThink( void );
|
||||||
void EXPORT CallMonsterThink( void ) { this->MonsterThink(); }
|
void EXPORT CallMonsterThink( void ) { this->MonsterThink(); }
|
||||||
virtual int IRelationship ( CMBaseEntity *pTarget );
|
virtual int IRelationship ( CMBaseEntity *pTarget );
|
||||||
|
virtual int IRelationship ( int iTargetClass );
|
||||||
|
int IRelationshipByClass ( int iClass );
|
||||||
virtual void MonsterInit ( void );
|
virtual void MonsterInit ( void );
|
||||||
virtual void MonsterInitDead( void ); // Call after animation/pose is set up
|
virtual void MonsterInitDead( void ); // Call after animation/pose is set up
|
||||||
virtual void BecomeDead( void );
|
virtual void BecomeDead( void );
|
||||||
@@ -213,7 +215,7 @@ public:
|
|||||||
void AdvanceRoute ( float distance );
|
void AdvanceRoute ( float distance );
|
||||||
virtual BOOL FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, edict_t *pTargetEnt, Vector *pApex );
|
virtual BOOL FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, edict_t *pTargetEnt, Vector *pApex );
|
||||||
void MakeIdealYaw( Vector vecTarget );
|
void MakeIdealYaw( Vector vecTarget );
|
||||||
virtual void SetYawSpeed ( void ) { return; };// allows different yaw_speeds for each activity
|
virtual void SetYawSpeed ( void ) { return; }; // allows different yaw_speeds for each activity
|
||||||
BOOL BuildRoute ( const Vector &vecGoal, int iMoveFlag, edict_t *pTarget );
|
BOOL BuildRoute ( const Vector &vecGoal, int iMoveFlag, edict_t *pTarget );
|
||||||
virtual BOOL BuildNearestRoute ( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist );
|
virtual BOOL BuildNearestRoute ( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist );
|
||||||
int RouteClassify( int iMoveFlag );
|
int RouteClassify( int iMoveFlag );
|
||||||
@@ -347,7 +349,7 @@ public:
|
|||||||
|
|
||||||
virtual float GetDamageAmount( void ) { return gSkillData.headcrabDmgBite; }
|
virtual float GetDamageAmount( void ) { return gSkillData.headcrabDmgBite; }
|
||||||
virtual int GetVoicePitch( void ) { return 100; }
|
virtual int GetVoicePitch( void ) { return 100; }
|
||||||
virtual float GetSoundVolue( void ) { return 1.0; }
|
virtual float GetSoundVolume( void ) { return 1.0; }
|
||||||
Schedule_t* GetScheduleOfType ( int Type );
|
Schedule_t* GetScheduleOfType ( int Type );
|
||||||
|
|
||||||
CUSTOM_SCHEDULES;
|
CUSTOM_SCHEDULES;
|
||||||
@@ -370,7 +372,7 @@ public:
|
|||||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||||
Schedule_t* GetScheduleOfType ( int Type );
|
Schedule_t* GetScheduleOfType ( int Type );
|
||||||
virtual int GetVoicePitch( void ) { return PITCH_NORM + RANDOM_LONG(40,50); }
|
virtual int GetVoicePitch( void ) { return PITCH_NORM + RANDOM_LONG(40,50); }
|
||||||
virtual float GetSoundVolue( void ) { return 0.8; }
|
virtual float GetSoundVolume( void ) { return 0.8; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -1087,6 +1089,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
edict_t *GargantuaCheckTraceHullAttack(float flDist, int iDamage, int iDmgType);
|
edict_t *GargantuaCheckTraceHullAttack(float flDist, int iDamage, int iDmgType);
|
||||||
|
|
||||||
|
protected:
|
||||||
CMSprite *m_pEyeGlow; // Glow around the eyes
|
CMSprite *m_pEyeGlow; // Glow around the eyes
|
||||||
CMBeam *m_pFlame[4]; // Flame beams
|
CMBeam *m_pFlame[4]; // Flame beams
|
||||||
|
|
||||||
@@ -1237,4 +1240,474 @@ public:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// opposing force monsters
|
||||||
|
//
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Gonome's guts projectile
|
||||||
|
//=========================================================
|
||||||
|
class CGonomeGuts : public CMBaseEntity
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Spawn( void );
|
||||||
|
|
||||||
|
static edict_t *Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity );
|
||||||
|
void GutsTouch( edict_t *pOther );
|
||||||
|
void EXPORT Animate( void );
|
||||||
|
|
||||||
|
int m_maxFrame;
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Gonome
|
||||||
|
//=========================================================
|
||||||
|
class CMGonome : public CMBaseMonster
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
void Spawn(void);
|
||||||
|
void Precache(void);
|
||||||
|
|
||||||
|
int Classify(void);
|
||||||
|
void SetYawSpeed();
|
||||||
|
void HandleAnimEvent(MonsterEvent_t *pEvent);
|
||||||
|
int IgnoreConditions();
|
||||||
|
void IdleSound( void );
|
||||||
|
void PainSound( void );
|
||||||
|
void DeathSound( void );
|
||||||
|
void AlertSound( void );
|
||||||
|
void StartTask(Task_t *pTask);
|
||||||
|
|
||||||
|
BOOL CheckMeleeAttack2(float flDot, float flDist);
|
||||||
|
BOOL CheckRangeAttack1(float flDot, float flDist);
|
||||||
|
void SetActivity( Activity NewActivity );
|
||||||
|
|
||||||
|
Schedule_t *GetSchedule();
|
||||||
|
Schedule_t *GetScheduleOfType( int Type );
|
||||||
|
void RunTask(Task_t* pTask);
|
||||||
|
|
||||||
|
int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType);
|
||||||
|
void Killed(entvars_t *pevAttacker, int iGib);
|
||||||
|
|
||||||
|
void UnlockPlayer();
|
||||||
|
CGonomeGuts* GetGonomeGuts(entvars_t *pevOwner, const Vector& pos);
|
||||||
|
void ClearGuts();
|
||||||
|
|
||||||
|
CUSTOM_SCHEDULES;
|
||||||
|
|
||||||
|
static const char* pPainSounds[];
|
||||||
|
static const char* pIdleSounds[];
|
||||||
|
static const char* pDeathSounds[];
|
||||||
|
static const char *pAttackHitSounds[];
|
||||||
|
static const char *pAttackMissSounds[];
|
||||||
|
|
||||||
|
protected:
|
||||||
|
float m_flNextFlinch;
|
||||||
|
float m_flNextThrowTime;// last time the gonome used the guts attack.
|
||||||
|
CGonomeGuts* m_pGonomeGuts;
|
||||||
|
|
||||||
|
BOOL m_fPlayerLocked;
|
||||||
|
EHANDLE m_lockedPlayer;
|
||||||
|
|
||||||
|
bool m_meleeAttack2;
|
||||||
|
bool m_playedAttackSound;
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Male Assassin
|
||||||
|
//=========================================================
|
||||||
|
class CMMassn : public CMHGrunt
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int Classify(void);
|
||||||
|
void HandleAnimEvent(MonsterEvent_t *pEvent);
|
||||||
|
void Sniperrifle(void);
|
||||||
|
|
||||||
|
BOOL FOkToSpeak(void);
|
||||||
|
|
||||||
|
void Spawn( void );
|
||||||
|
void Precache( void );
|
||||||
|
|
||||||
|
void DeathSound(void);
|
||||||
|
void PainSound(void);
|
||||||
|
void IdleSound(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Otis
|
||||||
|
//=========================================================
|
||||||
|
class CMOtis : public CMBarney
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void KeyValue(KeyValueData *pkvd);
|
||||||
|
|
||||||
|
void Spawn(void);
|
||||||
|
void Precache(void);
|
||||||
|
void BarneyFirePistol(void);
|
||||||
|
void AlertSound(void);
|
||||||
|
void HandleAnimEvent(MonsterEvent_t *pEvent);
|
||||||
|
|
||||||
|
int TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType);
|
||||||
|
|
||||||
|
// Override these to set behavior
|
||||||
|
Schedule_t *GetSchedule(void);
|
||||||
|
|
||||||
|
void TalkInit(void);
|
||||||
|
void TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||||
|
void Killed(entvars_t *pevAttacker, int iGib);
|
||||||
|
|
||||||
|
int head;
|
||||||
|
int bodystate;
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Pit Drone's spit projectile
|
||||||
|
//=========================================================
|
||||||
|
class CPitdroneSpike : public CMBaseEntity
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Spawn(void);
|
||||||
|
void EXPORT SpikeTouch(edict_t *pOther);
|
||||||
|
void EXPORT StartTrail();
|
||||||
|
static edict_t *Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, Vector vecAngles);
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Pit Drone
|
||||||
|
//=========================================================
|
||||||
|
class CMPitdrone : public CMBaseMonster
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Spawn(void);
|
||||||
|
void Precache(void);
|
||||||
|
void HandleAnimEvent(MonsterEvent_t *pEvent);
|
||||||
|
void SetYawSpeed(void);
|
||||||
|
int ISoundMask();
|
||||||
|
void KeyValue(KeyValueData *pkvd);
|
||||||
|
|
||||||
|
int Classify(void);
|
||||||
|
|
||||||
|
BOOL CheckMeleeAttack1(float flDot, float flDist);
|
||||||
|
BOOL CheckRangeAttack1(float flDot, float flDist);
|
||||||
|
void IdleSound(void);
|
||||||
|
void PainSound(void);
|
||||||
|
void AlertSound(void);
|
||||||
|
void DeathSound(void);
|
||||||
|
void BodyChange(float spikes);
|
||||||
|
int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType);
|
||||||
|
int IgnoreConditions(void);
|
||||||
|
Schedule_t* GetSchedule(void);
|
||||||
|
Schedule_t* GetScheduleOfType(int Type);
|
||||||
|
void StartTask(Task_t *pTask);
|
||||||
|
void RunTask(Task_t *pTask);
|
||||||
|
void RunAI(void);
|
||||||
|
void CheckAmmo();
|
||||||
|
void GibMonster();
|
||||||
|
CUSTOM_SCHEDULES;
|
||||||
|
|
||||||
|
float m_flLastHurtTime;
|
||||||
|
float m_flNextSpitTime;// last time the PitDrone used the spit attack.
|
||||||
|
float m_flNextFlinch;
|
||||||
|
int m_iInitialAmmo;
|
||||||
|
bool shouldAttackWithLeftClaw;
|
||||||
|
|
||||||
|
static const char *pIdleSounds[];
|
||||||
|
static const char *pAlertSounds[];
|
||||||
|
static const char *pPainSounds[];
|
||||||
|
static const char *pDieSounds[];
|
||||||
|
static const char *pAttackMissSounds[];
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Shock Roach
|
||||||
|
//=========================================================
|
||||||
|
class CMShockRoach : public CMHeadCrab
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Spawn(void);
|
||||||
|
void Precache(void);
|
||||||
|
void EXPORT LeapTouch(edict_t *pOther);
|
||||||
|
void PainSound(void);
|
||||||
|
void DeathSound(void);
|
||||||
|
void IdleSound(void);
|
||||||
|
void AlertSound(void);
|
||||||
|
void MonsterThink(void);
|
||||||
|
void StartTask(Task_t* pTask);
|
||||||
|
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||||
|
|
||||||
|
static const char *pIdleSounds[];
|
||||||
|
static const char *pAlertSounds[];
|
||||||
|
static const char *pPainSounds[];
|
||||||
|
static const char *pAttackSounds[];
|
||||||
|
static const char *pDeathSounds[];
|
||||||
|
static const char *pBiteSounds[];
|
||||||
|
|
||||||
|
float m_flBirthTime;
|
||||||
|
BOOL m_fRoachSolid;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void AttackSound();
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Shock Trooper
|
||||||
|
//=========================================================
|
||||||
|
class CMStrooper : public CMHGrunt
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Spawn(void);
|
||||||
|
void MonsterThink();
|
||||||
|
void Precache(void);
|
||||||
|
int Classify(void);
|
||||||
|
BOOL CheckRangeAttack1(float flDot, float flDist);
|
||||||
|
BOOL CheckRangeAttack2(float flDot, float flDist);
|
||||||
|
void HandleAnimEvent(MonsterEvent_t *pEvent);
|
||||||
|
void SetObjectCollisionBox( void )
|
||||||
|
{
|
||||||
|
pev->absmin = pev->origin + Vector( -24, -24, 0 );
|
||||||
|
pev->absmax = pev->origin + Vector( 24, 24, 72 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetActivity(Activity NewActivity);
|
||||||
|
|
||||||
|
void DeathSound(void);
|
||||||
|
void PainSound(void);
|
||||||
|
void IdleSound(void);
|
||||||
|
void GibMonster(void);
|
||||||
|
|
||||||
|
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||||
|
|
||||||
|
void DropShockRoach(bool gibbed);
|
||||||
|
|
||||||
|
Schedule_t *GetSchedule(void);
|
||||||
|
Schedule_t *GetScheduleOfType(int Type);
|
||||||
|
|
||||||
|
void SpeakSentence();
|
||||||
|
|
||||||
|
BOOL m_fRightClaw;
|
||||||
|
float m_rechargeTime;
|
||||||
|
float m_blinkTime;
|
||||||
|
float m_eyeChangeTime;
|
||||||
|
|
||||||
|
static const char *pGruntSentences[];
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Voltigore's energy ball projectile
|
||||||
|
//=========================================================
|
||||||
|
#define VOLTIGORE_MAX_BEAMS 8
|
||||||
|
class CMVoltigoreEnergyBall : public CMBaseEntity
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Spawn(void);
|
||||||
|
|
||||||
|
static edict_t *Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity);
|
||||||
|
void EXPORT BallTouch(edict_t *pOther);
|
||||||
|
void EXPORT FlyThink(void);
|
||||||
|
|
||||||
|
void CreateBeams();
|
||||||
|
void ClearBeams();
|
||||||
|
void UpdateBeams();
|
||||||
|
|
||||||
|
CMBeam* m_pBeam[VOLTIGORE_MAX_BEAMS];
|
||||||
|
int m_iBeams;
|
||||||
|
float m_timeToDie;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
void CreateBeam(int nIndex, const Vector& vecPos, int width, int brightness);
|
||||||
|
void UpdateBeam(int nIndex, const Vector& vecPos, bool show);
|
||||||
|
void ClearBeam(int nIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Voltigore
|
||||||
|
//=========================================================
|
||||||
|
class CMVoltigore : public CMBaseMonster
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void Spawn(void);
|
||||||
|
virtual void Precache(void);
|
||||||
|
void SetYawSpeed(void);
|
||||||
|
virtual int Classify(void);
|
||||||
|
virtual void HandleAnimEvent(MonsterEvent_t *pEvent);
|
||||||
|
virtual void IdleSound(void);
|
||||||
|
virtual void PainSound(void);
|
||||||
|
virtual void DeathSound(void);
|
||||||
|
virtual void AlertSound(void);
|
||||||
|
void AttackSound(void);
|
||||||
|
virtual void StartTask(Task_t *pTask);
|
||||||
|
virtual BOOL CheckMeleeAttack1(float flDot, float flDist);
|
||||||
|
virtual BOOL CheckRangeAttack1(float flDot, float flDist);
|
||||||
|
virtual void RunAI(void);
|
||||||
|
virtual void GibMonster();
|
||||||
|
Schedule_t *GetSchedule(void);
|
||||||
|
Schedule_t *GetScheduleOfType(int Type);
|
||||||
|
virtual int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType);
|
||||||
|
virtual void Killed(entvars_t *pevAttacker, int iGib);
|
||||||
|
|
||||||
|
CUSTOM_SCHEDULES
|
||||||
|
|
||||||
|
float m_flNextZapTime; // last time the voltigore used the spit attack.
|
||||||
|
BOOL m_fShouldUpdateBeam;
|
||||||
|
CMBeam* m_pBeam[3];
|
||||||
|
CMSprite* m_pBeamGlow;
|
||||||
|
int m_glowBrightness;
|
||||||
|
|
||||||
|
static const char* pAlertSounds[];
|
||||||
|
static const char* pAttackMeleeSounds[];
|
||||||
|
static const char* pMeleeHitSounds[];
|
||||||
|
static const char* pMeleeMissSounds[];
|
||||||
|
static const char* pComSounds[];
|
||||||
|
static const char* pDeathSounds[];
|
||||||
|
static const char* pFootstepSounds[];
|
||||||
|
static const char* pIdleSounds[];
|
||||||
|
static const char* pPainSounds[];
|
||||||
|
static const char* pGruntSounds[];
|
||||||
|
|
||||||
|
void CreateBeams();
|
||||||
|
void DestroyBeams();
|
||||||
|
void UpdateBeams();
|
||||||
|
|
||||||
|
void CreateGlow();
|
||||||
|
void DestroyGlow();
|
||||||
|
void GlowUpdate();
|
||||||
|
void GlowOff(void);
|
||||||
|
void GlowOn(int level);
|
||||||
|
protected:
|
||||||
|
void GibBeamDamage();
|
||||||
|
void PrecacheImpl(char* modelName);
|
||||||
|
int m_beamTexture;
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Baby Voltigore
|
||||||
|
//=========================================================
|
||||||
|
class CMBabyVoltigore : public CMVoltigore
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Spawn(void);
|
||||||
|
void Precache(void);
|
||||||
|
void HandleAnimEvent(MonsterEvent_t* pEvent);
|
||||||
|
BOOL CheckMeleeAttack1(float flDot, float flDist);
|
||||||
|
BOOL CheckRangeAttack1(float flDot, float flDist);
|
||||||
|
void StartTask(Task_t *pTask);
|
||||||
|
void Killed(entvars_t *pevAttacker, int iGib);
|
||||||
|
void GibMonster();
|
||||||
|
Schedule_t* GetSchedule();
|
||||||
|
Schedule_t* GetScheduleOfType(int Type);
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// sven co-op monsters
|
||||||
|
//
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Baby Gargantua
|
||||||
|
//=========================================================
|
||||||
|
class CMBabyGargantua : public CMGargantua
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Spawn( void );
|
||||||
|
void Precache( void );
|
||||||
|
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||||
|
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType );
|
||||||
|
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||||
|
|
||||||
|
BOOL CheckMeleeAttack1( float flDot, float flDist ); // Swipe
|
||||||
|
BOOL CheckMeleeAttack2( float flDot, float flDist ); // Flames
|
||||||
|
BOOL CheckRangeAttack1( float flDot, float flDist ); // Stomp attack
|
||||||
|
|
||||||
|
void StartTask( Task_t *pTask );
|
||||||
|
void RunTask( Task_t *pTask );
|
||||||
|
|
||||||
|
void StompAttack( void );
|
||||||
|
void FlameCreate( void );
|
||||||
|
void FlameUpdate( void );
|
||||||
|
void FlameDestroy( void );
|
||||||
|
|
||||||
|
static const char *pBeamAttackSounds[];
|
||||||
|
static const char *pFootSounds[];
|
||||||
|
static const char *pIdleSounds[];
|
||||||
|
static const char *pAlertSounds[];
|
||||||
|
static const char *pPainSounds[];
|
||||||
|
static const char *pAttackSounds[];
|
||||||
|
static const char *pStompSounds[];
|
||||||
|
static const char *pBreatheSounds[];
|
||||||
|
static const char *pDieSounds[];
|
||||||
|
|
||||||
|
private:
|
||||||
|
edict_t *BabyGargCheckTraceHullAttack(float flDist, int iDamage, int iDmgType);
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Heavy Weapons Grunt
|
||||||
|
//=========================================================
|
||||||
|
class CMHWGrunt : public CMHGrunt
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Spawn( void );
|
||||||
|
void Precache( void );
|
||||||
|
int Classify(void);
|
||||||
|
|
||||||
|
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||||
|
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType );
|
||||||
|
|
||||||
|
void HandleAnimEvent(MonsterEvent_t *pEvent);
|
||||||
|
void SetActivity(Activity NewActivity);
|
||||||
|
|
||||||
|
Schedule_t *GetSchedule( void );
|
||||||
|
Schedule_t *GetScheduleOfType ( int Type );
|
||||||
|
|
||||||
|
BOOL CheckRangeAttack1( float flDot, float flDist );
|
||||||
|
BOOL CheckMeleeAttack1( float flDot, float flDist );
|
||||||
|
BOOL CheckRangeAttack2( float flDot, float flDist );
|
||||||
|
|
||||||
|
void Minigun(void);
|
||||||
|
|
||||||
|
CUSTOM_SCHEDULES
|
||||||
|
|
||||||
|
float m_flMinigunSpinTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Robo Grunt
|
||||||
|
//=========================================================
|
||||||
|
class CMRGrunt : public CMHGrunt
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int Classify(void);
|
||||||
|
|
||||||
|
BOOL FOkToSpeak(void);
|
||||||
|
|
||||||
|
void Spawn( void );
|
||||||
|
void Precache( void );
|
||||||
|
|
||||||
|
void DeathSound(void);
|
||||||
|
void PainSound(void);
|
||||||
|
void IdleSound(void);
|
||||||
|
|
||||||
|
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||||
|
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType );
|
||||||
|
|
||||||
|
void RunAI(void);
|
||||||
|
|
||||||
|
void Killed(entvars_t *pevAttacker, int iGib);
|
||||||
|
void GibMonster();
|
||||||
|
|
||||||
|
void EXPORT SparkTouch(edict_t *pOther);
|
||||||
|
void EXPORT StartGib(void);
|
||||||
|
|
||||||
|
float m_flNextSpark;
|
||||||
|
float m_flNextDischarge;
|
||||||
|
float m_flActiveDischarge;
|
||||||
|
|
||||||
|
int m_iBodyGibs;
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Looking for Stukabat? It's located in cmflyingmonster.h
|
||||||
|
//=========================================================
|
||||||
|
|
||||||
#endif // BASEMONSTER_H
|
#endif // BASEMONSTER_H
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
#ifndef FLYINGMONSTER_H
|
#ifndef FLYINGMONSTER_H
|
||||||
#define FLYINGMONSTER_H
|
#define FLYINGMONSTER_H
|
||||||
|
|
||||||
|
#include "cmbasemonster.h"
|
||||||
|
|
||||||
class CMFlyingMonster : public CMBaseMonster
|
class CMFlyingMonster : public CMBaseMonster
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -49,5 +51,31 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Stukabat
|
||||||
|
//=========================================================
|
||||||
|
class CMStukabat : public CMFlyingMonster
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Spawn( void );
|
||||||
|
void Precache( void );
|
||||||
|
void SetYawSpeed( void );
|
||||||
|
int Classify ( void );
|
||||||
|
|
||||||
|
void SetActivity ( Activity NewActivity );
|
||||||
|
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||||
|
Schedule_t *GetScheduleOfType ( int Type );
|
||||||
|
|
||||||
|
int GetBitePitch( void ) { return PITCH_NORM + RANDOM_LONG( 40, 50 ); }
|
||||||
|
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||||
|
|
||||||
|
// Not used
|
||||||
|
BOOL CheckRangeAttack2 ( float flDot, float flDist ) { return FALSE; }
|
||||||
|
BOOL CheckMeleeAttack1 ( float flDot, float flDist ) { return FALSE; }
|
||||||
|
BOOL CheckMeleeAttack2 ( float flDot, float flDist ) { return FALSE; }
|
||||||
|
|
||||||
|
CUSTOM_SCHEDULES
|
||||||
|
};
|
||||||
|
|
||||||
#endif //FLYINGMONSTER_H
|
#endif //FLYINGMONSTER_H
|
||||||
|
|
||||||
|
|||||||
@@ -177,7 +177,15 @@ void CMGib :: SpawnHeadGib( entvars_t *pevVictim )
|
|||||||
pGib->LimitVelocity();
|
pGib->LimitVelocity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Overload
|
||||||
void CMGib :: SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human )
|
void CMGib :: SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human )
|
||||||
|
{
|
||||||
|
if ( human )
|
||||||
|
CMGib::SpawnRandomGibs( pevVictim, cGibs, "models/hgibs.mdl", human );
|
||||||
|
else
|
||||||
|
CMGib::SpawnRandomGibs( pevVictim, cGibs, "models/agibs.mdl", human );
|
||||||
|
}
|
||||||
|
void CMGib :: SpawnRandomGibs( entvars_t *pevVictim, int cGibs, const char *pGibModel, int human )
|
||||||
{
|
{
|
||||||
int cSplat;
|
int cSplat;
|
||||||
|
|
||||||
@@ -191,13 +199,13 @@ void CMGib :: SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human )
|
|||||||
if ( human )
|
if ( human )
|
||||||
{
|
{
|
||||||
// human pieces
|
// human pieces
|
||||||
pGib->Spawn( "models/hgibs.mdl" );
|
pGib->Spawn( pGibModel );
|
||||||
pGib->pev->body = RANDOM_LONG(1,HUMAN_GIB_COUNT-1);// start at one to avoid throwing random amounts of skulls (0th gib)
|
pGib->pev->body = RANDOM_LONG(1,HUMAN_GIB_COUNT-1);// start at one to avoid throwing random amounts of skulls (0th gib)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// aliens
|
// aliens
|
||||||
pGib->Spawn( "models/agibs.mdl" );
|
pGib->Spawn( pGibModel );
|
||||||
pGib->pev->body = RANDOM_LONG(0,ALIEN_GIB_COUNT-1);
|
pGib->pev->body = RANDOM_LONG(0,ALIEN_GIB_COUNT-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -610,14 +618,11 @@ void CMBaseMonster :: Killed( entvars_t *pevAttacker, int iGib )
|
|||||||
SetConditions( bits_COND_LIGHT_DAMAGE );
|
SetConditions( bits_COND_LIGHT_DAMAGE );
|
||||||
|
|
||||||
// tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality.
|
// tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality.
|
||||||
/*jlb monstermaker
|
|
||||||
CMBaseEntity *pOwner = CMBaseEntity::Instance(pev->owner);
|
CMBaseEntity *pOwner = CMBaseEntity::Instance(pev->owner);
|
||||||
if ( pOwner )
|
if ( pOwner )
|
||||||
{
|
{
|
||||||
//jlb it crashes here sometimes!!!
|
|
||||||
pOwner->DeathNotice( pev );
|
pOwner->DeathNotice( pev );
|
||||||
}
|
}
|
||||||
jlb*/
|
|
||||||
|
|
||||||
if ( ShouldGibMonster( iGib ) )
|
if ( ShouldGibMonster( iGib ) )
|
||||||
{
|
{
|
||||||
@@ -1146,6 +1151,56 @@ void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
edict_t *pMonster = pEntity;
|
||||||
|
|
||||||
|
if ( iClassIgnore != CLASS_NONE && pMonster->v.iuser4 == iClassIgnore )
|
||||||
|
{// houndeyes don't hurt other houndeyes with their attack
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// blast's don't tavel into or out of water
|
||||||
|
if (bInWater && pEntity->v.waterlevel == 0)
|
||||||
|
continue;
|
||||||
|
if (!bInWater && pEntity->v.waterlevel == 3)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
vecSpot = UTIL_BodyTarget( pMonster, vecSrc );
|
||||||
|
|
||||||
|
UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, ENT(pevInflictor), &tr );
|
||||||
|
|
||||||
|
if ( tr.flFraction == 1.0 || tr.pHit == pEntity )
|
||||||
|
{// the explosion can 'see' this entity, so hurt them!
|
||||||
|
if (tr.fStartSolid)
|
||||||
|
{
|
||||||
|
// if we're stuck inside them, fixup the position and distance
|
||||||
|
tr.vecEndPos = vecSrc;
|
||||||
|
tr.flFraction = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// decrease damage for an ent that's farther from the bomb.
|
||||||
|
flAdjustedDamage = ( vecSrc - tr.vecEndPos ).Length() * falloff;
|
||||||
|
flAdjustedDamage = flDamage - flAdjustedDamage;
|
||||||
|
|
||||||
|
if ( flAdjustedDamage < 0 )
|
||||||
|
{
|
||||||
|
flAdjustedDamage = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ALERT( at_console, "hit %s\n", STRING( pEntity->pev->classname ) );
|
||||||
|
if (tr.flFraction != 1.0)
|
||||||
|
{
|
||||||
|
ClearMultiDamage( );
|
||||||
|
UTIL_TraceAttack( pMonster, pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize( ), &tr, bitsDamageType );
|
||||||
|
ApplyMultiDamage( pevInflictor, pevAttacker );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UTIL_TakeDamageExternal( pMonster, pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1199,6 +1254,8 @@ edict_t* CMBaseMonster :: CheckTraceHullAttack( float flDist, int iDamage, int i
|
|||||||
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
|
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
|
||||||
pMonster->TakeDamage( pev, pev, iDamage, iDmgType );
|
pMonster->TakeDamage( pev, pev, iDamage, iDmgType );
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
UTIL_TakeDamageExternal( pEntity, pev, pev, iDamage, iDmgType );
|
||||||
}
|
}
|
||||||
|
|
||||||
return pEntity;
|
return pEntity;
|
||||||
@@ -1360,6 +1417,8 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
|
|||||||
case BULLET_MONSTER_MP5:
|
case BULLET_MONSTER_MP5:
|
||||||
case BULLET_MONSTER_9MM:
|
case BULLET_MONSTER_9MM:
|
||||||
case BULLET_MONSTER_12MM:
|
case BULLET_MONSTER_12MM:
|
||||||
|
case BULLET_MONSTER_762:
|
||||||
|
case BULLET_MONSTER_357:
|
||||||
default:
|
default:
|
||||||
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, vecTracerSrc );
|
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, vecTracerSrc );
|
||||||
WRITE_BYTE( TE_TRACER );
|
WRITE_BYTE( TE_TRACER );
|
||||||
@@ -1386,7 +1445,9 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
|
|||||||
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
||||||
DecalGunshot( &tr, iBulletType );
|
DecalGunshot( &tr, iBulletType );
|
||||||
}
|
}
|
||||||
else switch(iBulletType)
|
else
|
||||||
|
{
|
||||||
|
switch(iBulletType)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case BULLET_MONSTER_9MM:
|
case BULLET_MONSTER_9MM:
|
||||||
@@ -1412,6 +1473,20 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case BULLET_MONSTER_762:
|
||||||
|
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg762, vecDir, &tr, DMG_BULLET);
|
||||||
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
||||||
|
DecalGunshot( &tr, iBulletType );
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BULLET_MONSTER_357:
|
||||||
|
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg357, vecDir, &tr, DMG_BULLET);
|
||||||
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
||||||
|
DecalGunshot( &tr, iBulletType );
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case BULLET_NONE: // FIX
|
case BULLET_NONE: // FIX
|
||||||
UTIL_TraceAttack(pPlayer, pevAttacker, 50, vecDir, &tr, DMG_CLUB);
|
UTIL_TraceAttack(pPlayer, pevAttacker, 50, vecDir, &tr, DMG_CLUB);
|
||||||
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
||||||
@@ -1424,7 +1499,8 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (tr.pHit->v.euser4 != NULL)
|
}
|
||||||
|
else if (tr.pHit->v.euser4 != NULL) // monstermod monster
|
||||||
{
|
{
|
||||||
CMBaseEntity *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit));
|
CMBaseEntity *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit));
|
||||||
|
|
||||||
@@ -1434,7 +1510,9 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
|
|||||||
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
||||||
DecalGunshot( &tr, iBulletType );
|
DecalGunshot( &tr, iBulletType );
|
||||||
}
|
}
|
||||||
else switch(iBulletType)
|
else
|
||||||
|
{
|
||||||
|
switch(iBulletType)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case BULLET_MONSTER_9MM:
|
case BULLET_MONSTER_9MM:
|
||||||
@@ -1460,6 +1538,20 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case BULLET_MONSTER_762:
|
||||||
|
pMonster->TraceAttack(pevAttacker, gSkillData.monDmg762, vecDir, &tr, DMG_BULLET);
|
||||||
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
||||||
|
DecalGunshot( &tr, iBulletType );
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BULLET_MONSTER_357:
|
||||||
|
pMonster->TraceAttack(pevAttacker, gSkillData.monDmg357, vecDir, &tr, DMG_BULLET);
|
||||||
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
||||||
|
DecalGunshot( &tr, iBulletType );
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case BULLET_NONE: // FIX
|
case BULLET_NONE: // FIX
|
||||||
pMonster->TraceAttack(pevAttacker, 50, vecDir, &tr, DMG_CLUB);
|
pMonster->TraceAttack(pevAttacker, 50, vecDir, &tr, DMG_CLUB);
|
||||||
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
||||||
@@ -1473,6 +1565,72 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else // normal game entity
|
||||||
|
{
|
||||||
|
edict_t *pMonster = tr.pHit;
|
||||||
|
|
||||||
|
if ( iDamage )
|
||||||
|
{
|
||||||
|
UTIL_TraceAttack(pMonster, pevAttacker, iDamage, vecDir, &tr, DMG_BULLET | ((iDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) );
|
||||||
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
||||||
|
DecalGunshot( &tr, iBulletType );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch(iBulletType)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case BULLET_MONSTER_9MM:
|
||||||
|
UTIL_TraceAttack(pMonster, pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET);
|
||||||
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
||||||
|
DecalGunshot( &tr, iBulletType );
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BULLET_MONSTER_MP5:
|
||||||
|
UTIL_TraceAttack(pMonster, pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET);
|
||||||
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
||||||
|
DecalGunshot( &tr, iBulletType );
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BULLET_MONSTER_12MM:
|
||||||
|
UTIL_TraceAttack(pMonster, pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET);
|
||||||
|
if ( !tracer )
|
||||||
|
{
|
||||||
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
||||||
|
DecalGunshot( &tr, iBulletType );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BULLET_MONSTER_762:
|
||||||
|
UTIL_TraceAttack(pMonster, pevAttacker, gSkillData.monDmg762, vecDir, &tr, DMG_BULLET);
|
||||||
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
||||||
|
DecalGunshot( &tr, iBulletType );
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BULLET_MONSTER_357:
|
||||||
|
UTIL_TraceAttack(pMonster, pevAttacker, gSkillData.monDmg357, vecDir, &tr, DMG_BULLET);
|
||||||
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
||||||
|
DecalGunshot( &tr, iBulletType );
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BULLET_NONE: // FIX
|
||||||
|
UTIL_TraceAttack(pMonster, pevAttacker, 50, vecDir, &tr, DMG_CLUB);
|
||||||
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
||||||
|
// only decal glass
|
||||||
|
if ( !FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0)
|
||||||
|
{
|
||||||
|
UTIL_DecalTrace( &tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0,2) );
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// make bullet trails
|
// make bullet trails
|
||||||
UTIL_BubbleTrail( vecSrc, tr.vecEndPos, (flDistance * tr.flFraction) / 64.0 );
|
UTIL_BubbleTrail( vecSrc, tr.vecEndPos, (flDistance * tr.flFraction) / 64.0 );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ void CMController :: Spawn()
|
|||||||
{
|
{
|
||||||
Precache( );
|
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 ));
|
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ));
|
||||||
|
|
||||||
pev->solid = SOLID_SLIDEBOX;
|
pev->solid = SOLID_SLIDEBOX;
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
|
|
||||||
#include "cmbase.h"
|
#include "cmbase.h"
|
||||||
#include "cmbasemonster.h"
|
#include "cmbasemonster.h"
|
||||||
|
#include "cmbaseextra.h"
|
||||||
#include "monsters.h"
|
#include "monsters.h"
|
||||||
#include "weapons.h"
|
#include "weapons.h"
|
||||||
#include "hornet.h"
|
#include "hornet.h"
|
||||||
@@ -68,7 +69,6 @@ int g_DamageVictim;
|
|||||||
edict_t *g_DamageAttacker[33];
|
edict_t *g_DamageAttacker[33];
|
||||||
int g_DamageBits[33];
|
int g_DamageBits[33];
|
||||||
bool g_PlayerKilled[33];
|
bool g_PlayerKilled[33];
|
||||||
float g_flWaitTillMessage[33];
|
|
||||||
|
|
||||||
// DeathMsg
|
// DeathMsg
|
||||||
int g_DeathMsg;
|
int g_DeathMsg;
|
||||||
@@ -134,7 +134,7 @@ monster_type_t monster_types[]=
|
|||||||
// can be spawned. Monsters should go first.
|
// can be spawned. Monsters should go first.
|
||||||
// DO NOT ALTER THE ORDER OF ELEMENTS!
|
// DO NOT ALTER THE ORDER OF ELEMENTS!
|
||||||
|
|
||||||
"monster_alien_grunt", FALSE, // Monsters
|
"monster_alien_grunt", FALSE, // Original Half-Life Monsters
|
||||||
"monster_apache", FALSE,
|
"monster_apache", FALSE,
|
||||||
"monster_barney", FALSE,
|
"monster_barney", FALSE,
|
||||||
"monster_bigmomma", FALSE,
|
"monster_bigmomma", FALSE,
|
||||||
@@ -152,8 +152,21 @@ monster_type_t monster_types[]=
|
|||||||
"monster_turret", FALSE,
|
"monster_turret", FALSE,
|
||||||
"monster_miniturret", FALSE,
|
"monster_miniturret", FALSE,
|
||||||
"monster_sentry", FALSE,
|
"monster_sentry", FALSE,
|
||||||
|
"monster_gonome", FALSE, // Opposing Force Monsters
|
||||||
|
"monster_male_assassin", FALSE,
|
||||||
|
"monster_otis", FALSE,
|
||||||
|
"monster_pitdrone", FALSE,
|
||||||
|
"monster_shockroach", FALSE,
|
||||||
|
"monster_shocktrooper", 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", FALSE, // Nodes
|
||||||
"info_node_air", FALSE,
|
"info_node_air", FALSE,
|
||||||
|
"monstermaker", FALSE, // Extra entities
|
||||||
"", FALSE
|
"", FALSE
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -167,6 +180,7 @@ node_spawnpoint_t node_spawnpoint[MAX_NODES];
|
|||||||
int node_spawn_count = 0;
|
int node_spawn_count = 0;
|
||||||
|
|
||||||
float check_respawn_time;
|
float check_respawn_time;
|
||||||
|
float check_graph_time;
|
||||||
|
|
||||||
bool process_monster_cfg(void);
|
bool process_monster_cfg(void);
|
||||||
bool process_monster_precache_cfg(void);
|
bool process_monster_precache_cfg(void);
|
||||||
@@ -197,15 +211,6 @@ int GetMonsterIndex(void)
|
|||||||
|
|
||||||
void FreeMonsterIndex(int index)
|
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;
|
delete monsters[index].pMonster;
|
||||||
|
|
||||||
monsters[index].monster_index = 0;
|
monsters[index].monster_index = 0;
|
||||||
@@ -388,25 +393,38 @@ void check_player_dead( edict_t *pPlayer )
|
|||||||
// Killed by a monster?
|
// Killed by a monster?
|
||||||
if ( pAttacker->v.flags & FL_MONSTER )
|
if ( pAttacker->v.flags & FL_MONSTER )
|
||||||
{
|
{
|
||||||
// Check the first character for 'aeiou'.
|
// Try to get the name of the monster
|
||||||
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pAttacker));
|
char szName[129], szCheck[2];
|
||||||
char szCheck[2];
|
|
||||||
strncpy( szCheck, STRING( pMonster->m_szMonsterName ), 1 );
|
|
||||||
|
|
||||||
// Make the first character lowercase
|
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pAttacker));
|
||||||
szCheck[0] = tolower( szCheck[ 0 ] );
|
if ( pMonster != NULL )
|
||||||
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 ) );
|
// One of our monsters
|
||||||
else
|
strcpy(szName, STRING( pMonster->m_szMonsterName ));
|
||||||
sprintf( szMessage, "* %s was killed by a %s.\n", szPlayerName, STRING( pMonster->m_szMonsterName ) );
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Any messages from here should only be shown if allowed.
|
// SOMETHING that is a monster
|
||||||
// Level 0 = Disabled
|
if ( !FStringNull( pAttacker->v.netname ) )
|
||||||
// Level 1 = All messages
|
strcpy(szName, STRING( pAttacker->v.netname ));
|
||||||
// Level 2 = Only monster deaths
|
else
|
||||||
if (monster_show_deaths->value == 1)
|
{
|
||||||
|
// 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, szName );
|
||||||
|
else
|
||||||
|
sprintf( szMessage, "* %s was killed by a %s.\n", szPlayerName, szName );
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// Suicide?
|
// Suicide?
|
||||||
if ( pAttacker == pPlayer )
|
if ( pAttacker == pPlayer )
|
||||||
@@ -422,13 +440,13 @@ void check_player_dead( edict_t *pPlayer )
|
|||||||
else if ( g_DamageBits[ iPlayerIndex ] & DMG_BULLET )
|
else if ( g_DamageBits[ iPlayerIndex ] & DMG_BULLET )
|
||||||
sprintf( szMessage, "* %s was shot.\n", szPlayerName );
|
sprintf( szMessage, "* %s was shot.\n", szPlayerName );
|
||||||
else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLASH )
|
else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLASH )
|
||||||
sprintf( szMessage, "* %s lost it's jelly.\n", szPlayerName );
|
sprintf( szMessage, "* %s lost its jelly.\n", szPlayerName );
|
||||||
else if ( g_DamageBits[ iPlayerIndex ] & DMG_BURN )
|
else if ( g_DamageBits[ iPlayerIndex ] & DMG_BURN )
|
||||||
sprintf( szMessage, "* %s burned to death.\n", szPlayerName );
|
sprintf( szMessage, "* %s burned to death.\n", szPlayerName );
|
||||||
else if ( g_DamageBits[ iPlayerIndex ] & DMG_FREEZE )
|
else if ( g_DamageBits[ iPlayerIndex ] & DMG_FREEZE )
|
||||||
sprintf( szMessage, "* %s froze to death.\n", szPlayerName );
|
sprintf( szMessage, "* %s froze to death.\n", szPlayerName );
|
||||||
else if ( g_DamageBits[ iPlayerIndex ] & DMG_FALL )
|
else if ( g_DamageBits[ iPlayerIndex ] & DMG_FALL )
|
||||||
sprintf( szMessage, "* %s broke it's bones.\n", szPlayerName );
|
sprintf( szMessage, "* %s broke its bones.\n", szPlayerName );
|
||||||
else if ( g_DamageBits[ iPlayerIndex ] & DMG_BLAST )
|
else if ( g_DamageBits[ iPlayerIndex ] & DMG_BLAST )
|
||||||
sprintf( szMessage, "* %s blew up.\n", szPlayerName );
|
sprintf( szMessage, "* %s blew up.\n", szPlayerName );
|
||||||
else if ( g_DamageBits[ iPlayerIndex ] & DMG_CLUB )
|
else if ( g_DamageBits[ iPlayerIndex ] & DMG_CLUB )
|
||||||
@@ -448,7 +466,7 @@ void check_player_dead( edict_t *pPlayer )
|
|||||||
else if ( g_DamageBits[ iPlayerIndex ] & DMG_PARALYZE )
|
else if ( g_DamageBits[ iPlayerIndex ] & DMG_PARALYZE )
|
||||||
sprintf( szMessage, "* %s was paralyzed.\n", szPlayerName );
|
sprintf( szMessage, "* %s was paralyzed.\n", szPlayerName );
|
||||||
else if ( g_DamageBits[ iPlayerIndex ] & DMG_NERVEGAS )
|
else if ( g_DamageBits[ iPlayerIndex ] & DMG_NERVEGAS )
|
||||||
sprintf( szMessage, "* %s lost it's brain.\n", szPlayerName );
|
sprintf( szMessage, "* %s lost its brain.\n", szPlayerName );
|
||||||
else if ( g_DamageBits[ iPlayerIndex ] & DMG_POISON )
|
else if ( g_DamageBits[ iPlayerIndex ] & DMG_POISON )
|
||||||
sprintf( szMessage, "* %s had a slow death.\n", szPlayerName );
|
sprintf( szMessage, "* %s had a slow death.\n", szPlayerName );
|
||||||
else if ( g_DamageBits[ iPlayerIndex ] & DMG_RADIATION )
|
else if ( g_DamageBits[ iPlayerIndex ] & DMG_RADIATION )
|
||||||
@@ -462,9 +480,7 @@ void check_player_dead( edict_t *pPlayer )
|
|||||||
else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLOWFREEZE )
|
else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLOWFREEZE )
|
||||||
sprintf( szMessage, "* %s died of hypothermia.\n", szPlayerName );
|
sprintf( szMessage, "* %s died of hypothermia.\n", szPlayerName );
|
||||||
else if ( g_DamageBits[ iPlayerIndex ] & DMG_MORTAR )
|
else if ( g_DamageBits[ iPlayerIndex ] & DMG_MORTAR )
|
||||||
sprintf( szMessage, "* %s blew his missile pet.\n", szPlayerName );
|
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.
|
else // other mods could have more DMG_ variants that aren't registered here.
|
||||||
sprintf( szMessage, "* %s deadly died.\n", szPlayerName );
|
sprintf( szMessage, "* %s deadly died.\n", szPlayerName );
|
||||||
}
|
}
|
||||||
@@ -472,7 +488,6 @@ void check_player_dead( edict_t *pPlayer )
|
|||||||
else
|
else
|
||||||
sprintf( szMessage, "* %s fell or drowned or something.\n", szPlayerName );
|
sprintf( szMessage, "* %s fell or drowned or something.\n", szPlayerName );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Print the message
|
// Print the message
|
||||||
if ( strlen( szMessage ) > 0 )
|
if ( strlen( szMessage ) > 0 )
|
||||||
@@ -514,15 +529,47 @@ void check_monster_info( edict_t *pPlayer )
|
|||||||
|
|
||||||
// Hit an entity?
|
// Hit an entity?
|
||||||
if (tr.pHit != NULL)
|
if (tr.pHit != NULL)
|
||||||
|
{
|
||||||
|
// It should be alive
|
||||||
|
if ( UTIL_IsAlive( tr.pHit ) )
|
||||||
{
|
{
|
||||||
// Must be a monster
|
// Must be a monster
|
||||||
if (tr.pHit->v.flags & FL_MONSTER)
|
if (tr.pHit->v.flags & FL_MONSTER)
|
||||||
{
|
{
|
||||||
|
char szName[129];
|
||||||
|
float monsterHealth, monsterFrags;
|
||||||
|
int classify;
|
||||||
|
BOOL isAlly = FALSE;
|
||||||
|
|
||||||
// Get monster info
|
// Get monster info
|
||||||
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit));
|
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];
|
// Unless it is strictly ally to us, treat as enemy monster
|
||||||
sprintf(szInfo, "Enemy: %s\nHealth: %.0f\nFrags: %.0f\n", STRING( pMonster->m_szMonsterName ), pMonster->pev->health, pMonster->pev->frags );
|
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
|
// Create a TE_TEXTMESSAGE and show the monster information
|
||||||
MESSAGE_BEGIN( MSG_ONE, SVC_TEMPENTITY, NULL, pPlayer );
|
MESSAGE_BEGIN( MSG_ONE, SVC_TEMPENTITY, NULL, pPlayer );
|
||||||
@@ -531,9 +578,18 @@ void check_monster_info( edict_t *pPlayer )
|
|||||||
WRITE_SHORT( 327 ); // X
|
WRITE_SHORT( 327 ); // X
|
||||||
WRITE_SHORT( 4771 ); // Y
|
WRITE_SHORT( 4771 ); // Y
|
||||||
WRITE_BYTE( 0 ); // Effect
|
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( 171 ); // R1
|
||||||
WRITE_BYTE( 23 ); // G1
|
WRITE_BYTE( 23 ); // G1
|
||||||
WRITE_BYTE( 7 ); // B1
|
WRITE_BYTE( 7 ); // B1
|
||||||
|
}
|
||||||
WRITE_BYTE( 0 ); // A1
|
WRITE_BYTE( 0 ); // A1
|
||||||
WRITE_BYTE( 207 ); // R2
|
WRITE_BYTE( 207 ); // R2
|
||||||
WRITE_BYTE( 23 ); // G2
|
WRITE_BYTE( 23 ); // G2
|
||||||
@@ -546,13 +602,14 @@ void check_monster_info( edict_t *pPlayer )
|
|||||||
MESSAGE_END();
|
MESSAGE_END();
|
||||||
|
|
||||||
// Delay till next scan
|
// Delay till next scan
|
||||||
g_NextMessage[ ENTINDEX( pPlayer ) ] = gpGlobals->time + 0.875;
|
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;
|
int monster_index;
|
||||||
edict_t *monster_pent;
|
edict_t *monster_pent;
|
||||||
@@ -562,7 +619,7 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i
|
|||||||
{
|
{
|
||||||
//META_CONS("[MONSTER] ERROR: No FREE Monster edicts!");
|
//META_CONS("[MONSTER] ERROR: No FREE Monster edicts!");
|
||||||
LOG_MESSAGE(PLID, "ERROR: No FREE Monster edicts!");
|
LOG_MESSAGE(PLID, "ERROR: No FREE Monster edicts!");
|
||||||
return TRUE;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// was this monster NOT precached?
|
// was this monster NOT precached?
|
||||||
@@ -596,11 +653,12 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i
|
|||||||
LOG_MESSAGE(PLID, "%s", msg);
|
LOG_MESSAGE(PLID, "%s", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (monster_type)
|
switch (monster_type)
|
||||||
{
|
{
|
||||||
|
// Monsters
|
||||||
case 0: monsters[monster_index].pMonster = CreateClassPtr((CMAGrunt *)NULL); break;
|
case 0: monsters[monster_index].pMonster = CreateClassPtr((CMAGrunt *)NULL); break;
|
||||||
case 1: monsters[monster_index].pMonster = CreateClassPtr((CMApache *)NULL); break;
|
case 1: monsters[monster_index].pMonster = CreateClassPtr((CMApache *)NULL); break;
|
||||||
case 2: monsters[monster_index].pMonster = CreateClassPtr((CMBarney *)NULL); break;
|
case 2: monsters[monster_index].pMonster = CreateClassPtr((CMBarney *)NULL); break;
|
||||||
@@ -619,17 +677,29 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i
|
|||||||
case 15: monsters[monster_index].pMonster = CreateClassPtr((CMTurret *)NULL); break;
|
case 15: monsters[monster_index].pMonster = CreateClassPtr((CMTurret *)NULL); break;
|
||||||
case 16: monsters[monster_index].pMonster = CreateClassPtr((CMMiniTurret *)NULL); break;
|
case 16: monsters[monster_index].pMonster = CreateClassPtr((CMMiniTurret *)NULL); break;
|
||||||
case 17: monsters[monster_index].pMonster = CreateClassPtr((CMSentry *)NULL); break;
|
case 17: monsters[monster_index].pMonster = CreateClassPtr((CMSentry *)NULL); break;
|
||||||
|
case 18: monsters[monster_index].pMonster = CreateClassPtr((CMGonome *)NULL); break;
|
||||||
|
case 19: monsters[monster_index].pMonster = CreateClassPtr((CMMassn *)NULL); break;
|
||||||
|
case 20: monsters[monster_index].pMonster = CreateClassPtr((CMOtis *)NULL); break;
|
||||||
|
case 21: monsters[monster_index].pMonster = CreateClassPtr((CMPitdrone *)NULL); break;
|
||||||
|
case 22: monsters[monster_index].pMonster = CreateClassPtr((CMShockRoach *)NULL); break;
|
||||||
|
case 23: monsters[monster_index].pMonster = CreateClassPtr((CMStrooper *)NULL); break;
|
||||||
|
case 24: monsters[monster_index].pMonster = CreateClassPtr((CMVoltigore *)NULL); break;
|
||||||
|
case 25: monsters[monster_index].pMonster = CreateClassPtr((CMBabyVoltigore *)NULL); break;
|
||||||
|
case 26: monsters[monster_index].pMonster = CreateClassPtr((CMBabyGargantua *)NULL); break;
|
||||||
|
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)
|
if (monsters[monster_index].pMonster == NULL)
|
||||||
{
|
{
|
||||||
//META_CONS("[MONSTER] ERROR: Error Creating Monster!" );
|
//META_CONS("[MONSTER] ERROR: Error Creating Monster!" );
|
||||||
LOG_MESSAGE(PLID, "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);
|
monster_pent = ENT(monsters[monster_index].pMonster->pev);
|
||||||
monsters[monster_index].monster_pent = monster_pent;
|
monsters[monster_index].monster_pent = monster_pent;
|
||||||
|
|
||||||
@@ -656,15 +726,19 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i
|
|||||||
|
|
||||||
monsters[monster_index].pMonster->Spawn();
|
monsters[monster_index].pMonster->Spawn();
|
||||||
|
|
||||||
|
// Only modify starting spawnflags for monsters, not for entities!
|
||||||
|
if ( monster_index <= 29 )
|
||||||
|
{
|
||||||
// Reverse fadecorpse behaviour
|
// Reverse fadecorpse behaviour
|
||||||
if ( ( spawnflags & SF_MONSTER_FADECORPSE ) )
|
if ( ( spawnflags & SF_MONSTER_FADECORPSE ) )
|
||||||
monster_pent->v.spawnflags &= ~SF_MONSTER_FADECORPSE;
|
monster_pent->v.spawnflags &= ~SF_MONSTER_FADECORPSE;
|
||||||
else
|
else
|
||||||
monster_pent->v.spawnflags |= SF_MONSTER_FADECORPSE;
|
monster_pent->v.spawnflags |= SF_MONSTER_FADECORPSE;
|
||||||
|
}
|
||||||
|
|
||||||
monster_pent->v.fuser4 = monster_pent->v.health; // save the original health
|
monster_pent->v.fuser4 = monster_pent->v.health; // save the original health
|
||||||
|
|
||||||
return FALSE;
|
return monster_pent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -681,8 +755,7 @@ void check_respawn(void)
|
|||||||
|
|
||||||
for (int index=0; index < monster_spawn_count; index++)
|
for (int index=0; index < monster_spawn_count; index++)
|
||||||
{
|
{
|
||||||
if (monster_spawnpoint[index].need_to_respawn &&
|
if (monster_spawnpoint[index].need_to_respawn)
|
||||||
(monster_spawnpoint[index].respawn_time <= gpGlobals->time))
|
|
||||||
{
|
{
|
||||||
monster_spawnpoint[index].need_to_respawn = FALSE;
|
monster_spawnpoint[index].need_to_respawn = FALSE;
|
||||||
|
|
||||||
@@ -696,12 +769,10 @@ void check_respawn(void)
|
|||||||
|
|
||||||
keyvalue = monster_spawnpoint[index].keyvalue;
|
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...
|
// spawn_monster failed
|
||||||
monster_spawnpoint[index].need_to_respawn = TRUE;
|
ALERT( at_error, "Failed to spawn %s at origin %f %f %f\n", monster_types[monster_type].name, origin.x, origin.y, origin.z );
|
||||||
monster_spawnpoint[index].respawn_time = gpGlobals->time +
|
|
||||||
monster_spawnpoint[index].delay;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -710,6 +781,7 @@ void check_respawn(void)
|
|||||||
|
|
||||||
DLL_GLOBAL short g_sModelIndexFireball;// holds the index for the fireball
|
DLL_GLOBAL short g_sModelIndexFireball;// holds the index for the fireball
|
||||||
DLL_GLOBAL short g_sModelIndexSmoke;// holds the index for the smoke cloud
|
DLL_GLOBAL short g_sModelIndexSmoke;// holds the index for the smoke cloud
|
||||||
|
DLL_GLOBAL short g_sModelIndexTinySpit;// holds the index for the spore grenade explosion
|
||||||
DLL_GLOBAL short g_sModelIndexWExplosion;// holds the index for the underwater explosion
|
DLL_GLOBAL short g_sModelIndexWExplosion;// holds the index for the underwater explosion
|
||||||
DLL_GLOBAL short g_sModelIndexBubbles;// holds the index for the bubbles model
|
DLL_GLOBAL short g_sModelIndexBubbles;// holds the index for the bubbles model
|
||||||
DLL_GLOBAL short g_sModelIndexBloodDrop;// holds the sprite index for the initial blood
|
DLL_GLOBAL short g_sModelIndexBloodDrop;// holds the sprite index for the initial blood
|
||||||
@@ -722,6 +794,7 @@ void world_precache(void)
|
|||||||
{
|
{
|
||||||
g_sModelIndexFireball = PRECACHE_MODEL ("sprites/zerogxplode.spr");// fireball
|
g_sModelIndexFireball = PRECACHE_MODEL ("sprites/zerogxplode.spr");// fireball
|
||||||
g_sModelIndexSmoke = PRECACHE_MODEL ("sprites/steam1.spr");// smoke
|
g_sModelIndexSmoke = PRECACHE_MODEL ("sprites/steam1.spr");// smoke
|
||||||
|
g_sModelIndexTinySpit = PRECACHE_MODEL ("sprites/tinyspit.spr");// spore
|
||||||
g_sModelIndexWExplosion = PRECACHE_MODEL ("sprites/WXplo1.spr");// underwater fireball
|
g_sModelIndexWExplosion = PRECACHE_MODEL ("sprites/WXplo1.spr");// underwater fireball
|
||||||
g_sModelIndexBubbles = PRECACHE_MODEL ("sprites/bubble.spr");//bubbles
|
g_sModelIndexBubbles = PRECACHE_MODEL ("sprites/bubble.spr");//bubbles
|
||||||
g_sModelIndexBloodSpray = PRECACHE_MODEL ("sprites/bloodspray.spr"); // initial blood
|
g_sModelIndexBloodSpray = PRECACHE_MODEL ("sprites/bloodspray.spr"); // initial blood
|
||||||
@@ -733,7 +806,6 @@ void world_precache(void)
|
|||||||
PRECACHE_MODEL ("models/w_grenade.mdl");
|
PRECACHE_MODEL ("models/w_grenade.mdl");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MonsterCommand(void)
|
void MonsterCommand(void)
|
||||||
{
|
{
|
||||||
int index;
|
int index;
|
||||||
@@ -867,7 +939,7 @@ void MonsterCommand(void)
|
|||||||
if (monster_angle.y < 0)
|
if (monster_angle.y < 0)
|
||||||
monster_angle.y += 360;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@@ -895,7 +967,7 @@ void MonsterCommand(void)
|
|||||||
if (monster_angle.y < 0)
|
if (monster_angle.y < 0)
|
||||||
monster_angle.y += 360;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@@ -923,7 +995,7 @@ void MonsterCommand(void)
|
|||||||
if (monster_angle.y < 0)
|
if (monster_angle.y < 0)
|
||||||
monster_angle.y += 360;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@@ -950,7 +1022,7 @@ void MonsterCommand(void)
|
|||||||
if (monster_angle.y < 0)
|
if (monster_angle.y < 0)
|
||||||
monster_angle.y += 360;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@@ -977,7 +1049,7 @@ void MonsterCommand(void)
|
|||||||
if (monster_angle.y < 0)
|
if (monster_angle.y < 0)
|
||||||
monster_angle.y += 360;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1004,7 +1076,7 @@ void MonsterCommand(void)
|
|||||||
if (monster_angle.y < 0)
|
if (monster_angle.y < 0)
|
||||||
monster_angle.y += 360;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1190,29 +1262,9 @@ int mmDispatchSpawn( edict_t *pent )
|
|||||||
// node support. -Giegue
|
// node support. -Giegue
|
||||||
// init the WorldGraph.
|
// init the WorldGraph.
|
||||||
WorldGraph.InitGraph();
|
WorldGraph.InitGraph();
|
||||||
|
check_graph_time = gpGlobals->time + 2.00; // give enough gap
|
||||||
|
|
||||||
// make sure the .NOD file is newer than the .BSP file.
|
check_respawn_time = gpGlobals->time + 4.00;
|
||||||
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;
|
|
||||||
|
|
||||||
for (index = 0; index < MAX_MONSTER_ENTS; index++)
|
for (index = 0; index < MAX_MONSTER_ENTS; index++)
|
||||||
{
|
{
|
||||||
@@ -1276,7 +1328,8 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
|
|||||||
{
|
{
|
||||||
int index;
|
int index;
|
||||||
|
|
||||||
CMAGrunt agrunt;
|
// Monsters
|
||||||
|
CMAGrunt agrunt; // 0
|
||||||
CMApache apache;
|
CMApache apache;
|
||||||
CMBarney barney;
|
CMBarney barney;
|
||||||
CMBigMomma bigmomma;
|
CMBigMomma bigmomma;
|
||||||
@@ -1294,6 +1347,21 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
|
|||||||
CMTurret turret;
|
CMTurret turret;
|
||||||
CMMiniTurret miniturret;
|
CMMiniTurret miniturret;
|
||||||
CMSentry sentry;
|
CMSentry sentry;
|
||||||
|
CMGonome gonome;
|
||||||
|
CMMassn massn;
|
||||||
|
CMOtis otis;
|
||||||
|
CMPitdrone pitdrone;
|
||||||
|
CMShockRoach shockroach;
|
||||||
|
CMStrooper strooper;
|
||||||
|
CMVoltigore voltigore;
|
||||||
|
CMBabyVoltigore babyvoltigore;
|
||||||
|
CMBabyGargantua babygargantua;
|
||||||
|
CMHWGrunt hwgrunt;
|
||||||
|
CMRGrunt rgrunt;
|
||||||
|
CMStukabat stukabat; // 29
|
||||||
|
|
||||||
|
// Extra entities
|
||||||
|
CMMonsterMaker monstermaker; // 32
|
||||||
|
|
||||||
g_psv_gravity = CVAR_GET_POINTER( "sv_gravity" );
|
g_psv_gravity = CVAR_GET_POINTER( "sv_gravity" );
|
||||||
|
|
||||||
@@ -1329,6 +1397,19 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
|
|||||||
case 15: turret.Precache(); break;
|
case 15: turret.Precache(); break;
|
||||||
case 16: miniturret.Precache(); break;
|
case 16: miniturret.Precache(); break;
|
||||||
case 17: sentry.Precache(); break;
|
case 17: sentry.Precache(); break;
|
||||||
|
case 18: gonome.Precache(); break;
|
||||||
|
case 19: massn.Precache(); break;
|
||||||
|
case 20: otis.Precache(); break;
|
||||||
|
case 21: pitdrone.Precache(); break;
|
||||||
|
case 22: shockroach.Precache(); break;
|
||||||
|
case 23: strooper.Precache(); break;
|
||||||
|
case 24: voltigore.Precache(); break;
|
||||||
|
case 25: babyvoltigore.Precache(); break;
|
||||||
|
case 26: babygargantua.Precache(); break;
|
||||||
|
case 27: hwgrunt.Precache(); break;
|
||||||
|
case 28: rgrunt.Precache(); break;
|
||||||
|
case 29: stukabat.Precache(); break;
|
||||||
|
case 32: monstermaker.Precache(); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1341,9 +1422,62 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
|
|||||||
monsters[index].pMonster = NULL;
|
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;
|
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
|
// spawn nodes
|
||||||
|
int index;
|
||||||
for (index = 0; index < node_spawn_count; index++)
|
for (index = 0; index < node_spawn_count; index++)
|
||||||
{
|
{
|
||||||
CMBaseEntity *pNode;
|
CMBaseEntity *pNode;
|
||||||
@@ -1366,12 +1500,10 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
|
|||||||
pNode->Spawn();
|
pNode->Spawn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RETURN_META(MRES_IGNORED);
|
// Wait for node graph before spawning the monsters
|
||||||
}
|
|
||||||
|
|
||||||
void mmStartFrame( void )
|
|
||||||
{
|
|
||||||
if (check_respawn_time <= gpGlobals->time)
|
if (check_respawn_time <= gpGlobals->time)
|
||||||
{
|
{
|
||||||
check_respawn_time = gpGlobals->time + 1.0;
|
check_respawn_time = gpGlobals->time + 1.0;
|
||||||
|
|||||||
64
src/dlls/explode.cpp
Executable file → Normal file
64
src/dlls/explode.cpp
Executable file → Normal file
@@ -81,13 +81,52 @@ void CMShower::Touch( CMBaseEntity *pOther )
|
|||||||
pev->speed = 0;
|
pev->speed = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Puff of Smoke
|
||||||
|
class CSmoker : public CMBaseEntity
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Spawn( void );
|
||||||
|
void Think( void );
|
||||||
|
};
|
||||||
|
|
||||||
|
void CSmoker::Spawn( void )
|
||||||
|
{
|
||||||
|
pev->movetype = MOVETYPE_NONE;
|
||||||
|
pev->nextthink = gpGlobals->time;
|
||||||
|
pev->solid = SOLID_NOT;
|
||||||
|
UTIL_SetSize(pev, g_vecZero, g_vecZero );
|
||||||
|
pev->effects |= EF_NODRAW;
|
||||||
|
pev->angles = g_vecZero;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSmoker::Think( void )
|
||||||
|
{
|
||||||
|
// lots of smoke
|
||||||
|
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
|
||||||
|
WRITE_BYTE( TE_SMOKE );
|
||||||
|
WRITE_COORD( pev->origin.x + RANDOM_FLOAT( -pev->dmg, pev->dmg ));
|
||||||
|
WRITE_COORD( pev->origin.y + RANDOM_FLOAT( -pev->dmg, pev->dmg ));
|
||||||
|
WRITE_COORD( pev->origin.z);
|
||||||
|
WRITE_SHORT( g_sModelIndexSmoke );
|
||||||
|
WRITE_BYTE( RANDOM_LONG(pev->scale, pev->scale * 1.1) );
|
||||||
|
WRITE_BYTE( RANDOM_LONG(8,14) ); // framerate
|
||||||
|
MESSAGE_END();
|
||||||
|
|
||||||
|
pev->health--;
|
||||||
|
if ( pev->health > 0 )
|
||||||
|
pev->nextthink = gpGlobals->time + RANDOM_FLOAT(0.1, 0.2);
|
||||||
|
else
|
||||||
|
UTIL_Remove( this->edict() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explosion
|
||||||
class CMEnvExplosion : public CMBaseMonster
|
class CMEnvExplosion : public CMBaseMonster
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void Spawn( );
|
void Spawn( );
|
||||||
void EXPORT Smoke ( void );
|
void EXPORT Smoke ( void );
|
||||||
void KeyValue( KeyValueData *pkvd );
|
void KeyValue( KeyValueData *pkvd );
|
||||||
void DelayUse( void );
|
void EXPORT DelayUse( void );
|
||||||
void Use( CMBaseEntity *pActivator, CMBaseEntity *pCaller, USE_TYPE useType, float value );
|
void Use( CMBaseEntity *pActivator, CMBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||||
|
|
||||||
int m_iMagnitude;// how large is the fireball? how much damage?
|
int m_iMagnitude;// how large is the fireball? how much damage?
|
||||||
@@ -259,7 +298,8 @@ void CMEnvExplosion::Smoke( void )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Stock to quickly create a one-time explosion
|
// Stocks:
|
||||||
|
// Create a one-time explosion
|
||||||
void ExplosionCreate( const Vector ¢er, const Vector &angles, edict_t *pOwner, int magnitude, int flags, float delay )
|
void ExplosionCreate( const Vector ¢er, const Vector &angles, edict_t *pOwner, int magnitude, int flags, float delay )
|
||||||
{
|
{
|
||||||
KeyValueData kvd;
|
KeyValueData kvd;
|
||||||
@@ -287,15 +327,19 @@ void ExplosionCreate( const Vector ¢er, const Vector &angles, edict_t *pOwne
|
|||||||
pExplosion->pev->spawnflags &= ~SF_ENVEXPLOSION_REPEATABLE;
|
pExplosion->pev->spawnflags &= ~SF_ENVEXPLOSION_REPEATABLE;
|
||||||
|
|
||||||
pExplosion->Spawn();
|
pExplosion->Spawn();
|
||||||
if ( delay > 0.0f )
|
|
||||||
{
|
|
||||||
//pExplosion->SetThink( &CMBaseEntity::SUB_CallUseToggle ); // i don't trust you
|
|
||||||
pExplosion->SetThink( &CMEnvExplosion::DelayUse );
|
pExplosion->SetThink( &CMEnvExplosion::DelayUse );
|
||||||
pExplosion->pev->nextthink = gpGlobals->time + delay;
|
pExplosion->pev->nextthink = gpGlobals->time + delay;
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
|
||||||
pExplosion->Use( NULL, NULL, USE_TOGGLE, 0 );
|
// Emit smoke
|
||||||
}
|
void SmokeCreate( const Vector &origin, int amount, int size, int radius, float delay )
|
||||||
}
|
{
|
||||||
|
CMBaseEntity *pSmoker = CreateClassPtr((CSmoker *)NULL); // CMBaseEntity::Create( "env_smoker", pev->origin, g_vecZero, NULL );
|
||||||
|
UTIL_SetOrigin( pSmoker->pev, origin );
|
||||||
|
pSmoker->Spawn();
|
||||||
|
pSmoker->pev->health = amount; // number of smoke balls
|
||||||
|
pSmoker->pev->scale = size; // size in 0.1x - size 10 = x1.0
|
||||||
|
pSmoker->pev->dmg = radius; // radial distribution
|
||||||
|
pSmoker->pev->nextthink = gpGlobals->time + delay; // Start in ... seconds
|
||||||
}
|
}
|
||||||
@@ -25,8 +25,9 @@
|
|||||||
|
|
||||||
extern DLL_GLOBAL short g_sModelIndexFireball;
|
extern DLL_GLOBAL short g_sModelIndexFireball;
|
||||||
extern DLL_GLOBAL short g_sModelIndexSmoke;
|
extern DLL_GLOBAL short g_sModelIndexSmoke;
|
||||||
|
extern DLL_GLOBAL short g_sModelIndexTinySpit;
|
||||||
|
|
||||||
extern void ExplosionCreate( const Vector ¢er, const Vector &angles, edict_t *pOwner, int magnitude, int flags, float delay );
|
extern void ExplosionCreate( const Vector ¢er, const Vector &angles, edict_t *pOwner, int magnitude, int flags, float delay );
|
||||||
|
extern void SmokeCreate( const Vector &origin, int amount, int size, int radius, float delay );
|
||||||
|
|
||||||
#endif //EXPLODE_H
|
#endif //EXPLODE_H
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#pragma warning(disable : 4201) // nameless struct/union
|
#pragma warning(disable : 4201) // nameless struct/union
|
||||||
#pragma warning(disable : 4514) // unreferenced inline function removed
|
#pragma warning(disable : 4514) // unreferenced inline function removed
|
||||||
#pragma warning(disable : 4100) // unreferenced formal parameter
|
#pragma warning(disable : 4100) // unreferenced formal parameter
|
||||||
|
#pragma warning(disable : 4390) // empty controlled statement (seems to work fine? monster_api.cpp[101/115])
|
||||||
|
|
||||||
// Prevent tons of unused windows definitions
|
// Prevent tons of unused windows definitions
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "cmbase.h"
|
#include "cmbase.h"
|
||||||
#include "cmbasemonster.h"
|
#include "cmbasemonster.h"
|
||||||
|
#include "cmflyingmonster.h"
|
||||||
#include "monsters.h"
|
#include "monsters.h"
|
||||||
#include "schedule.h"
|
#include "schedule.h"
|
||||||
|
|
||||||
@@ -23,8 +24,6 @@
|
|||||||
#define FLYING_AE_FLAPSOUND (9)
|
#define FLYING_AE_FLAPSOUND (9)
|
||||||
|
|
||||||
|
|
||||||
extern DLL_GLOBAL edict_t *g_pBodyQueueHead;
|
|
||||||
|
|
||||||
int CMFlyingMonster :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, edict_t *pTarget, float *pflDist )
|
int CMFlyingMonster :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, edict_t *pTarget, float *pflDist )
|
||||||
{
|
{
|
||||||
// UNDONE: need to check more than the endpoint
|
// UNDONE: need to check more than the endpoint
|
||||||
|
|||||||
631
src/dlls/gargantua.cpp
Executable file → Normal file
631
src/dlls/gargantua.cpp
Executable file → Normal file
@@ -62,13 +62,6 @@ const float GARG_ATTACKDIST = 80.0;
|
|||||||
int gStompSprite = 0, gGargGibModel = 0;
|
int gStompSprite = 0, gGargGibModel = 0;
|
||||||
void SpawnExplosion( Vector center, float randomRange, float time, int magnitude, edict_t *owner );
|
void SpawnExplosion( Vector center, float randomRange, float time, int magnitude, edict_t *owner );
|
||||||
|
|
||||||
class CSmoker : public CMBaseEntity
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void Spawn( void );
|
|
||||||
void Think( void );
|
|
||||||
};
|
|
||||||
|
|
||||||
// Spiral Effect
|
// Spiral Effect
|
||||||
class CSpiral : public CMBaseEntity
|
class CSpiral : public CMBaseEntity
|
||||||
{
|
{
|
||||||
@@ -84,14 +77,14 @@ class CStomp : public CMBaseEntity
|
|||||||
public:
|
public:
|
||||||
void Spawn( void );
|
void Spawn( void );
|
||||||
void Think( void );
|
void Think( void );
|
||||||
static CStomp *StompCreate( const Vector &origin, const Vector &end, float speed );
|
static CStomp *StompCreate( const Vector &origin, const Vector &end, float speed, float damage );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// UNDONE: re-use this sprite list instead of creating new ones all the time
|
// UNDONE: re-use this sprite list instead of creating new ones all the time
|
||||||
// CSprite *m_pSprites[ STOMP_SPRITE_COUNT ];
|
// CSprite *m_pSprites[ STOMP_SPRITE_COUNT ];
|
||||||
};
|
};
|
||||||
|
|
||||||
CStomp *CStomp::StompCreate( const Vector &origin, const Vector &end, float speed )
|
CStomp *CStomp::StompCreate( const Vector &origin, const Vector &end, float speed, float damage )
|
||||||
{
|
{
|
||||||
CStomp *pStomp = CreateClassPtr( (CStomp *)NULL );
|
CStomp *pStomp = CreateClassPtr( (CStomp *)NULL );
|
||||||
|
|
||||||
@@ -100,6 +93,7 @@ CStomp *CStomp::StompCreate( const Vector &origin, const Vector &end, float spee
|
|||||||
pStomp->pev->scale = dir.Length();
|
pStomp->pev->scale = dir.Length();
|
||||||
pStomp->pev->movedir = dir.Normalize();
|
pStomp->pev->movedir = dir.Normalize();
|
||||||
pStomp->pev->speed = speed;
|
pStomp->pev->speed = speed;
|
||||||
|
pStomp->pev->dmg = damage;
|
||||||
pStomp->Spawn();
|
pStomp->Spawn();
|
||||||
|
|
||||||
return pStomp;
|
return pStomp;
|
||||||
@@ -142,7 +136,7 @@ void CStomp::Think( void )
|
|||||||
pevOwner = VARS(pev->owner);
|
pevOwner = VARS(pev->owner);
|
||||||
|
|
||||||
if ( pEntity )
|
if ( pEntity )
|
||||||
pEntity->TakeDamage( pev, pevOwner, gSkillData.gargantuaDmgStomp, DMG_SONIC );
|
pEntity->TakeDamage( pev, pevOwner, pev->dmg, DMG_SONIC );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accelerate the effect
|
// Accelerate the effect
|
||||||
@@ -388,7 +382,7 @@ void CMGargantua::StompAttack( void )
|
|||||||
Vector vecEnd = (vecAim * 1024) + vecStart;
|
Vector vecEnd = (vecAim * 1024) + vecStart;
|
||||||
|
|
||||||
UTIL_TraceLine( vecStart, vecEnd, ignore_monsters, edict(), &trace );
|
UTIL_TraceLine( vecStart, vecEnd, ignore_monsters, edict(), &trace );
|
||||||
CStomp::StompCreate( vecStart, trace.vecEndPos, 0 );
|
CStomp::StompCreate( vecStart, trace.vecEndPos, 0, gSkillData.gargantuaDmgStomp );
|
||||||
UTIL_ScreenShake( pev->origin, 12.0, 100.0, 2.0, 1000 );
|
UTIL_ScreenShake( pev->origin, 12.0, 100.0, 2.0, 1000 );
|
||||||
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pStompSounds[ RANDOM_LONG(0,ARRAYSIZE(pStompSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) );
|
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pStompSounds[ RANDOM_LONG(0,ARRAYSIZE(pStompSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) );
|
||||||
|
|
||||||
@@ -489,8 +483,8 @@ void CMGargantua :: FlameUpdate( void )
|
|||||||
streaks = TRUE;
|
streaks = TRUE;
|
||||||
UTIL_DecalTrace( &trace, DECAL_SMALLSCORCH1 + RANDOM_LONG(0,2) );
|
UTIL_DecalTrace( &trace, DECAL_SMALLSCORCH1 + RANDOM_LONG(0,2) );
|
||||||
}
|
}
|
||||||
// RadiusDamage( trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, CLASS_ALIEN_MONSTER, DMG_BURN );
|
// RadiusDamage( trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, Classify(), DMG_BURN );
|
||||||
FlameDamage( vecStart, trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, CLASS_ALIEN_MONSTER, DMG_BURN );
|
FlameDamage( vecStart, trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, Classify(), DMG_BURN );
|
||||||
|
|
||||||
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
|
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
|
||||||
WRITE_BYTE( TE_ELIGHT );
|
WRITE_BYTE( TE_ELIGHT );
|
||||||
@@ -670,7 +664,7 @@ void CMGargantua :: Spawn()
|
|||||||
{
|
{
|
||||||
Precache( );
|
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 ) );
|
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
|
||||||
|
|
||||||
pev->solid = SOLID_SLIDEBOX;
|
pev->solid = SOLID_SLIDEBOX;
|
||||||
@@ -780,7 +774,7 @@ void CMGargantua::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector ve
|
|||||||
// if ( RANDOM_LONG(0,100) < 25 )
|
// if ( RANDOM_LONG(0,100) < 25 )
|
||||||
// EMIT_SOUND_DYN( ENT(pev), CHAN_BODY, pRicSounds[ RANDOM_LONG(0,ARRAYSIZE(pRicSounds)-1) ], 1.0, ATTN_NORM, 0, PITCH_NORM );
|
// EMIT_SOUND_DYN( ENT(pev), CHAN_BODY, pRicSounds[ RANDOM_LONG(0,ARRAYSIZE(pRicSounds)-1) ], 1.0, ATTN_NORM, 0, PITCH_NORM );
|
||||||
}
|
}
|
||||||
flDamage *= (1.01f - gSkillData.gargantuaArmor); // Again, for mods (see below)
|
// flDamage *= (1.00f - gSkillData.gargantuaArmor); // in here...?
|
||||||
}
|
}
|
||||||
|
|
||||||
CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
|
CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
|
||||||
@@ -788,13 +782,12 @@ void CMGargantua::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector ve
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int CMGargantua::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
int CMGargantua::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||||
{
|
{
|
||||||
if ( IsAlive() )
|
if ( IsAlive() )
|
||||||
{
|
{
|
||||||
if ( !(bitsDamageType & GARG_DAMAGE) )
|
if ( !(bitsDamageType & GARG_DAMAGE) )
|
||||||
flDamage *= (1.01f - gSkillData.gargantuaArmor); // This is for mods that don't use explosives of any kind or do not work with the gargantua.
|
flDamage *= (1.00f - gSkillData.gargantuaArmor); // This is for mods that don't use explosives of any kind or do not work with the gargantua.
|
||||||
|
|
||||||
// Always set
|
// Always set
|
||||||
SetConditions( bits_COND_LIGHT_DAMAGE );
|
SetConditions( bits_COND_LIGHT_DAMAGE );
|
||||||
@@ -821,13 +814,11 @@ void CMGargantua::DeathEffect( void )
|
|||||||
position.z += 15;
|
position.z += 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
CMBaseEntity *pSmoker = CreateClassPtr((CSmoker *)NULL); // CMBaseEntity::Create( "env_smoker", pev->origin, g_vecZero, NULL );
|
// 1 smoke balls
|
||||||
UTIL_SetOrigin( pSmoker->pev, pev->origin );
|
// 4.6X normal size
|
||||||
pSmoker->Spawn();
|
// 0 radial distribution
|
||||||
pSmoker->pev->health = 1; // 1 smoke balls
|
// start in 2.5 seconds
|
||||||
pSmoker->pev->scale = 46; // 4.6X normal size
|
SmokeCreate( pev->origin, 1, 46, 0, 2.5 );
|
||||||
pSmoker->pev->dmg = 0; // 0 radial distribution
|
|
||||||
pSmoker->pev->nextthink = gpGlobals->time + 2.5; // Start in 2.5 seconds
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1166,38 +1157,6 @@ void CMGargantua::RunTask( Task_t *pTask )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSmoker::Spawn( void )
|
|
||||||
{
|
|
||||||
pev->movetype = MOVETYPE_NONE;
|
|
||||||
pev->nextthink = gpGlobals->time;
|
|
||||||
pev->solid = SOLID_NOT;
|
|
||||||
UTIL_SetSize(pev, g_vecZero, g_vecZero );
|
|
||||||
pev->effects |= EF_NODRAW;
|
|
||||||
pev->angles = g_vecZero;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void CSmoker::Think( void )
|
|
||||||
{
|
|
||||||
// lots of smoke
|
|
||||||
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
|
|
||||||
WRITE_BYTE( TE_SMOKE );
|
|
||||||
WRITE_COORD( pev->origin.x + RANDOM_FLOAT( -pev->dmg, pev->dmg ));
|
|
||||||
WRITE_COORD( pev->origin.y + RANDOM_FLOAT( -pev->dmg, pev->dmg ));
|
|
||||||
WRITE_COORD( pev->origin.z);
|
|
||||||
WRITE_SHORT( g_sModelIndexSmoke );
|
|
||||||
WRITE_BYTE( RANDOM_LONG(pev->scale, pev->scale * 1.1) );
|
|
||||||
WRITE_BYTE( RANDOM_LONG(8,14) ); // framerate
|
|
||||||
MESSAGE_END();
|
|
||||||
|
|
||||||
pev->health--;
|
|
||||||
if ( pev->health > 0 )
|
|
||||||
pev->nextthink = gpGlobals->time + RANDOM_FLOAT(0.1, 0.2);
|
|
||||||
else
|
|
||||||
UTIL_Remove( this->edict() );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void CSpiral::Spawn( void )
|
void CSpiral::Spawn( void )
|
||||||
{
|
{
|
||||||
pev->movetype = MOVETYPE_NONE;
|
pev->movetype = MOVETYPE_NONE;
|
||||||
@@ -1265,13 +1224,10 @@ void CSpiral::Think( void )
|
|||||||
|
|
||||||
void SpawnExplosion( Vector center, float randomRange, float time, int magnitude, edict_t *owner )
|
void SpawnExplosion( Vector center, float randomRange, float time, int magnitude, edict_t *owner )
|
||||||
{
|
{
|
||||||
|
/* no need for this
|
||||||
KeyValueData kvd;
|
KeyValueData kvd;
|
||||||
char buf[128];
|
char buf[128];
|
||||||
|
|
||||||
center.x += RANDOM_FLOAT( -randomRange, randomRange );
|
|
||||||
center.y += RANDOM_FLOAT( -randomRange, randomRange );
|
|
||||||
|
|
||||||
/*
|
|
||||||
CMBaseEntity *pExplosion = CreateClassPtr((CEnvExplosion *)NULL); // CMBaseEntity::Create( "env_explosion", center, g_vecZero, NULL );
|
CMBaseEntity *pExplosion = CreateClassPtr((CEnvExplosion *)NULL); // CMBaseEntity::Create( "env_explosion", center, g_vecZero, NULL );
|
||||||
sprintf( buf, "%3d", magnitude );
|
sprintf( buf, "%3d", magnitude );
|
||||||
kvd.szKeyName = "iMagnitude";
|
kvd.szKeyName = "iMagnitude";
|
||||||
@@ -1284,6 +1240,561 @@ void SpawnExplosion( Vector center, float randomRange, float time, int magnitude
|
|||||||
pExplosion->pev->nextthink = gpGlobals->time + time;
|
pExplosion->pev->nextthink = gpGlobals->time + time;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
center.x += RANDOM_FLOAT( -randomRange, randomRange );
|
||||||
|
center.y += RANDOM_FLOAT( -randomRange, randomRange );
|
||||||
|
|
||||||
// explode.h
|
// explode.h
|
||||||
ExplosionCreate( center, g_vecZero, owner, magnitude, SF_ENVEXPLOSION_NODAMAGE, time );
|
ExplosionCreate( center, g_vecZero, owner, magnitude, SF_ENVEXPLOSION_NODAMAGE, time );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Sven Co-op's monster code was recreated from scratch.
|
||||||
|
* They do not contain their unique new attacks... YET. -Giegue
|
||||||
|
* */
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Baby Gargantua
|
||||||
|
//=========================================================
|
||||||
|
const float BABYGARG_ATTACKDIST = 65.0;
|
||||||
|
#define BABYGARG_FLAME_LENGTH 180
|
||||||
|
|
||||||
|
|
||||||
|
const char *CMBabyGargantua::pBeamAttackSounds[] =
|
||||||
|
{
|
||||||
|
"babygarg/gar_flameoff1.wav",
|
||||||
|
"babygarg/gar_flameon1.wav",
|
||||||
|
"babygarg/gar_flamerun1.wav",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *CMBabyGargantua::pFootSounds[] =
|
||||||
|
{
|
||||||
|
"babygarg/gar_step1.wav",
|
||||||
|
"babygarg/gar_step2.wav",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *CMBabyGargantua::pIdleSounds[] =
|
||||||
|
{
|
||||||
|
"babygarg/gar_idle1.wav",
|
||||||
|
"babygarg/gar_idle2.wav",
|
||||||
|
"babygarg/gar_idle3.wav",
|
||||||
|
"babygarg/gar_idle4.wav",
|
||||||
|
"babygarg/gar_idle5.wav",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *CMBabyGargantua::pAttackSounds[] =
|
||||||
|
{
|
||||||
|
"babygarg/gar_attack1.wav",
|
||||||
|
"babygarg/gar_attack2.wav",
|
||||||
|
"babygarg/gar_attack3.wav",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *CMBabyGargantua::pAlertSounds[] =
|
||||||
|
{
|
||||||
|
"babygarg/gar_alert1.wav",
|
||||||
|
"babygarg/gar_alert2.wav",
|
||||||
|
"babygarg/gar_alert3.wav",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *CMBabyGargantua::pPainSounds[] =
|
||||||
|
{
|
||||||
|
"babygarg/gar_pain1.wav",
|
||||||
|
"babygarg/gar_pain2.wav",
|
||||||
|
"babygarg/gar_pain3.wav",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *CMBabyGargantua::pStompSounds[] =
|
||||||
|
{
|
||||||
|
"babygarg/gar_stomp1.wav",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *CMBabyGargantua::pBreatheSounds[] =
|
||||||
|
{
|
||||||
|
"babygarg/gar_breathe1.wav",
|
||||||
|
"babygarg/gar_breathe2.wav",
|
||||||
|
"babygarg/gar_breathe3.wav",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *CMBabyGargantua::pDieSounds[] =
|
||||||
|
{
|
||||||
|
"babygarg/gar_die1.wav",
|
||||||
|
"babygarg/gar_die2.wav",
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Spawn
|
||||||
|
//=========================================================
|
||||||
|
void CMBabyGargantua::Spawn()
|
||||||
|
{
|
||||||
|
Precache( );
|
||||||
|
|
||||||
|
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;
|
||||||
|
pev->movetype = MOVETYPE_STEP;
|
||||||
|
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||||
|
pev->health = gSkillData.babygargHealth;
|
||||||
|
//pev->view_ofs = Vector ( 0, 0, 96 );// taken from mdl file
|
||||||
|
m_flFieldOfView = -0.2;// width of forward view cone ( as a dotproduct result )
|
||||||
|
m_MonsterState = MONSTERSTATE_NONE;
|
||||||
|
|
||||||
|
MonsterInit();
|
||||||
|
|
||||||
|
// Sven Co-op uses a modified gargeye1.spr for the eye and stomp effects.
|
||||||
|
// To economize precache count, we are going to recycle the normal garg's sprites.
|
||||||
|
|
||||||
|
m_pEyeGlow = CMSprite::SpriteCreate( GARG_EYE_SPRITE_NAME, pev->origin, FALSE );
|
||||||
|
m_pEyeGlow->SetTransparency( kRenderGlow, 255, 255, 255, 0, kRenderFxNoDissipation );
|
||||||
|
m_pEyeGlow->SetAttachment( edict(), 1 );
|
||||||
|
EyeOff();
|
||||||
|
m_seeTime = gpGlobals->time + 5;
|
||||||
|
m_flameTime = gpGlobals->time + 2;
|
||||||
|
|
||||||
|
pev->classname = MAKE_STRING( "monster_babygarg" );
|
||||||
|
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
|
||||||
|
{
|
||||||
|
// default name
|
||||||
|
m_szMonsterName = MAKE_STRING( "Baby Gargantua" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Precache - precaches all resources this monster needs
|
||||||
|
//=========================================================
|
||||||
|
void CMBabyGargantua::Precache()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
PRECACHE_MODEL("models/babygarg.mdl");
|
||||||
|
PRECACHE_MODEL( GARG_EYE_SPRITE_NAME );
|
||||||
|
PRECACHE_MODEL( GARG_BEAM_SPRITE_NAME );
|
||||||
|
PRECACHE_MODEL( GARG_BEAM_SPRITE2 );
|
||||||
|
gStompSprite = PRECACHE_MODEL( GARG_STOMP_SPRITE_NAME );
|
||||||
|
gGargGibModel = PRECACHE_MODEL( GARG_GIB_MODEL );
|
||||||
|
PRECACHE_SOUND( GARG_STOMP_BUZZ_SOUND );
|
||||||
|
|
||||||
|
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ )
|
||||||
|
PRECACHE_SOUND((char *)pAttackHitSounds[i]);
|
||||||
|
|
||||||
|
for ( i = 0; i < ARRAYSIZE( pBeamAttackSounds ); i++ )
|
||||||
|
PRECACHE_SOUND((char *)pBeamAttackSounds[i]);
|
||||||
|
|
||||||
|
for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ )
|
||||||
|
PRECACHE_SOUND((char *)pAttackMissSounds[i]);
|
||||||
|
|
||||||
|
for ( i = 0; i < ARRAYSIZE( pRicSounds ); i++ )
|
||||||
|
PRECACHE_SOUND((char *)pRicSounds[i]);
|
||||||
|
|
||||||
|
for ( i = 0; i < ARRAYSIZE( pFootSounds ); i++ )
|
||||||
|
PRECACHE_SOUND((char *)pFootSounds[i]);
|
||||||
|
|
||||||
|
for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ )
|
||||||
|
PRECACHE_SOUND((char *)pIdleSounds[i]);
|
||||||
|
|
||||||
|
for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ )
|
||||||
|
PRECACHE_SOUND((char *)pAlertSounds[i]);
|
||||||
|
|
||||||
|
for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ )
|
||||||
|
PRECACHE_SOUND((char *)pPainSounds[i]);
|
||||||
|
|
||||||
|
for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ )
|
||||||
|
PRECACHE_SOUND((char *)pAttackSounds[i]);
|
||||||
|
|
||||||
|
for ( i = 0; i < ARRAYSIZE( pStompSounds ); i++ )
|
||||||
|
PRECACHE_SOUND((char *)pStompSounds[i]);
|
||||||
|
|
||||||
|
for ( i = 0; i < ARRAYSIZE( pBreatheSounds ); i++ )
|
||||||
|
PRECACHE_SOUND((char *)pBreatheSounds[i]);
|
||||||
|
|
||||||
|
for ( i = 0; i < ARRAYSIZE( pDieSounds ); i++ )
|
||||||
|
PRECACHE_SOUND((char *)pDieSounds[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMBabyGargantua::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
|
||||||
|
{
|
||||||
|
ALERT( at_aiconsole, "CMBabyGargantua::TraceAttack\n");
|
||||||
|
|
||||||
|
if ( !IsAlive() )
|
||||||
|
{
|
||||||
|
CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( m_painSoundTime < gpGlobals->time )
|
||||||
|
{
|
||||||
|
EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM );
|
||||||
|
m_painSoundTime = gpGlobals->time + RANDOM_FLOAT( 2.5, 4 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override Gargantua's specific damage. Baby Garg has no protection from those.
|
||||||
|
CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
|
||||||
|
}
|
||||||
|
|
||||||
|
int CMBabyGargantua::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||||
|
{
|
||||||
|
if ( IsAlive() )
|
||||||
|
{
|
||||||
|
// Always set
|
||||||
|
SetConditions( bits_COND_LIGHT_DAMAGE );
|
||||||
|
}
|
||||||
|
|
||||||
|
return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// HandleAnimEvent - catches the monster-specific messages
|
||||||
|
// that occur when tagged animation frames are played.
|
||||||
|
//=========================================================
|
||||||
|
void CMBabyGargantua::HandleAnimEvent(MonsterEvent_t *pEvent)
|
||||||
|
{
|
||||||
|
switch( pEvent->event )
|
||||||
|
{
|
||||||
|
case GARG_AE_SLASH_LEFT:
|
||||||
|
{
|
||||||
|
// HACKHACK!!!
|
||||||
|
edict_t *pHurt = BabyGargCheckTraceHullAttack( BABYGARG_ATTACKDIST + 10.0, gSkillData.babygargDmgSlash, DMG_SLASH );
|
||||||
|
if (pHurt)
|
||||||
|
{
|
||||||
|
if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) )
|
||||||
|
{
|
||||||
|
// Slightly lower numbers for babygarg (-20%)
|
||||||
|
pHurt->v.punchangle.x = -24; // pitch
|
||||||
|
pHurt->v.punchangle.y = -24; // yaw
|
||||||
|
pHurt->v.punchangle.z = 24; // roll
|
||||||
|
//UTIL_MakeVectors(pev->angles); // called by CheckTraceHullAttack
|
||||||
|
pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_right * 80;
|
||||||
|
}
|
||||||
|
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 50 + RANDOM_LONG(0,15) );
|
||||||
|
}
|
||||||
|
else // Play a random attack miss sound
|
||||||
|
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 50 + RANDOM_LONG(0,15) );
|
||||||
|
|
||||||
|
Vector forward;
|
||||||
|
UTIL_MakeVectorsPrivate( pev->angles, forward, NULL, NULL );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GARG_AE_RIGHT_FOOT:
|
||||||
|
case GARG_AE_LEFT_FOOT:
|
||||||
|
// babygarg does not shake the screen
|
||||||
|
EMIT_SOUND_DYN ( edict(), CHAN_BODY, pFootSounds[ RANDOM_LONG(0,ARRAYSIZE(pFootSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GARG_AE_STOMP:
|
||||||
|
StompAttack();
|
||||||
|
m_seeTime = gpGlobals->time + 12;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GARG_AE_BREATHE:
|
||||||
|
EMIT_SOUND_DYN ( edict(), CHAN_VOICE, pBreatheSounds[ RANDOM_LONG(0,ARRAYSIZE(pBreatheSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) );
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
CMBaseMonster::HandleAnimEvent(pEvent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// CheckMeleeAttack1
|
||||||
|
// Garg swipe attack
|
||||||
|
//=========================================================
|
||||||
|
BOOL CMBabyGargantua::CheckMeleeAttack1( float flDot, float flDist )
|
||||||
|
{
|
||||||
|
// ALERT(at_aiconsole, "CheckMelee(%f, %f)\n", flDot, flDist);
|
||||||
|
|
||||||
|
if (flDot >= 0.7)
|
||||||
|
{
|
||||||
|
if (flDist <= BABYGARG_ATTACKDIST)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// CheckMeleeAttack2
|
||||||
|
// Flame thrower madness!
|
||||||
|
//=========================================================
|
||||||
|
BOOL CMBabyGargantua::CheckMeleeAttack2( float flDot, float flDist )
|
||||||
|
{
|
||||||
|
// ALERT(at_aiconsole, "CheckMelee(%f, %f)\n", flDot, flDist);
|
||||||
|
|
||||||
|
if ( gpGlobals->time > m_flameTime )
|
||||||
|
{
|
||||||
|
if (flDot >= 0.8 && flDist > BABYGARG_ATTACKDIST)
|
||||||
|
{
|
||||||
|
if ( flDist <= BABYGARG_FLAME_LENGTH )
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// CheckRangeAttack1
|
||||||
|
// Stomp attack
|
||||||
|
//=========================================================
|
||||||
|
BOOL CMBabyGargantua::CheckRangeAttack1( float flDot, float flDist )
|
||||||
|
{
|
||||||
|
if ( gpGlobals->time > m_seeTime )
|
||||||
|
{
|
||||||
|
if (flDot >= 0.7 && flDist > BABYGARG_ATTACKDIST)
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMBabyGargantua::StartTask( Task_t *pTask )
|
||||||
|
{
|
||||||
|
switch ( pTask->iTask )
|
||||||
|
{
|
||||||
|
case TASK_FLAME_SWEEP:
|
||||||
|
FlameCreate();
|
||||||
|
m_flWaitFinished = gpGlobals->time + pTask->flData;
|
||||||
|
m_flameTime = gpGlobals->time + 6;
|
||||||
|
m_flameX = 0;
|
||||||
|
m_flameY = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TASK_SOUND_ATTACK:
|
||||||
|
if ( RANDOM_LONG(0,100) < 30 )
|
||||||
|
EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM );
|
||||||
|
TaskComplete();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TASK_DIE:
|
||||||
|
// no death effect for babygarg, but give it a sound
|
||||||
|
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pDieSounds[ RANDOM_LONG(0,ARRAYSIZE(pDieSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM );
|
||||||
|
default:
|
||||||
|
CMBaseMonster::StartTask( pTask );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// RunTask
|
||||||
|
//=========================================================
|
||||||
|
void CMBabyGargantua::RunTask( Task_t *pTask )
|
||||||
|
{
|
||||||
|
switch ( pTask->iTask )
|
||||||
|
{
|
||||||
|
// babygarg does not explode upon death
|
||||||
|
case TASK_FLAME_SWEEP:
|
||||||
|
if ( gpGlobals->time > m_flWaitFinished )
|
||||||
|
{
|
||||||
|
FlameDestroy();
|
||||||
|
TaskComplete();
|
||||||
|
FlameControls( 0, 0 );
|
||||||
|
SetBoneController( 0, 0 );
|
||||||
|
SetBoneController( 1, 0 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BOOL cancel = FALSE;
|
||||||
|
|
||||||
|
Vector angles = g_vecZero;
|
||||||
|
|
||||||
|
FlameUpdate();
|
||||||
|
CMBaseEntity *pEnemy = CMBaseEntity::Instance( m_hEnemy.Get() );
|
||||||
|
if ( pEnemy )
|
||||||
|
{
|
||||||
|
Vector org = pev->origin;
|
||||||
|
org.z += 64;
|
||||||
|
Vector dir = pEnemy->BodyTarget(org) - org;
|
||||||
|
angles = UTIL_VecToAngles( dir );
|
||||||
|
angles.x = -angles.x;
|
||||||
|
angles.y -= pev->angles.y;
|
||||||
|
if ( dir.Length() > 400 )
|
||||||
|
cancel = TRUE;
|
||||||
|
}
|
||||||
|
if ( fabs(angles.y) > 60 )
|
||||||
|
cancel = TRUE;
|
||||||
|
|
||||||
|
if ( cancel )
|
||||||
|
{
|
||||||
|
m_flWaitFinished -= 0.5;
|
||||||
|
m_flameTime -= 0.5;
|
||||||
|
}
|
||||||
|
// FlameControls( angles.x + 2 * sin(gpGlobals->time*8), angles.y + 28 * sin(gpGlobals->time*8.5) );
|
||||||
|
FlameControls( angles.x, angles.y );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
CMBaseMonster::RunTask( pTask );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMBabyGargantua::StompAttack( void )
|
||||||
|
{
|
||||||
|
TraceResult trace;
|
||||||
|
|
||||||
|
UTIL_MakeVectors( pev->angles );
|
||||||
|
Vector vecStart = pev->origin + Vector(0,0,60) + 35 * gpGlobals->v_forward;
|
||||||
|
Vector vecAim = ShootAtEnemy( vecStart );
|
||||||
|
Vector vecEnd = (vecAim * 1024) + vecStart;
|
||||||
|
|
||||||
|
UTIL_TraceLine( vecStart, vecEnd, ignore_monsters, edict(), &trace );
|
||||||
|
CStomp::StompCreate( vecStart, trace.vecEndPos, 0, gSkillData.babygargDmgStomp );
|
||||||
|
UTIL_ScreenShake( pev->origin, 9.6, 80.0, 1.8, 800 ); // -20% "power" to the babygarg's stomp
|
||||||
|
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pStompSounds[ RANDOM_LONG(0,ARRAYSIZE(pStompSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) );
|
||||||
|
|
||||||
|
UTIL_TraceLine( pev->origin, pev->origin - Vector(0,0,20), ignore_monsters, edict(), &trace );
|
||||||
|
if ( trace.flFraction < 1.0 )
|
||||||
|
UTIL_DecalTrace( &trace, DECAL_GARGSTOMP1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMBabyGargantua::FlameCreate( void )
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
Vector posGun, angleGun;
|
||||||
|
TraceResult trace;
|
||||||
|
|
||||||
|
UTIL_MakeVectors( pev->angles );
|
||||||
|
|
||||||
|
for ( i = 0; i < 4; i++ )
|
||||||
|
{
|
||||||
|
if ( i < 2 )
|
||||||
|
m_pFlame[i] = CMBeam::BeamCreate( GARG_BEAM_SPRITE_NAME, 120 );
|
||||||
|
else
|
||||||
|
m_pFlame[i] = CMBeam::BeamCreate( GARG_BEAM_SPRITE2, 70 );
|
||||||
|
if ( m_pFlame[i] )
|
||||||
|
{
|
||||||
|
int attach = i%2;
|
||||||
|
// attachment is 0 based in GetAttachment
|
||||||
|
GetAttachment( attach+1, posGun, angleGun );
|
||||||
|
|
||||||
|
Vector vecEnd = (gpGlobals->v_forward * BABYGARG_FLAME_LENGTH) + posGun;
|
||||||
|
UTIL_TraceLine( posGun, vecEnd, dont_ignore_monsters, edict(), &trace );
|
||||||
|
|
||||||
|
m_pFlame[i]->PointEntInit( trace.vecEndPos, entindex() );
|
||||||
|
if ( i < 2 )
|
||||||
|
m_pFlame[i]->SetColor( 255, 130, 90 );
|
||||||
|
else
|
||||||
|
m_pFlame[i]->SetColor( 0, 120, 255 );
|
||||||
|
m_pFlame[i]->SetBrightness( 190 );
|
||||||
|
m_pFlame[i]->SetFlags( BEAM_FSHADEIN );
|
||||||
|
m_pFlame[i]->SetScrollRate( 20 );
|
||||||
|
// attachment is 1 based in SetEndAttachment
|
||||||
|
m_pFlame[i]->SetEndAttachment( attach + 2 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EMIT_SOUND_DYN ( edict(), CHAN_BODY, pBeamAttackSounds[ 1 ], 1.0, ATTN_NORM, 0, PITCH_NORM );
|
||||||
|
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pBeamAttackSounds[ 2 ], 1.0, ATTN_NORM, 0, PITCH_NORM );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMBabyGargantua::FlameUpdate( void )
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
static float offset[2] = { 60, -60 };
|
||||||
|
TraceResult trace;
|
||||||
|
Vector vecStart, angleGun;
|
||||||
|
BOOL streaks = FALSE;
|
||||||
|
|
||||||
|
for ( i = 0; i < 2; i++ )
|
||||||
|
{
|
||||||
|
if ( m_pFlame[i] )
|
||||||
|
{
|
||||||
|
Vector vecAim = pev->angles;
|
||||||
|
vecAim.x += m_flameX;
|
||||||
|
vecAim.y += m_flameY;
|
||||||
|
|
||||||
|
UTIL_MakeVectors( vecAim );
|
||||||
|
|
||||||
|
GetAttachment( i+1, vecStart, angleGun );
|
||||||
|
Vector vecEnd = vecStart + (gpGlobals->v_forward * BABYGARG_FLAME_LENGTH); // - offset[i] * gpGlobals->v_right;
|
||||||
|
|
||||||
|
UTIL_TraceLine( vecStart, vecEnd, dont_ignore_monsters, edict(), &trace );
|
||||||
|
|
||||||
|
m_pFlame[i]->SetStartPos( trace.vecEndPos );
|
||||||
|
m_pFlame[i+2]->SetStartPos( (vecStart * 0.6) + (trace.vecEndPos * 0.4) );
|
||||||
|
|
||||||
|
if ( trace.flFraction != 1.0 && gpGlobals->time > m_streakTime )
|
||||||
|
{
|
||||||
|
StreakSplash( trace.vecEndPos, trace.vecPlaneNormal, 6, 20, 50, 400 );
|
||||||
|
streaks = TRUE;
|
||||||
|
UTIL_DecalTrace( &trace, DECAL_SMALLSCORCH1 + RANDOM_LONG(0,2) );
|
||||||
|
}
|
||||||
|
// RadiusDamage( trace.vecEndPos, pev, pev, gSkillData.babygargDmgFire, Classify(), DMG_BURN );
|
||||||
|
FlameDamage( vecStart, trace.vecEndPos, pev, pev, gSkillData.babygargDmgFire, Classify(), DMG_BURN );
|
||||||
|
|
||||||
|
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
|
||||||
|
WRITE_BYTE( TE_ELIGHT );
|
||||||
|
WRITE_SHORT( entindex( ) + 0x1000 * (i + 2) ); // entity, attachment
|
||||||
|
WRITE_COORD( vecStart.x ); // origin
|
||||||
|
WRITE_COORD( vecStart.y );
|
||||||
|
WRITE_COORD( vecStart.z );
|
||||||
|
WRITE_COORD( RANDOM_FLOAT( 32, 48 ) ); // radius
|
||||||
|
WRITE_BYTE( 255 ); // R
|
||||||
|
WRITE_BYTE( 255 ); // G
|
||||||
|
WRITE_BYTE( 255 ); // B
|
||||||
|
WRITE_BYTE( 2 ); // life * 10
|
||||||
|
WRITE_COORD( 0 ); // decay
|
||||||
|
MESSAGE_END();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( streaks )
|
||||||
|
m_streakTime = gpGlobals->time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMBabyGargantua::FlameDestroy( void )
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pBeamAttackSounds[ 0 ], 1.0, ATTN_NORM, 0, PITCH_NORM ); // sound must stop.
|
||||||
|
for ( i = 0; i < 4; i++ )
|
||||||
|
{
|
||||||
|
if ( m_pFlame[i] )
|
||||||
|
{
|
||||||
|
UTIL_Remove( m_pFlame[i]->edict() );
|
||||||
|
m_pFlame[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// CheckTraceHullAttack - expects a length to trace, amount
|
||||||
|
// of damage to do, and damage type. Returns a pointer to
|
||||||
|
// the damaged entity in case the monster wishes to do
|
||||||
|
// other stuff to the victim (punchangle, etc)
|
||||||
|
// Used for many contact-range melee attacks. Bites, claws, etc.
|
||||||
|
|
||||||
|
// Overridden for Gargantua because his swing starts lower as
|
||||||
|
// a percentage of his height (otherwise he swings over the
|
||||||
|
// players head)
|
||||||
|
|
||||||
|
// Also overriden for Baby Gargantua to prevent players from
|
||||||
|
// dodging the swing attacks by crouching.
|
||||||
|
//=========================================================
|
||||||
|
edict_t *CMBabyGargantua::BabyGargCheckTraceHullAttack(float flDist, int iDamage, int iDmgType)
|
||||||
|
{
|
||||||
|
TraceResult tr;
|
||||||
|
|
||||||
|
UTIL_MakeVectors( pev->angles );
|
||||||
|
Vector vecStart = pev->origin;
|
||||||
|
vecStart.z += 32;
|
||||||
|
Vector vecEnd = vecStart + (gpGlobals->v_forward * flDist) - (gpGlobals->v_up * flDist * 0.3);
|
||||||
|
|
||||||
|
UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr );
|
||||||
|
|
||||||
|
if ( tr.pHit )
|
||||||
|
{
|
||||||
|
if ( iDamage > 0 )
|
||||||
|
{
|
||||||
|
if ( UTIL_IsPlayer( tr.pHit ) )
|
||||||
|
UTIL_TakeDamage( tr.pHit, pev, pev, iDamage, iDmgType );
|
||||||
|
else if ( tr.pHit->v.euser4 != NULL )
|
||||||
|
{
|
||||||
|
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit));
|
||||||
|
pMonster->TakeDamage( pev, pev, iDamage, iDmgType );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tr.pHit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|||||||
881
src/dlls/gonome.cpp
Normal file
881
src/dlls/gonome.cpp
Normal file
@@ -0,0 +1,881 @@
|
|||||||
|
// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository!
|
||||||
|
|
||||||
|
/***
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
****/
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Gonome.cpp
|
||||||
|
//=========================================================
|
||||||
|
|
||||||
|
#include "extdll.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "cmbase.h"
|
||||||
|
#include "cmbasemonster.h"
|
||||||
|
#include "monsters.h"
|
||||||
|
#include "schedule.h"
|
||||||
|
#include "animation.h"
|
||||||
|
#include "decals.h"
|
||||||
|
#include "nodes.h"
|
||||||
|
|
||||||
|
#define GONOME_MELEE_ATTACK_RADIUS 70
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
TASK_GONOME_GET_PATH_TO_ENEMY_CORPSE = LAST_COMMON_TASK + 1
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Monster's Anim Events Go Here
|
||||||
|
//=========================================================
|
||||||
|
|
||||||
|
#define GONOME_AE_SLASH_RIGHT ( 1 )
|
||||||
|
#define GONOME_AE_SLASH_LEFT ( 2 )
|
||||||
|
#define GONOME_AE_SPIT ( 3 )
|
||||||
|
#define GONOME_AE_THROW ( 4 )
|
||||||
|
|
||||||
|
#define GONOME_AE_BITE1 ( 19 )
|
||||||
|
#define GONOME_AE_BITE2 ( 20 )
|
||||||
|
#define GONOME_AE_BITE3 ( 21 )
|
||||||
|
#define GONOME_AE_BITE4 ( 22 )
|
||||||
|
|
||||||
|
#define GONOME_SCRIPT_EVENT_SOUND ( 1011 )
|
||||||
|
|
||||||
|
void CGonomeGuts :: Spawn( void )
|
||||||
|
{
|
||||||
|
pev->movetype = MOVETYPE_FLY;
|
||||||
|
pev->classname = MAKE_STRING( "gonomeguts" );
|
||||||
|
|
||||||
|
pev->solid = SOLID_BBOX;
|
||||||
|
pev->rendermode = kRenderTransAlpha;
|
||||||
|
pev->renderamt = 255;
|
||||||
|
|
||||||
|
SET_MODEL( ENT( pev ), "sprites/bigspit.spr" );
|
||||||
|
pev->frame = 0;
|
||||||
|
pev->scale = 0.5;
|
||||||
|
pev->rendercolor.x = 255;
|
||||||
|
|
||||||
|
UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) );
|
||||||
|
|
||||||
|
m_maxFrame = (float)MODEL_FRAMES( pev->modelindex ) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGonomeGuts :: Animate( void )
|
||||||
|
{
|
||||||
|
pev->nextthink = gpGlobals->time + 0.1;
|
||||||
|
|
||||||
|
if ( pev->frame++ )
|
||||||
|
{
|
||||||
|
if ( pev->frame > m_maxFrame )
|
||||||
|
{
|
||||||
|
pev->frame = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edict_t *CGonomeGuts :: Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity )
|
||||||
|
{
|
||||||
|
CGonomeGuts *pSpit = CreateClassPtr( (CGonomeGuts *)NULL );
|
||||||
|
|
||||||
|
if (pSpit == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
pSpit->Spawn();
|
||||||
|
|
||||||
|
UTIL_SetOrigin( pSpit->pev, vecStart );
|
||||||
|
pSpit->pev->velocity = vecVelocity;
|
||||||
|
pSpit->pev->owner = ENT(pevOwner);
|
||||||
|
|
||||||
|
pSpit->SetThink ( &CGonomeGuts::Animate );
|
||||||
|
pSpit->pev->nextthink = gpGlobals->time + 0.1;
|
||||||
|
pSpit->SetTouch ( &CGonomeGuts::GutsTouch );
|
||||||
|
|
||||||
|
return pSpit->edict();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGonomeGuts :: GutsTouch( edict_t *pOther )
|
||||||
|
{
|
||||||
|
TraceResult tr;
|
||||||
|
int iPitch;
|
||||||
|
|
||||||
|
// splat sound
|
||||||
|
iPitch = RANDOM_FLOAT( 90, 110 );
|
||||||
|
|
||||||
|
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "bullchicken/bc_acid1.wav", 1, ATTN_NORM, 0, iPitch );
|
||||||
|
|
||||||
|
switch( RANDOM_LONG( 0, 1 ) )
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "bullchicken/bc_spithit1.wav", 1, ATTN_NORM, 0, iPitch );
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "bullchicken/bc_spithit2.wav", 1, ATTN_NORM, 0, iPitch );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !pOther->v.takedamage )
|
||||||
|
{
|
||||||
|
// make a splat on the wall
|
||||||
|
UTIL_TraceLine( pev->origin, pev->origin + pev->velocity * 10, dont_ignore_monsters, ENT( pev ), &tr );
|
||||||
|
UTIL_BloodDecalTrace( &tr, BLOOD_COLOR_RED );
|
||||||
|
UTIL_BloodDrips( tr.vecEndPos, UTIL_RandomBloodVector(), BLOOD_COLOR_RED, 35 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (UTIL_IsPlayer(pOther))
|
||||||
|
UTIL_TakeDamage( pOther, pev, pev, gSkillData.gonomeDmgGuts, DMG_GENERIC );
|
||||||
|
else if (pOther->v.euser4 != NULL)
|
||||||
|
{
|
||||||
|
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
|
||||||
|
pMonster->TakeDamage ( pev, pev, gSkillData.gonomeDmgGuts, DMG_GENERIC );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetThink( &CGonomeGuts::SUB_Remove );
|
||||||
|
pev->nextthink = gpGlobals->time;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char* CMGonome::pPainSounds[] = {
|
||||||
|
"gonome/gonome_pain1.wav",
|
||||||
|
"gonome/gonome_pain2.wav",
|
||||||
|
"gonome/gonome_pain3.wav",
|
||||||
|
"gonome/gonome_pain4.wav"
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* CMGonome::pIdleSounds[] = {
|
||||||
|
"gonome/gonome_idle1.wav",
|
||||||
|
"gonome/gonome_idle2.wav",
|
||||||
|
"gonome/gonome_idle3.wav"
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* CMGonome::pDeathSounds[] = {
|
||||||
|
"gonome/gonome_death2.wav",
|
||||||
|
"gonome/gonome_death3.wav",
|
||||||
|
"gonome/gonome_death4.wav"
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* CMGonome::pAttackHitSounds[] =
|
||||||
|
{
|
||||||
|
"zombie/claw_strike1.wav",
|
||||||
|
"zombie/claw_strike2.wav",
|
||||||
|
"zombie/claw_strike3.wav",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* CMGonome::pAttackMissSounds[] =
|
||||||
|
{
|
||||||
|
"zombie/claw_miss1.wav",
|
||||||
|
"zombie/claw_miss2.wav",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void CMGonome::Killed(entvars_t *pevAttacker, int iGib)
|
||||||
|
{
|
||||||
|
ClearGuts();
|
||||||
|
UnlockPlayer();
|
||||||
|
CMBaseMonster::Killed(pevAttacker, iGib);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMGonome::UnlockPlayer()
|
||||||
|
{
|
||||||
|
if (m_fPlayerLocked)
|
||||||
|
{
|
||||||
|
edict_t *player = 0;
|
||||||
|
if (m_lockedPlayer != 0 && UTIL_IsPlayer(m_lockedPlayer))
|
||||||
|
player = m_lockedPlayer;
|
||||||
|
else // if ehandle is empty for some reason just unlock the first player
|
||||||
|
player = UTIL_FindEntityByClassname(0, "player");
|
||||||
|
|
||||||
|
if (player)
|
||||||
|
player->v.flags &= ~FL_FROZEN;
|
||||||
|
|
||||||
|
m_lockedPlayer = 0;
|
||||||
|
m_fPlayerLocked = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CGonomeGuts* CMGonome::GetGonomeGuts(entvars_t *pevOwner, const Vector &pos)
|
||||||
|
{
|
||||||
|
if (m_pGonomeGuts)
|
||||||
|
return m_pGonomeGuts;
|
||||||
|
edict_t *pEdict = CGonomeGuts::Shoot( pevOwner, g_vecZero, g_vecZero );
|
||||||
|
CGonomeGuts *pGuts = GetClassPtr((CGonomeGuts*)VARS(pEdict));
|
||||||
|
pGuts->Spawn();
|
||||||
|
|
||||||
|
UTIL_SetOrigin( pGuts->pev, pos );
|
||||||
|
|
||||||
|
m_pGonomeGuts = pGuts;
|
||||||
|
return m_pGonomeGuts;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMGonome::ClearGuts()
|
||||||
|
{
|
||||||
|
if (m_pGonomeGuts)
|
||||||
|
{
|
||||||
|
UTIL_Remove( m_pGonomeGuts->edict() );
|
||||||
|
m_pGonomeGuts = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMGonome::PainSound( void )
|
||||||
|
{
|
||||||
|
int pitch = 95 + RANDOM_LONG( 0, 9 );
|
||||||
|
|
||||||
|
if( RANDOM_LONG( 0, 5 ) < 2 )
|
||||||
|
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), 1.0, ATTN_NORM, 0, pitch );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMGonome::DeathSound( void )
|
||||||
|
{
|
||||||
|
int pitch = 95 + RANDOM_LONG( 0, 9 );
|
||||||
|
|
||||||
|
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), 1.0, ATTN_NORM, 0, pitch );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMGonome::IdleSound( void )
|
||||||
|
{
|
||||||
|
int pitch = 95 + RANDOM_LONG( 0, 9 );
|
||||||
|
|
||||||
|
// Play a random idle sound
|
||||||
|
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), 1.0, ATTN_NORM, 0, pitch );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMGonome::AlertSound( void )
|
||||||
|
{
|
||||||
|
const int iPitch = RANDOM_LONG(0, 9) + 95;
|
||||||
|
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), 1, ATTN_NORM, 0, iPitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMGonome::SetActivity( Activity NewActivity )
|
||||||
|
{
|
||||||
|
Activity OldActivity = m_Activity;
|
||||||
|
int iSequence = ACTIVITY_NOT_AVAILABLE;
|
||||||
|
|
||||||
|
if (NewActivity != ACT_RANGE_ATTACK1)
|
||||||
|
{
|
||||||
|
ClearGuts();
|
||||||
|
}
|
||||||
|
if (NewActivity == ACT_MELEE_ATTACK1 && m_hEnemy != 0)
|
||||||
|
{
|
||||||
|
// special melee animations
|
||||||
|
if ((pev->origin - m_hEnemy->v.origin).Length2D() >= 48 )
|
||||||
|
{
|
||||||
|
m_meleeAttack2 = false;
|
||||||
|
iSequence = LookupSequence("attack1");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_meleeAttack2 = true;
|
||||||
|
iSequence = LookupSequence("attack2");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UnlockPlayer();
|
||||||
|
|
||||||
|
if (NewActivity == ACT_RUN && m_hEnemy != 0)
|
||||||
|
{
|
||||||
|
// special run animations
|
||||||
|
if ((pev->origin - m_hEnemy->v.origin).Length2D() <= 512 )
|
||||||
|
{
|
||||||
|
iSequence = LookupSequence("runshort");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iSequence = LookupSequence("runlong");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iSequence = LookupActivity(NewActivity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present
|
||||||
|
|
||||||
|
// In case someone calls this with something other than the ideal activity
|
||||||
|
m_IdealActivity = m_Activity;
|
||||||
|
|
||||||
|
// Set to the desired anim, or default anim if the desired is not present
|
||||||
|
if( iSequence > ACTIVITY_NOT_AVAILABLE )
|
||||||
|
{
|
||||||
|
if( pev->sequence != iSequence || !m_fSequenceLoops )
|
||||||
|
{
|
||||||
|
// don't reset frame between walk and run
|
||||||
|
if( !( OldActivity == ACT_WALK || OldActivity == ACT_RUN ) || !( NewActivity == ACT_WALK || NewActivity == ACT_RUN ) )
|
||||||
|
pev->frame = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pev->sequence = iSequence; // Set to the reset anim (if it's there)
|
||||||
|
ResetSequenceInfo();
|
||||||
|
SetYawSpeed();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Not available try to get default anim
|
||||||
|
ALERT( at_aiconsole, "%s has no sequence for act:%d\n", STRING( pev->classname ), NewActivity );
|
||||||
|
pev->sequence = 0; // Set to the reset anim (if it's there)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Classify - indicates this monster's place in the
|
||||||
|
// relationship table.
|
||||||
|
//=========================================================
|
||||||
|
int CMGonome::Classify(void)
|
||||||
|
{
|
||||||
|
if ( m_iClassifyOverride == -1 ) // helper
|
||||||
|
return CLASS_NONE;
|
||||||
|
else if ( m_iClassifyOverride > 0 )
|
||||||
|
return m_iClassifyOverride; // override
|
||||||
|
|
||||||
|
return CLASS_ALIEN_MONSTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// TakeDamage - overridden for gonome so we can keep track
|
||||||
|
// of how much time has passed since it was last injured
|
||||||
|
//=========================================================
|
||||||
|
int CMGonome::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType)
|
||||||
|
{
|
||||||
|
// Take 15% damage from bullets
|
||||||
|
if( bitsDamageType == DMG_BULLET )
|
||||||
|
{
|
||||||
|
Vector vecDir = pev->origin - (pevInflictor->absmin + pevInflictor->absmax) * 0.5;
|
||||||
|
vecDir = vecDir.Normalize();
|
||||||
|
float flForce = DamageForce( flDamage );
|
||||||
|
pev->velocity = pev->velocity + vecDir * flForce;
|
||||||
|
flDamage *= 0.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HACK HACK -- until we fix this.
|
||||||
|
if( IsAlive() )
|
||||||
|
PainSound();
|
||||||
|
return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// CheckRangeAttack1
|
||||||
|
//=========================================================
|
||||||
|
BOOL CMGonome::CheckRangeAttack1(float flDot, float flDist)
|
||||||
|
{
|
||||||
|
if (flDist < 256)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (IsMoving() && flDist >= 512)
|
||||||
|
{
|
||||||
|
// squid will far too far behind if he stops running to spit at this distance from the enemy.
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flDist > 64 && flDist <= 784 && flDot >= 0.5 && gpGlobals->time >= m_flNextThrowTime)
|
||||||
|
{
|
||||||
|
if (m_hEnemy != 0)
|
||||||
|
{
|
||||||
|
if (fabs(pev->origin.z - m_hEnemy->v.origin.z) > 256)
|
||||||
|
{
|
||||||
|
// don't try to spit at someone up really high or down really low.
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsMoving())
|
||||||
|
{
|
||||||
|
// don't spit again for a long time, resume chasing enemy.
|
||||||
|
m_flNextThrowTime = gpGlobals->time + 5;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// not moving, so spit again pretty soon.
|
||||||
|
m_flNextThrowTime = gpGlobals->time + 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// CheckMeleeAttack2 - both gonome's melee attacks are ACT_MELEE_ATTACK1
|
||||||
|
//=========================================================
|
||||||
|
BOOL CMGonome::CheckMeleeAttack2(float flDot, float flDist)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// SetYawSpeed - allows each sequence to have a different
|
||||||
|
// turn rate associated with it.
|
||||||
|
//=========================================================
|
||||||
|
void CMGonome::SetYawSpeed( void )
|
||||||
|
{
|
||||||
|
pev->yaw_speed = 120;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// HandleAnimEvent - catches the monster-specific messages
|
||||||
|
// that occur when tagged animation frames are played.
|
||||||
|
//=========================================================
|
||||||
|
void CMGonome::HandleAnimEvent(MonsterEvent_t *pEvent)
|
||||||
|
{
|
||||||
|
switch (pEvent->event)
|
||||||
|
{
|
||||||
|
case GONOME_SCRIPT_EVENT_SOUND:
|
||||||
|
if (m_Activity != ACT_MELEE_ATTACK1)
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_BODY, pEvent->options, 1, ATTN_NORM);
|
||||||
|
break;
|
||||||
|
case GONOME_AE_SPIT:
|
||||||
|
{
|
||||||
|
Vector vecArmPos, vecArmAng;
|
||||||
|
GetAttachment(0, vecArmPos, vecArmAng);
|
||||||
|
|
||||||
|
if (GetGonomeGuts(pev, vecArmPos))
|
||||||
|
{
|
||||||
|
m_pGonomeGuts->pev->skin = entindex();
|
||||||
|
m_pGonomeGuts->pev->body = 1;
|
||||||
|
m_pGonomeGuts->pev->aiment = ENT(pev);
|
||||||
|
m_pGonomeGuts->pev->movetype = MOVETYPE_FOLLOW;
|
||||||
|
}
|
||||||
|
UTIL_BloodDrips( vecArmPos, UTIL_RandomBloodVector(), BLOOD_COLOR_RED, 35 );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GONOME_AE_THROW:
|
||||||
|
{
|
||||||
|
UTIL_MakeVectors(pev->angles);
|
||||||
|
Vector vecArmPos, vecArmAng;
|
||||||
|
GetAttachment(0, vecArmPos, vecArmAng);
|
||||||
|
|
||||||
|
if (GetGonomeGuts(pev, vecArmPos))
|
||||||
|
{
|
||||||
|
Vector vecSpitDir;
|
||||||
|
|
||||||
|
Vector vecEnemyPosition;
|
||||||
|
if (m_hEnemy != 0)
|
||||||
|
vecEnemyPosition = (m_hEnemy->v.origin + m_hEnemy->v.view_ofs);
|
||||||
|
else
|
||||||
|
vecEnemyPosition = m_vecEnemyLKP;
|
||||||
|
vecSpitDir = (vecEnemyPosition - vecArmPos).Normalize();
|
||||||
|
|
||||||
|
vecSpitDir.x += RANDOM_FLOAT(-0.05, 0.05);
|
||||||
|
vecSpitDir.y += RANDOM_FLOAT(-0.05, 0.05);
|
||||||
|
vecSpitDir.z += RANDOM_FLOAT(-0.05, 0);
|
||||||
|
|
||||||
|
m_pGonomeGuts->pev->body = 0;
|
||||||
|
m_pGonomeGuts->pev->skin = 0;
|
||||||
|
m_pGonomeGuts->pev->owner = ENT( pev );
|
||||||
|
m_pGonomeGuts->pev->aiment = 0;
|
||||||
|
m_pGonomeGuts->pev->movetype = MOVETYPE_FLY;
|
||||||
|
m_pGonomeGuts->pev->velocity = vecSpitDir * 900;
|
||||||
|
m_pGonomeGuts->SetThink( &CGonomeGuts::Animate );
|
||||||
|
m_pGonomeGuts->pev->nextthink = gpGlobals->time + 0.1;
|
||||||
|
UTIL_SetOrigin(m_pGonomeGuts->pev, vecArmPos);
|
||||||
|
|
||||||
|
m_pGonomeGuts = 0;
|
||||||
|
}
|
||||||
|
UTIL_BloodDrips( vecArmPos, UTIL_RandomBloodVector(), BLOOD_COLOR_RED, 35 );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GONOME_AE_SLASH_LEFT:
|
||||||
|
{
|
||||||
|
edict_t *pHurt = CheckTraceHullAttack(GONOME_MELEE_ATTACK_RADIUS, gSkillData.gonomeDmgOneSlash, DMG_SLASH);
|
||||||
|
if (pHurt)
|
||||||
|
{
|
||||||
|
if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT))
|
||||||
|
{
|
||||||
|
pHurt->v.punchangle.z = 9;
|
||||||
|
pHurt->v.punchangle.x = 5;
|
||||||
|
pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * 25;
|
||||||
|
}
|
||||||
|
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackHitSounds), 1, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackMissSounds), 1, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GONOME_AE_SLASH_RIGHT:
|
||||||
|
{
|
||||||
|
edict_t *pHurt = CheckTraceHullAttack(GONOME_MELEE_ATTACK_RADIUS, gSkillData.gonomeDmgOneSlash, DMG_SLASH);
|
||||||
|
if (pHurt)
|
||||||
|
{
|
||||||
|
if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT))
|
||||||
|
{
|
||||||
|
pHurt->v.punchangle.z = -9;
|
||||||
|
pHurt->v.punchangle.x = 5;
|
||||||
|
pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * -25;
|
||||||
|
}
|
||||||
|
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackHitSounds), 1, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackMissSounds), 1, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GONOME_AE_BITE1:
|
||||||
|
case GONOME_AE_BITE2:
|
||||||
|
case GONOME_AE_BITE3:
|
||||||
|
case GONOME_AE_BITE4:
|
||||||
|
{
|
||||||
|
int iPitch;
|
||||||
|
edict_t *pHurt = CheckTraceHullAttack(GONOME_MELEE_ATTACK_RADIUS, gSkillData.gonomeDmgOneBite, DMG_SLASH);
|
||||||
|
|
||||||
|
if (pHurt)
|
||||||
|
{
|
||||||
|
// croonchy bite sound
|
||||||
|
iPitch = RANDOM_FLOAT(90, 110);
|
||||||
|
switch (RANDOM_LONG(0, 1))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "bullchicken/bc_bite2.wav", 1, ATTN_NORM, 0, iPitch);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "bullchicken/bc_bite3.wav", 1, ATTN_NORM, 0, iPitch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT))
|
||||||
|
{
|
||||||
|
if (pEvent->event == GONOME_AE_BITE4)
|
||||||
|
{
|
||||||
|
pHurt->v.punchangle.x = 15;
|
||||||
|
pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_forward * 75;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pHurt->v.punchangle.x = 9;
|
||||||
|
pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_forward * 25;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// lock player
|
||||||
|
if (pEvent->event == GONOME_AE_BITE4)
|
||||||
|
{
|
||||||
|
UnlockPlayer();
|
||||||
|
}
|
||||||
|
else if (UTIL_IsPlayer( pHurt ) && UTIL_IsAlive( pHurt ))
|
||||||
|
{
|
||||||
|
if (!m_fPlayerLocked)
|
||||||
|
{
|
||||||
|
edict_t *player = pHurt;
|
||||||
|
player->v.flags |= FL_FROZEN;
|
||||||
|
m_lockedPlayer = player;
|
||||||
|
m_fPlayerLocked = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
CMBaseMonster::HandleAnimEvent(pEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GONOME_FLINCH_DELAY 2
|
||||||
|
|
||||||
|
int CMGonome::IgnoreConditions( void )
|
||||||
|
{
|
||||||
|
int iIgnore = CMBaseMonster::IgnoreConditions();
|
||||||
|
|
||||||
|
if( m_Activity == ACT_MELEE_ATTACK1 )
|
||||||
|
{
|
||||||
|
if( m_flNextFlinch >= gpGlobals->time )
|
||||||
|
iIgnore |= ( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ( m_Activity == ACT_SMALL_FLINCH ) || ( m_Activity == ACT_BIG_FLINCH ) )
|
||||||
|
{
|
||||||
|
if( m_flNextFlinch < gpGlobals->time )
|
||||||
|
m_flNextFlinch = gpGlobals->time + GONOME_FLINCH_DELAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return iIgnore;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Spawn
|
||||||
|
//=========================================================
|
||||||
|
void CMGonome::Spawn()
|
||||||
|
{
|
||||||
|
Precache();
|
||||||
|
|
||||||
|
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;
|
||||||
|
pev->movetype = MOVETYPE_STEP;
|
||||||
|
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||||
|
pev->effects = 0;
|
||||||
|
pev->health = gSkillData.gonomeHealth;
|
||||||
|
m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||||
|
m_MonsterState = MONSTERSTATE_NONE;
|
||||||
|
|
||||||
|
m_flNextThrowTime = gpGlobals->time;
|
||||||
|
|
||||||
|
MonsterInit();
|
||||||
|
|
||||||
|
pev->classname = MAKE_STRING( "monster_gonome" );
|
||||||
|
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
|
||||||
|
{
|
||||||
|
// default name
|
||||||
|
m_szMonsterName = MAKE_STRING( "Gonome" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Precache - precaches all resources this monster needs
|
||||||
|
//=========================================================
|
||||||
|
void CMGonome::Precache()
|
||||||
|
{
|
||||||
|
PRECACHE_MODEL("models/gonome.mdl");
|
||||||
|
|
||||||
|
PRECACHE_MODEL("sprites/bigspit.spr");// spit projectile.
|
||||||
|
|
||||||
|
PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event
|
||||||
|
|
||||||
|
PRECACHE_SOUND("gonome/gonome_eat.wav");
|
||||||
|
PRECACHE_SOUND("gonome/gonome_jumpattack.wav");
|
||||||
|
PRECACHE_SOUND("gonome/gonome_melee1.wav");
|
||||||
|
PRECACHE_SOUND("gonome/gonome_melee2.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND_ARRAY(pIdleSounds);
|
||||||
|
PRECACHE_SOUND_ARRAY(pPainSounds);
|
||||||
|
PRECACHE_SOUND_ARRAY(pDeathSounds);
|
||||||
|
|
||||||
|
PRECACHE_SOUND("gonome/gonome_run.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("bullchicken/bc_acid1.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("bullchicken/bc_bite2.wav");
|
||||||
|
PRECACHE_SOUND("bullchicken/bc_bite3.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("bullchicken/bc_spithit1.wav");
|
||||||
|
PRECACHE_SOUND("bullchicken/bc_spithit2.wav");
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// GetSchedule
|
||||||
|
//=========================================================
|
||||||
|
Schedule_t *CMGonome::GetSchedule( void )
|
||||||
|
{
|
||||||
|
switch( m_MonsterState )
|
||||||
|
{
|
||||||
|
case MONSTERSTATE_COMBAT:
|
||||||
|
{
|
||||||
|
// dead enemy
|
||||||
|
if( HasConditions( bits_COND_ENEMY_DEAD ) )
|
||||||
|
{
|
||||||
|
// call base class, all code to handle dead enemies is centralized there.
|
||||||
|
return CMBaseMonster::GetSchedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
if( HasConditions( bits_COND_NEW_ENEMY ) )
|
||||||
|
{
|
||||||
|
return GetScheduleOfType( SCHED_WAKE_ANGRY );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) )
|
||||||
|
{
|
||||||
|
return GetScheduleOfType( SCHED_RANGE_ATTACK1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) )
|
||||||
|
{
|
||||||
|
return GetScheduleOfType( SCHED_MELEE_ATTACK1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( HasConditions( bits_COND_CAN_MELEE_ATTACK2 ) )
|
||||||
|
{
|
||||||
|
return GetScheduleOfType( SCHED_MELEE_ATTACK2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetScheduleOfType( SCHED_CHASE_ENEMY );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CMBaseMonster::GetSchedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
// primary range attack
|
||||||
|
Task_t tlGonomeRangeAttack1[] =
|
||||||
|
{
|
||||||
|
{ TASK_STOP_MOVING, 0 },
|
||||||
|
{ TASK_FACE_IDEAL, (float)0 },
|
||||||
|
{ TASK_RANGE_ATTACK1, (float)0 },
|
||||||
|
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
|
||||||
|
};
|
||||||
|
|
||||||
|
Schedule_t slGonomeRangeAttack1[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
tlGonomeRangeAttack1,
|
||||||
|
ARRAYSIZE( tlGonomeRangeAttack1 ),
|
||||||
|
bits_COND_NEW_ENEMY |
|
||||||
|
bits_COND_ENEMY_DEAD |
|
||||||
|
bits_COND_HEAVY_DAMAGE |
|
||||||
|
bits_COND_ENEMY_OCCLUDED |
|
||||||
|
bits_COND_NO_AMMO_LOADED,
|
||||||
|
0,
|
||||||
|
"Gonome Range Attack1"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Chase enemy schedule
|
||||||
|
Task_t tlGonomeChaseEnemy1[] =
|
||||||
|
{
|
||||||
|
{ TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 },// !!!OEM - this will stop nasty squid oscillation.
|
||||||
|
{ TASK_GET_PATH_TO_ENEMY, (float)0 },
|
||||||
|
{ TASK_RUN_PATH, (float)0 },
|
||||||
|
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
Schedule_t slGonomeChaseEnemy[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
tlGonomeChaseEnemy1,
|
||||||
|
ARRAYSIZE( tlGonomeChaseEnemy1 ),
|
||||||
|
bits_COND_NEW_ENEMY |
|
||||||
|
bits_COND_ENEMY_DEAD |
|
||||||
|
bits_COND_SMELL_FOOD |
|
||||||
|
bits_COND_CAN_RANGE_ATTACK1 |
|
||||||
|
bits_COND_CAN_MELEE_ATTACK1 |
|
||||||
|
bits_COND_CAN_MELEE_ATTACK2 |
|
||||||
|
bits_COND_TASK_FAILED,
|
||||||
|
0,
|
||||||
|
"Gonome Chase Enemy"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// victory dance (eating body)
|
||||||
|
Task_t tlGonomeVictoryDance[] =
|
||||||
|
{
|
||||||
|
{ TASK_STOP_MOVING, (float)0 },
|
||||||
|
{ TASK_WAIT, (float)0.1 },
|
||||||
|
{ TASK_GONOME_GET_PATH_TO_ENEMY_CORPSE, (float)0 },
|
||||||
|
{ TASK_WALK_PATH, (float)0 },
|
||||||
|
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
|
||||||
|
{ TASK_FACE_ENEMY, (float)0 },
|
||||||
|
{ TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE },
|
||||||
|
{ TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE },
|
||||||
|
{ TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }
|
||||||
|
};
|
||||||
|
|
||||||
|
Schedule_t slGonomeVictoryDance[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
tlGonomeVictoryDance,
|
||||||
|
ARRAYSIZE( tlGonomeVictoryDance ),
|
||||||
|
bits_COND_NEW_ENEMY |
|
||||||
|
bits_COND_LIGHT_DAMAGE |
|
||||||
|
bits_COND_HEAVY_DAMAGE,
|
||||||
|
0,
|
||||||
|
"GonomeVictoryDance"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_CUSTOM_SCHEDULES( CMGonome )
|
||||||
|
{
|
||||||
|
slGonomeRangeAttack1,
|
||||||
|
slGonomeChaseEnemy,
|
||||||
|
slGonomeVictoryDance,
|
||||||
|
};
|
||||||
|
|
||||||
|
IMPLEMENT_CUSTOM_SCHEDULES( CMGonome, CMBaseMonster )
|
||||||
|
|
||||||
|
Schedule_t* CMGonome::GetScheduleOfType(int Type)
|
||||||
|
{
|
||||||
|
switch ( Type )
|
||||||
|
{
|
||||||
|
case SCHED_RANGE_ATTACK1:
|
||||||
|
return &slGonomeRangeAttack1[0];
|
||||||
|
break;
|
||||||
|
case SCHED_CHASE_ENEMY:
|
||||||
|
return &slGonomeChaseEnemy[0];
|
||||||
|
break;
|
||||||
|
case SCHED_VICTORY_DANCE:
|
||||||
|
return &slGonomeVictoryDance[0];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return CMBaseMonster::GetScheduleOfType(Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMGonome::RunTask(Task_t *pTask)
|
||||||
|
{
|
||||||
|
// HACK to stop Gonome from playing attack sound twice
|
||||||
|
if (pTask->iTask == TASK_MELEE_ATTACK1)
|
||||||
|
{
|
||||||
|
if (!m_playedAttackSound)
|
||||||
|
{
|
||||||
|
const char* sample = NULL;
|
||||||
|
if (m_meleeAttack2)
|
||||||
|
{
|
||||||
|
sample = "gonome/gonome_melee2.wav";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sample = "gonome/gonome_melee1.wav";
|
||||||
|
}
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_BODY, sample, 1, ATTN_NORM);
|
||||||
|
m_playedAttackSound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_playedAttackSound = false;
|
||||||
|
}
|
||||||
|
CMBaseMonster::RunTask(pTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Start task - selects the correct activity and performs
|
||||||
|
// any necessary calculations to start the next task on the
|
||||||
|
// schedule.
|
||||||
|
//=========================================================
|
||||||
|
void CMGonome::StartTask(Task_t *pTask)
|
||||||
|
{
|
||||||
|
m_iTaskStatus = TASKSTATUS_RUNNING;
|
||||||
|
|
||||||
|
switch (pTask->iTask)
|
||||||
|
{
|
||||||
|
case TASK_GONOME_GET_PATH_TO_ENEMY_CORPSE:
|
||||||
|
{
|
||||||
|
UTIL_MakeVectors( pev->angles );
|
||||||
|
if( BuildRoute( m_vecEnemyLKP - gpGlobals->v_forward * 40, bits_MF_TO_LOCATION, NULL ) )
|
||||||
|
{
|
||||||
|
TaskComplete();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ALERT( at_aiconsole, "GonomeGetPathToEnemyCorpse failed!!\n" );
|
||||||
|
TaskFail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
CMBaseMonster::StartTask(pTask);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -215,7 +215,7 @@ void CMHAssassin :: Spawn()
|
|||||||
{
|
{
|
||||||
Precache( );
|
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);
|
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
|
||||||
|
|
||||||
pev->solid = SOLID_SLIDEBOX;
|
pev->solid = SOLID_SLIDEBOX;
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ void CMHeadCrab :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
|||||||
|
|
||||||
int iSound = RANDOM_LONG(0,2);
|
int iSound = RANDOM_LONG(0,2);
|
||||||
if ( iSound != 0 )
|
if ( iSound != 0 )
|
||||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, pAttackSounds[iSound], GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
EMIT_SOUND_DYN( edict(), CHAN_VOICE, pAttackSounds[iSound], GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||||
|
|
||||||
pev->velocity = vecJumpDir;
|
pev->velocity = vecJumpDir;
|
||||||
m_flNextAttack = gpGlobals->time + 2;
|
m_flNextAttack = gpGlobals->time + 2;
|
||||||
@@ -247,7 +247,7 @@ void CMHeadCrab :: Spawn()
|
|||||||
{
|
{
|
||||||
Precache( );
|
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));
|
UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24));
|
||||||
|
|
||||||
pev->solid = SOLID_SLIDEBOX;
|
pev->solid = SOLID_SLIDEBOX;
|
||||||
@@ -325,7 +325,7 @@ void CMHeadCrab :: LeapTouch ( edict_t *pOther )
|
|||||||
// Don't hit if back on ground
|
// Don't hit if back on ground
|
||||||
if ( !FBitSet( pev->flags, FL_ONGROUND ) )
|
if ( !FBitSet( pev->flags, FL_ONGROUND ) )
|
||||||
{
|
{
|
||||||
EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBiteSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBiteSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||||
|
|
||||||
if (UTIL_IsPlayer(pOther))
|
if (UTIL_IsPlayer(pOther))
|
||||||
UTIL_TakeDamage( pOther, pev, pev, GetDamageAmount(), DMG_SLASH );
|
UTIL_TakeDamage( pOther, pev, pev, GetDamageAmount(), DMG_SLASH );
|
||||||
@@ -359,7 +359,7 @@ void CMHeadCrab :: StartTask ( Task_t *pTask )
|
|||||||
{
|
{
|
||||||
case TASK_RANGE_ATTACK1:
|
case TASK_RANGE_ATTACK1:
|
||||||
{
|
{
|
||||||
EMIT_SOUND_DYN( edict(), CHAN_WEAPON, pAttackSounds[0], GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
EMIT_SOUND_DYN( edict(), CHAN_WEAPON, pAttackSounds[0], GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||||
m_IdealActivity = ACT_RANGE_ATTACK1;
|
m_IdealActivity = ACT_RANGE_ATTACK1;
|
||||||
SetTouch ( &CMHeadCrab::LeapTouch );
|
SetTouch ( &CMHeadCrab::LeapTouch );
|
||||||
break;
|
break;
|
||||||
@@ -415,7 +415,7 @@ int CMHeadCrab :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, f
|
|||||||
#define CRAB_ATTN_IDLE (float)1.5
|
#define CRAB_ATTN_IDLE (float)1.5
|
||||||
void CMHeadCrab :: IdleSound ( void )
|
void CMHeadCrab :: IdleSound ( void )
|
||||||
{
|
{
|
||||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||||
}
|
}
|
||||||
|
|
||||||
//=========================================================
|
//=========================================================
|
||||||
@@ -423,7 +423,7 @@ void CMHeadCrab :: IdleSound ( void )
|
|||||||
//=========================================================
|
//=========================================================
|
||||||
void CMHeadCrab :: AlertSound ( void )
|
void CMHeadCrab :: AlertSound ( void )
|
||||||
{
|
{
|
||||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||||
}
|
}
|
||||||
|
|
||||||
//=========================================================
|
//=========================================================
|
||||||
@@ -431,7 +431,7 @@ void CMHeadCrab :: AlertSound ( void )
|
|||||||
//=========================================================
|
//=========================================================
|
||||||
void CMHeadCrab :: PainSound ( void )
|
void CMHeadCrab :: PainSound ( void )
|
||||||
{
|
{
|
||||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||||
}
|
}
|
||||||
|
|
||||||
//=========================================================
|
//=========================================================
|
||||||
@@ -439,7 +439,7 @@ void CMHeadCrab :: PainSound ( void )
|
|||||||
//=========================================================
|
//=========================================================
|
||||||
void CMHeadCrab :: DeathSound ( void )
|
void CMHeadCrab :: DeathSound ( void )
|
||||||
{
|
{
|
||||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||||
}
|
}
|
||||||
|
|
||||||
Schedule_t* CMHeadCrab :: GetScheduleOfType ( int Type )
|
Schedule_t* CMHeadCrab :: GetScheduleOfType ( int Type )
|
||||||
@@ -461,7 +461,7 @@ Schedule_t* CMHeadCrab :: GetScheduleOfType ( int Type )
|
|||||||
void CMBabyCrab :: Spawn( void )
|
void CMBabyCrab :: Spawn( void )
|
||||||
{
|
{
|
||||||
CMHeadCrab::Spawn();
|
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->rendermode = kRenderTransTexture;
|
||||||
pev->renderamt = 192;
|
pev->renderamt = 192;
|
||||||
UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24));
|
UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24));
|
||||||
|
|||||||
@@ -663,7 +663,9 @@ void CMHGrunt :: Shoot ( void )
|
|||||||
|
|
||||||
pev->effects |= EF_MUZZLEFLASH;
|
pev->effects |= EF_MUZZLEFLASH;
|
||||||
|
|
||||||
m_cAmmoLoaded--;// take away a bullet!
|
// BUG - For some reason that still eludes me, grunts are completely unable to reload their weapons.
|
||||||
|
// As a temporary fix, give them infinite ammo. It will look bad I know... I gotta find a solution. -Giegue
|
||||||
|
//m_cAmmoLoaded--;// take away a bullet!
|
||||||
|
|
||||||
Vector angDir = UTIL_VecToAngles( vecShootDir );
|
Vector angDir = UTIL_VecToAngles( vecShootDir );
|
||||||
SetBlending( 0, angDir.x );
|
SetBlending( 0, angDir.x );
|
||||||
@@ -690,7 +692,9 @@ void CMHGrunt :: Shotgun ( void )
|
|||||||
|
|
||||||
pev->effects |= EF_MUZZLEFLASH;
|
pev->effects |= EF_MUZZLEFLASH;
|
||||||
|
|
||||||
m_cAmmoLoaded--;// take away a bullet!
|
// BUG - For some reason that still eludes me, grunts are completely unable to reload their weapons.
|
||||||
|
// As a temporary fix, give them infinite ammo. It will look bad I know... I gotta find a solution. -Giegue
|
||||||
|
//m_cAmmoLoaded--;// take away a bullet!
|
||||||
|
|
||||||
Vector angDir = UTIL_VecToAngles( vecShootDir );
|
Vector angDir = UTIL_VecToAngles( vecShootDir );
|
||||||
SetBlending( 0, angDir.x );
|
SetBlending( 0, angDir.x );
|
||||||
@@ -827,7 +831,7 @@ void CMHGrunt :: Spawn()
|
|||||||
{
|
{
|
||||||
Precache( );
|
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);
|
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
|
||||||
|
|
||||||
pev->solid = SOLID_SLIDEBOX;
|
pev->solid = SOLID_SLIDEBOX;
|
||||||
|
|||||||
@@ -326,11 +326,10 @@ void CMHornet :: TrackTouch ( edict_t *pOther )
|
|||||||
}
|
}
|
||||||
|
|
||||||
// is this NOT a player and IS a monster?
|
// 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));
|
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
|
||||||
|
if ( pMonster != NULL && IRelationship( pMonster ) <= R_NO || IRelationshipByClass( pOther->v.iuser4 ) <= R_NO )
|
||||||
if ( IRelationship( pMonster ) <= R_NO )
|
|
||||||
{
|
{
|
||||||
// hit something we don't want to hurt, so turn around.
|
// hit something we don't want to hurt, so turn around.
|
||||||
|
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ void CMHoundeye :: Spawn()
|
|||||||
{
|
{
|
||||||
Precache( );
|
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 ) );
|
UTIL_SetSize(pev, Vector ( -16, -16, 0 ), Vector ( 16, 16, 36 ) );
|
||||||
|
|
||||||
pev->solid = SOLID_SLIDEBOX;
|
pev->solid = SOLID_SLIDEBOX;
|
||||||
|
|||||||
883
src/dlls/hwgrunt.cpp
Normal file
883
src/dlls/hwgrunt.cpp
Normal file
@@ -0,0 +1,883 @@
|
|||||||
|
/***
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This source code contains proprietary and confidential information of
|
||||||
|
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||||
|
* persons who have executed a written SDK license with Valve. Any access,
|
||||||
|
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||||
|
*
|
||||||
|
****/
|
||||||
|
//=========================================================
|
||||||
|
// Heavy Weapons Grunt
|
||||||
|
//=========================================================
|
||||||
|
|
||||||
|
#include "extdll.h"
|
||||||
|
#include "plane.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "cmbase.h"
|
||||||
|
#include "cmbasemonster.h"
|
||||||
|
#include "monsters.h"
|
||||||
|
#include "schedule.h"
|
||||||
|
#include "animation.h"
|
||||||
|
#include "weapons.h"
|
||||||
|
#include "cmtalkmonster.h"
|
||||||
|
#include "effects.h"
|
||||||
|
#include "customentity.h"
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// monster-specific DEFINE's
|
||||||
|
//=========================================================
|
||||||
|
#define HWGRUNT_9MM_CLIP_SIZE 17 // clip ammo per gun
|
||||||
|
#define HWGRUNT_DGL_CLIP_SIZE 7
|
||||||
|
#define HWGRUNT_357_CLIP_SIZE 6
|
||||||
|
|
||||||
|
// Weapon flags
|
||||||
|
#define HWGRUNT_MINIGUN 0
|
||||||
|
#define HWGRUNT_PISTOL_9MM 1
|
||||||
|
#define HWGRUNT_PISTOL_DGL 2
|
||||||
|
#define HWGRUNT_PISTOL_357 3
|
||||||
|
|
||||||
|
#define GUN_GROUP 1
|
||||||
|
|
||||||
|
// Gun values
|
||||||
|
#define GUN_MINIGUN 0
|
||||||
|
#define GUN_PISTOL_9MM 1
|
||||||
|
#define GUN_PISTOL_357 2
|
||||||
|
#define GUN_PISTOL_DGL 3
|
||||||
|
#define GUN_NONE 4
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Monster's Anim Events Go Here
|
||||||
|
//=========================================================
|
||||||
|
#define HWGRUNT_AE_DEATH ( 11 )
|
||||||
|
#define HWGRUNT_AE_MINIGUN ( 5001 )
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// monster-specific schedule types
|
||||||
|
//=========================================================
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
SCHED_HWGRUNT_SUPPRESS = LAST_COMMON_SCHEDULE + 1,
|
||||||
|
SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE,// move to a location to set up an attack against the enemy. (usually when a friendly is in the way).
|
||||||
|
SCHED_HWGRUNT_SWEEP,
|
||||||
|
SCHED_HWGRUNT_REPEL,
|
||||||
|
SCHED_HWGRUNT_REPEL_ATTACK,
|
||||||
|
SCHED_HWGRUNT_REPEL_LAND,
|
||||||
|
SCHED_HWGRUNT_WAIT_FACE_ENEMY,
|
||||||
|
SCHED_HWGRUNT_ELOF_FAIL,
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Classify - indicates this monster's place in the
|
||||||
|
// relationship table.
|
||||||
|
//=========================================================
|
||||||
|
int CMHWGrunt::Classify(void)
|
||||||
|
{
|
||||||
|
if ( m_iClassifyOverride == -1 ) // helper
|
||||||
|
return CLASS_NONE;
|
||||||
|
else if ( m_iClassifyOverride > 0 )
|
||||||
|
return m_iClassifyOverride; // override
|
||||||
|
|
||||||
|
return CLASS_HUMAN_MILITARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// CheckRangeAttack1 - HWGrunt doesn't care about melee
|
||||||
|
//=========================================================
|
||||||
|
BOOL CMHWGrunt :: CheckRangeAttack1 ( float flDot, float flDist )
|
||||||
|
{
|
||||||
|
if ( !HasConditions( bits_COND_ENEMY_OCCLUDED ) && flDist <= 2048 && flDot >= 0.5 )
|
||||||
|
{
|
||||||
|
TraceResult tr;
|
||||||
|
|
||||||
|
Vector vecSrc = GetGunPosition();
|
||||||
|
|
||||||
|
// verify that a bullet fired from the gun will hit the enemy before the world.
|
||||||
|
UTIL_TraceLine( vecSrc, UTIL_BodyTarget(m_hEnemy, vecSrc), ignore_monsters, ignore_glass, ENT(pev), &tr);
|
||||||
|
|
||||||
|
if ( tr.flFraction == 1.0 )
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// CheckMeleeAttack1 - HWGrunt does not kick
|
||||||
|
//=========================================================
|
||||||
|
BOOL CMHWGrunt :: CheckMeleeAttack1 ( float flDot, float flDist )
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// CheckRangeAttack2 - HWGrunt has no grenades
|
||||||
|
//=========================================================
|
||||||
|
BOOL CMHWGrunt :: CheckRangeAttack2 ( float flDot, float flDist )
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Shoot
|
||||||
|
//=========================================================
|
||||||
|
void CMHWGrunt::Minigun(void)
|
||||||
|
{
|
||||||
|
if (m_hEnemy == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector vecShootOrigin = GetGunPosition();
|
||||||
|
Vector vecShootDir = ShootAtEnemy(vecShootOrigin);
|
||||||
|
|
||||||
|
FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_10DEGREES, 2048, BULLET_MONSTER_12MM); // shoot +-5 degrees
|
||||||
|
|
||||||
|
pev->effects |= EF_MUZZLEFLASH;
|
||||||
|
|
||||||
|
// Minigunners have infinite ammo
|
||||||
|
//m_cAmmoLoaded--;// take away a bullet!
|
||||||
|
|
||||||
|
Vector angDir = UTIL_VecToAngles(vecShootDir);
|
||||||
|
SetBlending(0, angDir.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// TraceAttack - hwgrunts do not wear helmets
|
||||||
|
//=========================================================
|
||||||
|
void CMHWGrunt :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
|
||||||
|
{
|
||||||
|
CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// TakeDamage - overridden for hwgrunts.
|
||||||
|
// They are meant to be aggresive, never take cover.
|
||||||
|
//=========================================================
|
||||||
|
int CMHWGrunt :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||||
|
{
|
||||||
|
return CMBaseMonster :: TakeDamage ( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// HandleAnimEvent - catches the monster-specific messages
|
||||||
|
// that occur when tagged animation frames are played.
|
||||||
|
//=========================================================
|
||||||
|
void CMHWGrunt::HandleAnimEvent(MonsterEvent_t *pEvent)
|
||||||
|
{
|
||||||
|
switch (pEvent->event)
|
||||||
|
{
|
||||||
|
case HWGRUNT_AE_DEATH:
|
||||||
|
break; // don't get rid of gun
|
||||||
|
|
||||||
|
case HWGRUNT_AE_MINIGUN:
|
||||||
|
{
|
||||||
|
// Sven Co-op uses a modified hassault/hw_gun4.wav for it's fire sound
|
||||||
|
Minigun();
|
||||||
|
|
||||||
|
// We don't want looping WAVs. Pick a different sound and change pitch on it
|
||||||
|
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "hassault/hw_shoot1.wav", 1, ATTN_NORM, 0, PITCH_NORM + RANDOM_LONG(-5,5));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
CMHGrunt::HandleAnimEvent(pEvent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Spawn
|
||||||
|
//=========================================================
|
||||||
|
void CMHWGrunt::Spawn()
|
||||||
|
{
|
||||||
|
Precache();
|
||||||
|
|
||||||
|
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;
|
||||||
|
pev->movetype = MOVETYPE_STEP;
|
||||||
|
m_bloodColor = BLOOD_COLOR_RED;
|
||||||
|
pev->effects = 0;
|
||||||
|
pev->health = gSkillData.hwgruntHealth;
|
||||||
|
m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||||
|
m_MonsterState = MONSTERSTATE_NONE;
|
||||||
|
//m_flNextGrenadeCheck = gpGlobals->time + 1;
|
||||||
|
m_flNextPainTime = gpGlobals->time;
|
||||||
|
m_flMinigunSpinTime = 0; // be able to spin up/down minigun right away
|
||||||
|
m_iSentence = -1;
|
||||||
|
m_fStanding = TRUE;
|
||||||
|
|
||||||
|
//m_afCapability = bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
|
||||||
|
m_afCapability = bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
|
||||||
|
|
||||||
|
//m_fEnemyEluded = FALSE;
|
||||||
|
m_fFirstEncounter = FALSE;// false because hwgrunt does not send signals of any kind
|
||||||
|
|
||||||
|
m_HackedGunPos = Vector(0, 0, 55);
|
||||||
|
|
||||||
|
// Don't setup pev->weapons, always minigun if not specified
|
||||||
|
/*
|
||||||
|
if (FBitSet(pev->weapons, HWGRUNT_MINIGUN))
|
||||||
|
{
|
||||||
|
SetBodygroup(GUN_GROUP, GUN_MINIGUN);
|
||||||
|
m_cClipSize = 1;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
m_cAmmoLoaded = 99;
|
||||||
|
m_cClipSize = 99;
|
||||||
|
|
||||||
|
CMTalkMonster::g_talkWaitTime = 0;
|
||||||
|
|
||||||
|
MonsterInit();
|
||||||
|
|
||||||
|
pev->classname = MAKE_STRING( "monster_hwgrunt" );
|
||||||
|
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
|
||||||
|
{
|
||||||
|
// default name
|
||||||
|
m_szMonsterName = MAKE_STRING( "Heavy Weapons Grunt" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Precache - precaches all resources this monster needs
|
||||||
|
//=========================================================
|
||||||
|
void CMHWGrunt::Precache()
|
||||||
|
{
|
||||||
|
PRECACHE_MODEL("models/hwgrunt.mdl");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("hassault/hw_shoot1.wav");
|
||||||
|
PRECACHE_SOUND("hassault/hw_spinup.wav");
|
||||||
|
PRECACHE_SOUND("hassault/hw_spindown.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("common/null.wav");
|
||||||
|
|
||||||
|
// get voice pitch
|
||||||
|
if (RANDOM_LONG(0, 1))
|
||||||
|
m_voicePitch = 102 + RANDOM_LONG(0, 7);
|
||||||
|
else
|
||||||
|
m_voicePitch = 93; // slight voice change for hwgrunt
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// AI Schedules Specific to this monster
|
||||||
|
//=========================================================
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// GruntFail
|
||||||
|
//=========================================================
|
||||||
|
Task_t tlHWGruntFail[] =
|
||||||
|
{
|
||||||
|
{ TASK_STOP_MOVING, 0 },
|
||||||
|
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
|
||||||
|
{ TASK_WAIT, (float)2 },
|
||||||
|
{ TASK_WAIT_PVS, (float)0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
Schedule_t slHWGruntFail[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
tlHWGruntFail,
|
||||||
|
ARRAYSIZE ( tlHWGruntFail ),
|
||||||
|
bits_COND_CAN_RANGE_ATTACK1,
|
||||||
|
0,
|
||||||
|
"HWGrunt Fail"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Grunt Combat Fail
|
||||||
|
//=========================================================
|
||||||
|
Task_t tlHWGruntCombatFail[] =
|
||||||
|
{
|
||||||
|
{ TASK_STOP_MOVING, 0 },
|
||||||
|
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
|
||||||
|
{ TASK_WAIT_FACE_ENEMY, (float)2 },
|
||||||
|
{ TASK_WAIT_PVS, (float)0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
Schedule_t slHWGruntCombatFail[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
tlHWGruntCombatFail,
|
||||||
|
ARRAYSIZE ( tlHWGruntCombatFail ),
|
||||||
|
bits_COND_CAN_RANGE_ATTACK1,
|
||||||
|
0,
|
||||||
|
"HWGrunt Combat Fail"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Victory dance!
|
||||||
|
//=========================================================
|
||||||
|
Task_t tlHWGruntVictoryDance[] =
|
||||||
|
{
|
||||||
|
{ TASK_STOP_MOVING, (float)0 },
|
||||||
|
{ TASK_FACE_ENEMY, (float)0 },
|
||||||
|
{ TASK_WAIT, (float)1.5 },
|
||||||
|
{ TASK_GET_PATH_TO_ENEMY_CORPSE, (float)0 },
|
||||||
|
{ TASK_WALK_PATH, (float)0 },
|
||||||
|
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
|
||||||
|
{ TASK_FACE_ENEMY, (float)0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
Schedule_t slHWGruntVictoryDance[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
tlHWGruntVictoryDance,
|
||||||
|
ARRAYSIZE ( tlHWGruntVictoryDance ),
|
||||||
|
bits_COND_NEW_ENEMY,
|
||||||
|
0,
|
||||||
|
"HWGruntVictoryDance"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// ELOF fail, just wait and try again
|
||||||
|
//=========================================================
|
||||||
|
Task_t tlHWGruntELOFFail[] =
|
||||||
|
{
|
||||||
|
{ TASK_STOP_MOVING, (float)0 },
|
||||||
|
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
|
||||||
|
{ TASK_FACE_ENEMY, (float)0 },
|
||||||
|
{ TASK_WAIT, (float)1.5 },
|
||||||
|
{ TASK_SET_SCHEDULE, (float)SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE },
|
||||||
|
};
|
||||||
|
|
||||||
|
Schedule_t slHWGruntELOFFail[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
tlHWGruntELOFFail,
|
||||||
|
ARRAYSIZE ( tlHWGruntELOFFail ),
|
||||||
|
bits_COND_NEW_ENEMY |
|
||||||
|
bits_COND_ENEMY_DEAD |
|
||||||
|
bits_COND_CAN_RANGE_ATTACK1,
|
||||||
|
0,
|
||||||
|
"HWGrunt Failed ELOF"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Establish line of fire - move to a position that allows
|
||||||
|
// the grunt to attack.
|
||||||
|
//=========================================================
|
||||||
|
Task_t tlHWGruntEstablishLineOfFire[] =
|
||||||
|
{
|
||||||
|
{ TASK_SET_FAIL_SCHEDULE, (float)SCHED_HWGRUNT_ELOF_FAIL },
|
||||||
|
{ TASK_GET_PATH_TO_ENEMY, (float)0 },
|
||||||
|
{ TASK_RUN_PATH, (float)0 },
|
||||||
|
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
Schedule_t slHWGruntEstablishLineOfFire[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
tlHWGruntEstablishLineOfFire,
|
||||||
|
ARRAYSIZE ( tlHWGruntEstablishLineOfFire ),
|
||||||
|
bits_COND_NEW_ENEMY |
|
||||||
|
bits_COND_ENEMY_DEAD |
|
||||||
|
bits_COND_CAN_RANGE_ATTACK1 |
|
||||||
|
bits_COND_HEAR_SOUND,
|
||||||
|
0,
|
||||||
|
"HWGruntEstablishLineOfFire"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// GruntCombatFace Schedule
|
||||||
|
//=========================================================
|
||||||
|
Task_t tlHWGruntCombatFace1[] =
|
||||||
|
{
|
||||||
|
{ TASK_STOP_MOVING, 0 },
|
||||||
|
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
|
||||||
|
{ TASK_FACE_ENEMY, (float)0 },
|
||||||
|
{ TASK_WAIT, (float)1.5 },
|
||||||
|
{ TASK_SET_SCHEDULE, (float)SCHED_HWGRUNT_SWEEP },
|
||||||
|
};
|
||||||
|
|
||||||
|
Schedule_t slHWGruntCombatFace[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
tlHWGruntCombatFace1,
|
||||||
|
ARRAYSIZE ( tlHWGruntCombatFace1 ),
|
||||||
|
bits_COND_NEW_ENEMY |
|
||||||
|
bits_COND_ENEMY_DEAD |
|
||||||
|
bits_COND_CAN_RANGE_ATTACK1,
|
||||||
|
0,
|
||||||
|
"HWCombat Face"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Task_t tlHWGruntSuppress[] =
|
||||||
|
{
|
||||||
|
{ TASK_STOP_MOVING, 0 },
|
||||||
|
{ TASK_FACE_ENEMY, (float)0 },
|
||||||
|
{ TASK_RANGE_ATTACK1, (float)0 },
|
||||||
|
{ TASK_FACE_ENEMY, (float)0 },
|
||||||
|
{ TASK_RANGE_ATTACK1, (float)0 },
|
||||||
|
{ TASK_FACE_ENEMY, (float)0 },
|
||||||
|
{ TASK_RANGE_ATTACK1, (float)0 },
|
||||||
|
{ TASK_FACE_ENEMY, (float)0 },
|
||||||
|
{ TASK_RANGE_ATTACK1, (float)0 },
|
||||||
|
{ TASK_FACE_ENEMY, (float)0 },
|
||||||
|
{ TASK_RANGE_ATTACK1, (float)0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
Schedule_t slHWGruntSuppress[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
tlHWGruntSuppress,
|
||||||
|
ARRAYSIZE ( tlHWGruntSuppress ),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"HWSuppress"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// grunt wait in cover - we don't allow danger or the ability
|
||||||
|
// to attack to break a grunt's run to cover schedule, but
|
||||||
|
// when a grunt is in cover, we do want them to attack if they can.
|
||||||
|
//=========================================================
|
||||||
|
Task_t tlHWGruntWaitInCover[] =
|
||||||
|
{
|
||||||
|
{ TASK_STOP_MOVING, (float)0 },
|
||||||
|
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
|
||||||
|
{ TASK_WAIT_FACE_ENEMY, (float)1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
Schedule_t slHWGruntWaitInCover[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
tlHWGruntWaitInCover,
|
||||||
|
ARRAYSIZE ( tlHWGruntWaitInCover ),
|
||||||
|
bits_COND_NEW_ENEMY |
|
||||||
|
bits_COND_HEAR_SOUND |
|
||||||
|
bits_COND_CAN_RANGE_ATTACK1,
|
||||||
|
0,
|
||||||
|
"HWGruntWaitInCover"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Do a turning sweep of the area
|
||||||
|
//=========================================================
|
||||||
|
Task_t tlHWGruntSweep[] =
|
||||||
|
{
|
||||||
|
{ TASK_TURN_LEFT, (float)179 },
|
||||||
|
{ TASK_WAIT, (float)1 },
|
||||||
|
{ TASK_TURN_LEFT, (float)179 },
|
||||||
|
{ TASK_WAIT, (float)1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
Schedule_t slHWGruntSweep[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
tlHWGruntSweep,
|
||||||
|
ARRAYSIZE ( tlHWGruntSweep ),
|
||||||
|
bits_COND_NEW_ENEMY |
|
||||||
|
bits_COND_CAN_RANGE_ATTACK1,
|
||||||
|
0,
|
||||||
|
"HWGrunt Sweep"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// primary range attack. Overriden because base class stops attacking when the enemy is occluded.
|
||||||
|
// grunt's grenade toss requires the enemy be occluded.
|
||||||
|
//=========================================================
|
||||||
|
Task_t tlHWGruntRangeAttack1B[] =
|
||||||
|
{
|
||||||
|
{ TASK_STOP_MOVING, (float)0 },
|
||||||
|
{ TASK_PLAY_SEQUENCE_FACE_ENEMY,(float)ACT_IDLE_ANGRY },
|
||||||
|
{ TASK_RANGE_ATTACK1, (float)0 },
|
||||||
|
{ TASK_FACE_ENEMY, (float)0 },
|
||||||
|
{ TASK_RANGE_ATTACK1, (float)0 },
|
||||||
|
{ TASK_FACE_ENEMY, (float)0 },
|
||||||
|
{ TASK_RANGE_ATTACK1, (float)0 },
|
||||||
|
{ TASK_FACE_ENEMY, (float)0 },
|
||||||
|
{ TASK_RANGE_ATTACK1, (float)0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
Schedule_t slHWGruntRangeAttack1B[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
tlHWGruntRangeAttack1B,
|
||||||
|
ARRAYSIZE ( tlHWGruntRangeAttack1B ),
|
||||||
|
bits_COND_NEW_ENEMY |
|
||||||
|
bits_COND_ENEMY_DEAD |
|
||||||
|
bits_COND_ENEMY_OCCLUDED,
|
||||||
|
0,
|
||||||
|
"HWRange Attack1B"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// repel
|
||||||
|
//=========================================================
|
||||||
|
Task_t tlHWGruntRepel[] =
|
||||||
|
{
|
||||||
|
{ TASK_STOP_MOVING, (float)0 },
|
||||||
|
{ TASK_FACE_IDEAL, (float)0 },
|
||||||
|
{ TASK_PLAY_SEQUENCE, (float)ACT_GLIDE },
|
||||||
|
};
|
||||||
|
|
||||||
|
Schedule_t slHWGruntRepel[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
tlHWGruntRepel,
|
||||||
|
ARRAYSIZE ( tlHWGruntRepel ),
|
||||||
|
bits_COND_SEE_ENEMY |
|
||||||
|
bits_COND_NEW_ENEMY,
|
||||||
|
0,
|
||||||
|
"HWRepel"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// repel
|
||||||
|
//=========================================================
|
||||||
|
Task_t tlHWGruntRepelAttack[] =
|
||||||
|
{
|
||||||
|
{ TASK_STOP_MOVING, (float)0 },
|
||||||
|
{ TASK_FACE_ENEMY, (float)0 },
|
||||||
|
{ TASK_PLAY_SEQUENCE, (float)ACT_FLY },
|
||||||
|
};
|
||||||
|
|
||||||
|
Schedule_t slHWGruntRepelAttack[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
tlHWGruntRepelAttack,
|
||||||
|
ARRAYSIZE ( tlHWGruntRepelAttack ),
|
||||||
|
bits_COND_ENEMY_OCCLUDED,
|
||||||
|
0,
|
||||||
|
"HWRepel Attack"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// repel land
|
||||||
|
//=========================================================
|
||||||
|
Task_t tlHWGruntRepelLand[] =
|
||||||
|
{
|
||||||
|
{ TASK_STOP_MOVING, (float)0 },
|
||||||
|
{ TASK_PLAY_SEQUENCE, (float)ACT_LAND },
|
||||||
|
{ TASK_GET_PATH_TO_LASTPOSITION,(float)0 },
|
||||||
|
{ TASK_RUN_PATH, (float)0 },
|
||||||
|
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
|
||||||
|
{ TASK_CLEAR_LASTPOSITION, (float)0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
Schedule_t slHWGruntRepelLand[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
tlHWGruntRepelLand,
|
||||||
|
ARRAYSIZE ( tlHWGruntRepelLand ),
|
||||||
|
bits_COND_SEE_ENEMY |
|
||||||
|
bits_COND_NEW_ENEMY,
|
||||||
|
0,
|
||||||
|
"HWRepel Land"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
DEFINE_CUSTOM_SCHEDULES( CMHWGrunt )
|
||||||
|
{
|
||||||
|
slHWGruntFail,
|
||||||
|
slHWGruntCombatFail,
|
||||||
|
slHWGruntVictoryDance,
|
||||||
|
slHWGruntELOFFail,
|
||||||
|
slHWGruntEstablishLineOfFire,
|
||||||
|
slHWGruntCombatFace,
|
||||||
|
slHWGruntSuppress,
|
||||||
|
slHWGruntWaitInCover,
|
||||||
|
slHWGruntSweep,
|
||||||
|
slHWGruntRangeAttack1B,
|
||||||
|
slHWGruntRepel,
|
||||||
|
slHWGruntRepelAttack,
|
||||||
|
slHWGruntRepelLand,
|
||||||
|
};
|
||||||
|
|
||||||
|
IMPLEMENT_CUSTOM_SCHEDULES( CMHWGrunt, CMBaseMonster );
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// SetActivity - different set than normal hgrunt, adapt
|
||||||
|
//=========================================================
|
||||||
|
void CMHWGrunt :: SetActivity ( Activity NewActivity )
|
||||||
|
{
|
||||||
|
int iSequence = ACTIVITY_NOT_AVAILABLE;
|
||||||
|
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||||
|
bool refreshActivity = TRUE;
|
||||||
|
|
||||||
|
// PS: This is terrible code. -Giegue
|
||||||
|
|
||||||
|
// Time to die?
|
||||||
|
if ( NewActivity < ACT_DIESIMPLE )
|
||||||
|
{
|
||||||
|
if ( pev->sequence == LookupSequence( "attack" ) )
|
||||||
|
{
|
||||||
|
// I won't do anything else if I'm attacking!
|
||||||
|
refreshActivity = FALSE;
|
||||||
|
|
||||||
|
// Unless the enemy has gone out of my sight
|
||||||
|
if ( m_hEnemy == 0 || !UTIL_IsAlive( m_hEnemy ) || !UTIL_FVisible( m_hEnemy, ENT(pev) ) )
|
||||||
|
{
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "hassault/hw_spindown.wav", 0.8, ATTN_NORM);
|
||||||
|
m_flMinigunSpinTime = gpGlobals->time + 1.40;
|
||||||
|
iSequence = LookupSequence( "spindown" ); // time to relax
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( pev->sequence == LookupSequence( "spindown" ) )
|
||||||
|
{
|
||||||
|
// Not yet!
|
||||||
|
refreshActivity = FALSE;
|
||||||
|
|
||||||
|
// Wait until the minigun is no longer spinning before doing something else
|
||||||
|
if ( gpGlobals->time > m_flMinigunSpinTime )
|
||||||
|
{
|
||||||
|
refreshActivity = TRUE;
|
||||||
|
m_flMinigunSpinTime = 0; // do spin up again when required
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refreshActivity)
|
||||||
|
{
|
||||||
|
switch (NewActivity)
|
||||||
|
{
|
||||||
|
case ACT_RANGE_ATTACK1:
|
||||||
|
// if carring a gun, either standing or crouched.
|
||||||
|
// always standing when firing minigun
|
||||||
|
if (pev->weapons > 0) // any pistol
|
||||||
|
{
|
||||||
|
// same animation regardless of pistol
|
||||||
|
if ( m_fStanding )
|
||||||
|
{
|
||||||
|
// get aimable sequence
|
||||||
|
iSequence = LookupSequence( "pistol_shoot" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// get crouching shoot
|
||||||
|
iSequence = LookupSequence( "pistol_crouchshoot" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // minigun
|
||||||
|
{
|
||||||
|
if ( m_flMinigunSpinTime == 0 ) // starting to spin up the minigun
|
||||||
|
{
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "hassault/hw_spinup.wav", 0.8, ATTN_NORM);
|
||||||
|
m_flMinigunSpinTime = gpGlobals->time + 1.15;
|
||||||
|
iSequence = LookupSequence( "spinup" );
|
||||||
|
}
|
||||||
|
else if ( gpGlobals->time > m_flMinigunSpinTime ) // spun up, ready to fire
|
||||||
|
iSequence = LookupSequence( "attack" );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ACT_IDLE:
|
||||||
|
if ( m_MonsterState == MONSTERSTATE_COMBAT )
|
||||||
|
{
|
||||||
|
NewActivity = ACT_IDLE_ANGRY;
|
||||||
|
}
|
||||||
|
iSequence = LookupActivity ( NewActivity );
|
||||||
|
break;
|
||||||
|
case ACT_RUN:
|
||||||
|
case ACT_WALK:
|
||||||
|
default:
|
||||||
|
iSequence = LookupActivity ( NewActivity );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set to the desired anim, or default anim if the desired is not present
|
||||||
|
if ( iSequence > ACTIVITY_NOT_AVAILABLE )
|
||||||
|
{
|
||||||
|
if ( pev->sequence != iSequence || !m_fSequenceLoops )
|
||||||
|
{
|
||||||
|
pev->frame = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pev->sequence = iSequence; // Set to the reset anim (if it's there)
|
||||||
|
ResetSequenceInfo( );
|
||||||
|
SetYawSpeed();
|
||||||
|
}
|
||||||
|
else if (refreshActivity)
|
||||||
|
{
|
||||||
|
// Not available try to get default anim
|
||||||
|
ALERT ( at_console, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity );
|
||||||
|
pev->sequence = 0; // Set to the reset anim (if it's there)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Get Schedule!
|
||||||
|
//=========================================================
|
||||||
|
Schedule_t *CMHWGrunt :: GetSchedule( void )
|
||||||
|
{
|
||||||
|
// flying? If PRONE, barnacle has me. IF not, it's assumed I am rapelling.
|
||||||
|
if ( pev->movetype == MOVETYPE_FLY && m_MonsterState != MONSTERSTATE_PRONE )
|
||||||
|
{
|
||||||
|
if (pev->flags & FL_ONGROUND)
|
||||||
|
{
|
||||||
|
// just landed
|
||||||
|
pev->movetype = MOVETYPE_STEP;
|
||||||
|
return GetScheduleOfType ( SCHED_HWGRUNT_REPEL_LAND );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// repel down a rope,
|
||||||
|
if ( m_MonsterState == MONSTERSTATE_COMBAT )
|
||||||
|
return GetScheduleOfType ( SCHED_HWGRUNT_REPEL_ATTACK );
|
||||||
|
else
|
||||||
|
return GetScheduleOfType ( SCHED_HWGRUNT_REPEL );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ( m_MonsterState )
|
||||||
|
{
|
||||||
|
case MONSTERSTATE_COMBAT:
|
||||||
|
{
|
||||||
|
// dead enemy
|
||||||
|
if ( HasConditions( bits_COND_ENEMY_DEAD ) )
|
||||||
|
{
|
||||||
|
// call base class, all code to handle dead enemies is centralized there.
|
||||||
|
return CMBaseMonster :: GetSchedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
// new enemy
|
||||||
|
if ( HasConditions(bits_COND_NEW_ENEMY) )
|
||||||
|
{
|
||||||
|
if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) )
|
||||||
|
{
|
||||||
|
return GetScheduleOfType ( SCHED_HWGRUNT_SUPPRESS );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetScheduleOfType ( SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no ammo
|
||||||
|
else if ( HasConditions ( bits_COND_NO_AMMO_LOADED ) )
|
||||||
|
{
|
||||||
|
// Stop believing you have no ammo! -Giegue
|
||||||
|
ClearConditions( bits_COND_NO_AMMO_LOADED );
|
||||||
|
if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) )
|
||||||
|
{
|
||||||
|
return GetScheduleOfType ( SCHED_HWGRUNT_SUPPRESS );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetScheduleOfType ( SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// can shoot
|
||||||
|
else if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) )
|
||||||
|
{
|
||||||
|
// Force attack!
|
||||||
|
return GetScheduleOfType ( SCHED_HWGRUNT_SUPPRESS );
|
||||||
|
}
|
||||||
|
// can't see enemy
|
||||||
|
|
||||||
|
if ( HasConditions( bits_COND_SEE_ENEMY ) && !HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) )
|
||||||
|
{
|
||||||
|
return GetScheduleOfType ( SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no special cases here, call the base class
|
||||||
|
return CMBaseMonster :: GetSchedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
//=========================================================
|
||||||
|
Schedule_t* CMHWGrunt :: GetScheduleOfType ( int Type )
|
||||||
|
{
|
||||||
|
switch ( Type )
|
||||||
|
{
|
||||||
|
case SCHED_HWGRUNT_ELOF_FAIL:
|
||||||
|
{
|
||||||
|
// human grunt is unable to move to a position that allows him to attack the enemy.
|
||||||
|
return &slHWGruntELOFFail[ 0 ];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE:
|
||||||
|
{
|
||||||
|
return &slHWGruntEstablishLineOfFire[ 0 ];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCHED_RANGE_ATTACK1:
|
||||||
|
{
|
||||||
|
// no pistols yet, always do standing attack
|
||||||
|
return &slHWGruntRangeAttack1B[ 0 ];
|
||||||
|
}
|
||||||
|
case SCHED_COMBAT_FACE:
|
||||||
|
{
|
||||||
|
return &slHWGruntCombatFace[ 0 ];
|
||||||
|
}
|
||||||
|
case SCHED_HWGRUNT_WAIT_FACE_ENEMY:
|
||||||
|
{
|
||||||
|
return &slHWGruntWaitInCover[ 0 ];
|
||||||
|
}
|
||||||
|
case SCHED_HWGRUNT_SWEEP:
|
||||||
|
{
|
||||||
|
return &slHWGruntSweep[ 0 ];
|
||||||
|
}
|
||||||
|
case SCHED_VICTORY_DANCE:
|
||||||
|
{
|
||||||
|
return &slHWGruntVictoryDance[ 0 ];
|
||||||
|
}
|
||||||
|
case SCHED_HWGRUNT_SUPPRESS:
|
||||||
|
{
|
||||||
|
return &slHWGruntSuppress[ 0 ];
|
||||||
|
}
|
||||||
|
case SCHED_FAIL:
|
||||||
|
{
|
||||||
|
if ( m_hEnemy != NULL )
|
||||||
|
{
|
||||||
|
// grunt has an enemy, so pick a different default fail schedule most likely to help recover.
|
||||||
|
return &slHWGruntCombatFail[ 0 ];
|
||||||
|
}
|
||||||
|
|
||||||
|
return &slHWGruntFail[ 0 ];
|
||||||
|
}
|
||||||
|
case SCHED_HWGRUNT_REPEL:
|
||||||
|
{
|
||||||
|
if (pev->velocity.z > -128)
|
||||||
|
pev->velocity.z -= 32;
|
||||||
|
return &slHWGruntRepel[ 0 ];
|
||||||
|
}
|
||||||
|
case SCHED_HWGRUNT_REPEL_ATTACK:
|
||||||
|
{
|
||||||
|
if (pev->velocity.z > -128)
|
||||||
|
pev->velocity.z -= 32;
|
||||||
|
return &slHWGruntRepelAttack[ 0 ];
|
||||||
|
}
|
||||||
|
case SCHED_HWGRUNT_REPEL_LAND:
|
||||||
|
{
|
||||||
|
return &slHWGruntRepelLand[ 0 ];
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
return CMBaseMonster :: GetScheduleOfType ( Type );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -416,7 +416,7 @@ void CMISlave :: Spawn()
|
|||||||
{
|
{
|
||||||
Precache( );
|
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);
|
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
|
||||||
|
|
||||||
pev->solid = SOLID_SLIDEBOX;
|
pev->solid = SOLID_SLIDEBOX;
|
||||||
|
|||||||
311
src/dlls/massn.cpp
Normal file
311
src/dlls/massn.cpp
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository!
|
||||||
|
|
||||||
|
/***
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This source code contains proprietary and confidential information of
|
||||||
|
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||||
|
* persons who have executed a written SDK license with Valve. Any access,
|
||||||
|
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||||
|
*
|
||||||
|
****/
|
||||||
|
//=========================================================
|
||||||
|
// Black Ops - Male Assassin
|
||||||
|
//=========================================================
|
||||||
|
|
||||||
|
#include "extdll.h"
|
||||||
|
#include "plane.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "cmbase.h"
|
||||||
|
#include "cmbasemonster.h"
|
||||||
|
#include "monsters.h"
|
||||||
|
#include "schedule.h"
|
||||||
|
#include "animation.h"
|
||||||
|
#include "weapons.h"
|
||||||
|
#include "cmtalkmonster.h"
|
||||||
|
#include "effects.h"
|
||||||
|
#include "customentity.h"
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// monster-specific DEFINE's
|
||||||
|
//=========================================================
|
||||||
|
#define MASSN_CLIP_SIZE 36 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x!
|
||||||
|
|
||||||
|
// Weapon flags
|
||||||
|
#define MASSN_9MMAR (1 << 0)
|
||||||
|
#define MASSN_HANDGRENADE (1 << 1)
|
||||||
|
#define MASSN_GRENADELAUNCHER (1 << 2)
|
||||||
|
#define MASSN_SNIPERRIFLE (1 << 3)
|
||||||
|
|
||||||
|
// Body groups.
|
||||||
|
#define HEAD_GROUP 1
|
||||||
|
#define GUN_GROUP 2
|
||||||
|
|
||||||
|
// Head values
|
||||||
|
#define HEAD_WHITE 0
|
||||||
|
#define HEAD_BLACK 1
|
||||||
|
#define HEAD_GOGGLES 2
|
||||||
|
|
||||||
|
// Gun values
|
||||||
|
#define GUN_MP5 0
|
||||||
|
#define GUN_SNIPERRIFLE 1
|
||||||
|
#define GUN_NONE 2
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Monster's Anim Events Go Here
|
||||||
|
//=========================================================
|
||||||
|
#define MASSN_AE_KICK ( 3 )
|
||||||
|
#define MASSN_AE_BURST1 ( 4 )
|
||||||
|
#define MASSN_AE_CAUGHT_ENEMY ( 10 ) // grunt established sight with an enemy (player only) that had previously eluded the squad.
|
||||||
|
#define MASSN_AE_DROP_GUN ( 11 ) // grunt (probably dead) is dropping his mp5.
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Override a few behaviours to make this grunt silent
|
||||||
|
//=========================================================
|
||||||
|
BOOL CMMassn::FOkToSpeak(void)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMMassn::IdleSound(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMMassn::PainSound(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMMassn::DeathSound(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Classify - indicates this monster's place in the
|
||||||
|
// relationship table.
|
||||||
|
//=========================================================
|
||||||
|
int CMMassn::Classify(void)
|
||||||
|
{
|
||||||
|
if ( m_iClassifyOverride == -1 ) // helper
|
||||||
|
return CLASS_NONE;
|
||||||
|
else if ( m_iClassifyOverride > 0 )
|
||||||
|
return m_iClassifyOverride; // override
|
||||||
|
|
||||||
|
return CLASS_HUMAN_MILITARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Shoot
|
||||||
|
//=========================================================
|
||||||
|
void CMMassn::Sniperrifle(void)
|
||||||
|
{
|
||||||
|
if (m_hEnemy == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector vecShootOrigin = GetGunPosition();
|
||||||
|
Vector vecShootDir = ShootAtEnemy(vecShootOrigin);
|
||||||
|
|
||||||
|
UTIL_MakeVectors(pev->angles);
|
||||||
|
|
||||||
|
Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40, 90) + gpGlobals->v_up * RANDOM_FLOAT(75, 200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40);
|
||||||
|
EjectBrass(vecShootOrigin - vecShootDir * 24, vecShellVelocity, pev->angles.y, m_iBrassShell, TE_BOUNCE_SHELL);
|
||||||
|
FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_1DEGREES, 2048, BULLET_MONSTER_762, 0); // shoot +-7.5 degrees
|
||||||
|
|
||||||
|
pev->effects |= EF_MUZZLEFLASH;
|
||||||
|
|
||||||
|
// BUG - For some reason that still eludes me, grunts are completely unable to reload their weapons.
|
||||||
|
// As a temporary fix, give them infinite ammo. It will look bad I know... I gotta find a solution. -Giegue
|
||||||
|
//m_cAmmoLoaded--;// take away a bullet!
|
||||||
|
|
||||||
|
Vector angDir = UTIL_VecToAngles(vecShootDir);
|
||||||
|
SetBlending(0, angDir.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// HandleAnimEvent - catches the monster-specific messages
|
||||||
|
// that occur when tagged animation frames are played.
|
||||||
|
//=========================================================
|
||||||
|
void CMMassn::HandleAnimEvent(MonsterEvent_t *pEvent)
|
||||||
|
{
|
||||||
|
Vector vecShootDir;
|
||||||
|
Vector vecShootOrigin;
|
||||||
|
|
||||||
|
switch (pEvent->event)
|
||||||
|
{
|
||||||
|
case MASSN_AE_DROP_GUN:
|
||||||
|
{
|
||||||
|
Vector vecGunPos;
|
||||||
|
Vector vecGunAngles;
|
||||||
|
|
||||||
|
GetAttachment(0, vecGunPos, vecGunAngles);
|
||||||
|
|
||||||
|
// switch to body group with no gun.
|
||||||
|
SetBodygroup(GUN_GROUP, GUN_NONE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case MASSN_AE_BURST1:
|
||||||
|
{
|
||||||
|
if (FBitSet(pev->weapons, MASSN_9MMAR))
|
||||||
|
{
|
||||||
|
Shoot();
|
||||||
|
|
||||||
|
// the first round of the three round burst plays the sound and puts a sound in the world sound list.
|
||||||
|
if (RANDOM_LONG(0, 1))
|
||||||
|
{
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "hgrunt/gr_mgun1.wav", 1, ATTN_NORM);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "hgrunt/gr_mgun2.wav", 1, ATTN_NORM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (FBitSet(pev->weapons, MASSN_SNIPERRIFLE))
|
||||||
|
{
|
||||||
|
Sniperrifle();
|
||||||
|
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/sniper_fire.wav", 1, ATTN_NORM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MASSN_AE_KICK:
|
||||||
|
{
|
||||||
|
edict_t *pHurt = Kick();
|
||||||
|
|
||||||
|
if (pHurt)
|
||||||
|
{
|
||||||
|
// SOUND HERE!
|
||||||
|
UTIL_MakeVectors(pev->angles);
|
||||||
|
pHurt->v.punchangle.x = 15;
|
||||||
|
pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50;
|
||||||
|
if (UTIL_IsPlayer(pHurt))
|
||||||
|
UTIL_TakeDamage( pHurt, pev, pev, gSkillData.massnDmgKick, DMG_CLUB );
|
||||||
|
else if (pHurt->v.euser4 != NULL)
|
||||||
|
{
|
||||||
|
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt));
|
||||||
|
pMonster->TakeDamage( pev, pev, gSkillData.massnDmgKick, DMG_CLUB );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MASSN_AE_CAUGHT_ENEMY:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
CMHGrunt::HandleAnimEvent(pEvent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Spawn
|
||||||
|
//=========================================================
|
||||||
|
void CMMassn::Spawn()
|
||||||
|
{
|
||||||
|
Precache();
|
||||||
|
|
||||||
|
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;
|
||||||
|
pev->movetype = MOVETYPE_STEP;
|
||||||
|
m_bloodColor = BLOOD_COLOR_RED;
|
||||||
|
pev->effects = 0;
|
||||||
|
pev->health = gSkillData.massnHealth;
|
||||||
|
m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||||
|
m_MonsterState = MONSTERSTATE_NONE;
|
||||||
|
m_flNextGrenadeCheck = gpGlobals->time + 1;
|
||||||
|
m_flNextPainTime = gpGlobals->time;
|
||||||
|
m_iSentence = -1;
|
||||||
|
|
||||||
|
//m_afCapability = bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
|
||||||
|
m_afCapability = bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
|
||||||
|
|
||||||
|
//m_fEnemyEluded = FALSE;
|
||||||
|
m_fFirstEncounter = TRUE;// this is true when the grunt spawns, because he hasn't encountered an enemy yet.
|
||||||
|
|
||||||
|
m_HackedGunPos = Vector(0, 0, 55);
|
||||||
|
|
||||||
|
if (pev->weapons == 0)
|
||||||
|
{
|
||||||
|
// weapons not specified, randomize
|
||||||
|
switch ( RANDOM_LONG( 0, 2 ) )
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
pev->weapons = MASSN_9MMAR | MASSN_HANDGRENADE;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
pev->weapons = MASSN_9MMAR | MASSN_GRENADELAUNCHER;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
pev->weapons = MASSN_SNIPERRIFLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FBitSet(pev->weapons, MASSN_SNIPERRIFLE))
|
||||||
|
{
|
||||||
|
SetBodygroup(GUN_GROUP, GUN_SNIPERRIFLE);
|
||||||
|
m_cClipSize = 5;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_cClipSize = MASSN_CLIP_SIZE;
|
||||||
|
}
|
||||||
|
m_cAmmoLoaded = m_cClipSize;
|
||||||
|
|
||||||
|
if (RANDOM_LONG(0, 99) < 80)
|
||||||
|
pev->skin = 0; // light skin
|
||||||
|
else
|
||||||
|
pev->skin = 1; // dark skin
|
||||||
|
|
||||||
|
CMTalkMonster::g_talkWaitTime = 0;
|
||||||
|
|
||||||
|
MonsterInit();
|
||||||
|
|
||||||
|
pev->classname = MAKE_STRING( "monster_male_assassin" );
|
||||||
|
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
|
||||||
|
{
|
||||||
|
// default name
|
||||||
|
m_szMonsterName = MAKE_STRING( "Male Assassin" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Precache - precaches all resources this monster needs
|
||||||
|
//=========================================================
|
||||||
|
void CMMassn::Precache()
|
||||||
|
{
|
||||||
|
PRECACHE_MODEL("models/massn.mdl");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("hgrunt/gr_mgun1.wav");
|
||||||
|
PRECACHE_SOUND("hgrunt/gr_mgun2.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("hgrunt/gr_reload1.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("weapons/glauncher.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("weapons/sniper_bolt1.wav");
|
||||||
|
PRECACHE_SOUND("weapons/sniper_fire.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event
|
||||||
|
|
||||||
|
// get voice pitch
|
||||||
|
if (RANDOM_LONG(0, 1))
|
||||||
|
m_voicePitch = 109 + RANDOM_LONG(0, 7);
|
||||||
|
else
|
||||||
|
m_voicePitch = 100;
|
||||||
|
|
||||||
|
m_iBrassShell = PRECACHE_MODEL("models/shell.mdl");// brass shell
|
||||||
|
}
|
||||||
@@ -56,8 +56,8 @@ static META_FUNCTIONS gMetaFunctionTable =
|
|||||||
plugin_info_t Plugin_info = {
|
plugin_info_t Plugin_info = {
|
||||||
META_INTERFACE_VERSION, // interface version
|
META_INTERFACE_VERSION, // interface version
|
||||||
"MonsterMod", // name
|
"MonsterMod", // name
|
||||||
"2.0", // version
|
"3.0", // version
|
||||||
"03/06/2020", // date in DD/MM/YYYY format
|
"24/02/2023", // date in DD/MM/YYYY format
|
||||||
"botman, Rick90, Giegue", // original authors + recreation by...
|
"botman, Rick90, Giegue", // original authors + recreation by...
|
||||||
"https://github.com/JulianR0/monstermod-redo", // url
|
"https://github.com/JulianR0/monstermod-redo", // url
|
||||||
"MONSTER", // logtag
|
"MONSTER", // logtag
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ void scan_monster_cfg(FILE *fp)
|
|||||||
// Now that I think about it this looks slow and bad code >.>
|
// Now that I think about it this looks slow and bad code >.>
|
||||||
|
|
||||||
// A match is found. What is this?
|
// 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
|
// It's a monster, add it to the list
|
||||||
if (monster_spawn_count == MAX_MONSTERS)
|
if (monster_spawn_count == MAX_MONSTERS)
|
||||||
@@ -110,6 +110,22 @@ void scan_monster_cfg(FILE *fp)
|
|||||||
monster = TRUE;
|
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)
|
else if (strcmp(monster_types[mIndex].name, "info_node") == 0)
|
||||||
{
|
{
|
||||||
// Normal node
|
// Normal node
|
||||||
@@ -142,7 +158,7 @@ void scan_monster_cfg(FILE *fp)
|
|||||||
}
|
}
|
||||||
if (monster_types[mIndex].name[0] == 0)
|
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!");
|
LOG_MESSAGE(PLID, "ERROR: nothing will spawn here!");
|
||||||
badent = TRUE;
|
badent = TRUE;
|
||||||
}
|
}
|
||||||
@@ -150,8 +166,8 @@ void scan_monster_cfg(FILE *fp)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// What are you doing?!
|
// What are you doing?!
|
||||||
LOG_MESSAGE(PLID, "ERROR: BAD ENTITY STRUCTURE! Last line was %s", input); // print conflictive line
|
LOG_MESSAGE(PLID, "ERROR: BAD ENTITY STRUCTURE! Last line was %s", data[kvd_index-1].key); // print conflictive line
|
||||||
LOG_MESSAGE(PLID, "ERROR: nothing will spawn here!");
|
LOG_MESSAGE(PLID, "ERROR: classname MUST be the last entry of the entity!" );
|
||||||
badent = TRUE;
|
badent = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,23 +208,6 @@ void scan_monster_cfg(FILE *fp)
|
|||||||
node_spawnpoint[node_spawn_count].origin[2] = z;
|
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)
|
else if (strcmp(data[i].key, "angles") == 0)
|
||||||
{
|
{
|
||||||
if (monster)
|
if (monster)
|
||||||
@@ -241,6 +240,62 @@ void scan_monster_cfg(FILE *fp)
|
|||||||
monster_spawnpoint[monster_spawn_count].spawnflags = x;
|
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
|
else
|
||||||
{
|
{
|
||||||
// We do not know this keyvalue, but an specific entity might use it.
|
// 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)
|
if (monster)
|
||||||
{
|
{
|
||||||
// Init monster
|
// Spawn right away
|
||||||
monster_spawnpoint[monster_spawn_count].respawn_time = gpGlobals->time + 0.1; // spawn (nearly) right away
|
|
||||||
monster_spawnpoint[monster_spawn_count].need_to_respawn = TRUE;
|
monster_spawnpoint[monster_spawn_count].need_to_respawn = TRUE;
|
||||||
monster_spawn_count++;
|
monster_spawn_count++;
|
||||||
}
|
}
|
||||||
@@ -282,7 +336,7 @@ void scan_monster_cfg(FILE *fp)
|
|||||||
// Bruteforce to remove quotes
|
// Bruteforce to remove quotes
|
||||||
char parse[66] = {0};
|
char parse[66] = {0};
|
||||||
int skip = 0;
|
int skip = 0;
|
||||||
for (int i = 0; i < strlen(input); i++)
|
for (unsigned i = 0; i < strlen(input); i++)
|
||||||
{
|
{
|
||||||
if (input[i] == '"')
|
if (input[i] == '"')
|
||||||
{
|
{
|
||||||
@@ -317,12 +371,11 @@ void scan_monster_cfg(FILE *fp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_monster_cfg(void)
|
bool process_monster_cfg(void)
|
||||||
{
|
{
|
||||||
char game_dir[256];
|
char game_dir[256];
|
||||||
char filename[256];
|
char filename[256];
|
||||||
FILE *fp = NULL;
|
FILE *fp = NULL;
|
||||||
bool status = FALSE; // no error
|
|
||||||
|
|
||||||
monster_spawn_count = 0;
|
monster_spawn_count = 0;
|
||||||
|
|
||||||
@@ -351,8 +404,7 @@ void process_monster_cfg(void)
|
|||||||
{
|
{
|
||||||
//META_CONS("[MONSTER] ERROR: Could not open \"%s\"!", filename);
|
//META_CONS("[MONSTER] ERROR: Could not open \"%s\"!", filename);
|
||||||
LOG_MESSAGE(PLID, "ERROR: Could not open \"%s\" file!", filename);
|
LOG_MESSAGE(PLID, "ERROR: Could not open \"%s\" file!", filename);
|
||||||
|
return TRUE; // error
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scan_monster_cfg(fp);
|
scan_monster_cfg(fp);
|
||||||
@@ -360,7 +412,7 @@ void process_monster_cfg(void)
|
|||||||
fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return FALSE; // all ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ RSC=rc.exe
|
|||||||
# PROP Ignore_Export_Lib 0
|
# PROP Ignore_Export_Lib 0
|
||||||
# PROP Target_Dir ""
|
# PROP Target_Dir ""
|
||||||
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "monster_mm_EXPORTS" /YX /FD /c
|
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "monster_mm_EXPORTS" /YX /FD /c
|
||||||
# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\dlls" /I "..\common" /I "..\engine" /I "..\pm_shared" /I "..\..\metamod" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "monster_mm_EXPORTS" /D strcasecmp=stricmp /D strncasecmp=_strnicmp /FD /c
|
# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\dlls" /I "..\common" /I "..\engine" /I "..\pm_shared" /I "..\metamod" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "monster_mm_EXPORTS" /D strcasecmp=stricmp /D strncasecmp=_strnicmp /FD /c
|
||||||
# SUBTRACT CPP /YX
|
# SUBTRACT CPP /YX
|
||||||
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||||
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||||
@@ -55,22 +55,6 @@ BSC32=bscmake.exe
|
|||||||
LINK32=link.exe
|
LINK32=link.exe
|
||||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
|
||||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /def:".\monster_mm.def"
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /def:".\monster_mm.def"
|
||||||
# Begin Custom Build - Copying to DLL folder
|
|
||||||
TargetPath=.\Release\monster_mm.dll
|
|
||||||
TargetName=monster_mm
|
|
||||||
InputPath=.\Release\monster_mm.dll
|
|
||||||
SOURCE="$(InputPath)"
|
|
||||||
|
|
||||||
"$(TargetName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
|
|
||||||
copy $(TargetPath) D:\Half-Life\valve\dlls
|
|
||||||
copy $(TargetPath) D:\Half-Life\tfc\dlls
|
|
||||||
copy $(TargetPath) D:\Half-Life\cstrike\dlls
|
|
||||||
copy $(TargetPath) D:\Half-Life\dmc\dlls
|
|
||||||
copy $(TargetPath) D:\Half-Life\dod\dlls
|
|
||||||
copy $(TargetPath) D:\Half-Life\firearms\dlls
|
|
||||||
copy $(TargetPath) D:\Half-Life\frontline\dlls
|
|
||||||
|
|
||||||
# End Custom Build
|
|
||||||
|
|
||||||
!ELSEIF "$(CFG)" == "monster_mm - Win32 Debug"
|
!ELSEIF "$(CFG)" == "monster_mm - Win32 Debug"
|
||||||
|
|
||||||
@@ -86,7 +70,7 @@ SOURCE="$(InputPath)"
|
|||||||
# PROP Ignore_Export_Lib 0
|
# PROP Ignore_Export_Lib 0
|
||||||
# PROP Target_Dir ""
|
# PROP Target_Dir ""
|
||||||
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "monster_mm_EXPORTS" /YX /FD /GZ /c
|
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "monster_mm_EXPORTS" /YX /FD /GZ /c
|
||||||
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\dlls" /I "..\common" /I "..\engine" /I "..\pm_shared" /I "..\..\metamod" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "monster_mm_EXPORTS" /D strcasecmp=stricmp /D strncasecmp=_strnicmp /FD /GZ /c
|
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\dlls" /I "..\common" /I "..\engine" /I "..\pm_shared" /I "..\metamod" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "monster_mm_EXPORTS" /D strcasecmp=stricmp /D strncasecmp=_strnicmp /FD /GZ /c
|
||||||
# SUBTRACT CPP /YX
|
# SUBTRACT CPP /YX
|
||||||
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||||
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||||
@@ -98,23 +82,6 @@ BSC32=bscmake.exe
|
|||||||
LINK32=link.exe
|
LINK32=link.exe
|
||||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
|
||||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /def:".\monster_mm.def" /pdbtype:sept
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /def:".\monster_mm.def" /pdbtype:sept
|
||||||
# Begin Custom Build - Copying to DLL folder
|
|
||||||
TargetPath=.\Debug\monster_mm.dll
|
|
||||||
TargetName=monster_mm
|
|
||||||
InputPath=.\Debug\monster_mm.dll
|
|
||||||
SOURCE="$(InputPath)"
|
|
||||||
|
|
||||||
"$(TargetName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
|
|
||||||
copy $(TargetPath) D:\Half-Life\valve\dlls
|
|
||||||
copy $(TargetPath) D:\Half-Life\tfc\dlls
|
|
||||||
copy $(TargetPath) D:\Half-Life\cstrike\dlls
|
|
||||||
copy $(TargetPath) D:\Half-Life\dmc\dlls
|
|
||||||
copy $(TargetPath) D:\Half-Life\dod\dlls
|
|
||||||
copy $(TargetPath) D:\Half-Life\firearms\dlls
|
|
||||||
copy $(TargetPath) D:\Half-Life\frontline\dlls
|
|
||||||
copy $(TargetPath) D:\Half-Life\gearbox\dlls
|
|
||||||
|
|
||||||
# End Custom Build
|
|
||||||
|
|
||||||
!ENDIF
|
!ENDIF
|
||||||
|
|
||||||
@@ -183,14 +150,26 @@ SOURCE=.\effects.cpp
|
|||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\explode.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\flyingmonster.cpp
|
SOURCE=.\flyingmonster.cpp
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\gargantua.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\ggrenade.cpp
|
SOURCE=.\ggrenade.cpp
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\gonome.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\h_ai.cpp
|
SOURCE=.\h_ai.cpp
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
@@ -219,10 +198,18 @@ SOURCE=.\houndeye.cpp
|
|||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\hwgrunt.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\islave.cpp
|
SOURCE=.\islave.cpp
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\massn.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\monster_api.cpp
|
SOURCE=.\monster_api.cpp
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
@@ -231,6 +218,10 @@ SOURCE=.\monster_config.cpp
|
|||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\monstermaker.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\monsters.cpp
|
SOURCE=.\monsters.cpp
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
@@ -243,10 +234,30 @@ SOURCE=.\nodes.cpp
|
|||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\otis.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\pitdrone.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\rgrunt.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\scientist.cpp
|
SOURCE=.\scientist.cpp
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\shock.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\shockroach.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\skill.cpp
|
SOURCE=.\skill.cpp
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
@@ -255,10 +266,22 @@ SOURCE=.\sound.cpp
|
|||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\sporegrenade.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\squeakgrenade.cpp
|
SOURCE=.\squeakgrenade.cpp
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\strooper.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\stukabat.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\subs.cpp
|
SOURCE=.\subs.cpp
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
@@ -267,10 +290,18 @@ SOURCE=.\talkmonster.cpp
|
|||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\turret.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\util.cpp
|
SOURCE=.\util.cpp
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\voltigore.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\weapons.cpp
|
SOURCE=.\weapons.cpp
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
@@ -287,6 +318,10 @@ SOURCE=.\activity.h
|
|||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\activitymap.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\animation.h
|
SOURCE=.\animation.h
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
@@ -299,6 +334,10 @@ SOURCE=.\cmbase.h
|
|||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\cmbaseextra.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\cmbasemonster.h
|
SOURCE=.\cmbasemonster.h
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
@@ -315,6 +354,10 @@ SOURCE=.\decals.h
|
|||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\defaultai.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\doors.h
|
SOURCE=.\doors.h
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
@@ -339,10 +382,18 @@ SOURCE=.\func_break.h
|
|||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\hornet.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\monster_plugin.h
|
SOURCE=.\monster_plugin.h
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\monsterevent.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\monsters.h
|
SOURCE=.\monsters.h
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
@@ -351,10 +402,18 @@ SOURCE=.\nodes.h
|
|||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\plane.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\schedule.h
|
SOURCE=.\schedule.h
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\shock.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\skill.h
|
SOURCE=.\skill.h
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
#ifndef MONSTER_PLUGIN_H
|
#ifndef MONSTER_PLUGIN_H
|
||||||
#define MONSTER_PLUGIN_H
|
#define MONSTER_PLUGIN_H
|
||||||
|
|
||||||
typedef struct pKVD
|
typedef struct
|
||||||
{
|
{
|
||||||
char key[33];
|
char key[33];
|
||||||
char value[33];
|
char value[33];
|
||||||
};
|
} pKVD;
|
||||||
|
|
||||||
#define MAX_KEYVALUES 32
|
#define MAX_KEYVALUES 32
|
||||||
|
|
||||||
@@ -27,7 +27,6 @@ typedef struct
|
|||||||
int monster_index;
|
int monster_index;
|
||||||
edict_t *monster_pent;
|
edict_t *monster_pent;
|
||||||
bool killed;
|
bool killed;
|
||||||
int respawn_index;
|
|
||||||
CMBaseMonster *pMonster;
|
CMBaseMonster *pMonster;
|
||||||
} monster_t;
|
} monster_t;
|
||||||
|
|
||||||
@@ -38,11 +37,9 @@ extern monster_t monsters[MAX_MONSTER_ENTS];
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
Vector origin;
|
Vector origin;
|
||||||
Vector angles;
|
Vector angles;
|
||||||
float delay;
|
|
||||||
unsigned char monster;
|
unsigned char monster;
|
||||||
int spawnflags;
|
int spawnflags;
|
||||||
pKVD *keyvalue;
|
pKVD *keyvalue;
|
||||||
float respawn_time;
|
|
||||||
bool need_to_respawn;
|
bool need_to_respawn;
|
||||||
} monster_spawnpoint_t;
|
} 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -137,17 +137,14 @@ void CMBaseMonster :: Look ( int iDistance )
|
|||||||
{
|
{
|
||||||
pSightEnt = pList[i];
|
pSightEnt = pList[i];
|
||||||
// !!!temporarily only considering other monsters and clients, don't see prisoners
|
// !!!temporarily only considering other monsters and clients, don't see prisoners
|
||||||
if ( pSightEnt != this->edict() &&
|
if ( pSightEnt != this->edict() && !FBitSet( pSightEnt->v.spawnflags, SF_MONSTER_PRISONER ) && pSightEnt->v.health > 0 )
|
||||||
!FBitSet( pSightEnt->v.spawnflags, SF_MONSTER_PRISONER ) &&
|
|
||||||
pSightEnt->v.health > 0 )
|
|
||||||
{
|
{
|
||||||
// is this a player AND are they alive?
|
// is this a player AND are they alive?
|
||||||
if (UTIL_IsPlayer(pSightEnt) && UTIL_IsAlive(pSightEnt))
|
if (UTIL_IsPlayer(pSightEnt) && UTIL_IsAlive(pSightEnt))
|
||||||
{
|
{
|
||||||
// the looker will want to consider this entity
|
// the looker will want to consider this entity
|
||||||
// don't check anything else about an entity that can't be seen.
|
// don't check anything else about an entity that can't be seen.
|
||||||
if ( UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) &&
|
if ( UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) && !FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) )
|
||||||
!FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) )
|
|
||||||
{
|
{
|
||||||
m_edictList[m_edictList_count] = pSightEnt;
|
m_edictList[m_edictList_count] = pSightEnt;
|
||||||
m_edictList_count++;
|
m_edictList_count++;
|
||||||
@@ -170,12 +167,12 @@ void CMBaseMonster :: Look ( int iDistance )
|
|||||||
}
|
}
|
||||||
else if (pSightEnt->v.euser4 != NULL)
|
else if (pSightEnt->v.euser4 != NULL)
|
||||||
{
|
{
|
||||||
|
/* MonsterMod monster looking at another MonsterMod monster */
|
||||||
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pSightEnt));
|
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pSightEnt));
|
||||||
|
|
||||||
// the looker will want to consider this entity
|
// the looker will want to consider this entity
|
||||||
// don't check anything else about an entity that can't be seen, or an entity that you don't care about.
|
// don't check anything else about an entity that can't be seen, or an entity that you don't care about.
|
||||||
if ( IRelationship( pMonster ) != R_NO && UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) &&
|
if ( IRelationship( pMonster ) != R_NO && UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) && !FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) )
|
||||||
!FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) )
|
|
||||||
{
|
{
|
||||||
m_edictList[m_edictList_count] = pSightEnt;
|
m_edictList[m_edictList_count] = pSightEnt;
|
||||||
m_edictList_count++;
|
m_edictList_count++;
|
||||||
@@ -210,6 +207,47 @@ void CMBaseMonster :: Look ( int iDistance )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* MonsterMod monster looking at a NON-MonsterMod monster */
|
||||||
|
|
||||||
|
// the looker will want to consider this entity
|
||||||
|
// don't check anything else about an entity that can't be seen, or an entity that you don't care about.
|
||||||
|
if ( IRelationship( pSightEnt->v.iuser4 ) != R_NO && UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) && !FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) )
|
||||||
|
{
|
||||||
|
m_edictList[m_edictList_count] = pSightEnt;
|
||||||
|
m_edictList_count++;
|
||||||
|
|
||||||
|
if ( pSightEnt == m_hEnemy )
|
||||||
|
{
|
||||||
|
// we know this ent is visible, so if it also happens to be our enemy, store that now.
|
||||||
|
iSighted |= bits_COND_SEE_ENEMY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't add the Enemy's relationship to the conditions. We only want to worry about conditions when
|
||||||
|
// we see monsters other than the Enemy.
|
||||||
|
switch ( IRelationship ( pSightEnt->v.iuser4 ) )
|
||||||
|
{
|
||||||
|
case R_NM:
|
||||||
|
iSighted |= bits_COND_SEE_NEMESIS;
|
||||||
|
break;
|
||||||
|
case R_HT:
|
||||||
|
iSighted |= bits_COND_SEE_HATE;
|
||||||
|
break;
|
||||||
|
case R_DL:
|
||||||
|
iSighted |= bits_COND_SEE_DISLIKE;
|
||||||
|
break;
|
||||||
|
case R_FR:
|
||||||
|
iSighted |= bits_COND_SEE_FEAR;
|
||||||
|
break;
|
||||||
|
case R_AL:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ALERT ( at_aiconsole, "%s can't assess %s\n", STRING(pev->classname), STRING(pSightEnt->v.classname ) );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1743,6 +1781,9 @@ void CMBaseMonster :: StartMonster ( void )
|
|||||||
SetActivity( ACT_IDLE );
|
SetActivity( ACT_IDLE );
|
||||||
ChangeSchedule( GetScheduleOfType( SCHED_WAIT_TRIGGER ) );
|
ChangeSchedule( GetScheduleOfType( SCHED_WAIT_TRIGGER ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Notify normal game engine of monster classify
|
||||||
|
pev->iuser4 = Classify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1785,25 +1826,35 @@ int CMBaseMonster::TaskIsRunning( void )
|
|||||||
//=========================================================
|
//=========================================================
|
||||||
int CMBaseMonster::IRelationship ( CMBaseEntity *pTarget )
|
int CMBaseMonster::IRelationship ( CMBaseEntity *pTarget )
|
||||||
{
|
{
|
||||||
static int iEnemy[14][14] =
|
return IRelationshipByClass( pTarget->Classify() );
|
||||||
{ // NONE MACH PLYR HPASS HMIL AMIL APASS AMONST APREY APRED INSECT PLRALY PBWPN ABWPN
|
}
|
||||||
/*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 },
|
int CMBaseMonster::IRelationship ( int iTargetClass )
|
||||||
/*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 },
|
{
|
||||||
/*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 },
|
return IRelationshipByClass( iTargetClass );
|
||||||
/*HUMANPASSIVE*/{ R_NO ,R_NO ,R_AL ,R_AL ,R_HT ,R_FR ,R_NO ,R_HT ,R_DL ,R_FR ,R_NO ,R_AL, R_NO, R_NO },
|
}
|
||||||
/*HUMANMILITAR*/{ R_NO ,R_NO ,R_HT ,R_DL ,R_NO ,R_HT ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_HT, R_NO, R_NO },
|
int CMBaseMonster::IRelationshipByClass ( int iClass )
|
||||||
/*ALIENMILITAR*/{ R_NO ,R_DL ,R_HT ,R_DL ,R_HT ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO },
|
{
|
||||||
/*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 },
|
static int iEnemy[16][16] =
|
||||||
/*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 },
|
{ // NONE MACH PLYR HPASS HMIL AMIL APASS AMONST APREY APRED INSECT PLRALY PBWPN ABWPN RXPIT RXSHK
|
||||||
/*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 },
|
/*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 },
|
||||||
/*ALIENPREDATO*/{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_HT ,R_DL ,R_NO ,R_DL, 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 },
|
||||||
/*INSECT*/ { R_FR ,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 },
|
/*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 },
|
||||||
/*PLAYERALLY*/ { R_NO ,R_DL ,R_AL ,R_AL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_NO, R_NO },
|
/*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 },
|
||||||
/*PBIOWEAPON*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_NO, R_DL },
|
/*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 },
|
||||||
/*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 }
|
/*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[ Classify() ][ pTarget->Classify() ];
|
return iEnemy[ Classify() ][ iClass ];
|
||||||
}
|
}
|
||||||
|
|
||||||
//=========================================================
|
//=========================================================
|
||||||
@@ -2034,21 +2085,35 @@ edict_t *CMBaseMonster :: BestVisibleEnemy ( void )
|
|||||||
if ( UTIL_IsPlayer(pEnt) )
|
if ( UTIL_IsPlayer(pEnt) )
|
||||||
{
|
{
|
||||||
// it's a player...
|
// 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();
|
iDist = ( pEnt->v.origin - pev->origin ).Length();
|
||||||
|
|
||||||
if ( iDist <= iNearest )
|
if ( iDist <= iNearest )
|
||||||
{
|
{
|
||||||
iNearest = iDist;
|
iNearest = iDist;
|
||||||
iBestRelationship = R_NM; // player is always nemsis
|
iBestRelationship = IRelationshipByClass( CLASS_PLAYER );
|
||||||
pReturn = pEnt;
|
pReturn = pEnt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (pEnt->v.euser4 != NULL)
|
else if (pEnt->v.euser4 != NULL)
|
||||||
{
|
{
|
||||||
|
// it's a monstermod monster...
|
||||||
CMBaseMonster *pNextEnt = GetClassPtr((CMBaseMonster *)VARS(pEnt));
|
CMBaseMonster *pNextEnt = GetClassPtr((CMBaseMonster *)VARS(pEnt));
|
||||||
if ( pNextEnt->IsAlive() )
|
if ( pNextEnt->IsAlive() )
|
||||||
{
|
{
|
||||||
if ( IRelationship( pNextEnt) > iBestRelationship )
|
if ( IRelationship( pNextEnt ) > iBestRelationship )
|
||||||
{
|
{
|
||||||
// this entity is disliked MORE than the entity that we
|
// this entity is disliked MORE than the entity that we
|
||||||
// currently think is the best visible enemy. No need to do
|
// currently think is the best visible enemy. No need to do
|
||||||
@@ -2057,7 +2122,7 @@ edict_t *CMBaseMonster :: BestVisibleEnemy ( void )
|
|||||||
iNearest = ( pNextEnt->pev->origin - pev->origin ).Length();
|
iNearest = ( pNextEnt->pev->origin - pev->origin ).Length();
|
||||||
pReturn = pEnt;
|
pReturn = pEnt;
|
||||||
}
|
}
|
||||||
else if ( IRelationship( pNextEnt) == iBestRelationship )
|
else if ( IRelationship( pNextEnt ) == iBestRelationship )
|
||||||
{
|
{
|
||||||
// this entity is disliked just as much as the entity that
|
// this entity is disliked just as much as the entity that
|
||||||
// we currently think is the best visible enemy, so we only
|
// we currently think is the best visible enemy, so we only
|
||||||
@@ -2073,6 +2138,31 @@ edict_t *CMBaseMonster :: BestVisibleEnemy ( void )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// it's a normal game entity...
|
||||||
|
if ( UTIL_IsAlive(pEnt) )
|
||||||
|
{
|
||||||
|
//repeat3
|
||||||
|
if ( IRelationship( pEnt->v.iuser4 ) > iBestRelationship )
|
||||||
|
{
|
||||||
|
iBestRelationship = IRelationship( pEnt->v.iuser4 );
|
||||||
|
iNearest = ( pEnt->v.origin - pev->origin ).Length();
|
||||||
|
pReturn = pEnt;
|
||||||
|
}
|
||||||
|
else if ( IRelationship( pEnt->v.iuser4 ) == iBestRelationship )
|
||||||
|
{
|
||||||
|
iDist = ( pEnt->v.origin - pev->origin ).Length();
|
||||||
|
|
||||||
|
if ( iDist <= iNearest )
|
||||||
|
{
|
||||||
|
iNearest = iDist;
|
||||||
|
iBestRelationship = IRelationship( pEnt->v.iuser4 );
|
||||||
|
pReturn = pEnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
edictList_index++;
|
edictList_index++;
|
||||||
}
|
}
|
||||||
@@ -2519,6 +2609,11 @@ void CMBaseMonster :: KeyValue( KeyValueData *pkvd )
|
|||||||
m_iClassifyOverride = atoi( pkvd->szValue );
|
m_iClassifyOverride = atoi( pkvd->szValue );
|
||||||
pkvd->fHandled = TRUE;
|
pkvd->fHandled = TRUE;
|
||||||
}
|
}
|
||||||
|
else if (FStrEq(pkvd->szKeyName, "model"))
|
||||||
|
{
|
||||||
|
pev->model = ALLOC_STRING( pkvd->szValue );
|
||||||
|
pkvd->fHandled = TRUE;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CMBaseToggle::KeyValue( pkvd );
|
CMBaseToggle::KeyValue( pkvd );
|
||||||
@@ -2882,9 +2977,14 @@ BOOL CMBaseMonster :: GetEnemy ( void )
|
|||||||
if (HasConditions(bits_COND_SEE_CLIENT) && (m_hEnemy == NULL))
|
if (HasConditions(bits_COND_SEE_CLIENT) && (m_hEnemy == NULL))
|
||||||
{
|
{
|
||||||
m_hEnemy = BestVisibleEnemy();
|
m_hEnemy = BestVisibleEnemy();
|
||||||
|
|
||||||
|
// the player we've just seen might not always be our enemy
|
||||||
|
if ( m_hEnemy != NULL )
|
||||||
|
{
|
||||||
m_hTargetEnt = m_hEnemy;
|
m_hTargetEnt = m_hEnemy;
|
||||||
m_vecEnemyLKP = m_hEnemy->v.origin;
|
m_vecEnemyLKP = m_hEnemy->v.origin;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// remember old enemies
|
// remember old enemies
|
||||||
if (m_hEnemy == NULL && PopEnemy( ))
|
if (m_hEnemy == NULL && PopEnemy( ))
|
||||||
|
|||||||
@@ -152,6 +152,7 @@ public:
|
|||||||
virtual int ObjectCaps( void ) { return (CMBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; }
|
virtual int ObjectCaps( void ) { return (CMBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; }
|
||||||
static void SpawnHeadGib( entvars_t *pevVictim );
|
static void SpawnHeadGib( entvars_t *pevVictim );
|
||||||
static void SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human );
|
static void SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human );
|
||||||
|
static void SpawnRandomGibs( entvars_t *pevVictim, int cGibs, const char *pGibModel, int human );
|
||||||
static void SpawnStickyGibs( entvars_t *pevVictim, Vector vecOrigin, int cGibs );
|
static void SpawnStickyGibs( entvars_t *pevVictim, Vector vecOrigin, int cGibs );
|
||||||
|
|
||||||
int m_bloodColor;
|
int m_bloodColor;
|
||||||
|
|||||||
@@ -25,12 +25,12 @@
|
|||||||
#include "animation.h"
|
#include "animation.h"
|
||||||
#include "doors.h"
|
#include "doors.h"
|
||||||
|
|
||||||
//#if !defined ( _WIN32 )
|
#if !defined ( _WIN32 )
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h> // mkdir
|
#include <unistd.h> // mkdir
|
||||||
//#endif
|
#endif
|
||||||
|
|
||||||
#define HULL_STEP_SIZE 16// how far the test hull moves on each step
|
#define HULL_STEP_SIZE 16// how far the test hull moves on each step
|
||||||
#define NODE_HEIGHT 8 // how high to lift nodes off the ground after we drop them all (make stair/ramp mapping easier)
|
#define NODE_HEIGHT 8 // how high to lift nodes off the ground after we drop them all (make stair/ramp mapping easier)
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
// to help eliminate node clutter by level designers, this is used to cap how many other nodes
|
// to help eliminate node clutter by level designers, this is used to cap how many other nodes
|
||||||
// any given node is allowed to 'see' in the first stage of graph creation "LinkVisibleNodes()".
|
// any given node is allowed to 'see' in the first stage of graph creation "LinkVisibleNodes()".
|
||||||
#define MAX_NODE_INITIAL_LINKS 128
|
#define MAX_NODE_INITIAL_LINKS 128
|
||||||
#define MAX_NODES 1024
|
//#define MAX_NODES 1024 // already defined in monster_plugin.h
|
||||||
|
|
||||||
Vector VecBModelOrigin( entvars_t* pevBModel );
|
Vector VecBModelOrigin( entvars_t* pevBModel );
|
||||||
|
|
||||||
|
|||||||
329
src/dlls/otis.cpp
Normal file
329
src/dlls/otis.cpp
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository!
|
||||||
|
|
||||||
|
/***
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This source code contains proprietary and confidential information of
|
||||||
|
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||||
|
* persons who have executed a written SDK license with Valve. Any access,
|
||||||
|
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||||
|
*
|
||||||
|
****/
|
||||||
|
//=========================================================
|
||||||
|
// monster template
|
||||||
|
//=========================================================
|
||||||
|
// UNDONE: Holster weapon?
|
||||||
|
|
||||||
|
#include "extdll.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "cmbase.h"
|
||||||
|
#include "cmbasemonster.h"
|
||||||
|
#include "monsters.h"
|
||||||
|
#include "schedule.h"
|
||||||
|
#include "defaultai.h"
|
||||||
|
#include "weapons.h"
|
||||||
|
|
||||||
|
#define NUM_OTIS_HEADS 2 // heads available for otis model
|
||||||
|
|
||||||
|
#define GUN_GROUP 1
|
||||||
|
#define HEAD_GROUP 2
|
||||||
|
|
||||||
|
#define HEAD_HAIR 0
|
||||||
|
#define HEAD_BALD 1
|
||||||
|
|
||||||
|
#define GUN_NONE 0
|
||||||
|
#define GUN_EAGLE 1
|
||||||
|
#define GUN_DONUT 2
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Monster's Anim Events Go Here
|
||||||
|
//=========================================================
|
||||||
|
// first flag is Otis dying for scripted sequences?
|
||||||
|
#define OTIS_AE_DRAW ( 2 )
|
||||||
|
#define OTIS_AE_SHOOT ( 3 )
|
||||||
|
#define OTIS_AE_HOLSTER ( 4 )
|
||||||
|
|
||||||
|
#define OTIS_BODY_GUNHOLSTERED 0
|
||||||
|
#define OTIS_BODY_GUNDRAWN 1
|
||||||
|
#define OTIS_BODY_DONUT 2
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// ALertSound - otis says "Freeze!"
|
||||||
|
//=========================================================
|
||||||
|
void CMOtis::AlertSound(void)
|
||||||
|
{
|
||||||
|
if (m_hEnemy != 0)
|
||||||
|
{
|
||||||
|
if (FOkToSpeak())
|
||||||
|
{
|
||||||
|
PlaySentence("OT_ATTACK", RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// BarneyFirePistol - shoots one round from the pistol at
|
||||||
|
// the enemy otis is facing.
|
||||||
|
//=========================================================
|
||||||
|
void CMOtis::BarneyFirePistol(void)
|
||||||
|
{
|
||||||
|
Vector vecShootOrigin;
|
||||||
|
|
||||||
|
UTIL_MakeVectors(pev->angles);
|
||||||
|
vecShootOrigin = pev->origin + Vector(0, 0, 55);
|
||||||
|
Vector vecShootDir = ShootAtEnemy(vecShootOrigin);
|
||||||
|
|
||||||
|
Vector angDir = UTIL_VecToAngles(vecShootDir);
|
||||||
|
SetBlending(0, angDir.x);
|
||||||
|
pev->effects = EF_MUZZLEFLASH;
|
||||||
|
|
||||||
|
FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024, BULLET_MONSTER_357);
|
||||||
|
|
||||||
|
int pitchShift = RANDOM_LONG(0, 20);
|
||||||
|
|
||||||
|
// Only shift about half the time
|
||||||
|
if (pitchShift > 10)
|
||||||
|
pitchShift = 0;
|
||||||
|
else
|
||||||
|
pitchShift -= 5;
|
||||||
|
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "weapons/desert_eagle_fire.wav", 1, ATTN_NORM, 0, 100 + pitchShift);
|
||||||
|
|
||||||
|
// UNDONE: Reload?
|
||||||
|
m_cAmmoLoaded--;// take away a bullet!
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// HandleAnimEvent - catches the monster-specific messages
|
||||||
|
// that occur when tagged animation frames are played.
|
||||||
|
//
|
||||||
|
// Returns number of events handled, 0 if none.
|
||||||
|
//=========================================================
|
||||||
|
void CMOtis::HandleAnimEvent(MonsterEvent_t *pEvent)
|
||||||
|
{
|
||||||
|
switch (pEvent->event)
|
||||||
|
{
|
||||||
|
case OTIS_AE_SHOOT:
|
||||||
|
BarneyFirePistol();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OTIS_AE_DRAW:
|
||||||
|
// otis' bodygroup switches here so he can pull gun from holster
|
||||||
|
// pev->body = OTIS_BODY_GUNDRAWN;
|
||||||
|
SetBodygroup( GUN_GROUP, GUN_EAGLE );
|
||||||
|
m_fGunDrawn = TRUE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OTIS_AE_HOLSTER:
|
||||||
|
// change bodygroup to replace gun in holster
|
||||||
|
// pev->body = OTIS_BODY_GUNHOLSTERED;
|
||||||
|
SetBodygroup( GUN_GROUP, GUN_NONE );
|
||||||
|
m_fGunDrawn = FALSE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
CMBarney::HandleAnimEvent(pEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Spawn
|
||||||
|
//=========================================================
|
||||||
|
void CMOtis::Spawn()
|
||||||
|
{
|
||||||
|
Precache();
|
||||||
|
|
||||||
|
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;
|
||||||
|
pev->movetype = MOVETYPE_STEP;
|
||||||
|
m_bloodColor = BLOOD_COLOR_RED;
|
||||||
|
pev->health = gSkillData.otisHealth;
|
||||||
|
pev->view_ofs = Vector(0, 0, 50);// position of the eyes relative to monster's origin.
|
||||||
|
m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello
|
||||||
|
m_MonsterState = MONSTERSTATE_NONE;
|
||||||
|
|
||||||
|
pev->body = 0; // gun in holster
|
||||||
|
m_fGunDrawn = FALSE;
|
||||||
|
|
||||||
|
m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
|
||||||
|
|
||||||
|
// Make sure hands are white.
|
||||||
|
pev->skin = 0;
|
||||||
|
|
||||||
|
// Select a random head.
|
||||||
|
if (head == -1)
|
||||||
|
{
|
||||||
|
SetBodygroup(HEAD_GROUP, RANDOM_LONG(0, NUM_OTIS_HEADS - 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetBodygroup(HEAD_GROUP, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bodystate == -1)
|
||||||
|
{
|
||||||
|
SetBodygroup(GUN_GROUP, RANDOM_LONG(OTIS_BODY_GUNHOLSTERED, OTIS_BODY_GUNDRAWN)); // don't random donut
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetBodygroup(GUN_GROUP, bodystate);
|
||||||
|
}
|
||||||
|
|
||||||
|
MonsterInit();
|
||||||
|
|
||||||
|
pev->classname = MAKE_STRING( "monster_otis" );
|
||||||
|
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
|
||||||
|
{
|
||||||
|
// default name
|
||||||
|
m_szMonsterName = MAKE_STRING( "Otis" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Precache - precaches all resources this monster needs
|
||||||
|
//=========================================================
|
||||||
|
void CMOtis::Precache()
|
||||||
|
{
|
||||||
|
PRECACHE_MODEL("models/otis.mdl");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("weapons/desert_eagle_fire.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("barney/ba_pain1.wav");
|
||||||
|
PRECACHE_SOUND("barney/ba_pain2.wav");
|
||||||
|
PRECACHE_SOUND("barney/ba_pain3.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("barney/ba_die1.wav");
|
||||||
|
PRECACHE_SOUND("barney/ba_die2.wav");
|
||||||
|
PRECACHE_SOUND("barney/ba_die3.wav");
|
||||||
|
|
||||||
|
// every new otis must call this, otherwise
|
||||||
|
// when a level is loaded, nobody will talk (time is reset to 0)
|
||||||
|
TalkInit();
|
||||||
|
CMTalkMonster::Precache();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init talk data
|
||||||
|
void CMOtis::TalkInit()
|
||||||
|
{
|
||||||
|
CMTalkMonster::TalkInit();
|
||||||
|
|
||||||
|
// scientists speach group names (group names are in sentences.txt)
|
||||||
|
|
||||||
|
m_szGrp[TLK_ANSWER] = "OT_ANSWER";
|
||||||
|
m_szGrp[TLK_QUESTION] = "OT_QUESTION";
|
||||||
|
m_szGrp[TLK_IDLE] = "OT_IDLE";
|
||||||
|
m_szGrp[TLK_STARE] = "OT_STARE";
|
||||||
|
m_szGrp[TLK_USE] = "OT_OK";
|
||||||
|
m_szGrp[TLK_UNUSE] = "OT_WAIT";
|
||||||
|
m_szGrp[TLK_STOP] = "OT_STOP";
|
||||||
|
|
||||||
|
m_szGrp[TLK_NOSHOOT] = "OT_SCARED";
|
||||||
|
m_szGrp[TLK_HELLO] = "OT_HELLO";
|
||||||
|
|
||||||
|
m_szGrp[TLK_PLHURT1] = "!OT_CUREA";
|
||||||
|
m_szGrp[TLK_PLHURT2] = "!OT_CUREB";
|
||||||
|
m_szGrp[TLK_PLHURT3] = "!OT_CUREC";
|
||||||
|
|
||||||
|
m_szGrp[TLK_PHELLO] = NULL;
|
||||||
|
m_szGrp[TLK_PIDLE] = NULL;
|
||||||
|
m_szGrp[TLK_PQUESTION] = NULL;
|
||||||
|
|
||||||
|
m_szGrp[TLK_SMELL] = "OT_SMELL";
|
||||||
|
|
||||||
|
m_szGrp[TLK_WOUND] = "OT_WOUND";
|
||||||
|
m_szGrp[TLK_MORTAL] = "OT_MORTAL";
|
||||||
|
|
||||||
|
// get voice for head - just one otis voice for now
|
||||||
|
m_voicePitch = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int CMOtis::TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType)
|
||||||
|
{
|
||||||
|
// make sure friends talk about it if player hurts talkmonsters...
|
||||||
|
int ret = CMTalkMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType);
|
||||||
|
if (!IsAlive() || pev->deadflag == DEAD_DYING)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (m_MonsterState != MONSTERSTATE_PRONE && (pevAttacker->flags & FL_CLIENT))
|
||||||
|
{
|
||||||
|
// This is a heurstic to determine if the player intended to harm me
|
||||||
|
// If I have an enemy, we can't establish intent (may just be crossfire)
|
||||||
|
if ( ( m_hEnemy != NULL ) && UTIL_IsPlayer(m_hEnemy) )
|
||||||
|
{
|
||||||
|
Remember( bits_MEMORY_PROVOKED );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMOtis::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
|
||||||
|
{
|
||||||
|
switch (ptr->iHitgroup)
|
||||||
|
{
|
||||||
|
case HITGROUP_CHEST:
|
||||||
|
case HITGROUP_STOMACH:
|
||||||
|
if (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_BLAST))
|
||||||
|
{
|
||||||
|
flDamage = flDamage / 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 10: // Otis wears no helmet, so do not prevent taking headshot damage.
|
||||||
|
// always a head shot
|
||||||
|
ptr->iHitgroup = HITGROUP_HEAD;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CMTalkMonster::TraceAttack(pevAttacker, flDamage, vecDir, ptr, bitsDamageType);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CMOtis::Killed(entvars_t *pevAttacker, int iGib)
|
||||||
|
{
|
||||||
|
if (GetBodygroup(GUN_GROUP) != OTIS_BODY_GUNHOLSTERED)
|
||||||
|
{
|
||||||
|
// drop the gun!
|
||||||
|
SetBodygroup(GUN_GROUP, OTIS_BODY_GUNHOLSTERED);
|
||||||
|
}
|
||||||
|
|
||||||
|
CMTalkMonster::Killed(pevAttacker, iGib);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// AI Schedules Specific to this monster
|
||||||
|
//=========================================================
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// GetSchedule - Decides which type of schedule best suits
|
||||||
|
// the monster's current state and conditions. Then calls
|
||||||
|
// monster's member function to get a pointer to a schedule
|
||||||
|
// of the proper type.
|
||||||
|
//=========================================================
|
||||||
|
Schedule_t *CMOtis::GetSchedule(void)
|
||||||
|
{
|
||||||
|
if (HasConditions(bits_COND_ENEMY_DEAD) && FOkToSpeak())
|
||||||
|
{
|
||||||
|
PlaySentence("OT_KILL", 4, VOL_NORM, ATTN_NORM);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CMBarney::GetSchedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMOtis::KeyValue(KeyValueData *pkvd)
|
||||||
|
{
|
||||||
|
if (FStrEq(pkvd->szKeyName, "head"))
|
||||||
|
{
|
||||||
|
head = atoi(pkvd->szValue);
|
||||||
|
pkvd->fHandled = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
CMBarney::KeyValue(pkvd);
|
||||||
|
}
|
||||||
1064
src/dlls/pitdrone.cpp
Normal file
1064
src/dlls/pitdrone.cpp
Normal file
File diff suppressed because it is too large
Load Diff
423
src/dlls/rgrunt.cpp
Normal file
423
src/dlls/rgrunt.cpp
Normal file
@@ -0,0 +1,423 @@
|
|||||||
|
/***
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This source code contains proprietary and confidential information of
|
||||||
|
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||||
|
* persons who have executed a written SDK license with Valve. Any access,
|
||||||
|
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||||
|
*
|
||||||
|
****/
|
||||||
|
//=========================================================
|
||||||
|
// Robo Grunt
|
||||||
|
//=========================================================
|
||||||
|
|
||||||
|
#include "extdll.h"
|
||||||
|
#include "plane.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "cmbase.h"
|
||||||
|
#include "cmbasemonster.h"
|
||||||
|
#include "monsters.h"
|
||||||
|
#include "schedule.h"
|
||||||
|
#include "animation.h"
|
||||||
|
#include "weapons.h"
|
||||||
|
#include "cmtalkmonster.h"
|
||||||
|
#include "effects.h"
|
||||||
|
#include "explode.h"
|
||||||
|
#include "customentity.h"
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// monster-specific DEFINE's
|
||||||
|
//=========================================================
|
||||||
|
#define RGRUNT_CLIP_SIZE 36 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x!
|
||||||
|
|
||||||
|
// Weapon flags
|
||||||
|
#define RGRUNT_9MMAR (1 << 0)
|
||||||
|
#define RGRUNT_HANDGRENADE (1 << 1)
|
||||||
|
#define RGRUNT_GRENADELAUNCHER (1 << 2)
|
||||||
|
#define RGRUNT_SHOTGUN (1 << 3)
|
||||||
|
|
||||||
|
// Body groups
|
||||||
|
#define GUN_GROUP 2
|
||||||
|
|
||||||
|
// Gun values
|
||||||
|
#define GUN_MP5 0
|
||||||
|
#define GUN_SHOTGUN 1
|
||||||
|
#define GUN_NONE 2
|
||||||
|
|
||||||
|
// How many sparks to emit when low on health
|
||||||
|
#define RGRUNT_MAX_SPARKS 5
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// These sounds are muted for Robo Grunts
|
||||||
|
//=========================================================
|
||||||
|
BOOL CMRGrunt::FOkToSpeak(void)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMRGrunt::IdleSound(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMRGrunt::PainSound(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// DeathSound
|
||||||
|
//=========================================================
|
||||||
|
void CMRGrunt::DeathSound(void)
|
||||||
|
{
|
||||||
|
switch ( RANDOM_LONG(0,1) )
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
EMIT_SOUND( ENT(pev), CHAN_VOICE, "turret/tu_die.wav", 1, ATTN_IDLE );
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
EMIT_SOUND( ENT(pev), CHAN_VOICE, "turret/tu_die2.wav", 1, ATTN_IDLE );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use this spot to activate the explosion
|
||||||
|
int duration = RANDOM_LONG( 3, 9 );
|
||||||
|
|
||||||
|
/* Smoke effect */
|
||||||
|
// variable smoke balls
|
||||||
|
// 1.7X normal size
|
||||||
|
// 0 radial distribution
|
||||||
|
// instant start
|
||||||
|
SmokeCreate( pev->origin, duration * 7, 17, 0, 0 );
|
||||||
|
|
||||||
|
// "Gib"
|
||||||
|
pev->nextthink = gpGlobals->time + duration;
|
||||||
|
SetThink( &CMRGrunt::StartGib );
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Classify - indicates this monster's place in the
|
||||||
|
// relationship table.
|
||||||
|
//=========================================================
|
||||||
|
int CMRGrunt::Classify(void)
|
||||||
|
{
|
||||||
|
if ( m_iClassifyOverride == -1 ) // helper
|
||||||
|
return CLASS_NONE;
|
||||||
|
else if ( m_iClassifyOverride > 0 )
|
||||||
|
return m_iClassifyOverride; // override
|
||||||
|
|
||||||
|
return CLASS_HUMAN_MILITARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Killed - Explode a few seconds after death
|
||||||
|
//=========================================================
|
||||||
|
void CMRGrunt::Killed(entvars_t *pevAttacker, int iGib)
|
||||||
|
{
|
||||||
|
// Turn off electricity
|
||||||
|
if ( m_flActiveDischarge != 0 )
|
||||||
|
{
|
||||||
|
pev->renderfx = kRenderFxNone;
|
||||||
|
m_flActiveDischarge = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disallow this monster to fade away, need to keep it around for the explosion
|
||||||
|
pev->owner = 0;
|
||||||
|
pev->spawnflags &= ~SF_MONSTER_FADECORPSE;
|
||||||
|
pev->solid = SOLID_NOT; // stop interacting with the world
|
||||||
|
|
||||||
|
CMBaseMonster::Killed(pevAttacker, iGib);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMRGrunt::StartGib(void)
|
||||||
|
{
|
||||||
|
// derp
|
||||||
|
GibMonster();
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// GibMonster - Boom!
|
||||||
|
//=========================================================
|
||||||
|
void CMRGrunt::GibMonster()
|
||||||
|
{
|
||||||
|
// Don't call this more times than needed
|
||||||
|
if ( pev->iuser1 != 0 )
|
||||||
|
return;
|
||||||
|
pev->iuser1 = 1;
|
||||||
|
|
||||||
|
Vector vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5;
|
||||||
|
|
||||||
|
// Explosion
|
||||||
|
ExplosionCreate( vecSpot, g_vecZero, ENT(pev), 128, 0, 0 );
|
||||||
|
|
||||||
|
// Wreckage
|
||||||
|
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot );
|
||||||
|
WRITE_BYTE( TE_BREAKMODEL );
|
||||||
|
|
||||||
|
// position
|
||||||
|
WRITE_COORD( vecSpot.x );
|
||||||
|
WRITE_COORD( vecSpot.y );
|
||||||
|
WRITE_COORD( vecSpot.z );
|
||||||
|
|
||||||
|
// size
|
||||||
|
WRITE_COORD( 96 );
|
||||||
|
WRITE_COORD( 96 );
|
||||||
|
WRITE_COORD( 16 );
|
||||||
|
|
||||||
|
// velocity
|
||||||
|
WRITE_COORD( 0 );
|
||||||
|
WRITE_COORD( 0 );
|
||||||
|
WRITE_COORD( 30 );
|
||||||
|
|
||||||
|
// randomization
|
||||||
|
WRITE_BYTE( 15 );
|
||||||
|
|
||||||
|
// Model
|
||||||
|
WRITE_SHORT( m_iBodyGibs ); //model id#
|
||||||
|
|
||||||
|
// # of shards
|
||||||
|
WRITE_BYTE( 35 );
|
||||||
|
|
||||||
|
// duration
|
||||||
|
WRITE_BYTE( 100 );// 5.0 seconds
|
||||||
|
|
||||||
|
// flags
|
||||||
|
WRITE_BYTE( BREAK_METAL );
|
||||||
|
MESSAGE_END();
|
||||||
|
|
||||||
|
SetThink( &CMBaseEntity::SUB_Remove );
|
||||||
|
pev->nextthink = gpGlobals->time;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// RunAI - Robo Grunt emits sparks when its low on health.
|
||||||
|
//=========================================================
|
||||||
|
void CMRGrunt::RunAI(void)
|
||||||
|
{
|
||||||
|
CMBaseMonster::RunAI();
|
||||||
|
|
||||||
|
if ( pev->health <= ( pev->max_health / 10 ) ) // below 10% health
|
||||||
|
{
|
||||||
|
// Spark ON
|
||||||
|
if ( gpGlobals->time > m_flNextSpark )
|
||||||
|
{
|
||||||
|
// Code looks familiar? It's CBaseButton::DoSpark
|
||||||
|
for ( int spark_num = 0; spark_num < RGRUNT_MAX_SPARKS; spark_num++ )
|
||||||
|
{
|
||||||
|
Vector tmp = pev->origin + (pev->mins + pev->maxs) * 0.5; // grab center
|
||||||
|
tmp.x += RANDOM_FLOAT( -( pev->size.x / 2 ), pev->size.x / 2); // then randomize
|
||||||
|
tmp.y += RANDOM_FLOAT( -( pev->size.y / 2 ), pev->size.y / 2);
|
||||||
|
tmp.z += RANDOM_FLOAT( -( pev->size.z / 2 ), pev->size.z / 2);
|
||||||
|
UTIL_Sparks( tmp );
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ( (int)(RANDOM_FLOAT(0,1) * 6) )
|
||||||
|
{
|
||||||
|
case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark1.wav", 0.6, ATTN_NORM); break;
|
||||||
|
case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark2.wav", 0.6, ATTN_NORM); break;
|
||||||
|
case 2: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark3.wav", 0.6, ATTN_NORM); break;
|
||||||
|
case 3: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark4.wav", 0.6, ATTN_NORM); break;
|
||||||
|
case 4: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark5.wav", 0.6, ATTN_NORM); break;
|
||||||
|
case 5: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark6.wav", 0.6, ATTN_NORM); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_flNextSpark = gpGlobals->time + 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Glow/Hurt ON
|
||||||
|
if ( gpGlobals->time > m_flNextDischarge )
|
||||||
|
{
|
||||||
|
// Turn on the electric glow
|
||||||
|
pev->renderfx = kRenderFxGlowShell;
|
||||||
|
pev->rendercolor = Vector( 100, 150, 250 ); // r, g, b
|
||||||
|
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_BODY, "debris/beamstart14.wav", 0.8, ATTN_NORM);
|
||||||
|
|
||||||
|
// Sustain the electricity for this long
|
||||||
|
m_flActiveDischarge = gpGlobals->time + RANDOM_FLOAT( 0.3, 0.6 );
|
||||||
|
|
||||||
|
// Discharge again in...
|
||||||
|
m_flNextDischarge = gpGlobals->time + RANDOM_FLOAT( 0.9, 2.7 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Glow/Hurt OFF
|
||||||
|
if ( gpGlobals->time > m_flActiveDischarge )
|
||||||
|
{
|
||||||
|
// Turn off electricity
|
||||||
|
pev->renderfx = kRenderFxNone;
|
||||||
|
m_flActiveDischarge = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// SparkTouch - Hurt players who come too close to it
|
||||||
|
//=========================================================
|
||||||
|
void CMRGrunt::SparkTouch( edict_t *pOther )
|
||||||
|
{
|
||||||
|
// No electricity, no harm
|
||||||
|
if ( m_flActiveDischarge == 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Only affect players
|
||||||
|
if ( UTIL_IsPlayer( pOther ) )
|
||||||
|
{
|
||||||
|
// Because of Touch(), players are going to be hurt every server frame.
|
||||||
|
// Don't be bullshit like Sven Co-op, set the damage REALLY LOW.
|
||||||
|
UTIL_TakeDamage( pOther, pev, pev, 1, DMG_SHOCK );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// TraceAttack - Override for robo grunt
|
||||||
|
// Emit ricochet sparks if getting hurt from bullets
|
||||||
|
//=========================================================
|
||||||
|
void CMRGrunt::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
|
||||||
|
{
|
||||||
|
// Absorb damage and emit ricochet if bullets or melee attacks are used
|
||||||
|
if ( bitsDamageType & ( DMG_BULLET | DMG_SLASH | DMG_CLUB ) )
|
||||||
|
{
|
||||||
|
if ( RANDOM_LONG( 0, 100 ) < 20 )
|
||||||
|
{
|
||||||
|
UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT( 0.5, 1.5 ) );
|
||||||
|
// EMIT_SOUND_DYN( ENT(pev), CHAN_BODY, pRicSounds[ RANDOM_LONG(0,ARRAYSIZE(pRicSounds)-1) ], 1.0, ATTN_NORM, 0, PITCH_NORM );
|
||||||
|
}
|
||||||
|
flDamage *= (1.00f - gSkillData.rgruntArmor); // cut damage
|
||||||
|
}
|
||||||
|
// Lower protection against explosions
|
||||||
|
else if ( bitsDamageType & DMG_BLAST )
|
||||||
|
flDamage *= (1.00f - (gSkillData.rgruntArmor / 2.00f)); // 50% less protection
|
||||||
|
// No protection at all against other types of damages
|
||||||
|
|
||||||
|
CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// TakeDamage - Robo Grunts should not take cover as soon
|
||||||
|
// as they take damage.
|
||||||
|
//=========================================================
|
||||||
|
int CMRGrunt::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||||
|
{
|
||||||
|
return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Spawn
|
||||||
|
//=========================================================
|
||||||
|
void CMRGrunt::Spawn()
|
||||||
|
{
|
||||||
|
Precache();
|
||||||
|
|
||||||
|
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;
|
||||||
|
pev->movetype = MOVETYPE_STEP;
|
||||||
|
m_bloodColor = DONT_BLEED;
|
||||||
|
pev->effects = 0;
|
||||||
|
pev->health = gSkillData.rgruntHealth;
|
||||||
|
pev->max_health = pev->health; // to determine when sparks should be emitted
|
||||||
|
m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||||
|
m_MonsterState = MONSTERSTATE_NONE;
|
||||||
|
m_flNextGrenadeCheck = gpGlobals->time + 1;
|
||||||
|
m_flNextPainTime = gpGlobals->time;
|
||||||
|
m_flNextSpark = gpGlobals->time; // when to emit sparks again
|
||||||
|
m_flNextDischarge = gpGlobals->time; // when electric shell should activate
|
||||||
|
m_flActiveDischarge = 0; // how long to sustain the electricity
|
||||||
|
m_iSentence = -1;
|
||||||
|
|
||||||
|
//m_afCapability = bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
|
||||||
|
m_afCapability = bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
|
||||||
|
|
||||||
|
//m_fEnemyEluded = FALSE;
|
||||||
|
m_fFirstEncounter = TRUE;// this is true when the grunt spawns, because he hasn't encountered an enemy yet.
|
||||||
|
|
||||||
|
m_HackedGunPos = Vector(0, 0, 55);
|
||||||
|
|
||||||
|
if (pev->weapons == 0)
|
||||||
|
{
|
||||||
|
// weapons not specified, randomize
|
||||||
|
switch(RANDOM_LONG(0, 2))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
pev->weapons = RGRUNT_9MMAR | RGRUNT_HANDGRENADE;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
pev->weapons = RGRUNT_SHOTGUN;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
pev->weapons = RGRUNT_9MMAR | RGRUNT_GRENADELAUNCHER;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FBitSet(pev->weapons, RGRUNT_SHOTGUN))
|
||||||
|
{
|
||||||
|
SetBodygroup(GUN_GROUP, GUN_SHOTGUN);
|
||||||
|
m_cClipSize = 8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_cClipSize = RGRUNT_CLIP_SIZE;
|
||||||
|
}
|
||||||
|
m_cAmmoLoaded = m_cClipSize;
|
||||||
|
|
||||||
|
CMTalkMonster::g_talkWaitTime = 0;
|
||||||
|
|
||||||
|
MonsterInit();
|
||||||
|
|
||||||
|
SetTouch( &CMRGrunt::SparkTouch );
|
||||||
|
|
||||||
|
pev->classname = MAKE_STRING( "monster_robogrunt" );
|
||||||
|
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
|
||||||
|
{
|
||||||
|
// default name
|
||||||
|
m_szMonsterName = MAKE_STRING( "Robo Grunt" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Precache - precaches all resources this monster needs
|
||||||
|
//=========================================================
|
||||||
|
void CMRGrunt::Precache()
|
||||||
|
{
|
||||||
|
PRECACHE_MODEL("models/rgrunt.mdl");
|
||||||
|
|
||||||
|
m_iBodyGibs = PRECACHE_MODEL( "models/metalplategibs_green.mdl" );
|
||||||
|
|
||||||
|
PRECACHE_SOUND("hgrunt/gr_mgun1.wav");
|
||||||
|
PRECACHE_SOUND("hgrunt/gr_mgun2.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("turret/tu_die.wav");
|
||||||
|
PRECACHE_SOUND("turret/tu_die2.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("buttons/spark1.wav");
|
||||||
|
PRECACHE_SOUND("buttons/spark2.wav");
|
||||||
|
PRECACHE_SOUND("buttons/spark3.wav");
|
||||||
|
PRECACHE_SOUND("buttons/spark4.wav");
|
||||||
|
PRECACHE_SOUND("buttons/spark5.wav");
|
||||||
|
PRECACHE_SOUND("buttons/spark6.wav");
|
||||||
|
PRECACHE_SOUND("debris/beamstart14.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("hgrunt/gr_reload1.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("weapons/glauncher.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("weapons/sbarrel1.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event
|
||||||
|
|
||||||
|
/*
|
||||||
|
// get voice pitch
|
||||||
|
if (RANDOM_LONG(0, 1))
|
||||||
|
m_voicePitch = 109 + RANDOM_LONG(0, 7);
|
||||||
|
else
|
||||||
|
m_voicePitch = 100;
|
||||||
|
*/
|
||||||
|
|
||||||
|
m_iBrassShell = PRECACHE_MODEL("models/shell.mdl");// brass shell
|
||||||
|
}
|
||||||
@@ -596,7 +596,7 @@ void CMScientist :: Spawn( void )
|
|||||||
// when a level is loaded, nobody will talk (time is reset to 0)
|
// when a level is loaded, nobody will talk (time is reset to 0)
|
||||||
TalkInit();
|
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);
|
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
|
||||||
|
|
||||||
pev->solid = SOLID_SLIDEBOX;
|
pev->solid = SOLID_SLIDEBOX;
|
||||||
|
|||||||
233
src/dlls/shock.cpp
Normal file
233
src/dlls/shock.cpp
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository!
|
||||||
|
|
||||||
|
/***
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This source code contains proprietary and confidential information of
|
||||||
|
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||||
|
* persons who have executed a written SDK license with Valve. Any access,
|
||||||
|
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||||
|
*
|
||||||
|
****/
|
||||||
|
//=========================================================
|
||||||
|
// shock - projectile shot from shockrifles.
|
||||||
|
//=========================================================
|
||||||
|
|
||||||
|
#include "extdll.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "cmbase.h"
|
||||||
|
#include "cmbasemonster.h"
|
||||||
|
#include "monsters.h"
|
||||||
|
#include "schedule.h"
|
||||||
|
#include "effects.h"
|
||||||
|
#include "decals.h"
|
||||||
|
#include "weapons.h"
|
||||||
|
#include "customentity.h"
|
||||||
|
#include "shock.h"
|
||||||
|
|
||||||
|
|
||||||
|
void CMShock::Spawn()
|
||||||
|
{
|
||||||
|
Precache();
|
||||||
|
|
||||||
|
pev->movetype = MOVETYPE_FLY;
|
||||||
|
pev->solid = SOLID_BBOX;
|
||||||
|
pev->classname = MAKE_STRING("shock_beam");
|
||||||
|
SET_MODEL(ENT(pev), "models/shock_effect.mdl");
|
||||||
|
UTIL_SetOrigin(pev, pev->origin);
|
||||||
|
pev->dmg = gSkillData.monDmgShockroach;
|
||||||
|
UTIL_SetSize(pev, Vector(-4, -4, -4), Vector(4, 4, 4));
|
||||||
|
|
||||||
|
CreateEffects();
|
||||||
|
SetThink( &CMShock::FlyThink );
|
||||||
|
pev->nextthink = gpGlobals->time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMShock::Precache()
|
||||||
|
{
|
||||||
|
PRECACHE_MODEL("sprites/flare3.spr");
|
||||||
|
PRECACHE_MODEL("sprites/lgtning.spr");
|
||||||
|
PRECACHE_MODEL("models/shock_effect.mdl");
|
||||||
|
PRECACHE_SOUND("weapons/shock_impact.wav");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMShock::FlyThink()
|
||||||
|
{
|
||||||
|
if (pev->waterlevel == 3)
|
||||||
|
{
|
||||||
|
entvars_t *pevOwner = VARS(pev->owner);
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/shock_impact.wav", VOL_NORM, ATTN_NORM);
|
||||||
|
RadiusDamage(pev->origin, pev, pevOwner ? pevOwner : pev, pev->dmg * 3, 144, CLASS_NONE, DMG_SHOCK | DMG_ALWAYSGIB );
|
||||||
|
ClearEffects();
|
||||||
|
SetThink( &CMBaseEntity::SUB_Remove );
|
||||||
|
pev->nextthink = gpGlobals->time;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pev->nextthink = gpGlobals->time + 0.05;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edict_t *CMShock::Shoot(entvars_t *pevOwner, const Vector angles, const Vector vecStart, const Vector vecVelocity)
|
||||||
|
{
|
||||||
|
CMShock *pShock = CreateClassPtr((CMShock *)NULL);
|
||||||
|
|
||||||
|
if (pShock == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
UTIL_SetOrigin(pShock->pev, vecStart);
|
||||||
|
pShock->Spawn();
|
||||||
|
|
||||||
|
pShock->pev->velocity = vecVelocity;
|
||||||
|
pShock->pev->owner = ENT(pevOwner);
|
||||||
|
pShock->pev->angles = angles;
|
||||||
|
|
||||||
|
pShock->pev->nextthink = gpGlobals->time;
|
||||||
|
|
||||||
|
return pShock->edict();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMShock::Touch(edict_t *pOther)
|
||||||
|
{
|
||||||
|
// Do not collide with the owner.
|
||||||
|
if (pOther == pev->owner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TraceResult tr = UTIL_GetGlobalTrace( );
|
||||||
|
|
||||||
|
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
|
||||||
|
WRITE_BYTE(TE_DLIGHT);
|
||||||
|
WRITE_COORD(pev->origin.x); // X
|
||||||
|
WRITE_COORD(pev->origin.y); // Y
|
||||||
|
WRITE_COORD(pev->origin.z); // Z
|
||||||
|
WRITE_BYTE( 8 ); // radius * 0.1
|
||||||
|
WRITE_BYTE( 0 ); // r
|
||||||
|
WRITE_BYTE( 255 ); // g
|
||||||
|
WRITE_BYTE( 255 ); // b
|
||||||
|
WRITE_BYTE( 10 ); // time * 10
|
||||||
|
WRITE_BYTE( 10 ); // decay * 0.1
|
||||||
|
MESSAGE_END( );
|
||||||
|
|
||||||
|
ClearEffects();
|
||||||
|
if (!pOther->v.takedamage)
|
||||||
|
{
|
||||||
|
// make a splat on the wall
|
||||||
|
const int baseDecal = DECAL_SCORCH1;
|
||||||
|
UTIL_DecalTrace(&tr, baseDecal + RANDOM_LONG(0, 1));
|
||||||
|
|
||||||
|
int iContents = UTIL_PointContents(pev->origin);
|
||||||
|
|
||||||
|
// Create sparks
|
||||||
|
if (iContents != CONTENTS_WATER)
|
||||||
|
{
|
||||||
|
UTIL_Sparks(tr.vecEndPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int damageType = DMG_SHOCK;
|
||||||
|
ClearMultiDamage();
|
||||||
|
entvars_t *pevOwner = VARS(pev->owner);
|
||||||
|
entvars_t *pevAttacker = pevOwner ? pevOwner : pev;
|
||||||
|
|
||||||
|
if ( UTIL_IsPlayer( pOther ) )
|
||||||
|
UTIL_TraceAttack( pOther, pevAttacker, pev->dmg, pev->velocity.Normalize(), &tr, damageType );
|
||||||
|
else if ( pOther->v.euser4 != NULL )
|
||||||
|
{
|
||||||
|
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
|
||||||
|
pMonster->TraceAttack( pevAttacker, pev->dmg, pev->velocity.Normalize(), &tr, damageType );
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyMultiDamage(pev, pevAttacker);
|
||||||
|
}
|
||||||
|
|
||||||
|
// splat sound
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/shock_impact.wav", VOL_NORM, ATTN_NORM);
|
||||||
|
|
||||||
|
pev->modelindex = 0;
|
||||||
|
pev->solid = SOLID_NOT;
|
||||||
|
SetThink( &CMBaseEntity::SUB_Remove );
|
||||||
|
pev->nextthink = gpGlobals->time + 0.01; // let the sound play
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMShock::CreateEffects()
|
||||||
|
{
|
||||||
|
m_pSprite = CMSprite::SpriteCreate( "sprites/flare3.spr", pev->origin, FALSE );
|
||||||
|
m_pSprite->SetAttachment( edict(), 0 );
|
||||||
|
m_pSprite->pev->scale = 0.35;
|
||||||
|
m_pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 170, kRenderFxNoDissipation );
|
||||||
|
//m_pSprite->pev->spawnflags |= SF_SPRITE_TEMPORARY;
|
||||||
|
//m_pSprite->pev->flags |= FL_SKIPLOCALHOST;
|
||||||
|
|
||||||
|
m_pBeam = CMBeam::BeamCreate( "sprites/lgtning.spr", 30 );
|
||||||
|
|
||||||
|
if (m_pBeam)
|
||||||
|
{
|
||||||
|
m_pBeam->EntsInit( entindex(), entindex() );
|
||||||
|
m_pBeam->SetStartAttachment( 1 );
|
||||||
|
m_pBeam->SetEndAttachment( 2 );
|
||||||
|
m_pBeam->SetBrightness( 180 );
|
||||||
|
m_pBeam->SetScrollRate( 10 );
|
||||||
|
m_pBeam->SetNoise( 0 );
|
||||||
|
m_pBeam->SetFlags( BEAM_FSHADEOUT );
|
||||||
|
m_pBeam->SetColor( 0, 255, 255 );
|
||||||
|
//m_pBeam->pev->spawnflags = SF_BEAM_TEMPORARY;
|
||||||
|
m_pBeam->RelinkBeam();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ALERT(at_console, "Could not create shockbeam beam!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pNoise = CMBeam::BeamCreate( "sprites/lgtning.spr", 30 );
|
||||||
|
|
||||||
|
if (m_pNoise)
|
||||||
|
{
|
||||||
|
m_pNoise->EntsInit( entindex(), entindex() );
|
||||||
|
m_pNoise->SetStartAttachment( 1 );
|
||||||
|
m_pNoise->SetEndAttachment( 2 );
|
||||||
|
m_pNoise->SetBrightness( 180 );
|
||||||
|
m_pNoise->SetScrollRate( 30 );
|
||||||
|
m_pNoise->SetNoise( 30 );
|
||||||
|
m_pNoise->SetFlags( BEAM_FSHADEOUT );
|
||||||
|
m_pNoise->SetColor( 255, 255, 173 );
|
||||||
|
//m_pNoise->pev->spawnflags = SF_BEAM_TEMPORARY;
|
||||||
|
m_pNoise->RelinkBeam();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ALERT(at_console, "Could not create shockbeam noise!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMShock::ClearEffects()
|
||||||
|
{
|
||||||
|
if (m_pBeam)
|
||||||
|
{
|
||||||
|
UTIL_Remove( m_pBeam->edict() );
|
||||||
|
m_pBeam = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_pNoise)
|
||||||
|
{
|
||||||
|
UTIL_Remove( m_pNoise->edict() );
|
||||||
|
m_pNoise = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_pSprite)
|
||||||
|
{
|
||||||
|
UTIL_Remove( m_pSprite->edict() );
|
||||||
|
m_pSprite = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMShock::UpdateOnRemove()
|
||||||
|
{
|
||||||
|
CMBaseAnimating::UpdateOnRemove();
|
||||||
|
ClearEffects();
|
||||||
|
}
|
||||||
25
src/dlls/shock.h
Normal file
25
src/dlls/shock.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef SHOCKBEAM_H
|
||||||
|
#define SHOCKBEAM_H
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Shockrifle projectile
|
||||||
|
//=========================================================
|
||||||
|
class CMShock : public CMBaseAnimating
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Spawn(void);
|
||||||
|
void Precache(void);
|
||||||
|
|
||||||
|
static edict_t *Shoot(entvars_t *pevOwner, const Vector angles, const Vector vecStart, const Vector vecVelocity);
|
||||||
|
void Touch(edict_t *pOther);
|
||||||
|
void EXPORT FlyThink();
|
||||||
|
|
||||||
|
void CreateEffects();
|
||||||
|
void ClearEffects();
|
||||||
|
void UpdateOnRemove();
|
||||||
|
|
||||||
|
CMBeam *m_pBeam;
|
||||||
|
CMBeam *m_pNoise;
|
||||||
|
CMSprite *m_pSprite;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
223
src/dlls/shockroach.cpp
Normal file
223
src/dlls/shockroach.cpp
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository!
|
||||||
|
|
||||||
|
/***
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This source code contains proprietary and confidential information of
|
||||||
|
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||||
|
* persons who have executed a written SDK license with Valve. Any access,
|
||||||
|
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||||
|
*
|
||||||
|
****/
|
||||||
|
//=========================================================
|
||||||
|
// shockroach.cpp
|
||||||
|
//=========================================================
|
||||||
|
|
||||||
|
#include "extdll.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "cmbase.h"
|
||||||
|
#include "cmbasemonster.h"
|
||||||
|
#include "monsters.h"
|
||||||
|
#include "schedule.h"
|
||||||
|
#include "weapons.h"
|
||||||
|
|
||||||
|
const char *CMShockRoach::pIdleSounds[] =
|
||||||
|
{
|
||||||
|
"shockroach/shock_idle1.wav",
|
||||||
|
"shockroach/shock_idle2.wav",
|
||||||
|
"shockroach/shock_idle3.wav",
|
||||||
|
};
|
||||||
|
const char *CMShockRoach::pAlertSounds[] =
|
||||||
|
{
|
||||||
|
"shockroach/shock_angry.wav",
|
||||||
|
};
|
||||||
|
const char *CMShockRoach::pPainSounds[] =
|
||||||
|
{
|
||||||
|
"shockroach/shock_flinch.wav",
|
||||||
|
};
|
||||||
|
const char *CMShockRoach::pAttackSounds[] =
|
||||||
|
{
|
||||||
|
"shockroach/shock_jump1.wav",
|
||||||
|
"shockroach/shock_jump2.wav",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *CMShockRoach::pDeathSounds[] =
|
||||||
|
{
|
||||||
|
"shockroach/shock_die.wav",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *CMShockRoach::pBiteSounds[] =
|
||||||
|
{
|
||||||
|
"shockroach/shock_bite.wav",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Spawn
|
||||||
|
//=========================================================
|
||||||
|
void CMShockRoach::Spawn()
|
||||||
|
{
|
||||||
|
Precache();
|
||||||
|
|
||||||
|
SET_MODEL(ENT(pev), "models/w_shock_rifle.mdl");
|
||||||
|
UTIL_SetOrigin(pev, pev->origin);
|
||||||
|
|
||||||
|
pev->solid = SOLID_SLIDEBOX;
|
||||||
|
pev->movetype = MOVETYPE_FLY;
|
||||||
|
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||||
|
|
||||||
|
pev->effects = 0;
|
||||||
|
pev->health = gSkillData.roachHealth;
|
||||||
|
pev->view_ofs = Vector(0, 0, 20);// position of the eyes relative to monster's origin.
|
||||||
|
pev->yaw_speed = 5;//!!! should we put this in the monster's changeanim function since turn rates may vary with state/anim?
|
||||||
|
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||||
|
m_MonsterState = MONSTERSTATE_NONE;
|
||||||
|
|
||||||
|
m_fRoachSolid = 0;
|
||||||
|
m_flBirthTime = gpGlobals->time;
|
||||||
|
|
||||||
|
MonsterInit();
|
||||||
|
|
||||||
|
pev->classname = MAKE_STRING( "monster_shockroach" );
|
||||||
|
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
|
||||||
|
{
|
||||||
|
// default name
|
||||||
|
m_szMonsterName = MAKE_STRING( "Shock Roach" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Precache - precaches all resources this monster needs
|
||||||
|
//=========================================================
|
||||||
|
void CMShockRoach::Precache()
|
||||||
|
{
|
||||||
|
PRECACHE_SOUND_ARRAY(pIdleSounds);
|
||||||
|
PRECACHE_SOUND_ARRAY(pAlertSounds);
|
||||||
|
PRECACHE_SOUND_ARRAY(pPainSounds);
|
||||||
|
PRECACHE_SOUND_ARRAY(pAttackSounds);
|
||||||
|
PRECACHE_SOUND_ARRAY(pDeathSounds);
|
||||||
|
PRECACHE_SOUND_ARRAY(pBiteSounds);
|
||||||
|
|
||||||
|
PRECACHE_SOUND("shockroach/shock_walk.wav");
|
||||||
|
|
||||||
|
PRECACHE_MODEL("models/w_shock_rifle.mdl");
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// LeapTouch - this is the headcrab's touch function when it
|
||||||
|
// is in the air
|
||||||
|
//=========================================================
|
||||||
|
void CMShockRoach::LeapTouch(edict_t *pOther)
|
||||||
|
{
|
||||||
|
if (!pOther->v.takedamage)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't hit if back on ground
|
||||||
|
if (!FBitSet(pev->flags, FL_ONGROUND))
|
||||||
|
{
|
||||||
|
EMIT_SOUND_DYN(edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBiteSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
|
||||||
|
|
||||||
|
if (UTIL_IsPlayer(pOther))
|
||||||
|
UTIL_TakeDamage( pOther, pev, pev, GetDamageAmount(), DMG_SLASH );
|
||||||
|
else if (pOther->v.euser4 != NULL)
|
||||||
|
{
|
||||||
|
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
|
||||||
|
pMonster->TakeDamage( pev, pev, GetDamageAmount(), DMG_SLASH );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetTouch(NULL);
|
||||||
|
}
|
||||||
|
//=========================================================
|
||||||
|
// PrescheduleThink
|
||||||
|
//=========================================================
|
||||||
|
void CMShockRoach::MonsterThink(void)
|
||||||
|
{
|
||||||
|
float lifeTime = (gpGlobals->time - m_flBirthTime);
|
||||||
|
if (lifeTime >= 0.2)
|
||||||
|
{
|
||||||
|
pev->movetype = MOVETYPE_STEP;
|
||||||
|
}
|
||||||
|
if (!m_fRoachSolid && lifeTime >= 2.0 ) {
|
||||||
|
m_fRoachSolid = TRUE;
|
||||||
|
UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 4));
|
||||||
|
}
|
||||||
|
if (lifeTime >= gSkillData.roachLifespan)
|
||||||
|
{
|
||||||
|
pev->health = -1;
|
||||||
|
Killed(pev, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CMHeadCrab::MonsterThink();
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// IdleSound
|
||||||
|
//=========================================================
|
||||||
|
void CMShockRoach::IdleSound(void)
|
||||||
|
{
|
||||||
|
EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// AlertSound
|
||||||
|
//=========================================================
|
||||||
|
void CMShockRoach::AlertSound(void)
|
||||||
|
{
|
||||||
|
EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// AlertSound
|
||||||
|
//=========================================================
|
||||||
|
void CMShockRoach::PainSound(void)
|
||||||
|
{
|
||||||
|
EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// DeathSound
|
||||||
|
//=========================================================
|
||||||
|
void CMShockRoach::DeathSound(void)
|
||||||
|
{
|
||||||
|
EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CMShockRoach::StartTask(Task_t *pTask)
|
||||||
|
{
|
||||||
|
m_iTaskStatus = TASKSTATUS_RUNNING;
|
||||||
|
|
||||||
|
switch (pTask->iTask)
|
||||||
|
{
|
||||||
|
case TASK_RANGE_ATTACK1:
|
||||||
|
{
|
||||||
|
m_IdealActivity = ACT_RANGE_ATTACK1;
|
||||||
|
SetTouch(&CMShockRoach::LeapTouch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
CMHeadCrab::StartTask(pTask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int CMShockRoach::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||||
|
{
|
||||||
|
if ( gpGlobals->time - m_flBirthTime < 2.0 )
|
||||||
|
flDamage = 0.0;
|
||||||
|
// Skip headcrab's TakeDamage to avoid unwanted immunity to acid.
|
||||||
|
return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMShockRoach::AttackSound()
|
||||||
|
{
|
||||||
|
EMIT_SOUND_DYN(edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
|
||||||
|
}
|
||||||
@@ -72,11 +72,47 @@ skill_cfg_t skill_cfg[] = {
|
|||||||
{"sk_turret_health", &gSkillData.turretHealth},
|
{"sk_turret_health", &gSkillData.turretHealth},
|
||||||
{"sk_miniturret_health", &gSkillData.miniturretHealth},
|
{"sk_miniturret_health", &gSkillData.miniturretHealth},
|
||||||
{"sk_sentry_health", &gSkillData.sentryHealth},
|
{"sk_sentry_health", &gSkillData.sentryHealth},
|
||||||
|
{"sk_gonome_health", &gSkillData.gonomeHealth},
|
||||||
|
{"sk_gonome_dmg_guts", &gSkillData.gonomeDmgGuts},
|
||||||
|
{"sk_gonome_dmg_one_slash", &gSkillData.gonomeDmgOneSlash},
|
||||||
|
{"sk_gonome_dmg_one_bite", &gSkillData.gonomeDmgOneBite},
|
||||||
|
{"sk_massassin_health", &gSkillData.massnHealth},
|
||||||
|
{"sk_massassin_kick", &gSkillData.massnDmgKick},
|
||||||
|
{"sk_otis_health", &gSkillData.otisHealth},
|
||||||
|
{"sk_pitdrone_health", &gSkillData.pitdroneHealth},
|
||||||
|
{"sk_pitdrone_dmg_spit", &gSkillData.pitdroneDmgSpit},
|
||||||
|
{"sk_pitdrone_dmg_whip", &gSkillData.pitdroneDmgWhip},
|
||||||
|
{"sk_pitdrone_dmg_bite", &gSkillData.pitdroneDmgBite},
|
||||||
|
{"sk_shockroach_health", &gSkillData.roachHealth},
|
||||||
|
{"sk_shockroach_lifespan", &gSkillData.roachLifespan},
|
||||||
|
{"sk_shocktrooper_health", &gSkillData.strooperHealth},
|
||||||
|
{"sk_shocktrooper_kick", &gSkillData.strooperDmgKick},
|
||||||
|
{"sk_shocktrooper_maxcharge", &gSkillData.strooperMaxCharge},
|
||||||
|
{"sk_shocktrooper_rchgspeed", &gSkillData.strooperRchgSpeed},
|
||||||
|
{"sk_voltigore_health", &gSkillData.voltigoreHealth},
|
||||||
|
{"sk_voltigore_dmg_beam", &gSkillData.voltigoreDmgBeam},
|
||||||
|
{"sk_voltigore_dmg_punch", &gSkillData.voltigoreDmgPunch},
|
||||||
|
{"sk_babyvoltigore_health", &gSkillData.babyVoltigoreHealth},
|
||||||
|
{"sk_babyvoltigore_dmg_punch", &gSkillData.babyVoltigoreDmgPunch},
|
||||||
|
{"sk_babygarg_health", &gSkillData.babygargHealth},
|
||||||
|
{"sk_babygarg_dmg_slash", &gSkillData.babygargDmgSlash},
|
||||||
|
{"sk_babygarg_dmg_fire", &gSkillData.babygargDmgFire},
|
||||||
|
{"sk_babygarg_dmg_stomp", &gSkillData.babygargDmgStomp},
|
||||||
|
{"sk_hwgrunt_health", &gSkillData.hwgruntHealth},
|
||||||
|
{"sk_rgrunt_health", &gSkillData.rgruntHealth},
|
||||||
|
{"sk_rgrunt_armor", &gSkillData.rgruntArmor},
|
||||||
|
{"sk_stukabat_health", &gSkillData.stukabatHealth},
|
||||||
|
{"sk_stukabat_dmg_bite", &gSkillData.stukabatDmgBite},
|
||||||
|
{"sk_stukabat_speed", &gSkillData.stukabatSpeed},
|
||||||
{"sk_12mm_bullet", &gSkillData.monDmg9MM},
|
{"sk_12mm_bullet", &gSkillData.monDmg9MM},
|
||||||
{"sk_9mmAR_bullet", &gSkillData.monDmgMP5},
|
{"sk_9mmAR_bullet", &gSkillData.monDmgMP5},
|
||||||
{"sk_9mm_bullet", &gSkillData.monDmg12MM},
|
{"sk_9mm_bullet", &gSkillData.monDmg12MM},
|
||||||
{"sk_9mmAR_grenade", &gSkillData.monDmgM203Grenade},
|
{"sk_9mmAR_grenade", &gSkillData.monDmgM203Grenade},
|
||||||
|
{"sk_762_bullet", &gSkillData.monDmg762},
|
||||||
|
{"sk_357_bullet", &gSkillData.monDmg357},
|
||||||
{"sk_hornet_dmg", &gSkillData.monDmgHornet},
|
{"sk_hornet_dmg", &gSkillData.monDmgHornet},
|
||||||
|
{"sk_shock_dmg", &gSkillData.monDmgShockroach},
|
||||||
|
{"sk_spore_dmg", &gSkillData.monDmgSpore},
|
||||||
{"", NULL}
|
{"", NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -212,15 +248,78 @@ void monster_skill_init(void)
|
|||||||
// Sentry
|
// Sentry
|
||||||
gSkillData.sentryHealth = 40.0f;
|
gSkillData.sentryHealth = 40.0f;
|
||||||
|
|
||||||
|
// Gonome
|
||||||
|
gSkillData.gonomeHealth = 85.0f;
|
||||||
|
gSkillData.gonomeDmgGuts = 10.0f;
|
||||||
|
gSkillData.gonomeDmgOneSlash = 20.0f;
|
||||||
|
gSkillData.gonomeDmgOneBite = 14.0f;
|
||||||
|
|
||||||
|
// Male Assassin
|
||||||
|
gSkillData.massnHealth = 50.0f;
|
||||||
|
gSkillData.massnDmgKick = 25.0f;
|
||||||
|
|
||||||
|
// Otis
|
||||||
|
gSkillData.otisHealth = 35.0f;
|
||||||
|
|
||||||
|
// Pit Drone
|
||||||
|
gSkillData.pitdroneHealth = 40.0f;
|
||||||
|
gSkillData.pitdroneDmgSpit = 10.0f;
|
||||||
|
gSkillData.pitdroneDmgWhip = 35.0f;
|
||||||
|
gSkillData.pitdroneDmgBite = 25.0f;
|
||||||
|
|
||||||
|
// Shock Roach
|
||||||
|
gSkillData.roachHealth = 10.0f;
|
||||||
|
gSkillData.roachLifespan = 10.0f;
|
||||||
|
|
||||||
|
// Shock Trooper
|
||||||
|
gSkillData.strooperHealth = 50.0f;
|
||||||
|
gSkillData.strooperDmgKick = 10.0f;
|
||||||
|
gSkillData.strooperMaxCharge = 8.0f;
|
||||||
|
gSkillData.strooperRchgSpeed = 1.0f;
|
||||||
|
|
||||||
|
// Voltigore
|
||||||
|
gSkillData.voltigoreHealth = 320.0f;
|
||||||
|
gSkillData.voltigoreDmgBeam = 50.0f;
|
||||||
|
gSkillData.voltigoreDmgPunch = 40.0f;
|
||||||
|
|
||||||
|
// Baby Voltigore
|
||||||
|
gSkillData.babyVoltigoreHealth = 60.0f;
|
||||||
|
gSkillData.babyVoltigoreDmgPunch = 15.0f;
|
||||||
|
|
||||||
|
// Baby Gargantua
|
||||||
|
gSkillData.babygargHealth = 640.0f;
|
||||||
|
gSkillData.babygargDmgSlash = 24.0f;
|
||||||
|
gSkillData.babygargDmgFire = 4.0f;
|
||||||
|
gSkillData.babygargDmgStomp = 80.0f;
|
||||||
|
|
||||||
|
// Heavy Weapons Grunt
|
||||||
|
gSkillData.hwgruntHealth = 60.0f;
|
||||||
|
|
||||||
|
// Robo Grunt
|
||||||
|
gSkillData.rgruntHealth = 50.0f;
|
||||||
|
gSkillData.rgruntArmor = 0.75f;
|
||||||
|
|
||||||
|
// Stukabat
|
||||||
|
gSkillData.stukabatHealth = 80.0f;
|
||||||
|
gSkillData.stukabatDmgBite = 12.0f;
|
||||||
|
gSkillData.stukabatSpeed = 400.0f;
|
||||||
|
|
||||||
// MONSTER WEAPONS
|
// MONSTER WEAPONS
|
||||||
gSkillData.monDmg9MM = 5.0f;
|
gSkillData.monDmg9MM = 5.0f;
|
||||||
gSkillData.monDmgMP5 = 4.0f;
|
gSkillData.monDmgMP5 = 4.0f;
|
||||||
gSkillData.monDmg12MM = 10.0f;
|
gSkillData.monDmg12MM = 10.0f;
|
||||||
gSkillData.monDmgM203Grenade = 100.0f;
|
gSkillData.monDmgM203Grenade = 100.0f;
|
||||||
|
gSkillData.monDmg762 = 100.0f;
|
||||||
|
gSkillData.monDmg357 = 40.0f;
|
||||||
|
|
||||||
// HORNET
|
// HORNET
|
||||||
gSkillData.monDmgHornet = 5.0f;
|
gSkillData.monDmgHornet = 5.0f;
|
||||||
|
|
||||||
|
// SHOCK ROACH
|
||||||
|
gSkillData.monDmgShockroach = 15.0f;
|
||||||
|
|
||||||
|
// SPORE GRENADE
|
||||||
|
gSkillData.monDmgSpore = 50.0f;
|
||||||
|
|
||||||
// find the directory name of the currently running MOD...
|
// find the directory name of the currently running MOD...
|
||||||
(*g_engfuncs.pfnGetGameDir)(game_dir);
|
(*g_engfuncs.pfnGetGameDir)(game_dir);
|
||||||
|
|||||||
@@ -86,13 +86,65 @@ struct skilldata_t
|
|||||||
float miniturretHealth;
|
float miniturretHealth;
|
||||||
float sentryHealth;
|
float sentryHealth;
|
||||||
|
|
||||||
|
|
||||||
|
//OP4 monsters
|
||||||
|
float gonomeHealth;
|
||||||
|
float gonomeDmgGuts;
|
||||||
|
float gonomeDmgOneSlash;
|
||||||
|
float gonomeDmgOneBite;
|
||||||
|
|
||||||
|
float massnHealth;
|
||||||
|
float massnDmgKick;
|
||||||
|
|
||||||
|
float otisHealth;
|
||||||
|
|
||||||
|
float pitdroneHealth;
|
||||||
|
float pitdroneDmgSpit;
|
||||||
|
float pitdroneDmgWhip;
|
||||||
|
float pitdroneDmgBite;
|
||||||
|
|
||||||
|
float roachHealth;
|
||||||
|
float roachLifespan;
|
||||||
|
|
||||||
|
float strooperHealth;
|
||||||
|
float strooperDmgKick;
|
||||||
|
float strooperMaxCharge;
|
||||||
|
float strooperRchgSpeed;
|
||||||
|
|
||||||
|
float voltigoreHealth;
|
||||||
|
float voltigoreDmgBeam;
|
||||||
|
float voltigoreDmgPunch;
|
||||||
|
|
||||||
|
float babyVoltigoreHealth;
|
||||||
|
float babyVoltigoreDmgPunch;
|
||||||
|
|
||||||
|
|
||||||
|
//SC monsters
|
||||||
|
float babygargHealth;
|
||||||
|
float babygargDmgSlash;
|
||||||
|
float babygargDmgFire;
|
||||||
|
float babygargDmgStomp;
|
||||||
|
|
||||||
|
float hwgruntHealth;
|
||||||
|
|
||||||
|
float rgruntHealth;
|
||||||
|
float rgruntArmor;
|
||||||
|
|
||||||
|
float stukabatHealth;
|
||||||
|
float stukabatDmgBite;
|
||||||
|
float stukabatSpeed;
|
||||||
|
|
||||||
// weapons shared by monsters
|
// weapons shared by monsters
|
||||||
float monDmg9MM;
|
float monDmg9MM;
|
||||||
float monDmgMP5;
|
float monDmgMP5;
|
||||||
float monDmg12MM;
|
float monDmg12MM;
|
||||||
float monDmgM203Grenade;
|
float monDmgM203Grenade;
|
||||||
|
float monDmg762;
|
||||||
|
float monDmg357;
|
||||||
|
|
||||||
float monDmgHornet;
|
float monDmgHornet;
|
||||||
|
float monDmgShockroach;
|
||||||
|
float monDmgSpore;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern DLL_GLOBAL skilldata_t gSkillData;
|
extern DLL_GLOBAL skilldata_t gSkillData;
|
||||||
|
|||||||
345
src/dlls/sporegrenade.cpp
Normal file
345
src/dlls/sporegrenade.cpp
Normal file
@@ -0,0 +1,345 @@
|
|||||||
|
// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository!
|
||||||
|
|
||||||
|
/***
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
****/
|
||||||
|
|
||||||
|
|
||||||
|
#include "extdll.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "cmbase.h"
|
||||||
|
#include "cmbasemonster.h"
|
||||||
|
#include "monsters.h"
|
||||||
|
#include "weapons.h"
|
||||||
|
#include "decals.h"
|
||||||
|
#include "explode.h"
|
||||||
|
|
||||||
|
int gSporeExplode, gSporeExplodeC;
|
||||||
|
|
||||||
|
void CMSporeGrenade::Precache()
|
||||||
|
{
|
||||||
|
PRECACHE_MODEL("models/spore.mdl");
|
||||||
|
PRECACHE_MODEL("sprites/glow02.spr");
|
||||||
|
g_sModelIndexTinySpit = PRECACHE_MODEL("sprites/tinyspit.spr");
|
||||||
|
gSporeExplode = PRECACHE_MODEL("sprites/spore_exp_01.spr");
|
||||||
|
gSporeExplodeC = PRECACHE_MODEL("sprites/spore_exp_c_01.spr");
|
||||||
|
PRECACHE_SOUND("weapons/splauncher_bounce.wav");
|
||||||
|
PRECACHE_SOUND("weapons/splauncher_impact.wav");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMSporeGrenade::Explode(TraceResult *pTrace)
|
||||||
|
{
|
||||||
|
pev->solid = SOLID_NOT;// intangible
|
||||||
|
pev->takedamage = DAMAGE_NO;
|
||||||
|
|
||||||
|
// Pull out of the wall a bit
|
||||||
|
if (pTrace->flFraction != 1.0)
|
||||||
|
{
|
||||||
|
pev->origin = pTrace->vecEndPos + (pTrace->vecPlaneNormal * (pev->dmg - 24) * 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector vecSpraySpot = pTrace->vecEndPos;
|
||||||
|
float flSpraySpeed = RANDOM_LONG(10, 15);
|
||||||
|
|
||||||
|
// If the trace is pointing up, then place
|
||||||
|
// spawn position a few units higher.
|
||||||
|
if (pTrace->vecPlaneNormal.z > 0)
|
||||||
|
{
|
||||||
|
vecSpraySpot = vecSpraySpot + (pTrace->vecPlaneNormal * 8);
|
||||||
|
flSpraySpeed *= 2; // Double the speed to make them fly higher
|
||||||
|
// in the air.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn small particles at the explosion origin.
|
||||||
|
SpawnExplosionParticles(
|
||||||
|
vecSpraySpot, // position
|
||||||
|
pTrace->vecPlaneNormal, // direction
|
||||||
|
g_sModelIndexTinySpit, // modelindex
|
||||||
|
RANDOM_LONG(40, 50), // count
|
||||||
|
flSpraySpeed, // speed
|
||||||
|
RANDOM_FLOAT(600, 640)); // noise
|
||||||
|
|
||||||
|
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin );
|
||||||
|
WRITE_BYTE( TE_SPRITE );
|
||||||
|
WRITE_COORD( pev->origin.x );
|
||||||
|
WRITE_COORD( pev->origin.y );
|
||||||
|
WRITE_COORD( pev->origin.z );
|
||||||
|
WRITE_SHORT( RANDOM_LONG( 0, 1 ) ? gSporeExplode : gSporeExplodeC );
|
||||||
|
WRITE_BYTE( 25 ); // scale * 10
|
||||||
|
WRITE_BYTE( 155 ); // framerate
|
||||||
|
MESSAGE_END();
|
||||||
|
|
||||||
|
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
|
||||||
|
WRITE_BYTE(TE_DLIGHT);
|
||||||
|
WRITE_COORD( pev->origin.x ); // X
|
||||||
|
WRITE_COORD( pev->origin.y ); // Y
|
||||||
|
WRITE_COORD( pev->origin.z ); // Z
|
||||||
|
WRITE_BYTE( 12 ); // radius * 0.1
|
||||||
|
WRITE_BYTE( 0 ); // r
|
||||||
|
WRITE_BYTE( 180 ); // g
|
||||||
|
WRITE_BYTE( 0 ); // b
|
||||||
|
WRITE_BYTE( 20 ); // time * 10
|
||||||
|
WRITE_BYTE( 20 ); // decay * 0.1
|
||||||
|
MESSAGE_END( );
|
||||||
|
|
||||||
|
// Play explode sound.
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/splauncher_impact.wav", 1, ATTN_NORM);
|
||||||
|
|
||||||
|
entvars_t *pevOwner;
|
||||||
|
if (pev->owner)
|
||||||
|
pevOwner = VARS(pev->owner);
|
||||||
|
else
|
||||||
|
pevOwner = NULL;
|
||||||
|
|
||||||
|
pev->owner = NULL; // can't traceline attack owner if this is set
|
||||||
|
|
||||||
|
RadiusDamage(pev, pevOwner, pev->dmg, CLASS_NONE, DMG_BLAST);
|
||||||
|
|
||||||
|
// Place a decal on the surface that was hit.
|
||||||
|
UTIL_DecalTrace(pTrace, DECAL_SPIT1 + RANDOM_LONG(0, 1));
|
||||||
|
|
||||||
|
UpdateOnRemove();
|
||||||
|
UTIL_Remove( this->edict() );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMSporeGrenade::Detonate()
|
||||||
|
{
|
||||||
|
TraceResult tr;
|
||||||
|
Vector vecSpot = pev->origin + Vector(0, 0, 8);
|
||||||
|
UTIL_TraceLine(vecSpot, vecSpot + Vector(0, 0, -40), ignore_monsters, ENT(pev), &tr);
|
||||||
|
|
||||||
|
Explode(&tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CMSporeGrenade::BounceSound()
|
||||||
|
{
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/splauncher_bounce.wav", 0.25, ATTN_NORM);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMSporeGrenade::TumbleThink()
|
||||||
|
{
|
||||||
|
if (!IsInWorld())
|
||||||
|
{
|
||||||
|
UpdateOnRemove();
|
||||||
|
UTIL_Remove( this->edict() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pev->nextthink = gpGlobals->time + 0.1;
|
||||||
|
|
||||||
|
if (pev->dmgtime <= gpGlobals->time)
|
||||||
|
{
|
||||||
|
SetThink(&CMSporeGrenade::Detonate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn particles.
|
||||||
|
SpawnTrailParticles(
|
||||||
|
pev->origin, // position
|
||||||
|
-pev->velocity.Normalize(), // dir
|
||||||
|
g_sModelIndexTinySpit, // modelindex
|
||||||
|
RANDOM_LONG( 2, 4 ), // count
|
||||||
|
RANDOM_FLOAT(10, 15), // speed
|
||||||
|
RANDOM_FLOAT(2, 3) * 100); // noise ( client will divide by 100 )
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Contact grenade, explode when it touches something
|
||||||
|
//
|
||||||
|
void CMSporeGrenade::ExplodeTouch(edict_t *pOther)
|
||||||
|
{
|
||||||
|
TraceResult tr;
|
||||||
|
Vector vecSpot;// trace starts here!
|
||||||
|
|
||||||
|
pev->enemy = pOther;
|
||||||
|
|
||||||
|
vecSpot = pev->origin - pev->velocity.Normalize() * 32;
|
||||||
|
UTIL_TraceLine(vecSpot, vecSpot + pev->velocity.Normalize() * 64, ignore_monsters, ENT(pev), &tr);
|
||||||
|
|
||||||
|
Explode(&tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMSporeGrenade::DangerSoundThink()
|
||||||
|
{
|
||||||
|
if (!IsInWorld())
|
||||||
|
{
|
||||||
|
UpdateOnRemove();
|
||||||
|
UTIL_Remove( this->edict() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pev->nextthink = gpGlobals->time + 0.2;
|
||||||
|
|
||||||
|
// Spawn particles.
|
||||||
|
SpawnTrailParticles(
|
||||||
|
pev->origin, // position
|
||||||
|
-pev->velocity.Normalize(), // dir
|
||||||
|
g_sModelIndexTinySpit, // modelindex
|
||||||
|
RANDOM_LONG( 5, 10), // count
|
||||||
|
RANDOM_FLOAT(10, 15), // speed
|
||||||
|
RANDOM_FLOAT(2, 3) * 100); // noise ( client will divide by 100 )
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMSporeGrenade::BounceTouch(edict_t *pOther)
|
||||||
|
{
|
||||||
|
if ( !pOther->v.takedamage )
|
||||||
|
{
|
||||||
|
if (!(pev->flags & FL_ONGROUND)) {
|
||||||
|
if (pev->dmg_save < gpGlobals->time) {
|
||||||
|
BounceSound();
|
||||||
|
pev->dmg_save = gpGlobals->time + 0.1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pev->velocity = pev->velocity * 0.9;
|
||||||
|
}
|
||||||
|
if (pev->flags & FL_SWIM)
|
||||||
|
{
|
||||||
|
pev->velocity = pev->velocity * 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TraceResult tr = UTIL_GetGlobalTrace();
|
||||||
|
Explode(&tr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMSporeGrenade::Spawn()
|
||||||
|
{
|
||||||
|
Precache();
|
||||||
|
pev->classname = MAKE_STRING("spore");
|
||||||
|
pev->movetype = MOVETYPE_BOUNCE;
|
||||||
|
|
||||||
|
pev->solid = SOLID_BBOX;
|
||||||
|
|
||||||
|
SET_MODEL(ENT(pev), "models/spore.mdl");
|
||||||
|
UTIL_SetSize(pev, Vector(0, 0, 0), Vector(0, 0, 0));
|
||||||
|
|
||||||
|
//pev->gravity = 0.5;
|
||||||
|
|
||||||
|
pev->dmg = gSkillData.monDmgSpore;
|
||||||
|
|
||||||
|
m_pSporeGlow = CMSprite::SpriteCreate("sprites/glow02.spr", pev->origin, FALSE);
|
||||||
|
|
||||||
|
if (m_pSporeGlow)
|
||||||
|
{
|
||||||
|
m_pSporeGlow->SetTransparency(kRenderGlow, 150, 158, 19, 155, kRenderFxNoDissipation);
|
||||||
|
m_pSporeGlow->SetAttachment(edict(), 0);
|
||||||
|
m_pSporeGlow->SetScale(.75f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CMSporeGrenade* CMSporeGrenade::ShootTimed(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, bool ai)
|
||||||
|
{
|
||||||
|
CMSporeGrenade *pGrenade = CreateClassPtr((CMSporeGrenade *)NULL);
|
||||||
|
|
||||||
|
if (pGrenade == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
UTIL_SetOrigin(pGrenade->pev, vecStart);
|
||||||
|
pGrenade->Spawn();
|
||||||
|
pGrenade->pev->velocity = vecVelocity;
|
||||||
|
pGrenade->pev->angles = UTIL_VecToAngles(pGrenade->pev->velocity);
|
||||||
|
pGrenade->pev->owner = ENT(pevOwner);
|
||||||
|
|
||||||
|
pGrenade->SetTouch(&CMSporeGrenade::BounceTouch); // Bounce if touched
|
||||||
|
|
||||||
|
float lifetime = 2.0;
|
||||||
|
if (ai) {
|
||||||
|
lifetime = 4.0;
|
||||||
|
pGrenade->pev->gravity = 0.5;
|
||||||
|
pGrenade->pev->friction = 0.9;
|
||||||
|
}
|
||||||
|
pGrenade->pev->dmgtime = gpGlobals->time + lifetime;
|
||||||
|
pGrenade->SetThink(&CMSporeGrenade::TumbleThink);
|
||||||
|
pGrenade->pev->nextthink = gpGlobals->time + 0.1;
|
||||||
|
if (lifetime < 0.1)
|
||||||
|
{
|
||||||
|
pGrenade->pev->nextthink = gpGlobals->time;
|
||||||
|
pGrenade->pev->velocity = Vector(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pGrenade;
|
||||||
|
}
|
||||||
|
|
||||||
|
CMSporeGrenade *CMSporeGrenade::ShootContact(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity)
|
||||||
|
{
|
||||||
|
CMSporeGrenade *pGrenade = CreateClassPtr((CMSporeGrenade *)NULL);
|
||||||
|
|
||||||
|
if (pGrenade == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
UTIL_SetOrigin(pGrenade->pev, vecStart);
|
||||||
|
pGrenade->Spawn();
|
||||||
|
pGrenade->pev->movetype = MOVETYPE_FLY;
|
||||||
|
pGrenade->pev->velocity = vecVelocity;
|
||||||
|
pGrenade->pev->angles = UTIL_VecToAngles(pGrenade->pev->velocity);
|
||||||
|
pGrenade->pev->owner = ENT(pevOwner);
|
||||||
|
|
||||||
|
// make monsters afraid of it while in the air
|
||||||
|
pGrenade->SetThink(&CMSporeGrenade::DangerSoundThink);
|
||||||
|
pGrenade->pev->nextthink = gpGlobals->time;
|
||||||
|
|
||||||
|
// Explode on contact
|
||||||
|
pGrenade->SetTouch(&CMSporeGrenade::ExplodeTouch);
|
||||||
|
|
||||||
|
pGrenade->pev->gravity = 0.5;
|
||||||
|
pGrenade->pev->friction = 0.7;
|
||||||
|
|
||||||
|
return pGrenade;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMSporeGrenade::SpawnTrailParticles(const Vector& origin, const Vector& direction, int modelindex, int count, float speed, float noise)
|
||||||
|
{
|
||||||
|
MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, origin);
|
||||||
|
WRITE_BYTE(TE_SPRITE_SPRAY);
|
||||||
|
WRITE_COORD(origin.x); // pos
|
||||||
|
WRITE_COORD(origin.y);
|
||||||
|
WRITE_COORD(origin.z);
|
||||||
|
WRITE_COORD(direction.x); // dir
|
||||||
|
WRITE_COORD(direction.y);
|
||||||
|
WRITE_COORD(direction.z);
|
||||||
|
WRITE_SHORT(modelindex); // model
|
||||||
|
WRITE_BYTE(count); // count
|
||||||
|
WRITE_BYTE(speed); // speed
|
||||||
|
WRITE_BYTE(noise); // noise ( client will divide by 100 )
|
||||||
|
MESSAGE_END();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMSporeGrenade::SpawnExplosionParticles(const Vector& origin, const Vector& direction, int modelindex, int count, float speed, float noise)
|
||||||
|
{
|
||||||
|
MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, origin);
|
||||||
|
WRITE_BYTE(TE_SPRITE_SPRAY);
|
||||||
|
WRITE_COORD(origin.x); // pos
|
||||||
|
WRITE_COORD(origin.y);
|
||||||
|
WRITE_COORD(origin.z);
|
||||||
|
WRITE_COORD(direction.x); // dir
|
||||||
|
WRITE_COORD(direction.y);
|
||||||
|
WRITE_COORD(direction.z);
|
||||||
|
WRITE_SHORT(modelindex); // model
|
||||||
|
WRITE_BYTE(count); // count
|
||||||
|
WRITE_BYTE(speed); // speed
|
||||||
|
WRITE_BYTE(noise); // noise ( client will divide by 100 )
|
||||||
|
MESSAGE_END();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMSporeGrenade::UpdateOnRemove()
|
||||||
|
{
|
||||||
|
CMBaseMonster::UpdateOnRemove();
|
||||||
|
if (m_pSporeGlow)
|
||||||
|
{
|
||||||
|
UTIL_Remove(m_pSporeGlow->edict());
|
||||||
|
m_pSporeGlow = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
899
src/dlls/strooper.cpp
Normal file
899
src/dlls/strooper.cpp
Normal file
@@ -0,0 +1,899 @@
|
|||||||
|
// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository!
|
||||||
|
|
||||||
|
/***
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This source code contains proprietary and confidential information of
|
||||||
|
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||||
|
* persons who have executed a written SDK license with Valve. Any access,
|
||||||
|
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||||
|
*
|
||||||
|
****/
|
||||||
|
//=========================================================
|
||||||
|
// shocktrooper
|
||||||
|
//=========================================================
|
||||||
|
|
||||||
|
#include "extdll.h"
|
||||||
|
#include "plane.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "cmbase.h"
|
||||||
|
#include "cmbasemonster.h"
|
||||||
|
#include "monsters.h"
|
||||||
|
#include "schedule.h"
|
||||||
|
#include "animation.h"
|
||||||
|
#include "weapons.h"
|
||||||
|
#include "cmtalkmonster.h"
|
||||||
|
#include "effects.h"
|
||||||
|
#include "customentity.h"
|
||||||
|
#include "shock.h"
|
||||||
|
|
||||||
|
int g_fStrooperQuestion; // true if an idle grunt asked a question. Cleared when someone answers.
|
||||||
|
|
||||||
|
extern Schedule_t slGruntTakeCover[];
|
||||||
|
extern Schedule_t slGruntGrenadeCover[];
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// monster-specific DEFINE's
|
||||||
|
//=========================================================
|
||||||
|
#define STROOPER_CLIP_SIZE 10 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x!
|
||||||
|
#define STROOPER_VOL 0.35 // volume of grunt sounds
|
||||||
|
#define STROOPER_ATTN ATTN_NORM // attenutation of grunt sentences
|
||||||
|
#define STROOPER_LIMP_HEALTH 20
|
||||||
|
#define STROOPER_DMG_HEADSHOT ( DMG_BULLET | DMG_CLUB ) // damage types that can kill a grunt with a single headshot.
|
||||||
|
#define STROOPER_NUM_HEADS 2 // how many grunt heads are there?
|
||||||
|
#define STROOPER_MINIMUM_HEADSHOT_DAMAGE 15 // must do at least this much damage in one shot to head to score a headshot kill
|
||||||
|
#define STROOPER_SENTENCE_VOLUME (float)0.35 // volume of grunt sentences
|
||||||
|
#define STROOPER_MUZZLEFLASH "sprites/muzzle_shock.spr"
|
||||||
|
|
||||||
|
#define STROOPER_SHOCKRIFLE (1 << 0)
|
||||||
|
#define STROOPER_HANDGRENADE (1 << 1)
|
||||||
|
|
||||||
|
#define GUN_GROUP 1
|
||||||
|
#define GUN_SHOCKRIFLE 0
|
||||||
|
#define GUN_NONE 1
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Monster's Anim Events Go Here
|
||||||
|
//=========================================================
|
||||||
|
#define STROOPER_AE_RELOAD ( 2 )
|
||||||
|
#define STROOPER_AE_KICK ( 3 )
|
||||||
|
#define STROOPER_AE_BURST1 ( 4 )
|
||||||
|
#define STROOPER_AE_BURST2 ( 5 )
|
||||||
|
#define STROOPER_AE_BURST3 ( 6 )
|
||||||
|
#define STROOPER_AE_GREN_TOSS ( 7 )
|
||||||
|
#define STROOPER_AE_GREN_LAUNCH ( 8 )
|
||||||
|
#define STROOPER_AE_GREN_DROP ( 9 )
|
||||||
|
#define STROOPER_AE_CAUGHT_ENEMY ( 10 ) // shocktrooper established sight with an enemy (player only) that had previously eluded the squad.
|
||||||
|
#define STROOPER_AE_DROP_GUN ( 11 ) // shocktrooper (probably dead) is dropping his shockrifle.
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// monster-specific schedule types
|
||||||
|
//=========================================================
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
SCHED_STROOPER_SUPPRESS = LAST_COMMON_SCHEDULE + 1,
|
||||||
|
SCHED_STROOPER_ESTABLISH_LINE_OF_FIRE,// move to a location to set up an attack against the enemy. (usually when a friendly is in the way).
|
||||||
|
SCHED_STROOPER_COVER_AND_RELOAD,
|
||||||
|
SCHED_STROOPER_SWEEP,
|
||||||
|
SCHED_STROOPER_FOUND_ENEMY,
|
||||||
|
SCHED_STROOPER_REPEL,
|
||||||
|
SCHED_STROOPER_REPEL_ATTACK,
|
||||||
|
SCHED_STROOPER_REPEL_LAND,
|
||||||
|
SCHED_STROOPER_WAIT_FACE_ENEMY,
|
||||||
|
SCHED_STROOPER_TAKECOVER_FAILED,// special schedule type that forces analysis of conditions and picks the best possible schedule to recover from this type of failure.
|
||||||
|
SCHED_STROOPER_ELOF_FAIL,
|
||||||
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// monster-specific tasks
|
||||||
|
//=========================================================
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
TASK_STROOPER_FACE_TOSS_DIR = LAST_COMMON_TASK + 1,
|
||||||
|
TASK_STROOPER_SPEAK_SENTENCE,
|
||||||
|
TASK_STROOPER_CHECK_FIRE,
|
||||||
|
};
|
||||||
|
|
||||||
|
int iStrooperMuzzleFlash;
|
||||||
|
|
||||||
|
const char *CMStrooper::pGruntSentences[] =
|
||||||
|
{
|
||||||
|
"ST_GREN", // grenade scared grunt
|
||||||
|
"ST_ALERT", // sees player
|
||||||
|
"ST_MONST", // sees monster
|
||||||
|
"ST_COVER", // running to cover
|
||||||
|
"ST_THROW", // about to throw grenade
|
||||||
|
"ST_CHARGE", // running out to get the enemy
|
||||||
|
"ST_TAUNT", // say rude things
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
STROOPER_SENT_NONE = -1,
|
||||||
|
STROOPER_SENT_GREN = 0,
|
||||||
|
STROOPER_SENT_ALERT,
|
||||||
|
STROOPER_SENT_MONSTER,
|
||||||
|
STROOPER_SENT_COVER,
|
||||||
|
STROOPER_SENT_THROW,
|
||||||
|
STROOPER_SENT_CHARGE,
|
||||||
|
STROOPER_SENT_TAUNT,
|
||||||
|
} STROOPER_SENTENCE_TYPES;
|
||||||
|
|
||||||
|
void CMStrooper::SpeakSentence()
|
||||||
|
{
|
||||||
|
if( m_iSentence == STROOPER_SENT_NONE )
|
||||||
|
{
|
||||||
|
// no sentence cued up.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( FOkToSpeak() )
|
||||||
|
{
|
||||||
|
SENTENCEG_PlayRndSz( ENT( pev ), pGruntSentences[m_iSentence], STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch );
|
||||||
|
JustSpoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define STROOPER_GIB_COUNT 8
|
||||||
|
//=========================================================
|
||||||
|
// GibMonster - make gun fly through the air.
|
||||||
|
//=========================================================
|
||||||
|
void CMStrooper::GibMonster()
|
||||||
|
{
|
||||||
|
if (GetBodygroup(GUN_GROUP) != GUN_NONE)
|
||||||
|
{
|
||||||
|
DropShockRoach(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "common/bodysplat.wav", 1, ATTN_NORM );
|
||||||
|
|
||||||
|
if( CVAR_GET_FLOAT( "violence_agibs" ) != 0 ) // Should never get here, but someone might call it directly
|
||||||
|
{
|
||||||
|
CMGib::SpawnRandomGibs( pev, STROOPER_GIB_COUNT, "models/strooper_gibs.mdl", 0 ); // Throw alien gibs
|
||||||
|
}
|
||||||
|
SetThink( &CMBaseEntity::SUB_Remove );
|
||||||
|
pev->nextthink = gpGlobals->time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMStrooper::IdleSound()
|
||||||
|
{
|
||||||
|
if (FOkToSpeak() && (g_fStrooperQuestion || RANDOM_LONG(0, 1)))
|
||||||
|
{
|
||||||
|
if (!g_fStrooperQuestion)
|
||||||
|
{
|
||||||
|
// ask question or make statement
|
||||||
|
switch (RANDOM_LONG(0, 2))
|
||||||
|
{
|
||||||
|
case 0: // check in
|
||||||
|
SENTENCEG_PlayRndSz(ENT(pev), "ST_CHECK", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
|
||||||
|
g_fStrooperQuestion = 1;
|
||||||
|
break;
|
||||||
|
case 1: // question
|
||||||
|
SENTENCEG_PlayRndSz(ENT(pev), "ST_QUEST", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
|
||||||
|
g_fStrooperQuestion = 2;
|
||||||
|
break;
|
||||||
|
case 2: // statement
|
||||||
|
SENTENCEG_PlayRndSz(ENT(pev), "ST_IDLE", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (g_fStrooperQuestion)
|
||||||
|
{
|
||||||
|
case 1: // check in
|
||||||
|
SENTENCEG_PlayRndSz(ENT(pev), "ST_CLEAR", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
|
||||||
|
break;
|
||||||
|
case 2: // question
|
||||||
|
SENTENCEG_PlayRndSz(ENT(pev), "ST_ANSWER", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
g_fStrooperQuestion = 0;
|
||||||
|
}
|
||||||
|
JustSpoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Classify - indicates this monster's place in the
|
||||||
|
// relationship table.
|
||||||
|
//=========================================================
|
||||||
|
int CMStrooper::Classify()
|
||||||
|
{
|
||||||
|
if ( m_iClassifyOverride == -1 ) // helper
|
||||||
|
return CLASS_NONE;
|
||||||
|
else if ( m_iClassifyOverride > 0 )
|
||||||
|
return m_iClassifyOverride; // override
|
||||||
|
|
||||||
|
return CLASS_RACEX_SHOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CMStrooper::CheckRangeAttack1(float flDot, float flDist)
|
||||||
|
{
|
||||||
|
return m_cAmmoLoaded >= 1;// && CMHGrunt::CheckRangeAttack1(flDot, flDist);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CMStrooper::CheckRangeAttack2( float flDot, float flDist )
|
||||||
|
{
|
||||||
|
if( !FBitSet( pev->weapons, STROOPER_HANDGRENADE ) )
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return CMHGrunt::CheckRangeAttack2(flDot, flDist);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// HandleAnimEvent - catches the monster-specific messages
|
||||||
|
// that occur when tagged animation frames are played.
|
||||||
|
//=========================================================
|
||||||
|
void CMStrooper::HandleAnimEvent(MonsterEvent_t *pEvent)
|
||||||
|
{
|
||||||
|
switch (pEvent->event)
|
||||||
|
{
|
||||||
|
case STROOPER_AE_DROP_GUN:
|
||||||
|
{
|
||||||
|
if (GetBodygroup(GUN_GROUP) != GUN_NONE)
|
||||||
|
{
|
||||||
|
DropShockRoach(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STROOPER_AE_RELOAD:
|
||||||
|
m_cAmmoLoaded = m_cClipSize;
|
||||||
|
ClearConditions(bits_COND_NO_AMMO_LOADED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STROOPER_AE_GREN_TOSS:
|
||||||
|
{
|
||||||
|
UTIL_MakeVectors(pev->angles);
|
||||||
|
// CGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 34 + Vector (0, 0, 32), m_vecTossVelocity, 3.5 );
|
||||||
|
CMSporeGrenade::ShootTimed(pev, pev->origin + Vector(0,0,98), m_vecTossVelocity, 3.5);
|
||||||
|
|
||||||
|
m_fThrowGrenade = FALSE;
|
||||||
|
m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown.
|
||||||
|
// !!!LATER - when in a group, only try to throw grenade if ordered.
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STROOPER_AE_GREN_LAUNCH:
|
||||||
|
case STROOPER_AE_GREN_DROP:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STROOPER_AE_BURST1:
|
||||||
|
{
|
||||||
|
if (m_hEnemy)
|
||||||
|
{
|
||||||
|
Vector vecGunPos;
|
||||||
|
Vector vecGunAngles;
|
||||||
|
|
||||||
|
GetAttachment(0, vecGunPos, vecGunAngles);
|
||||||
|
|
||||||
|
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecGunPos );
|
||||||
|
WRITE_BYTE( TE_SPRITE );
|
||||||
|
WRITE_COORD( vecGunPos.x ); // pos
|
||||||
|
WRITE_COORD( vecGunPos.y );
|
||||||
|
WRITE_COORD( vecGunPos.z );
|
||||||
|
WRITE_SHORT( iStrooperMuzzleFlash ); // model
|
||||||
|
WRITE_BYTE( 4 ); // size * 10
|
||||||
|
WRITE_BYTE( 128 ); // brightness
|
||||||
|
MESSAGE_END();
|
||||||
|
|
||||||
|
UTIL_MakeVectors(pev->angles);
|
||||||
|
Vector vecShootOrigin = vecGunPos + gpGlobals->v_forward * 32;
|
||||||
|
Vector vecShootDir = ShootAtEnemy( vecShootOrigin );
|
||||||
|
vecGunAngles = UTIL_VecToAngles(vecShootDir);
|
||||||
|
|
||||||
|
//CBaseEntity *pShock = CBaseEntity::Create("shock_beam", vecShootOrigin, vecGunAngles, edict());
|
||||||
|
CMShock *pShock = CreateClassPtr((CMShock *)NULL);
|
||||||
|
|
||||||
|
if (pShock != NULL)
|
||||||
|
{
|
||||||
|
pShock->pev->origin = vecShootOrigin;
|
||||||
|
|
||||||
|
vecGunAngles.z += RANDOM_FLOAT( -0.05, 0 );
|
||||||
|
pShock->pev->angles = UTIL_VecToAngles( vecGunAngles );
|
||||||
|
pShock->pev->owner = edict();
|
||||||
|
|
||||||
|
// Initialize these for entities who don't link to the world
|
||||||
|
pShock->pev->absmin = pShock->pev->origin - Vector(1,1,1);
|
||||||
|
pShock->pev->absmax = pShock->pev->origin + Vector(1,1,1);
|
||||||
|
|
||||||
|
pShock->Spawn();
|
||||||
|
|
||||||
|
pShock->pev->velocity = vecShootDir * 2000;
|
||||||
|
pShock->pev->nextthink = gpGlobals->time;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cAmmoLoaded--;
|
||||||
|
SetBlending( 0, vecGunAngles.x );
|
||||||
|
|
||||||
|
// Play fire sound.
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/shock_fire.wav", 1, ATTN_NORM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STROOPER_AE_KICK:
|
||||||
|
{
|
||||||
|
EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "zombie/claw_miss2.wav", 1.0, ATTN_NORM, 0, PITCH_NORM + RANDOM_LONG( -5, 5 ) );
|
||||||
|
edict_t *pHurt = Kick();
|
||||||
|
|
||||||
|
if (pHurt)
|
||||||
|
{
|
||||||
|
// SOUND HERE!
|
||||||
|
UTIL_MakeVectors(pev->angles);
|
||||||
|
pHurt->v.punchangle.x = 15;
|
||||||
|
pHurt->v.punchangle.z = (m_fRightClaw) ? -10 : 10;
|
||||||
|
pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50;
|
||||||
|
|
||||||
|
if ( UTIL_IsPlayer( pHurt ) )
|
||||||
|
UTIL_TakeDamage( pHurt, pev, pev, gSkillData.strooperDmgKick, DMG_CLUB );
|
||||||
|
else if ( pHurt->v.euser4 != NULL )
|
||||||
|
{
|
||||||
|
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt));
|
||||||
|
pMonster->TakeDamage( pev, pev, gSkillData.strooperDmgKick, DMG_CLUB );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_fRightClaw = !m_fRightClaw;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STROOPER_AE_CAUGHT_ENEMY:
|
||||||
|
{
|
||||||
|
if (FOkToSpeak())
|
||||||
|
{
|
||||||
|
SENTENCEG_PlayRndSz(ENT(pev), "ST_ALERT", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch);
|
||||||
|
JustSpoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
CMHGrunt::HandleAnimEvent(pEvent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Spawn
|
||||||
|
//=========================================================
|
||||||
|
void CMStrooper::Spawn()
|
||||||
|
{
|
||||||
|
Precache();
|
||||||
|
|
||||||
|
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;
|
||||||
|
pev->movetype = MOVETYPE_STEP;
|
||||||
|
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||||
|
pev->effects = 0;
|
||||||
|
pev->health = gSkillData.strooperHealth;
|
||||||
|
m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||||
|
m_MonsterState = MONSTERSTATE_NONE;
|
||||||
|
m_flNextGrenadeCheck = gpGlobals->time + 1;
|
||||||
|
m_flNextPainTime = gpGlobals->time;
|
||||||
|
m_iSentence = STROOPER_SENT_NONE;
|
||||||
|
|
||||||
|
m_afCapability = bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
|
||||||
|
|
||||||
|
//m_fEnemyEluded = FALSE;
|
||||||
|
m_fFirstEncounter = TRUE;// this is true when the grunt spawns, because he hasn't encountered an enemy yet.
|
||||||
|
|
||||||
|
m_HackedGunPos = Vector(0, 0, 55);
|
||||||
|
|
||||||
|
if (pev->weapons == 0)
|
||||||
|
{
|
||||||
|
// initialize to original values
|
||||||
|
pev->weapons = STROOPER_SHOCKRIFLE | STROOPER_HANDGRENADE;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cClipSize = gSkillData.strooperMaxCharge;
|
||||||
|
|
||||||
|
m_cAmmoLoaded = m_cClipSize;
|
||||||
|
|
||||||
|
m_fRightClaw = FALSE;
|
||||||
|
|
||||||
|
CMTalkMonster::g_talkWaitTime = 0;
|
||||||
|
m_rechargeTime = gpGlobals->time + gSkillData.strooperRchgSpeed;
|
||||||
|
m_blinkTime = gpGlobals->time + RANDOM_FLOAT(3.0f, 7.0f);
|
||||||
|
|
||||||
|
MonsterInit();
|
||||||
|
|
||||||
|
pev->classname = MAKE_STRING( "monster_shocktrooper" );
|
||||||
|
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
|
||||||
|
{
|
||||||
|
// default name
|
||||||
|
m_szMonsterName = MAKE_STRING( "Shock Trooper" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMStrooper::MonsterThink()
|
||||||
|
{
|
||||||
|
if (m_cAmmoLoaded < m_cClipSize)
|
||||||
|
{
|
||||||
|
if (m_rechargeTime < gpGlobals->time)
|
||||||
|
{
|
||||||
|
m_cAmmoLoaded++;
|
||||||
|
m_rechargeTime = gpGlobals->time + gSkillData.strooperRchgSpeed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_blinkTime <= gpGlobals->time && pev->skin == 0) {
|
||||||
|
pev->skin = 1;
|
||||||
|
m_blinkTime = gpGlobals->time + RANDOM_FLOAT(3.0f, 7.0f);
|
||||||
|
m_eyeChangeTime = gpGlobals->time + 0.1;
|
||||||
|
}
|
||||||
|
if (pev->skin != 0) {
|
||||||
|
if (m_eyeChangeTime <= gpGlobals->time) {
|
||||||
|
m_eyeChangeTime = gpGlobals->time + 0.1;
|
||||||
|
pev->skin++;
|
||||||
|
if (pev->skin > 3) {
|
||||||
|
pev->skin = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CMHGrunt::MonsterThink();
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Precache - precaches all resources this monster needs
|
||||||
|
//=========================================================
|
||||||
|
void CMStrooper::Precache()
|
||||||
|
{
|
||||||
|
PRECACHE_MODEL("models/strooper.mdl");
|
||||||
|
PRECACHE_MODEL("models/strooper_gibs.mdl");
|
||||||
|
iStrooperMuzzleFlash = PRECACHE_MODEL(STROOPER_MUZZLEFLASH);
|
||||||
|
PRECACHE_SOUND("shocktrooper/shock_trooper_attack.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("shocktrooper/shock_trooper_die1.wav");
|
||||||
|
PRECACHE_SOUND("shocktrooper/shock_trooper_die2.wav");
|
||||||
|
PRECACHE_SOUND("shocktrooper/shock_trooper_die3.wav");
|
||||||
|
PRECACHE_SOUND("shocktrooper/shock_trooper_die4.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("shocktrooper/shock_trooper_pain1.wav");
|
||||||
|
PRECACHE_SOUND("shocktrooper/shock_trooper_pain2.wav");
|
||||||
|
PRECACHE_SOUND("shocktrooper/shock_trooper_pain3.wav");
|
||||||
|
PRECACHE_SOUND("shocktrooper/shock_trooper_pain4.wav");
|
||||||
|
PRECACHE_SOUND("shocktrooper/shock_trooper_pain5.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("weapons/shock_fire.wav");
|
||||||
|
PRECACHE_SOUND("weapons/shock_impact.wav");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event
|
||||||
|
|
||||||
|
// shock_beam
|
||||||
|
CMShock shock;
|
||||||
|
shock.Precache();
|
||||||
|
|
||||||
|
// spore
|
||||||
|
CMSporeGrenade spore;
|
||||||
|
spore.Precache();
|
||||||
|
|
||||||
|
// shockroach
|
||||||
|
CMShockRoach shockroach;
|
||||||
|
shockroach.Precache();
|
||||||
|
|
||||||
|
// get voice pitch
|
||||||
|
if (RANDOM_LONG(0, 1))
|
||||||
|
m_voicePitch = 109 + RANDOM_LONG(0, 7);
|
||||||
|
else
|
||||||
|
m_voicePitch = 100;
|
||||||
|
|
||||||
|
m_iBrassShell = PRECACHE_MODEL("models/shell.mdl");// brass shell
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// PainSound
|
||||||
|
//=========================================================
|
||||||
|
void CMStrooper::PainSound()
|
||||||
|
{
|
||||||
|
if (gpGlobals->time > m_flNextPainTime)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
if (RANDOM_LONG(0, 99) < 5)
|
||||||
|
{
|
||||||
|
// pain sentences are rare
|
||||||
|
if (FOkToSpeak())
|
||||||
|
{
|
||||||
|
SENTENCEG_PlayRndSz(ENT(pev), "HG_PAIN", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, PITCH_NORM);
|
||||||
|
JustSpoke();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
switch (RANDOM_LONG(0, 4))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain1.wav", 1, ATTN_NORM);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain2.wav", 1, ATTN_NORM);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain3.wav", 1, ATTN_NORM);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain4.wav", 1, ATTN_NORM);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain5.wav", 1, ATTN_NORM);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_flNextPainTime = gpGlobals->time + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// DeathSound
|
||||||
|
//=========================================================
|
||||||
|
void CMStrooper::DeathSound()
|
||||||
|
{
|
||||||
|
switch (RANDOM_LONG(0, 3))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die1.wav", 1, ATTN_IDLE);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die2.wav", 1, ATTN_IDLE);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die3.wav", 1, ATTN_IDLE);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die4.wav", 1, ATTN_IDLE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// TraceAttack - reimplemented in shock trooper because they never have helmets
|
||||||
|
//=========================================================
|
||||||
|
void CMStrooper::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
|
||||||
|
{
|
||||||
|
CMBaseMonster::TraceAttack(pevAttacker, flDamage, vecDir, ptr, bitsDamageType);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMStrooper::DropShockRoach(bool gibbed)
|
||||||
|
{
|
||||||
|
Vector vecGunPos;
|
||||||
|
Vector vecGunAngles;
|
||||||
|
|
||||||
|
GetAttachment(0, vecGunPos, vecGunAngles);
|
||||||
|
SetBodygroup(GUN_GROUP, GUN_NONE);
|
||||||
|
|
||||||
|
Vector vecDropAngles;
|
||||||
|
|
||||||
|
// Remove any pitch.
|
||||||
|
vecDropAngles.x = 0;
|
||||||
|
vecDropAngles.y = vecGunAngles.y;
|
||||||
|
vecDropAngles.z = 0;
|
||||||
|
|
||||||
|
Vector vecPos = pev->origin;
|
||||||
|
if (gibbed)
|
||||||
|
vecPos.z += 32;
|
||||||
|
else
|
||||||
|
vecPos.z += 48;
|
||||||
|
|
||||||
|
// now spawn a shockroach.
|
||||||
|
//CBaseEntity* roach = CBaseEntity::Create( "monster_shockroach", vecPos, vecDropAngles );
|
||||||
|
CMShockRoach *roach = CreateClassPtr((CMShockRoach *)NULL);
|
||||||
|
if (roach != NULL)
|
||||||
|
{
|
||||||
|
roach->pev->origin = vecPos;
|
||||||
|
roach->pev->angles = UTIL_VecToAngles( vecDropAngles );
|
||||||
|
|
||||||
|
// Initialize these for entities who don't link to the world
|
||||||
|
roach->pev->absmin = roach->pev->origin - Vector(1,1,1);
|
||||||
|
roach->pev->absmax = roach->pev->origin + Vector(1,1,1);
|
||||||
|
|
||||||
|
roach->Spawn();
|
||||||
|
|
||||||
|
if (ShouldFadeOnDeath())
|
||||||
|
roach->pev->spawnflags |= SF_MONSTER_FADECORPSE;
|
||||||
|
if (gibbed)
|
||||||
|
{
|
||||||
|
roach->pev->velocity = Vector(RANDOM_FLOAT(-100.0f, 100.0f), RANDOM_FLOAT(-100.0f, 100.0f), RANDOM_FLOAT(200.0f, 300.0f));
|
||||||
|
roach->pev->avelocity = Vector(0, RANDOM_FLOAT(200.0f, 300.0f), 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
roach->pev->velocity = Vector(RANDOM_FLOAT(-20.0f, 20.0f) , RANDOM_FLOAT(-20.0f, 20.0f), RANDOM_FLOAT(20.0f, 30.0f));
|
||||||
|
roach->pev->avelocity = Vector(0, RANDOM_FLOAT(20.0f, 40.0f), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// SetActivity
|
||||||
|
//=========================================================
|
||||||
|
void CMStrooper::SetActivity(Activity NewActivity)
|
||||||
|
{
|
||||||
|
int iSequence = ACTIVITY_NOT_AVAILABLE;
|
||||||
|
void *pmodel = GET_MODEL_PTR(ENT(pev));
|
||||||
|
|
||||||
|
switch (NewActivity)
|
||||||
|
{
|
||||||
|
case ACT_RANGE_ATTACK1:
|
||||||
|
// shocktrooper is either shooting standing or shooting crouched
|
||||||
|
if (m_fStanding)
|
||||||
|
{
|
||||||
|
// get aimable sequence
|
||||||
|
iSequence = LookupSequence("standing_mp5");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// get crouching shoot
|
||||||
|
iSequence = LookupSequence("crouching_mp5");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ACT_RANGE_ATTACK2:
|
||||||
|
// shocktrooper is going to throw a grenade.
|
||||||
|
|
||||||
|
// get toss anim
|
||||||
|
iSequence = LookupSequence("throwgrenade");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ACT_RUN:
|
||||||
|
if (pev->health <= STROOPER_LIMP_HEALTH)
|
||||||
|
{
|
||||||
|
// limp!
|
||||||
|
iSequence = LookupActivity(ACT_RUN_HURT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iSequence = LookupActivity(NewActivity);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ACT_WALK:
|
||||||
|
if (pev->health <= STROOPER_LIMP_HEALTH)
|
||||||
|
{
|
||||||
|
// limp!
|
||||||
|
iSequence = LookupActivity(ACT_WALK_HURT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iSequence = LookupActivity(NewActivity);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ACT_IDLE:
|
||||||
|
if (m_MonsterState == MONSTERSTATE_COMBAT)
|
||||||
|
{
|
||||||
|
NewActivity = ACT_IDLE_ANGRY;
|
||||||
|
}
|
||||||
|
iSequence = LookupActivity(NewActivity);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
iSequence = LookupActivity(NewActivity);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present
|
||||||
|
|
||||||
|
// Set to the desired anim, or default anim if the desired is not present
|
||||||
|
if (iSequence > ACTIVITY_NOT_AVAILABLE)
|
||||||
|
{
|
||||||
|
if (pev->sequence != iSequence || !m_fSequenceLoops)
|
||||||
|
{
|
||||||
|
pev->frame = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pev->sequence = iSequence; // Set to the reset anim (if it's there)
|
||||||
|
ResetSequenceInfo();
|
||||||
|
SetYawSpeed();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Not available try to get default anim
|
||||||
|
ALERT(at_console, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity);
|
||||||
|
pev->sequence = 0; // Set to the reset anim (if it's there)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Get Schedule!
|
||||||
|
//=========================================================
|
||||||
|
Schedule_t *CMStrooper::GetSchedule(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
// clear old sentence
|
||||||
|
m_iSentence = STROOPER_SENT_NONE;
|
||||||
|
|
||||||
|
// flying? If PRONE, barnacle has me. IF not, it's assumed I am rapelling.
|
||||||
|
if (pev->movetype == MOVETYPE_FLY && m_MonsterState != MONSTERSTATE_PRONE)
|
||||||
|
{
|
||||||
|
if (pev->flags & FL_ONGROUND)
|
||||||
|
{
|
||||||
|
// just landed
|
||||||
|
pev->movetype = MOVETYPE_STEP;
|
||||||
|
return GetScheduleOfType(SCHED_STROOPER_REPEL_LAND);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// repel down a rope,
|
||||||
|
if (m_MonsterState == MONSTERSTATE_COMBAT)
|
||||||
|
return GetScheduleOfType(SCHED_STROOPER_REPEL_ATTACK);
|
||||||
|
else
|
||||||
|
return GetScheduleOfType(SCHED_STROOPER_REPEL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (m_MonsterState)
|
||||||
|
{
|
||||||
|
case MONSTERSTATE_COMBAT:
|
||||||
|
{
|
||||||
|
// dead enemy
|
||||||
|
if (HasConditions(bits_COND_ENEMY_DEAD))
|
||||||
|
{
|
||||||
|
// call base class, all code to handle dead enemies is centralized there.
|
||||||
|
return CMBaseMonster::GetSchedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
// new enemy
|
||||||
|
if (HasConditions(bits_COND_NEW_ENEMY))
|
||||||
|
{
|
||||||
|
//!!!KELLY - the leader of a squad of grunts has just seen the player or a
|
||||||
|
// monster and has made it the squad's enemy. You
|
||||||
|
// can check pev->flags for FL_CLIENT to determine whether this is the player
|
||||||
|
// or a monster. He's going to immediately start
|
||||||
|
// firing, though. If you'd like, we can make an alternate "first sight"
|
||||||
|
// schedule where the leader plays a handsign anim
|
||||||
|
// that gives us enough time to hear a short sentence or spoken command
|
||||||
|
// before he starts pluggin away.
|
||||||
|
if (FOkToSpeak())// && RANDOM_LONG(0,1))
|
||||||
|
{
|
||||||
|
if ((m_hEnemy != 0) && UTIL_IsPlayer( m_hEnemy ))
|
||||||
|
// player
|
||||||
|
SENTENCEG_PlayRndSz(ENT(pev), "ST_ALERT", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch);
|
||||||
|
/*
|
||||||
|
else if ((m_hEnemy != 0) &&
|
||||||
|
(m_hEnemy->Classify() != CLASS_PLAYER_ALLY) &&
|
||||||
|
(m_hEnemy->Classify() != CLASS_HUMAN_PASSIVE) &&
|
||||||
|
(m_hEnemy->Classify() != CLASS_MACHINE))
|
||||||
|
// monster
|
||||||
|
SENTENCEG_PlayRndSz(ENT(pev), "ST_MONST", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch);
|
||||||
|
*/
|
||||||
|
JustSpoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasConditions(bits_COND_CAN_RANGE_ATTACK1))
|
||||||
|
{
|
||||||
|
return GetScheduleOfType(SCHED_STROOPER_SUPPRESS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetScheduleOfType(SCHED_STROOPER_ESTABLISH_LINE_OF_FIRE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no ammo
|
||||||
|
else if (HasConditions(bits_COND_NO_AMMO_LOADED))
|
||||||
|
{
|
||||||
|
//!!!KELLY - this individual just realized he's out of bullet ammo.
|
||||||
|
// He's going to try to find cover to run to and reload, but rarely, if
|
||||||
|
// none is available, he'll drop and reload in the open here.
|
||||||
|
return GetScheduleOfType(SCHED_STROOPER_COVER_AND_RELOAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
// damaged just a little
|
||||||
|
else if (HasConditions(bits_COND_LIGHT_DAMAGE))
|
||||||
|
{
|
||||||
|
// if hurt:
|
||||||
|
// 90% chance of taking cover
|
||||||
|
// 10% chance of flinch.
|
||||||
|
int iPercent = RANDOM_LONG(0, 99);
|
||||||
|
|
||||||
|
if (iPercent <= 90 && m_hEnemy != 0)
|
||||||
|
{
|
||||||
|
// only try to take cover if we actually have an enemy!
|
||||||
|
|
||||||
|
//!!!KELLY - this grunt was hit and is going to run to cover.
|
||||||
|
if (FOkToSpeak()) // && RANDOM_LONG(0,1))
|
||||||
|
{
|
||||||
|
//SENTENCEG_PlayRndSz( ENT(pev), "HG_COVER", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch);
|
||||||
|
m_iSentence = STROOPER_SENT_COVER;
|
||||||
|
//JustSpoke();
|
||||||
|
}
|
||||||
|
return GetScheduleOfType(SCHED_TAKE_COVER_FROM_ENEMY);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetScheduleOfType(SCHED_SMALL_FLINCH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// can kick
|
||||||
|
else if (HasConditions(bits_COND_CAN_MELEE_ATTACK1))
|
||||||
|
{
|
||||||
|
return GetScheduleOfType(SCHED_MELEE_ATTACK1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// can shoot
|
||||||
|
if (HasConditions(bits_COND_CAN_RANGE_ATTACK1))
|
||||||
|
{
|
||||||
|
if (HasConditions(bits_COND_CAN_RANGE_ATTACK2))
|
||||||
|
{
|
||||||
|
// throw a grenade if can and no engage slots are available
|
||||||
|
return GetScheduleOfType(SCHED_RANGE_ATTACK2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// hide!
|
||||||
|
return GetScheduleOfType(SCHED_TAKE_COVER_FROM_ENEMY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// can't see enemy
|
||||||
|
else if (HasConditions(bits_COND_ENEMY_OCCLUDED))
|
||||||
|
{
|
||||||
|
if (HasConditions(bits_COND_CAN_RANGE_ATTACK2))
|
||||||
|
{
|
||||||
|
//!!!KELLY - this grunt is about to throw or fire a grenade at the player. Great place for "fire in the hole" "frag out" etc
|
||||||
|
if (FOkToSpeak())
|
||||||
|
{
|
||||||
|
SENTENCEG_PlayRndSz(ENT(pev), "ST_THROW", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch);
|
||||||
|
JustSpoke();
|
||||||
|
}
|
||||||
|
return GetScheduleOfType(SCHED_RANGE_ATTACK2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//!!!KELLY - grunt is going to stay put for a couple seconds to see if
|
||||||
|
// the enemy wanders back out into the open, or approaches the
|
||||||
|
// grunt's covered position. Good place for a taunt, I guess?
|
||||||
|
if (FOkToSpeak() && RANDOM_LONG(0, 1))
|
||||||
|
{
|
||||||
|
SENTENCEG_PlayRndSz(ENT(pev), "ST_TAUNT", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch);
|
||||||
|
JustSpoke();
|
||||||
|
}
|
||||||
|
return GetScheduleOfType(SCHED_STANDOFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasConditions(bits_COND_SEE_ENEMY) && !HasConditions(bits_COND_CAN_RANGE_ATTACK1))
|
||||||
|
{
|
||||||
|
return GetScheduleOfType(SCHED_STROOPER_ESTABLISH_LINE_OF_FIRE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no special cases here, call the base class
|
||||||
|
return CMBaseMonster::GetSchedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
//=========================================================
|
||||||
|
Schedule_t* CMStrooper::GetScheduleOfType(int Type)
|
||||||
|
{
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case SCHED_TAKE_COVER_FROM_ENEMY:
|
||||||
|
{
|
||||||
|
if (RANDOM_LONG(0, 1))
|
||||||
|
{
|
||||||
|
return &slGruntTakeCover[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return &slGruntGrenadeCover[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
return CMHGrunt::GetScheduleOfType(Type);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
259
src/dlls/stukabat.cpp
Normal file
259
src/dlls/stukabat.cpp
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
/***
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This source code contains proprietary and confidential information of
|
||||||
|
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||||
|
* persons who have executed a written SDK license with Valve. Any access,
|
||||||
|
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||||
|
*
|
||||||
|
****/
|
||||||
|
//=========================================================
|
||||||
|
// Stukabat - Xen Birb
|
||||||
|
//=========================================================
|
||||||
|
|
||||||
|
#include "extdll.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "cmbase.h"
|
||||||
|
#include "cmflyingmonster.h"
|
||||||
|
#include "monsters.h"
|
||||||
|
#include "schedule.h"
|
||||||
|
#include "animation.h"
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Monster's Anim Events Go Here
|
||||||
|
//=========================================================
|
||||||
|
|
||||||
|
#define STUKABAT_AE_BITE 1
|
||||||
|
#define STUKABAT_AE_FLAP 8
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Classify - indicates this monster's place in the
|
||||||
|
// relationship table.
|
||||||
|
//=========================================================
|
||||||
|
int CMStukabat :: Classify ( void )
|
||||||
|
{
|
||||||
|
if ( m_iClassifyOverride == -1 ) // helper
|
||||||
|
return CLASS_NONE;
|
||||||
|
else if ( m_iClassifyOverride > 0 )
|
||||||
|
return m_iClassifyOverride; // override
|
||||||
|
|
||||||
|
return CLASS_ALIEN_PREDATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// SetYawSpeed - allows each sequence to have a different
|
||||||
|
// turn rate associated with it.
|
||||||
|
//=========================================================
|
||||||
|
void CMStukabat :: SetYawSpeed ( void )
|
||||||
|
{
|
||||||
|
int ys;
|
||||||
|
|
||||||
|
switch ( m_Activity )
|
||||||
|
{
|
||||||
|
case ACT_HOVER:
|
||||||
|
default:
|
||||||
|
ys = 90;
|
||||||
|
}
|
||||||
|
|
||||||
|
pev->yaw_speed = ys;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// HandleAnimEvent - catches the monster-specific messages
|
||||||
|
// that occur when tagged animation frames are played.
|
||||||
|
//=========================================================
|
||||||
|
void CMStukabat :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||||
|
{
|
||||||
|
switch( pEvent->event )
|
||||||
|
{
|
||||||
|
case STUKABAT_AE_BITE:
|
||||||
|
{
|
||||||
|
edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.stukabatDmgBite, DMG_SLASH|DMG_POISON );
|
||||||
|
if ( pHurt )
|
||||||
|
{
|
||||||
|
// Play bite sound
|
||||||
|
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "headcrab/hc_headbite.wav", 1.0, ATTN_NORM, 0, GetBitePitch() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case STUKABAT_AE_FLAP:
|
||||||
|
{
|
||||||
|
m_flightSpeed = gSkillData.stukabatSpeed; // set our own speed
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
CMFlyingMonster::HandleAnimEvent( pEvent );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Spawn
|
||||||
|
//=========================================================
|
||||||
|
void CMStukabat :: Spawn()
|
||||||
|
{
|
||||||
|
Precache( );
|
||||||
|
|
||||||
|
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;
|
||||||
|
pev->movetype = MOVETYPE_FLY;
|
||||||
|
pev->flags |= FL_FLY;
|
||||||
|
m_bloodColor = BLOOD_COLOR_YELLOW;
|
||||||
|
pev->health = gSkillData.stukabatHealth;
|
||||||
|
pev->view_ofs = Vector ( 0, 0, 22 );// position of the eyes relative to monster's origin.
|
||||||
|
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||||
|
m_MonsterState = MONSTERSTATE_NONE;
|
||||||
|
|
||||||
|
m_pFlapSound = "stukabat/stukabat_flap1.wav";
|
||||||
|
|
||||||
|
MonsterInit();
|
||||||
|
|
||||||
|
pev->classname = MAKE_STRING( "monster_stukabat" );
|
||||||
|
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
|
||||||
|
{
|
||||||
|
// default name
|
||||||
|
m_szMonsterName = MAKE_STRING( "Stukabat" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// Precache - precaches all resources this monster needs
|
||||||
|
//=========================================================
|
||||||
|
void CMStukabat :: Precache()
|
||||||
|
{
|
||||||
|
PRECACHE_MODEL("models/stukabat.mdl");
|
||||||
|
|
||||||
|
PRECACHE_SOUND("stukabat/stukabat_flap1.wav"); // flying sound
|
||||||
|
PRECACHE_SOUND("headcrab/hc_headbite.wav"); // bite sound
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// AI Schedules Specific to this monster
|
||||||
|
//=========================================================
|
||||||
|
|
||||||
|
/* Chase */
|
||||||
|
Task_t tlStukabatChaseEnemy[] =
|
||||||
|
{
|
||||||
|
{ TASK_GET_PATH_TO_ENEMY, (float)128 }, // is the 128 number really used?
|
||||||
|
{ TASK_SET_ACTIVITY, (float)ACT_FLY },
|
||||||
|
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
|
||||||
|
|
||||||
|
};
|
||||||
|
Schedule_t slStukabatChaseEnemy[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
tlStukabatChaseEnemy,
|
||||||
|
ARRAYSIZE ( tlStukabatChaseEnemy ),
|
||||||
|
bits_COND_NEW_ENEMY |
|
||||||
|
bits_COND_TASK_FAILED,
|
||||||
|
0,
|
||||||
|
"StukabatChaseEnemy"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Fail */
|
||||||
|
Task_t tlStukabatFail[] =
|
||||||
|
{
|
||||||
|
{ TASK_STOP_MOVING, 0 },
|
||||||
|
{ TASK_SET_ACTIVITY, (float)ACT_HOVER },
|
||||||
|
{ TASK_WAIT, (float)2 },
|
||||||
|
{ TASK_WAIT_PVS, (float)0 },
|
||||||
|
};
|
||||||
|
Schedule_t slStukabatFail[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
tlStukabatFail,
|
||||||
|
ARRAYSIZE ( tlStukabatFail ),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"StukabatFail"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
DEFINE_CUSTOM_SCHEDULES( CMStukabat )
|
||||||
|
{
|
||||||
|
slStukabatChaseEnemy,
|
||||||
|
slStukabatFail,
|
||||||
|
};
|
||||||
|
|
||||||
|
IMPLEMENT_CUSTOM_SCHEDULES( CMStukabat, CMFlyingMonster );
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// SetActivity
|
||||||
|
//=========================================================
|
||||||
|
void CMStukabat :: SetActivity ( Activity NewActivity )
|
||||||
|
{
|
||||||
|
int iSequence = ACTIVITY_NOT_AVAILABLE;
|
||||||
|
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||||
|
|
||||||
|
switch ( NewActivity )
|
||||||
|
{
|
||||||
|
case ACT_IDLE:
|
||||||
|
return; // refuse
|
||||||
|
case ACT_FLY:
|
||||||
|
iSequence = LookupActivity ( NewActivity );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
iSequence = LookupActivity ( NewActivity );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present
|
||||||
|
|
||||||
|
// Set to the desired anim, or default anim if the desired is not present
|
||||||
|
if ( iSequence > ACTIVITY_NOT_AVAILABLE )
|
||||||
|
{
|
||||||
|
if ( pev->sequence != iSequence || !m_fSequenceLoops )
|
||||||
|
{
|
||||||
|
pev->frame = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pev->sequence = iSequence; // Set to the reset anim (if it's there)
|
||||||
|
ResetSequenceInfo( );
|
||||||
|
SetYawSpeed();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Not available try to get default anim
|
||||||
|
ALERT ( at_console, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity );
|
||||||
|
pev->sequence = 0; // Set to the reset anim (if it's there)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// GetScheduleOfType
|
||||||
|
//=========================================================
|
||||||
|
Schedule_t* CMStukabat :: GetScheduleOfType ( int Type )
|
||||||
|
{
|
||||||
|
switch ( Type )
|
||||||
|
{
|
||||||
|
case SCHED_CHASE_ENEMY:
|
||||||
|
return slStukabatChaseEnemy;
|
||||||
|
case SCHED_FAIL:
|
||||||
|
return slStukabatFail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CMBaseMonster :: GetScheduleOfType( Type );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// CheckRangeAttack1 - Poisonous Bite
|
||||||
|
//=========================================================
|
||||||
|
BOOL CMStukabat :: CheckRangeAttack1 ( float flDot, float flDist )
|
||||||
|
{
|
||||||
|
if ( flDot > 0.7 && flDist <= 64 )
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
6
src/dlls/turret.cpp
Executable file → Normal file
6
src/dlls/turret.cpp
Executable file → Normal file
@@ -121,7 +121,7 @@ void CMBaseTurret::Precache( )
|
|||||||
void CMTurret::Spawn()
|
void CMTurret::Spawn()
|
||||||
{
|
{
|
||||||
Precache( );
|
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;
|
pev->health = gSkillData.turretHealth;
|
||||||
m_HackedGunPos = Vector( 0, 0, 12.75 );
|
m_HackedGunPos = Vector( 0, 0, 12.75 );
|
||||||
m_flMaxSpin = TURRET_MAXSPIN;
|
m_flMaxSpin = TURRET_MAXSPIN;
|
||||||
@@ -161,7 +161,7 @@ void CMTurret::Precache()
|
|||||||
void CMMiniTurret::Spawn()
|
void CMMiniTurret::Spawn()
|
||||||
{
|
{
|
||||||
Precache( );
|
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;
|
pev->health = gSkillData.miniturretHealth;
|
||||||
m_HackedGunPos = Vector( 0, 0, 12.75 );
|
m_HackedGunPos = Vector( 0, 0, 12.75 );
|
||||||
m_flMaxSpin = 0;
|
m_flMaxSpin = 0;
|
||||||
@@ -1014,7 +1014,7 @@ void CMSentry::Precache()
|
|||||||
void CMSentry::Spawn()
|
void CMSentry::Spawn()
|
||||||
{
|
{
|
||||||
Precache( );
|
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;
|
pev->health = gSkillData.sentryHealth;
|
||||||
m_HackedGunPos = Vector( 0, 0, 48 );
|
m_HackedGunPos = Vector( 0, 0, 48 );
|
||||||
pev->view_ofs.z = 48;
|
pev->view_ofs.z = 48;
|
||||||
|
|||||||
@@ -1520,15 +1520,13 @@ Vector VecBModelOrigin( entvars_t* pevBModel )
|
|||||||
|
|
||||||
bool UTIL_IsAlive(entvars_t *pev)
|
bool UTIL_IsAlive(entvars_t *pev)
|
||||||
{
|
{
|
||||||
return ((pev->deadflag == DEAD_NO) && (pev->health > 0) &&
|
return ((pev->deadflag == DEAD_NO) && (pev->health > 0));
|
||||||
((pev->flags & FL_NOTARGET) == 0) && (pev->takedamage != 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool UTIL_IsAlive(edict_t *pEdict)
|
bool UTIL_IsAlive(edict_t *pEdict)
|
||||||
{
|
{
|
||||||
return ((pEdict->v.deadflag == DEAD_NO) && (pEdict->v.health > 0) &&
|
return ((pEdict->v.deadflag == DEAD_NO) && (pEdict->v.health > 0));
|
||||||
((pEdict->v.flags & FL_NOTARGET) == 0) && (pEdict->v.takedamage != 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1677,8 +1675,8 @@ int UTIL_TakeDamage( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAtt
|
|||||||
flBonus = ARMOR_BONUS;
|
flBonus = ARMOR_BONUS;
|
||||||
flRatio = ARMOR_RATIO;
|
flRatio = ARMOR_RATIO;
|
||||||
|
|
||||||
if (!pEdict->v.takedamage)
|
if (!pEdict->v.takedamage || (pEdict->v.flags & FL_GODMODE))
|
||||||
return 0;
|
return 0; // refuse the damage
|
||||||
|
|
||||||
if ( ( bitsDamageType & DMG_BLAST ) )
|
if ( ( bitsDamageType & DMG_BLAST ) )
|
||||||
{
|
{
|
||||||
@@ -1875,7 +1873,11 @@ void UTIL_TraceBleed( edict_t *pEdict, float flDamage, Vector vecDir, TraceResul
|
|||||||
|
|
||||||
if ( Bloodtr.flFraction != 1.0 )
|
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 )
|
if ( pEdict->v.takedamage )
|
||||||
{
|
{
|
||||||
|
int bloodColor = pEdict->v.iuser3;
|
||||||
|
if ( !bloodColor )
|
||||||
|
bloodColor = BLOOD_COLOR_RED;
|
||||||
|
|
||||||
AddMultiDamage( pevAttacker, pEdict, flDamage, bitsDamageType );
|
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 );
|
UTIL_TraceBleed( pEdict, flDamage, vecDir, ptr, bitsDamageType );
|
||||||
}
|
}
|
||||||
@@ -1968,3 +1974,11 @@ bool UTIL_IsBSPModel( edict_t *pent )
|
|||||||
{
|
{
|
||||||
return (pent->v.solid == SOLID_BSP || pent->v.movetype == MOVETYPE_PUSHSTEP);
|
return (pent->v.solid == SOLID_BSP || pent->v.movetype == MOVETYPE_PUSHSTEP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UTIL_TakeDamageExternal( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||||
|
{
|
||||||
|
// Tell AMXX to call TakeDamage for us.
|
||||||
|
char extCmd[64];
|
||||||
|
sprintf( extCmd, "monster_hurt_entity %i %i %i %f %i\n", ENTINDEX( pEdict ), ENTINDEX( ENT( pevInflictor ) ), ENTINDEX( ENT( pevAttacker ) ), flDamage, bitsDamageType );
|
||||||
|
SERVER_COMMAND( extCmd );
|
||||||
|
}
|
||||||
|
|||||||
@@ -538,3 +538,4 @@ Vector UTIL_Center(edict_t *pEdict);
|
|||||||
edict_t *UTIL_GetNextTarget( edict_t *pEntity );
|
edict_t *UTIL_GetNextTarget( edict_t *pEntity );
|
||||||
edict_t *UTIL_FindNearestPlayer(edict_t *pEdict, float m_flFieldOfView);
|
edict_t *UTIL_FindNearestPlayer(edict_t *pEdict, float m_flFieldOfView);
|
||||||
bool UTIL_IsBSPModel( edict_t *pent );
|
bool UTIL_IsBSPModel( edict_t *pent );
|
||||||
|
void UTIL_TakeDamageExternal( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||||
|
|||||||
1323
src/dlls/voltigore.cpp
Normal file
1323
src/dlls/voltigore.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -79,6 +79,8 @@ void ApplyMultiDamage(entvars_t *pevInflictor, entvars_t *pevAttacker )
|
|||||||
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(gMultiDamage.pEntity));
|
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(gMultiDamage.pEntity));
|
||||||
pMonster->TakeDamage(pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type );
|
pMonster->TakeDamage(pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type );
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
UTIL_TakeDamageExternal(gMultiDamage.pEntity, pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -136,6 +138,19 @@ void DecalGunshot( TraceResult *pTrace, int iBulletType )
|
|||||||
|
|
||||||
switch( iBulletType )
|
switch( iBulletType )
|
||||||
{
|
{
|
||||||
|
case BULLET_PLAYER_CROWBAR:
|
||||||
|
{
|
||||||
|
// wall decal
|
||||||
|
UTIL_DecalTrace( pTrace, DamageDecal( pEntity, DMG_CLUB ) );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
// smoke and decal
|
||||||
|
UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* why the duplicate case?
|
||||||
case BULLET_PLAYER_9MM:
|
case BULLET_PLAYER_9MM:
|
||||||
case BULLET_MONSTER_9MM:
|
case BULLET_MONSTER_9MM:
|
||||||
case BULLET_PLAYER_MP5:
|
case BULLET_PLAYER_MP5:
|
||||||
@@ -154,6 +169,7 @@ void DecalGunshot( TraceResult *pTrace, int iBulletType )
|
|||||||
// wall decal
|
// wall decal
|
||||||
UTIL_DecalTrace( pTrace, DamageDecal( pEntity, DMG_CLUB ) );
|
UTIL_DecalTrace( pTrace, DamageDecal( pEntity, DMG_CLUB ) );
|
||||||
break;
|
break;
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,33 @@ public:
|
|||||||
BOOL m_fRegisteredSound;// whether or not this grenade has issued its DANGER sound to the world sound list yet.
|
BOOL m_fRegisteredSound;// whether or not this grenade has issued its DANGER sound to the world sound list yet.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Contact/Timed spore grenade
|
||||||
|
class CMSporeGrenade : public CMBaseMonster
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Precache(void);
|
||||||
|
void Spawn(void);
|
||||||
|
|
||||||
|
static CMSporeGrenade *ShootTimed(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, bool ai);
|
||||||
|
static CMSporeGrenade *ShootContact(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity);
|
||||||
|
|
||||||
|
void Explode(TraceResult *pTrace);
|
||||||
|
|
||||||
|
void EXPORT BounceTouch(edict_t *pOther);
|
||||||
|
void EXPORT ExplodeTouch(edict_t *pOther);
|
||||||
|
void EXPORT DangerSoundThink(void);
|
||||||
|
void EXPORT Detonate(void);
|
||||||
|
void EXPORT TumbleThink(void);
|
||||||
|
|
||||||
|
void BounceSound(void);
|
||||||
|
void DangerSound();
|
||||||
|
static void SpawnTrailParticles(const Vector& origin, const Vector& direction, int modelindex, int count, float speed, float noise);
|
||||||
|
static void SpawnExplosionParticles(const Vector& origin, const Vector& direction, int modelindex, int count, float speed, float noise);
|
||||||
|
|
||||||
|
void UpdateOnRemove();
|
||||||
|
|
||||||
|
CMSprite* m_pSporeGlow;
|
||||||
|
};
|
||||||
|
|
||||||
// constant items
|
// constant items
|
||||||
#define ITEM_HEALTHKIT 1
|
#define ITEM_HEALTHKIT 1
|
||||||
@@ -177,6 +204,8 @@ typedef enum
|
|||||||
BULLET_MONSTER_9MM,
|
BULLET_MONSTER_9MM,
|
||||||
BULLET_MONSTER_MP5,
|
BULLET_MONSTER_MP5,
|
||||||
BULLET_MONSTER_12MM,
|
BULLET_MONSTER_12MM,
|
||||||
|
BULLET_MONSTER_762,
|
||||||
|
BULLET_MONSTER_357,
|
||||||
} Bullet;
|
} Bullet;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ void CMZombie :: Spawn()
|
|||||||
{
|
{
|
||||||
Precache( );
|
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 );
|
UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX );
|
||||||
|
|
||||||
pev->solid = SOLID_SLIDEBOX;
|
pev->solid = SOLID_SLIDEBOX;
|
||||||
|
|||||||
@@ -37,7 +37,13 @@
|
|||||||
#ifndef ENGINE_API_H
|
#ifndef ENGINE_API_H
|
||||||
#define ENGINE_API_H
|
#define ENGINE_API_H
|
||||||
|
|
||||||
#include <stdint.h> // why?
|
// No proper C99 support in old MVSC6, use custom stdint.h
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "stdint_c99.h"
|
||||||
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "comp_dep.h"
|
#include "comp_dep.h"
|
||||||
|
|
||||||
// Plugin's GetEngineFunctions, called by metamod.
|
// Plugin's GetEngineFunctions, called by metamod.
|
||||||
|
|||||||
@@ -80,7 +80,9 @@
|
|||||||
// DLL.
|
// DLL.
|
||||||
#undef DLLEXPORT
|
#undef DLLEXPORT
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define DLLEXPORT __declspec(dllexport) __attribute__ ((externally_visible))
|
// __attribute__ ((externally_visible)) is not recognized by VC++6
|
||||||
|
// or, rather, is GCC-only. do we need it for windows builds? -Giegue
|
||||||
|
#define DLLEXPORT __declspec(dllexport)
|
||||||
// WINAPI should be provided in the windows compiler headers.
|
// WINAPI should be provided in the windows compiler headers.
|
||||||
// It's usually defined to something like "__stdcall".
|
// It's usually defined to something like "__stdcall".
|
||||||
#elif defined(linux)
|
#elif defined(linux)
|
||||||
|
|||||||
229
src/metamod/stdint_c99.h
Normal file
229
src/metamod/stdint_c99.h
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
/* ISO C9x 7.18 Integer types <stdint.h>
|
||||||
|
* Based on ISO/IEC SC22/WG14 9899 Committee draft (SC22 N2794)
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS NOT COPYRIGHTED
|
||||||
|
*
|
||||||
|
* Contributor: Danny Smith <danny_r_smith_2001@yahoo.co.nz>
|
||||||
|
*
|
||||||
|
* This source code is offered for use in the public domain. You may
|
||||||
|
* use, modify or distribute it freely.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful but
|
||||||
|
* WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
|
||||||
|
* DISCLAIMED. This includes but is not limited to warranties of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
*
|
||||||
|
* Date: 2000-12-02
|
||||||
|
*
|
||||||
|
* mwb: This was modified in the following ways:
|
||||||
|
*
|
||||||
|
* - make it compatible with Visual C++ 6 (which uses
|
||||||
|
* non-standard keywords and suffixes for 64-bit types)
|
||||||
|
* - some environments need stddef.h included (for wchar stuff?)
|
||||||
|
* - handle the fact that Microsoft's limits.h header defines
|
||||||
|
* SIZE_MAX
|
||||||
|
* - make corrections for SIZE_MAX, INTPTR_MIN, INTPTR_MAX, UINTPTR_MAX,
|
||||||
|
* PTRDIFF_MIN, PTRDIFF_MAX, SIG_ATOMIC_MIN, and SIG_ATOMIC_MAX
|
||||||
|
* to be 64-bit aware.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _STDINT_H
|
||||||
|
#define _STDINT_H
|
||||||
|
#define __need_wint_t
|
||||||
|
#define __need_wchar_t
|
||||||
|
#include <wchar.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#if _MSC_VER && (_MSC_VER < 1300)
|
||||||
|
/* using MSVC 6 or earlier - no "long long" type, but might have _int64 type */
|
||||||
|
#define __STDINT_LONGLONG __int64
|
||||||
|
#define __STDINT_LONGLONG_SUFFIX i64
|
||||||
|
#else
|
||||||
|
#define __STDINT_LONGLONG long long
|
||||||
|
#define __STDINT_LONGLONG_SUFFIX LL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined( PASTE)
|
||||||
|
#define PASTE2( x, y) x##y
|
||||||
|
#define PASTE( x, y) PASTE2( x, y)
|
||||||
|
#endif /* PASTE */
|
||||||
|
|
||||||
|
/* 7.18.1.1 Exact-width integer types */
|
||||||
|
typedef signed char int8_t;
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef short int16_t;
|
||||||
|
typedef unsigned short uint16_t;
|
||||||
|
typedef int int32_t;
|
||||||
|
typedef unsigned uint32_t;
|
||||||
|
typedef __STDINT_LONGLONG int64_t;
|
||||||
|
typedef unsigned __STDINT_LONGLONG uint64_t;
|
||||||
|
|
||||||
|
/* 7.18.1.2 Minimum-width integer types */
|
||||||
|
typedef signed char int_least8_t;
|
||||||
|
typedef unsigned char uint_least8_t;
|
||||||
|
typedef short int_least16_t;
|
||||||
|
typedef unsigned short uint_least16_t;
|
||||||
|
typedef int int_least32_t;
|
||||||
|
typedef unsigned uint_least32_t;
|
||||||
|
typedef __STDINT_LONGLONG int_least64_t;
|
||||||
|
typedef unsigned __STDINT_LONGLONG uint_least64_t;
|
||||||
|
|
||||||
|
/* 7.18.1.3 Fastest minimum-width integer types
|
||||||
|
* Not actually guaranteed to be fastest for all purposes
|
||||||
|
* Here we use the exact-width types for 8 and 16-bit ints.
|
||||||
|
*/
|
||||||
|
typedef char int_fast8_t;
|
||||||
|
typedef unsigned char uint_fast8_t;
|
||||||
|
typedef short int_fast16_t;
|
||||||
|
typedef unsigned short uint_fast16_t;
|
||||||
|
typedef int int_fast32_t;
|
||||||
|
typedef unsigned int uint_fast32_t;
|
||||||
|
typedef __STDINT_LONGLONG int_fast64_t;
|
||||||
|
typedef unsigned __STDINT_LONGLONG uint_fast64_t;
|
||||||
|
|
||||||
|
/* 7.18.1.4 Integer types capable of holding object pointers */
|
||||||
|
#ifndef _INTPTR_T_DEFINED
|
||||||
|
#define _INTPTR_T_DEFINED
|
||||||
|
#ifdef _WIN64
|
||||||
|
typedef __STDINT_LONGLONG intptr_t
|
||||||
|
#else
|
||||||
|
typedef int intptr_t;
|
||||||
|
#endif /* _WIN64 */
|
||||||
|
#endif /* _INTPTR_T_DEFINED */
|
||||||
|
|
||||||
|
#ifndef _UINTPTR_T_DEFINED
|
||||||
|
#define _UINTPTR_T_DEFINED
|
||||||
|
#ifdef _WIN64
|
||||||
|
typedef unsigned __STDINT_LONGLONG uintptr_t
|
||||||
|
#else
|
||||||
|
typedef unsigned int uintptr_t;
|
||||||
|
#endif /* _WIN64 */
|
||||||
|
#endif /* _UINTPTR_T_DEFINED */
|
||||||
|
|
||||||
|
/* 7.18.1.5 Greatest-width integer types */
|
||||||
|
typedef __STDINT_LONGLONG intmax_t;
|
||||||
|
typedef unsigned __STDINT_LONGLONG uintmax_t;
|
||||||
|
|
||||||
|
/* 7.18.2 Limits of specified-width integer types */
|
||||||
|
#if !defined ( __cplusplus) || defined (__STDC_LIMIT_MACROS)
|
||||||
|
|
||||||
|
/* 7.18.2.1 Limits of exact-width integer types */
|
||||||
|
#define INT8_MIN (-128)
|
||||||
|
#define INT16_MIN (-32768)
|
||||||
|
#define INT32_MIN (-2147483647 - 1)
|
||||||
|
#define INT64_MIN (PASTE( -9223372036854775807, __STDINT_LONGLONG_SUFFIX) - 1)
|
||||||
|
|
||||||
|
#define INT8_MAX 127
|
||||||
|
#define INT16_MAX 32767
|
||||||
|
#define INT32_MAX 2147483647
|
||||||
|
#define INT64_MAX (PASTE( 9223372036854775807, __STDINT_LONGLONG_SUFFIX))
|
||||||
|
|
||||||
|
#define UINT8_MAX 0xff /* 255U */
|
||||||
|
#define UINT16_MAX 0xffff /* 65535U */
|
||||||
|
#define UINT32_MAX 0xffffffff /* 4294967295U */
|
||||||
|
#define UINT64_MAX (PASTE( 0xffffffffffffffffU, __STDINT_LONGLONG_SUFFIX)) /* 18446744073709551615ULL */
|
||||||
|
|
||||||
|
/* 7.18.2.2 Limits of minimum-width integer types */
|
||||||
|
#define INT_LEAST8_MIN INT8_MIN
|
||||||
|
#define INT_LEAST16_MIN INT16_MIN
|
||||||
|
#define INT_LEAST32_MIN INT32_MIN
|
||||||
|
#define INT_LEAST64_MIN INT64_MIN
|
||||||
|
|
||||||
|
#define INT_LEAST8_MAX INT8_MAX
|
||||||
|
#define INT_LEAST16_MAX INT16_MAX
|
||||||
|
#define INT_LEAST32_MAX INT32_MAX
|
||||||
|
#define INT_LEAST64_MAX INT64_MAX
|
||||||
|
|
||||||
|
#define UINT_LEAST8_MAX UINT8_MAX
|
||||||
|
#define UINT_LEAST16_MAX UINT16_MAX
|
||||||
|
#define UINT_LEAST32_MAX UINT32_MAX
|
||||||
|
#define UINT_LEAST64_MAX UINT64_MAX
|
||||||
|
|
||||||
|
/* 7.18.2.3 Limits of fastest minimum-width integer types */
|
||||||
|
#define INT_FAST8_MIN INT8_MIN
|
||||||
|
#define INT_FAST16_MIN INT16_MIN
|
||||||
|
#define INT_FAST32_MIN INT32_MIN
|
||||||
|
#define INT_FAST64_MIN INT64_MIN
|
||||||
|
|
||||||
|
#define INT_FAST8_MAX INT8_MAX
|
||||||
|
#define INT_FAST16_MAX INT16_MAX
|
||||||
|
#define INT_FAST32_MAX INT32_MAX
|
||||||
|
#define INT_FAST64_MAX INT64_MAX
|
||||||
|
|
||||||
|
#define UINT_FAST8_MAX UINT8_MAX
|
||||||
|
#define UINT_FAST16_MAX UINT16_MAX
|
||||||
|
#define UINT_FAST32_MAX UINT32_MAX
|
||||||
|
#define UINT_FAST64_MAX UINT64_MAX
|
||||||
|
|
||||||
|
/* 7.18.2.4 Limits of integer types capable of holding object pointers */
|
||||||
|
#ifdef _WIN64
|
||||||
|
#define INTPTR_MIN INT64_MIN
|
||||||
|
#define INTPTR_MAX INT64_MAX
|
||||||
|
#define UINTPTR_MAX UINT64_MAX
|
||||||
|
#else
|
||||||
|
#define INTPTR_MIN INT32_MIN
|
||||||
|
#define INTPTR_MAX INT32_MAX
|
||||||
|
#define UINTPTR_MAX UINT32_MAX
|
||||||
|
#endif /* _WIN64 */
|
||||||
|
|
||||||
|
/* 7.18.2.5 Limits of greatest-width integer types */
|
||||||
|
#define INTMAX_MIN INT64_MIN
|
||||||
|
#define INTMAX_MAX INT64_MAX
|
||||||
|
#define UINTMAX_MAX UINT64_MAX
|
||||||
|
|
||||||
|
/* 7.18.3 Limits of other integer types */
|
||||||
|
#define PTRDIFF_MIN INTPTR_MIN
|
||||||
|
#define PTRDIFF_MAX INTPTR_MAX
|
||||||
|
|
||||||
|
#define SIG_ATOMIC_MIN INTPTR_MIN
|
||||||
|
#define SIG_ATOMIC_MAX INTPTR_MAX
|
||||||
|
|
||||||
|
/* we need to check for SIZE_MAX already defined because MS defines it in limits.h */
|
||||||
|
#ifndef SIZE_MAX
|
||||||
|
#define SIZE_MAX UINTPTR_MAX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WCHAR_MIN /* also in wchar.h */
|
||||||
|
#define WCHAR_MIN 0
|
||||||
|
#define WCHAR_MAX ((wchar_t)-1) /* UINT16_MAX */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* wint_t is unsigned short for compatibility with MS runtime */
|
||||||
|
#define WINT_MIN 0
|
||||||
|
#define WINT_MAX ((wint_t)-1) /* UINT16_MAX */
|
||||||
|
|
||||||
|
#endif /* !defined ( __cplusplus) || defined __STDC_LIMIT_MACROS */
|
||||||
|
|
||||||
|
/* 7.18.4 Macros for integer constants */
|
||||||
|
#if !defined ( __cplusplus) || defined (__STDC_CONSTANT_MACROS)
|
||||||
|
|
||||||
|
/* 7.18.4.1 Macros for minimum-width integer constants
|
||||||
|
|
||||||
|
Accoding to Douglas Gwyn <gwyn@arl.mil>:
|
||||||
|
"This spec was changed in ISO/IEC 9899:1999 TC1; in ISO/IEC
|
||||||
|
9899:1999 as initially published, the expansion was required
|
||||||
|
to be an integer constant of precisely matching type, which
|
||||||
|
is impossible to accomplish for the shorter types on most
|
||||||
|
platforms, because C99 provides no standard way to designate
|
||||||
|
an integer constant with width less than that of type int.
|
||||||
|
TC1 changed this to require just an integer constant
|
||||||
|
*expression* with *promoted* type."
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define INT8_C(val) ((int8_t) + (val))
|
||||||
|
#define UINT8_C(val) ((uint8_t) + (val##U))
|
||||||
|
#define INT16_C(val) ((int16_t) + (val))
|
||||||
|
#define UINT16_C(val) ((uint16_t) + (val##U))
|
||||||
|
|
||||||
|
#define INT32_C(val) val##L
|
||||||
|
#define UINT32_C(val) val##UL
|
||||||
|
#define INT64_C(val) (PASTE( val, __STDINT_LONGLONG_SUFFIX))
|
||||||
|
#define UINT64_C(val)(PASTE( PASTE( val, U), __STDINT_LONGLONG_SUFFIX))
|
||||||
|
|
||||||
|
/* 7.18.4.2 Macros for greatest-width integer constants */
|
||||||
|
#define INTMAX_C(val) INT64_C(val)
|
||||||
|
#define UINTMAX_C(val) UINT64_C(val)
|
||||||
|
|
||||||
|
#endif /* !defined ( __cplusplus) || defined __STDC_CONSTANT_MACROS */
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user