28 Commits

Author SHA1 Message Date
Giegue
4c7579c3e2 Merge pull request #5 from JulianR0/tier3
Tier3
2023-02-24 16:07:32 -03:00
Giegue
035e70e776 Update config files. 2023-02-24 15:29:07 -03:00
Giegue
d4ecb16bf7 Add custom model support. 2023-02-24 14:49:55 -03:00
Giegue
513cde6231 Add compatibility with Half-Life. 2023-02-23 03:29:53 -03:00
Giegue
e33eaf4d1e Full awareness of normal game entities. 2023-02-22 22:29:19 -03:00
Giegue
e29e79052d Use the game's generated graph if possible. 2023-02-21 20:16:11 -03:00
Giegue
8d28e23e16 Fixed wrong classname for Voltigore and Baby Voltigore.
Fixed unknown classnames showing as '}'.
Remove monster respawn.
Add 'monstermaker' entity.
2023-02-20 14:08:35 -03:00
Giegue
0622ccc638 Stop allied monsters from attacking the player.
Part B of making MonsterMod aware of normal HL entities.
2023-02-19 15:57:07 -03:00
Giegue
6e1081d793 Start making monstermod aware of normal HL monsters.
This is still heavily experimental and its use is not yet recommended.
2023-02-15 21:19:57 -03:00
Giegue
5830ac7908 Tweaked hwgrunt to be less crappy. 2023-02-15 21:14:13 -03:00
Giegue
2f80da1e15 Fix linux compilation.
Fix windows compilation, again.
2023-02-14 20:44:13 -03:00
Giegue
c32827b4cb Update milestones, again. 2023-02-07 18:32:55 -03:00
Giegue
5f7abc2df0 Add monster_stukabat. 2023-02-07 18:19:52 -03:00
Giegue
875d6610ae Add monster_robogrunt. 2023-02-05 17:13:39 -03:00
Giegue
94fc64226c Update README and milestones. 2023-02-03 03:04:08 -03:00
Giegue
19b7c69b14 Add monster_hwgrunt. 2023-02-03 02:37:30 -03:00
Giegue
0fd2d27b6f Fix windows compilation. 2023-02-01 01:07:35 -03:00
Julian
87210e5f6c Update readme to add information about precache. 2020-07-30 00:04:08 -03:00
Julian
9df28b9d3f Added monster_babygarg. 2020-07-29 22:35:01 -03:00
Julian
9602f862c9 Added monster_voltigore and monster_baby_voltigore. 2020-07-19 12:31:52 -03:00
Julian
5b0ced8e7d Add monster_shocktrooper. 2020-06-30 23:17:53 -03:00
Julian
63779c0471 Randomize Male Assassin weapons. 2020-06-12 04:26:25 -03:00
Julian
433bb24dcd Add monster_pitdrone. 2020-06-11 01:30:29 -03:00
Julian
ba25f7710c Add monster_otis. 2020-06-10 01:12:58 -03:00
Julian
bf607621b3 Add monster_male_assassin. 2020-06-09 18:21:51 -03:00
Julian
769ec8852f Update monster IRelationship to match Sven Co-op 5.x table. 2020-06-08 03:52:27 -03:00
Julian
7fcf39b6d3 Add monster_gonome. 2020-06-07 18:52:55 -03:00
Julian
2762dbc0b2 Update README. 2020-06-04 15:06:47 -03:00
158 changed files with 62842 additions and 52898 deletions

164
README.md
View File

@@ -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
View File

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

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

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

Binary file not shown.

View File

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

View File

@@ -4,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"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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) )

View File

@@ -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 );

View File

@@ -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;

View File

@@ -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 ) );

View File

@@ -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;

View File

@@ -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
View File

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

View File

@@ -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

View File

@@ -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

View File

@@ -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 );
} }

View File

@@ -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;

View File

@@ -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
View 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 &center, const Vector &angles, edict_t *pOwner, int magnitude, int flags, float delay ) void ExplosionCreate( const Vector &center, const Vector &angles, edict_t *pOwner, int magnitude, int flags, float delay )
{ {
KeyValueData kvd; KeyValueData kvd;
@@ -287,15 +327,19 @@ void ExplosionCreate( const Vector &center, 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
} }

View File

@@ -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 &center, const Vector &angles, edict_t *pOwner, int magnitude, int flags, float delay ); extern void ExplosionCreate( const Vector &center, 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

View File

@@ -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

View File

@@ -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
View 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
View 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;
}
}

View File

@@ -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;

View File

@@ -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));

View File

@@ -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;

View File

@@ -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.

View File

@@ -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
View 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 );
}
}
}

View File

@@ -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
View 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
}

View File

@@ -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

View File

@@ -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
} }

View File

@@ -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

View 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
View File

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

View File

@@ -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( ))

View File

@@ -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;

View File

@@ -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
View 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

File diff suppressed because it is too large Load Diff

423
src/dlls/rgrunt.cpp Normal file
View 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
}

View File

@@ -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
View 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
View 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
View 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());
}

View File

@@ -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);

View File

@@ -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
View 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
View 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
View 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
View 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;

View File

@@ -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 );
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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.

View File

@@ -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
View 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