60 Commits

Author SHA1 Message Date
Giegue
efb7a1117b Merge pull request #18 from JulianR0/tier4
Tier4
2023-09-12 17:08:06 -03:00
Giegue
91f34169b5 Update some texts. 2023-09-12 16:43:12 -03:00
Giegue
272482c5e7 A long overdue update...
- Fixed Alien Grunt's hornets being silent.
- Fixed many, many, many, many ... many GSR crashes.
- Fixed voltigore's beams not being cleared upon gibbing/removal.
- Prepare for T4 release.
2023-09-12 13:51:25 -03:00
Giegue
12f8eea011 Yet another GMR/GSR fix.
Monster maximum viewable distance is now customizable.
2023-07-01 18:23:59 -03:00
Giegue
1d2a284197 Fixed mistyped monster_bullchicken classname.
Fixed ambient_music not stopping sounds during mapchange.
Fixed monster_gargantua crashing the server upon death.
Fixed GMR/GSR being prone to crashing.
Fixed monster_human_grunt melee attack not damaging non-mm entities.
Fixed monster_male_assassin doing sentence speeches.
Fixed monster_shockroach using headcrab sounds for attack.
Removed CGraph::HandleLinkEnt capabilities as they crash the server.
Increased monster_alien_voltigore lightning attack range.
2023-07-01 16:27:32 -03:00
Giegue
fbe70a5691 Fix sound replacements crashing the server. 2023-06-01 19:46:02 -03:00
Giegue
b87538ff59 Fix monstermaker attempting to spawn alien grunt when monster type is undefined or invalid. 2023-06-01 10:32:28 -03:00
Giegue
fa39abd61c Fix BSP reader crashing the server, again. 2023-05-31 16:16:37 -03:00
Giegue
95461427b2 Add sentences support. 2023-05-02 23:40:00 -03:00
Giegue
c782c2aaf5 Fixed a heap corruption. 2023-04-30 11:32:12 -03:00
Giegue
efd801bdea Add "soundlist" keyvalue for individual sound replacements. 2023-04-29 02:04:40 -03:00
Giegue
92d14a6fd3 Update extra AMXX plugins. 2023-04-29 01:58:47 -03:00
Giegue
ba9414ea6e Add missing keyvalue in monstermaker.
Fix duplicate entity spawn in Half-Life.
2023-04-27 13:02:23 -03:00
Giegue
f751720e59 Recompile AMXX plugins (Did I forget to update them?). 2023-04-27 12:59:29 -03:00
Giegue
0ac8fc00d5 Merge branch 'tier4' of https://github.com/JulianR0/monstermod-redo into tier4 2023-04-26 01:45:28 -03:00
Giegue
5da691b5fe Fix apache/turrets not triggering their targets on death.
Fix apache not giving score upon kill.
Fix monster trigger target crashing the server.
Add "squadmaker" alias entity.
Added plenty of keyvalues to monstermaker, similar to SC's squadmaker.
2023-04-26 01:44:36 -03:00
Giegue
ec45c6f23b Merge branch 'tier4' of https://github.com/JulianR0/monstermod-redo into tier4 2023-04-25 22:33:53 -03:00
Giegue
d00fec91b8 Update extra AMXX plugins. 2023-04-25 22:32:36 -03:00
Giegue
e1e2a48f89 Fix Alien Grunt's TraceAttack method.
Fix extra cfg file crashing the server.
Added custom blood color for monsters.
2023-04-24 13:17:49 -03:00
Giegue
1d8a6768aa Fix monster blood not working.
Fix monster hitboxes not working.
2023-04-24 01:30:52 -03:00
Giegue
2cf22ab66b Fix Makefiles. 2023-04-23 13:43:10 -03:00
Giegue
bfa59a2419 Fix Male Assassin unable to use the Sniper Rifle.
Partially fixed the Stukabat being unable to move when target is unreachable.
Removed zombie/gonome bullet resistance.
Add Global Model Replacement and Global Sound Replacement.
Update Tier milestones.
2023-04-21 20:55:47 -03:00
Giegue
66be4861a3 Fix male assassin unable to use weapons properly.
Remove zombie/gonome bullet resistance.
Make it slighly harder to gib monsters.
Small navigation fix.
Fix compilation on Visual Studio 2015 and 2017.
Fix "use_monstermod" keyvalue not working.
2023-04-15 19:23:20 -03:00
Giegue
fb92c2369f Remove unsupported door info in AI navigation.
Fix keyvalue length being too small.
2023-04-03 12:51:01 -03:00
Giegue
62c17b14e4 Even more BSP reader fixes.
Also fix project file not including all files.
2023-04-01 23:57:11 -03:00
Giegue
28c18952f8 Fix BSP reader breaking when there are too many entities. 2023-04-01 03:03:17 -03:00
Giegue
18268776ae This objetive is complete. 2023-03-29 12:57:37 -03:00
Giegue
acc22bc9ef Fix BSP reader crashing the server. 2023-03-29 12:54:54 -03:00
Giegue
79d4b3b21d Read entities within the BSP itself.
NOT YET COMPLETE, HIGHLY UNSTABLE!

Disabled by default, prone to crashing under Windows.
Set CVar "monster_entity_config" to 0 (or 2) to enable reading from BSP.
2023-03-29 02:48:18 -03:00
Giegue
074635bf8d Derp. Fixed an inverted check. 2023-03-25 21:52:57 -03:00
Giegue
a3086aaaa4 Print number of entities found in BSP file.
Add 'ambient_music' entity.
2023-03-25 21:48:27 -03:00
Giegue
c813f6e76a Fix entvars keyvalues not working.
Fix monstermaker classname.
2023-03-25 21:46:22 -03:00
Giegue
b757c65345 Forgotten header update for windows compilation. 2023-03-16 16:06:45 -03:00
Giegue
ecf3bd9a3d Fixed tons of monsters not working/crashing when attacking HL monsters. 2023-03-16 15:58:09 -03:00
Giegue
bc7633bf9c Update readme. 2023-03-15 14:23:44 -03:00
Giegue
3b1c99d2cc [#11] Add basic death message on PvP kills. 2023-03-15 14:09:06 -03:00
Giegue
5478860f27 Merge branch 'tier4' of https://github.com/JulianR0/monstermod-redo into tier4 2023-03-15 14:07:20 -03:00
Giegue
76d4c3b4bd Compile under modern Visual Studio versions. 2023-03-15 14:06:02 -03:00
Giegue
9849c5296b Add link to wiki. 2023-03-08 19:17:38 -03:00
Giegue
b364915799 [#10] Fix linux compilation. 2023-03-07 13:47:03 -03:00
Giegue
bd9fe1f487 Handle entvar keyvalues. 2023-03-06 20:02:04 -03:00
Giegue
73f240ddb1 Fixed monster turn rate being too slow.
Fixed heavy weapons grunt AI being complete nonsense.
Small improvements to stukabat.
2023-03-06 02:08:37 -03:00
Giegue
750b916666 *screams in AI* 2023-03-04 17:24:23 -03:00
Giegue
bd02a95260 Fix broken shock roach death.
Fix hwgrunt not spinning down the minigun.
Other misc. fixes.
2023-03-04 17:24:01 -03:00
Giegue
62063e34da Scrap scaling idea, not possible. 2023-03-03 01:33:37 -03:00
Giegue
55dfee7d74 Fixed human grunts refusing to attack and refusing to reload.
This also fixes male assassins and shock troopers behaving erraticaly.
Probably the heavy weapons grunt becomes better, too.
2023-03-03 01:18:38 -03:00
Giegue
2140e3413d [#4] Fix compiler warnings. 2023-03-02 14:09:49 -03:00
Giegue
e1bd51b220 [#4] Fix wrong angles on shocktroopers beams and roaches. 2023-03-02 14:09:15 -03:00
Giegue
4b087efde8 I keep forgetting to update this... 2023-02-28 13:46:44 -03:00
Giegue
08846e8dee Merge pull request #6 from JulianR0/tier4
Fix 'git clone' not working on Lubuntu 22.10
2023-02-28 13:19:28 -03:00
Giegue
7075efe7d9 Fixed git clone not working because some distros REALLY dislike the "aux" name! 2023-02-28 13:14:04 -03:00
Giegue
b265a65fe1 Fixed damageBits not being catched for death messages.
Removed DeathMsg manipulation, it never worked correctly.
2023-02-27 10:27:21 -03:00
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
89 changed files with 5314 additions and 1829 deletions

150
README.md
View File

@@ -23,29 +23,33 @@ A small light is seen at the distance...
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.
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.
After 21 years since botman's original plugin was released, the future of the project became nothing but a forgotten, ancient relic of the past.
Not anymore...
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 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.
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.~~ **April 2023 update**: The page is gone, the original 2002 plugin can no longer be found.
Under no circumstances shall we allow this project to fade away and become lost amidst the gears of time.
## Installation
*NOTE: Outdated. Ideally, users should be able to use the plugin "out-of-the-box" without the need to do the complicated mess explained below.*
The plugin -should- be able to be used out-of-the-box by simply downloading the binary and adding the appropiate entry in metamod's plugin list.
*TODO: Add build instructions, just using 'make' on G++ 4.8 is really vague.*
**Windows:**
`win32 addons\monstermod\monster_mm.dll`
If you are trying to use the compiled binary, you must know that it has been compiled with a mayor GCC version and it will be highly unlikely that it will run on a vanilla HLDS server. If the plugin fails to load due to libstdc++ not having CXXABI_1.X.X or similar, read on. HLDS uses it's own libstdc++ library. Any plugins compiled with GCC versions 5.x and greater will not work with the outdated library.
**Linux:**
`linux addons/monstermod/monster_mm_i386.so`
To remedy this issue you have two options:
To start adding monsters onto your maps, additional configuration files are included in the release files, each explaining its usage and installation instructions. Nevertheless, if you are felling lost, the [wiki](https://github.com/JulianR0/monstermod-redo/wiki) contains usage instructions, and how to configure MonsterMod to your liking.
You can recompile the source code under g++ 4.8 and use the newly generated binary. Make sure to edit the Makefile so it points to that version of g++. Compilation is done by simply running `make` on the `src/dlls` folder.
Extra MonsterMod features can be unlocked with additional AMX Mod X plugins which are located in the `extra` folder. All these plugins are optional, and only required based on your use-case.
Alternatively, you can "remove" the outdated library to force HLDS to use the libstdc++ provided by the linux distro, which is generally more up to date. You might need to install GCC/G++ on the operating system if it doesn't work.
## Build Instructions
The [Building](https://github.com/JulianR0/monstermod-redo/wiki) section of the wiki contains instructions on how to compile MonsterMod by yourself.
## MonsterMod and ReHLDS
@@ -53,122 +57,21 @@ Usage of ReHLDS is highly recommended, as you can use the command `rescount` whi
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 non trivial sounds of all weapons. This means that sounds such as "clip-ins", "clip-outs" are added to the list, taking quite a bit of space in the precache count. Let it be a reminder that our good old Half-Life can only store a maximum of 512 precached resources. Since these sounds are handled client-side by the models themselves, theres is no need to be kept precached on the server. Only the weapons fire sounds are needed.
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
## Known Bugs and Issues
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.
- Rarely, Stukabats will become unable to fly towards their target, standing in air doing seemingly nothing.
- Male Assassins share the same AI as HGrunts, so their ammo problem is still a thing, and the same workaround is used.
- Monsters are very prone to gibbing when hurt during death animations.
- 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.
There are probably more issues that aren't listed here, I'll attempt to fix them as I find them. Of course, any bug report is welcome. If reporting a bug, try to explain step by step how the bug ocurred. The easier it is to replicate a bug, the easier it is to locate it and fix it.
## Milestones
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.
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.
Current milestones are separated by "Tiers", which are as follows:
### Tier 0
@@ -193,21 +96,28 @@ Current milestones are separated by "Tiers", which are as follows:
- Update source code so it can compile AND run **ON WINDOWS**. **[DONE]**
- Implement *-almost-* all Opposing Force monsters. **[DONE]**
- Implement *-almost-* all default Sven Co-op monsters. **[DONE]**
- Make MonsterMod aware of normal game entities. *-For those who want to use the plugin in vanilla HL.-*
- Custom model support.
- Make MonsterMod aware of normal game entities. *-For those wanting to use this in vanilla HL.-* **[DONE]**
- Custom model support. **[DONE]**
### Tier 4
- Implement reading entities within the BSP itself.
- Model scaling. *-If possible.-*
- Custom sound support, along with sentences.
- Fix all pending bugs.
- Implement reading entities within the BSP itself. **[DONE]**
- Add build instructions. **[DONE]**
- Global Model Replacement. **[DONE]**
- Global Sound Replacement. **[DONE]**
- Miscellaneous customization options, such as blood color. **[DONE]**
- Individual sound replacement: "soundlist" keyvalue for monsters. **[DONE]**
- Sentences support for speakable monsters. **[DONE]**
- Attempt to fix bugs as they appear. **[DONE]**
### Tier 5
- Enhance the AI.
- Add configurations to change AI behaviour.
- Optimize code and enhance the AI.
- Create "tool" entities for easier map customization.
- Optimize and clean the code.
- Add a wiki with full documentation.
- Fix **ALL** bugs, specially those not covered in Tier 4.
What will the future hold after all Tiers has been completed?

View File

@@ -2,35 +2,13 @@
// Make a copy of this file and rename it to "<mapname>_monster.cfg".
// Send this new file to your maps folder.
//
// To add entries to this file, just pretend it's ripent.
// Adding entities to this file is done in the same way as ripenting a map.
//
// "delay" means monster respawndelay.
//
// 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.
// "classify" to change the monster's default classification, use one of these values:
//
// CLASS_NONE -1
// CLASS_MACHINE 1
// CLASS_PLAYER 2
// CLASS_HUMAN_PASSIVE 3
// CLASS_HUMAN_MILITARY 4
// CLASS_ALIEN_MILITARY 5
// CLASS_ALIEN_PASSIVE 6
// CLASS_ALIEN_MONSTER 7
// CLASS_ALIEN_PREY 8
// CLASS_ALIEN_PREDATOR 9
// CLASS_INSECT 10
// CLASS_PLAYER_ALLY 11
// CLASS_PLAYER_BIOWEAPON 12
// CLASS_ALIEN_BIOWEAPON 13
// CLASS_RACEX_PITDRONE 14
// CLASS_RACEX_SHOCK 15
// For more detailed instructions, check the wiki at
// https://github.com/JulianR0/monstermod-redo/wiki
{
"origin" "90 -180 150"
"delay" "30"
"displayname" "Example Custom Name"
"orientation" "1"
"spawnflags" "32"
@@ -38,7 +16,6 @@
}
{
"origin" "123 456 789"
"delay" "5"
"classify" "7"
"angles" "0 45 0"
"classname" "monster_houndeye"

View File

@@ -1,9 +1,9 @@
// This file constains the monsters that you always want to precache for dynamic spawning.
//
// MONSTERS - monsters that you always want to precache (for dynamic spawning)
// Dynamic spawning means - Monsters that you want to invoke at any time, on any map.
//
// Install this file to your mod's base directory.
//
// Just remove the comment characters at the beginning of the line for the
// Then, remove the comment characters at the beginning of the line for the
// monsters that you always want to precache.
//
// Be wary if you precache too many monsters, it may easily crash your
@@ -13,7 +13,7 @@
//monster_apache
//monster_barney
//monster_bigmomma
//monster_bullsquid
//monster_bullchicken
//monster_gargantua
//monster_human_assassin
//monster_headcrab
@@ -32,6 +32,9 @@
//monster_otis
//monster_pitdrone
//monster_shocktrooper
//monster_voltigore
//monster_baby_voltigore
//monster_alien_voltigore
//monster_alien_babyvoltigore
//monster_babygarg
//monster_hwgrunt
//monster_robogrunt
//monster_stukabat

View File

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

9
extra/README.md Normal file
View File

@@ -0,0 +1,9 @@
## 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.
Plugins from the `base` folder should come first, then the ones from the appropiate mod folder.
Once all milestones are completed, the possibility of removing the need for external plugins will be considered. Until then, this has to stay.

29
extra/base/README.md Normal file
View File

@@ -0,0 +1,29 @@
## Base Plugins (All Mods)
Auxiliary Tools to use MonsterMod in any GoldSrc game.
These are base plugins that should be installed first before adding any mod-specific plugin.
### 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.
#### Use Dispatcher
Because MonsterMod entities are, -quite literally-, just a func_wall with its own logic, trying to trigger any MonsterMod entity from the outside will fail, as it will attempt to use the logic of game's "func_wall" instead of our own.
In normal circunstances, you do not need this plugin. But let's say you have a turret monster, disabled by default, and you gave it a targetname. Even with a name, triggering this turret regardless of the method used will not activate it.
The same problem occurs from the other side, a MonsterMod entity trying to trigger something will expect said entity to be part of MonsterMod as well.
This plugin redirects the game's func_wall Use() function to MonsterMod, as well as bridging Use() calls from MonsterMod back to the game, connecting the missing pieces and allowing you to trigger MonsterMod entities from the game, and game entities from MonsterMod.
#### External TakeDamage
When a MonsterMod entity tries to inflict damage to something, it expects said entity to be either a player or a MonsterMod monster. If something else is to be found, it will deal no damage to it, as it doesn't know how to manage it.
This plugin allows MonsterMod entities to inflict damage to normal game entities, such as breakables.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,57 @@
#pragma semicolon 1
#include <amxmodx>
#include <engine>
#include <hamsandwich>
public plugin_init()
{
register_plugin( "GAME-MONSTER: Use Dispatcher", "1.1", "Giegue" );
register_cvar( "_glb_use", "1" );
// Game --> MonsterMod
RegisterHam( Ham_Use, "func_wall", "DispatchUse" );
// MonsterMod --> Game
register_srvcmd( "_trigger", "FireTargets" );
}
public DispatchUse( entity, caller, activator, useType, Float:value )
{
// all monstermod entities have this set
if ( entity_get_edict( entity, EV_ENT_euser4 ) )
{
server_cmd( "_use %i %i %i %i %f", entity, caller, activator, useType, value );
return HAM_SUPERCEDE;
}
return HAM_IGNORED;
}
public FireTargets()
{
if ( read_argc() == 6 )
{
new entity, caller, activator;
new Float:value;
new useType;
entity = read_argv_int( 1 );
caller = read_argv_int( 2 );
activator = read_argv_int( 3 );
value = read_argv_float( 4 );
useType = read_argv_int( 5 );
// caller and activator can be null, but never allow entity to be null
if ( !is_valid_ent( entity ) )
return;
if ( !is_valid_ent( caller ) )
caller = 0;
if ( !is_valid_ent( activator ) )
activator = 0;
ExecuteHamB( Ham_Use, entity, caller, activator, useType, value );
}
}

View File

@@ -0,0 +1,42 @@
#pragma semicolon 1
#include <amxmodx>
#include <engine>
#include <hamsandwich>
public plugin_init()
{
register_plugin( "GAME-MONSTER: External TakeDamage", "1.0", "Giegue" );
register_cvar( "_glb_takedamage", "1" );
// MonsterMod --> Game
register_srvcmd( "_takedamage", "TakeDamage" );
}
public TakeDamage()
{
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 );
}
}

17
extra/cstrike/README.md Normal file
View File

@@ -0,0 +1,17 @@
## Counter-Strike (cstrike)
Auxiliary Tools to use MonsterMod in Counter-Strike.
### 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.
#### Unprecacher
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 GoldSrc can only store a maximum of 512 precached resources. Most of these sounds are handled client-side by the models themselves, so there is no need for them to be kept precached on the server. Only the weapons fire sounds are needed.
This plugin removes 85 sounds from the precache list, adding extra space for additional monsters to fit in the map.

Binary file not shown.

View File

@@ -0,0 +1,126 @@
#pragma semicolon 1
#include <amxmodx>
#include <fakemeta>
// List of sounds that must NOT be precached
new g_SoundList[][64] =
{
"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"
};
public plugin_init()
{
register_plugin( "CS-MONSTER: Unprecacher", "1.0", "Giegue" );
}
public plugin_precache()
{
register_forward( FM_PrecacheSound, "fw_PrecacheSound" );
}
public fw_PrecacheSound( const sound[] )
{
static bool:bBlock, i;
bBlock = false;
i = 0;
for ( i = 0; i < sizeof( g_SoundList ); i++ )
{
if (contain( sound, g_SoundList[ i ] ) != -1 )
{
bBlock = true;
break;
}
}
if ( bBlock )
return FMRES_SUPERCEDE;
return FMRES_IGNORED;
}

40
extra/valve/README.md Normal file
View File

@@ -0,0 +1,40 @@
## 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.
#### 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.
#### Soundlist
This plugin allows you to use the "soundlist" keyvalue in HL monsters to individually replace monster sounds. Path starts from `sound/MAPNAME`. Use `../` to go to the previous directory if needed.
The plugin can work standalone, meaning it can be used without MonsterMod.
#### Extra Keyvalues
**"Bridge Plugin" is REQUIRED for this add-on to work**
This plugin allows you use the following keyvalues on HL monsters:
- classify
- is_player_ally *(non-monstermaker entities)*
- bloodcolor
- respawn_as_playerally *(monstermaker entity)*
The plugin also adds a 5th keyvalue, **"use_monstermod"**. When reading entities from the BSP file, if MonsterMod finds an entity that already exists in the game, it will stick to the game entity and ignore it. By setting this keyvalue to "1", it will explicity use MonsterMod for this entity.
This is useful when, for example, you are trying to spawn a Pit Drone from a monstermaker. HL does not recognize monster_pitdrone, but MonsterMod does. By redirecting the entity to MonsterMod, Pit Drones will spawn from this monstermaker.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,232 @@
#pragma semicolon 1
#include <amxmodx>
#include <engine>
#include <fakemeta>
#include <hamsandwich>
const bits_MEMORY_SPAWN = ( 1 << 1 );
const BLOOD_COLOR_BLUE = 211;
const BLOOD_COLOR_PINK = 147;
const BLOOD_COLOR_WHITE = 11;
const BLOOD_COLOR_ORANGE = 231;
const BLOOD_COLOR_BLACK = 49; // not 100% black but close enough
const BLOOD_COLOR_DARKGREEN = 181; // GREEN is aliased to YELLOW, use custom name
public plugin_init()
{
register_plugin( "HL-MONSTER Extra Keyvalues", "1.0", "Giegue" );
/* CLASSIFY */
// HACK: since Ham_Spawn won't work, this should do as an alternative
RegisterHam( Ham_SetObjectCollisionBox, "monster_headcrab", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_babycrab", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_bullchicken", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_barnacle", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_bigmomma", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_houndeye", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_alien_slave", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_alien_controller", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_alien_grunt", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_zombie", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_ichthyosaur", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_human_grunt", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_human_assassin", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_barney", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_gman", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_scientist", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_sentry", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_snark", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_miniturret", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_turret", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_apache", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_osprey", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_gargantua", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_nihilanth", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_tentacle", "MonsterSpawn_Post", 1 );
/* BLOODCOLOR */
RegisterHam( Ham_BloodColor, "monster_headcrab", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_babycrab", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_bullchicken", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_barnacle", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_bigmomma", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_houndeye", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_alien_slave", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_alien_controller", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_alien_grunt", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_zombie", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_ichthyosaur", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_human_grunt", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_human_assassin", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_barney", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_gman", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_scientist", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_sentry", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_snark", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_miniturret", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_turret", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_apache", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_osprey", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_gargantua", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_nihilanth", "MonsterBlood" );
RegisterHam( Ham_BloodColor, "monster_tentacle", "MonsterBlood" );
// "use_monstermod" keyvalue
register_cvar( "_hl_explicit", "1" );
}
public plugin_precache()
{
register_forward( FM_KeyValue, "ScanKeys" );
}
public ScanKeys( entid, kvd_handle )
{
if (is_valid_ent(entid))
{
static classname[ 33 ], keyname[ 33 ], value[ 128 ];
get_kvd( kvd_handle, KV_ClassName, classname, charsmax( classname ) );
// Monsters
if ( equal( classname, "monster_", 8 ) )
{
get_kvd( kvd_handle, KV_KeyName, keyname, charsmax( keyname ) );
get_kvd( kvd_handle, KV_Value, value, charsmax( value ) );
// Classification override
if ( equal( keyname, "classify" ) )
entity_set_int( entid, EV_INT_iuser4, str_to_num( value ) );
// Allied monster
if ( equal( keyname, "is_player_ally" ) )
{
if ( str_to_num( value ) )
entity_set_int( entid, EV_INT_iuser4, 11 ); // CLASS_PLAYER_ALLY
}
// Custom blood color
if ( equal( keyname, "bloodcolor" ) )
{
switch ( str_to_num( value ) )
{
case -1: entity_set_int( entid, EV_INT_iuser3, DONT_BLEED );
// 0 = Monster Default
case 1: entity_set_int( entid, EV_INT_iuser3, BLOOD_COLOR_RED );
case 2: entity_set_int( entid, EV_INT_iuser3, BLOOD_COLOR_YELLOW );
case 3: entity_set_int( entid, EV_INT_iuser3, BLOOD_COLOR_BLUE );
case 4: entity_set_int( entid, EV_INT_iuser3, BLOOD_COLOR_PINK );
case 5: entity_set_int( entid, EV_INT_iuser3, BLOOD_COLOR_WHITE );
case 6: entity_set_int( entid, EV_INT_iuser3, BLOOD_COLOR_ORANGE );
case 7: entity_set_int( entid, EV_INT_iuser3, BLOOD_COLOR_BLACK );
case 8: entity_set_int( entid, EV_INT_iuser3, BLOOD_COLOR_DARKGREEN );
}
}
// Redirect entity to MonsterMod
if ( equal( keyname, "use_monstermod" ) )
{
if ( str_to_num( value ) )
{
// MonsterMod will inherit this entity, remove it from here
remove_entity( entid );
return FMRES_SUPERCEDE;
}
}
return FMRES_IGNORED;
}
// Monster Makers
else if ( equal( classname, "monstermaker" ) )
{
get_kvd( kvd_handle, KV_KeyName, keyname, charsmax( keyname ) );
get_kvd( kvd_handle, KV_Value, value, charsmax( value ) );
// Classification override
if ( equal( keyname, "classify" ) )
entity_set_int( entid, EV_INT_iuser4, str_to_num( value ) );
// Allied monster
if ( equal( keyname, "respawn_as_playerally" ) )
{
if ( str_to_num( value ) )
entity_set_int( entid, EV_INT_iuser4, 11 ); // CLASS_PLAYER_ALLY
}
// Custom blood color
if ( equal( keyname, "bloodcolor" ) )
{
switch ( str_to_num( value ) )
{
case -1: entity_set_int( entid, EV_INT_iuser3, DONT_BLEED );
// 0 = Monster Default
case 1: entity_set_int( entid, EV_INT_iuser3, BLOOD_COLOR_RED );
case 2: entity_set_int( entid, EV_INT_iuser3, BLOOD_COLOR_YELLOW );
case 3: entity_set_int( entid, EV_INT_iuser3, BLOOD_COLOR_BLUE );
case 4: entity_set_int( entid, EV_INT_iuser3, BLOOD_COLOR_PINK );
case 5: entity_set_int( entid, EV_INT_iuser3, BLOOD_COLOR_WHITE );
case 6: entity_set_int( entid, EV_INT_iuser3, BLOOD_COLOR_ORANGE );
case 7: entity_set_int( entid, EV_INT_iuser3, BLOOD_COLOR_BLACK );
case 8: entity_set_int( entid, EV_INT_iuser3, BLOOD_COLOR_DARKGREEN );
}
}
// Redirect entity to MonsterMod
if ( equal( keyname, "use_monstermod" ) )
{
if ( str_to_num( value ) )
{
// MonsterMod will inherit this entity, remove it from here
remove_entity( entid );
return FMRES_IGNORED; // not a typo, must not supercede or it will crash
}
}
return FMRES_IGNORED;
}
}
return FMRES_IGNORED;
}
public MonsterSpawn_Post( entity )
{
// Why is it called 3 times? Restrict this to only once
if ( !( entity_get_int( entity, EV_INT_impulse ) & bits_MEMORY_SPAWN ) )
{
// monstermaker sets owner after monster spawn, wait next frame
set_task( 0.000001, "MakerSpawn_Post", entity );
entity_set_int( entity, EV_INT_impulse, entity_get_int( entity, EV_INT_impulse ) | bits_MEMORY_SPAWN );
}
}
public MakerSpawn_Post( entity )
{
if ( is_valid_ent( entity ) )
{
static owner;
owner = entity_get_edict( entity, EV_ENT_owner );
if ( owner )
{
// pass parent configurations to child entity
entity_set_int( entity, EV_INT_iuser4, entity_get_int( owner, EV_INT_iuser4 ) ); // classify
entity_set_int( entity, EV_INT_iuser3, entity_get_int( owner, EV_INT_iuser3 ) ); // bloodcolor
}
}
}
public MonsterBlood( entity )
{
static newBlood;
newBlood = entity_get_int( entity, EV_INT_iuser3 );
if ( newBlood )
{
SetHamReturnInteger( newBlood );
return HAM_OVERRIDE;
}
return HAM_IGNORED;
}

View File

@@ -0,0 +1,200 @@
#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;
const bits_MEMORY_NAMED = ( 1 << 2 );
public plugin_init()
{
register_plugin( "HL-MONSTER Bridge", "1.2", "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" );
// HACK: since Ham_Spawn won't work, this should do as an alternative
RegisterHam( Ham_SetObjectCollisionBox, "monster_headcrab", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_babycrab", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_bullchicken", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_barnacle", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_bigmomma", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_houndeye", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_alien_slave", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_alien_controller", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_alien_grunt", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_zombie", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_ichthyosaur", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_human_grunt", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_human_assassin", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_barney", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_gman", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_scientist", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_sentry", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_snark", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_miniturret", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_turret", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_apache", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_osprey", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_gargantua", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_nihilanth", "hlSpawn", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_tentacle", "hlSpawn", 1 );
RegisterHam( Ham_Killed, "player", "PlayerKilled", 1 );
}
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 hlSpawn( entity )
{
// Why is it called 3 times? Restrict this to only once
if ( !( entity_get_int( entity, EV_INT_impulse ) & bits_MEMORY_NAMED ) )
{
// 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?
new szDisplayname[ 129 ];
entity_get_string( entity, EV_SZ_netname, szDisplayname, charsmax( szDisplayname ) );
if ( !strlen( szDisplayname ) )
{
// Find a default name
new szClassname[ 33 ], bool:found;
entity_get_string( entity, EV_SZ_classname, szClassname, charsmax( szClassname ) );
found = TrieGetString( g_HLDefaultNames, szClassname, szDisplayname, charsmax( szDisplayname ) );
// Use this name
entity_set_string( entity, EV_SZ_netname, ( found ? szDisplayname : "Monster" ) );
}
entity_set_int( entity, EV_INT_impulse, entity_get_int( entity, EV_INT_impulse ) | bits_MEMORY_NAMED );
}
}
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 PlayerKilled( victim, attacker, shouldgib )
{
// don't obstruct monstermod
if ( victim == attacker )
return HAM_IGNORED;
// fix monster score
if ( entity_get_int( attacker, EV_INT_flags ) & FL_MONSTER )
entity_set_float( attacker, EV_FL_frags, entity_get_float( attacker, EV_FL_frags ) + 2 );
entity_set_edict( victim, EV_ENT_dmg_inflictor, attacker );
return HAM_IGNORED;
}

View File

@@ -0,0 +1,204 @@
#pragma semicolon 1
#include <amxmodx>
#include <engine>
#include <fakemeta>
#include <hamsandwich>
const bits_MEMORY_SOUNDLIST = ( 1 << 3 );
new Trie:m_Sounds;
public plugin_init()
{
register_plugin( "HL-MONSTER Soundlist", "1.0", "Giegue" );
/* STOP DUPLICATING CODE FFS */
RegisterHam( Ham_SetObjectCollisionBox, "monster_headcrab", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_babycrab", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_bullchicken", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_barnacle", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_bigmomma", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_houndeye", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_alien_slave", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_alien_controller", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_alien_grunt", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_zombie", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_ichthyosaur", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_human_grunt", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_human_assassin", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_barney", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_gman", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_scientist", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_sentry", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_snark", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_miniturret", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_turret", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_apache", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_osprey", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_gargantua", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_nihilanth", "MonsterSpawn_Post", 1 );
RegisterHam( Ham_SetObjectCollisionBox, "monster_tentacle", "MonsterSpawn_Post", 1 );
}
public plugin_end()
{
if ( m_Sounds )
TrieDestroy( m_Sounds );
}
// has to be in precache or it won't work
public plugin_precache()
{
// check individual monster soundlists
register_forward( FM_KeyValue, "ScanSL" );
}
public ScanSL( entid, kvd_handle )
{
if (is_valid_ent(entid))
{
static classname[ 33 ], keyname[ 33 ], value[ 128 ];
get_kvd( kvd_handle, KV_ClassName, classname, charsmax( classname ) );
// Monsters
if ( equal( classname, "monster_", 8 ) )
{
get_kvd( kvd_handle, KV_KeyName, keyname, charsmax( keyname ) );
get_kvd( kvd_handle, KV_Value, value, charsmax( value ) );
// Individual sound replacement
if ( equal( keyname, "soundlist" ) )
{
ProcessSoundList( entid, value );
}
return FMRES_IGNORED;
}
// Monster Makers
else if ( equal( classname, "monstermaker" ) )
{
get_kvd( kvd_handle, KV_KeyName, keyname, charsmax( keyname ) );
get_kvd( kvd_handle, KV_Value, value, charsmax( value ) );
// Children sound list
if ( equal( keyname, "soundlist" ) )
{
ProcessSoundList( entid, value );
}
return FMRES_IGNORED;
}
}
return FMRES_IGNORED;
}
public ProcessSoundList( entity, const filename[] )
{
// First time?
if ( !m_Sounds )
m_Sounds = TrieCreate();
new fullPath[ 129 ];
new mapName[ 33 ], pFile;
get_mapname( mapName, charsmax( mapName ) );
// path always starts from sound/[MAPNAME] (SC behaviour)
formatex( fullPath, charsmax( fullPath ), "sound/%s/%s", mapName, filename );
pFile = fopen( fullPath, "r" );
if ( pFile )
{
new line[ 258 ], soundSrc[ 129 ], soundDest[ 129 ];
while ( fgets( pFile, line, charsmax( line ) ) )
{
// Replace newlines
replace_all( line, charsmax( line ), "^n", "" );
// Ignore blank lines
if ( !line[ 0 ] ) continue;
// source --> destination
parse( line, soundSrc, charsmax( soundSrc ), soundDest, charsmax( soundDest ) );
// Precache destination sound
// HACK: precache_sound outside of plugin_precache
engfunc( EngFunc_PrecacheSound, soundDest );
// HACK: prepend the entityID at the beginning of the soundSrc for later identification
format( soundSrc, charsmax( soundSrc ), "%i#%s", entity, soundSrc );
TrieSetString( m_Sounds, soundSrc, soundDest );
entity_set_int( entity, EV_INT_impulse, entity_get_int( entity, EV_INT_impulse ) | bits_MEMORY_SOUNDLIST );
}
fclose( pFile );
// file could be empty
if ( TrieGetSize( m_Sounds ) )
{
register_forward( FM_EmitSound, "ReplaceSound" );
}
}
}
public ReplaceSound( entity, channel, const sample[], Float:volume, Float:attn, flags, pitch )
{
static newSound[ 129 ];
// replace monster sound?
if ( entity_get_int( entity, EV_INT_impulse ) & bits_MEMORY_SOUNDLIST )
{
// get entityID
static owner, entid;
owner = entity_get_edict( entity, EV_ENT_owner );
if ( owner )
entid = owner;
else
entid = entity;
// get sound
static searchSound[ 129 ];
formatex( searchSound, charsmax( searchSound ), "%i#%s", entid, sample );
// if found, stick to that one
if ( TrieGetString( m_Sounds, searchSound, newSound, charsmax( newSound ) ) )
{
// emit new sound and supercede this one
emit_sound( entity, channel, newSound, volume, attn, flags, pitch );
return FMRES_SUPERCEDE;
}
}
return FMRES_IGNORED;
}
/* extra_keyvalues.sma duplication */
public MonsterSpawn_Post( entity )
{
// monstermaker sets owner after monster spawn, wait next frame
set_task( 0.000001, "MakerSpawn_Post", entity );
}
public MakerSpawn_Post( entity )
{
if ( is_valid_ent( entity ) )
{
static owner;
owner = entity_get_edict( entity, EV_ENT_owner );
if ( owner )
{
// monstermaker has soundlist defined?
if ( entity_get_int( owner, EV_INT_impulse ) & bits_MEMORY_SOUNDLIST )
{
// 3 time call
if ( !( entity_get_int( entity, EV_INT_impulse ) & bits_MEMORY_SOUNDLIST ) )
{
// this monster is to use sound replacements
entity_set_int( entity, EV_INT_impulse, entity_get_int( entity, EV_INT_impulse ) | bits_MEMORY_SOUNDLIST );
}
}
}
}
}

4
src/dlls/.gitignore vendored
View File

@@ -1,5 +1,9 @@
Release/
Debug/
msgs/
opt.*/
debug.*/
*.o
*.so
*.suo
*.sdf

View File

@@ -1085,7 +1085,7 @@ jlb*/
{
m_movementActivity = ACT_FLY;
}
if ( LookupActivity( ACT_WALK ) != ACTIVITY_NOT_AVAILABLE )
else if ( LookupActivity( ACT_WALK ) != ACTIVITY_NOT_AVAILABLE )
{
m_movementActivity = ACT_WALK;
}

View File

@@ -1,5 +1,5 @@
CPP = g++
BASEFLAGS = -Dstricmp=strcasecmp -Dstrcmpi=strcasecmp -m32
BASEFLAGS = -Dstricmp=strcasecmp -Dstrcmpi=strcasecmp -m32 -fPIC
OPTFLAGS = -O2
CPPFLAGS = ${BASEFLAGS} ${OPTFLAGS} -w -I. -I../engine -I../common -I../pm_shared -I../metamod
@@ -22,11 +22,12 @@ OBJ = \
flyingmonster.o \
gargantua.o \
ggrenade.o \
globalreplace.o \
gonome.o \
h_ai.o \
h_export.o \
hassassin.o \
headcrab.o \
h_export.o \
hgrunt.o \
hornet.o \
houndeye.o \
@@ -35,12 +36,15 @@ OBJ = \
massn.o \
monster_api.o \
monster_config.o \
monstermaker.o \
monsters.o \
monsterstate.o \
music.o \
nodes.o \
otis.o \
pitdrone.o \
rgrunt.o \
ripent.o \
scientist.o \
shock.o \
shockroach.o \

View File

@@ -87,6 +87,13 @@ const char *CMAGrunt::pAttackSounds[] =
"agrunt/ag_attack3.wav",
};
const char *CMAGrunt::pFireSounds[] =
{
"agrunt/ag_fire1.wav",
"agrunt/ag_fire2.wav",
"agrunt/ag_fire3.wav",
};
const char *CMAGrunt::pDieSounds[] =
{
"agrunt/ag_die1.wav",
@@ -184,13 +191,8 @@ void CMAGrunt :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vec
if (flDamage <= 0)
flDamage = 0.1;// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated
}
else
{
SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage);// a little surface blood.
TraceBleed( flDamage, vecDir, ptr, bitsDamageType );
}
AddMultiDamage( pevAttacker, this->edict(), flDamage, bitsDamageType );
CMBaseMonster::TraceAttack(pevAttacker, flDamage, vecDir, ptr, bitsDamageType);
}
//=========================================================
@@ -418,6 +420,8 @@ void CMAGrunt :: HandleAnimEvent( MonsterEvent_t *pEvent )
UTIL_MakeVectors ( pHornet->pev->angles );
pHornet->pev->velocity = gpGlobals->v_forward * 300;
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, pFireSounds[RANDOM_LONG(0, ARRAYSIZE(pFireSounds) - 1)], 1.0, ATTN_NORM, 0, 100);
CMBaseMonster *pHornetMonster = pHornet->MyMonsterPointer();
if ( pHornetMonster )
@@ -532,12 +536,12 @@ void CMAGrunt :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/agrunt.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/agrunt.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;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->effects = 0;
pev->health = gSkillData.agruntHealth;
m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
@@ -563,35 +567,18 @@ void CMAGrunt :: Spawn()
//=========================================================
void CMAGrunt :: Precache()
{
int i;
PRECACHE_MODEL("models/agrunt.mdl");
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ )
PRECACHE_SOUND((char *)pAttackHitSounds[i]);
PRECACHE_SOUND_ARRAY( pAttackHitSounds );
PRECACHE_SOUND_ARRAY( pAttackHitSounds );
PRECACHE_SOUND_ARRAY( pAttackMissSounds );
PRECACHE_SOUND_ARRAY( pIdleSounds );
PRECACHE_SOUND_ARRAY( pDieSounds );
PRECACHE_SOUND_ARRAY( pPainSounds );
PRECACHE_SOUND_ARRAY( pAttackSounds );
PRECACHE_SOUND_ARRAY( pAlertSounds );
for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ )
PRECACHE_SOUND((char *)pAttackMissSounds[i]);
for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ )
PRECACHE_SOUND((char *)pIdleSounds[i]);
for ( i = 0; i < ARRAYSIZE( pDieSounds ); i++ )
PRECACHE_SOUND((char *)pDieSounds[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( pAlertSounds ); i++ )
PRECACHE_SOUND((char *)pAlertSounds[i]);
PRECACHE_SOUND( "hassault/hw_shoot1.wav" );
iAgruntMuzzleFlash = PRECACHE_MODEL( "sprites/muz4.spr" );
iAgruntMuzzleFlash = PRECACHE_MODELINDEX("sprites/muz4.spr");
CMHornet hornet;
hornet.Precache();

View File

@@ -163,7 +163,11 @@ int LookupSequence( void *pmodel, const char *label )
for (int i = 0; i < pstudiohdr->numseq; i++)
{
#if defined (_WIN32)
if (_stricmp( pseqdesc[i].label, label ) == 0)
#else
if (stricmp( pseqdesc[i].label, label ) == 0)
#endif
return i;
}
@@ -209,7 +213,7 @@ void SequencePrecache( void *pmodel, const char *pSequenceName )
ALERT( at_error, "Bad sound event %d in sequence %s :: %s (sound is \"%s\")\n", pevent[i].event, pstudiohdr->name, pSequenceName, pevent[i].options );
}
PRECACHE_SOUND( (char *)(gpGlobals->pStringBase + ALLOC_STRING(pevent[i].options) ) );
PRECACHE_SOUND2( (char *)(gpGlobals->pStringBase + ALLOC_STRING(pevent[i].options) ) );
}
}
}

View File

@@ -35,7 +35,7 @@ void CMApache :: Spawn( void )
pev->movetype = MOVETYPE_FLY;
pev->solid = SOLID_BBOX;
SET_MODEL(ENT(pev), "models/apache.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/apache.mdl"));
UTIL_SetSize( pev, Vector( -32, -32, -64 ), Vector( 32, 32, 0 ) );
UTIL_SetOrigin( pev, pev->origin );
@@ -105,14 +105,14 @@ void CMApache::Precache( void )
PRECACHE_SOUND("weapons/mortarhit.wav");
m_iSpriteTexture = PRECACHE_MODEL( "sprites/white.spr" );
m_iSpriteTexture = PRECACHE_MODELINDEX( "sprites/white.spr" );
PRECACHE_SOUND("turret/tu_fire1.wav");
PRECACHE_MODEL("sprites/lgtning.spr");
m_iExplode = PRECACHE_MODEL( "sprites/fexplo.spr" );
m_iBodyGibs = PRECACHE_MODEL( "models/metalplategibs_green.mdl" );
m_iExplode = PRECACHE_MODELINDEX( "sprites/fexplo.spr" );
m_iBodyGibs = PRECACHE_MODELINDEX( "models/metalplategibs_green.mdl" );
CMApacheHVR apache_rocket;
apache_rocket.Precache();
@@ -157,6 +157,11 @@ void CMApache :: Killed( entvars_t *pevAttacker, int iGib )
pev->health = 0;
pev->takedamage = DAMAGE_NO;
pev->deadflag = DEAD_DYING;
FCheckAITrigger(); // trigger death condition
if ( UTIL_IsPlayer( ENT( pevAttacker ) ) ) // If a player killed this monster, add score
pevAttacker->frags += 1.0;
if (pev->spawnflags & SF_NOWRECKAGE)
{
m_flNextRocket = gpGlobals->time + 4.0;
@@ -178,7 +183,7 @@ void CMApache :: DyingThink( void )
if (m_flNextRocket > gpGlobals->time )
{
if (g_sModelIndexFireball == 0)
g_sModelIndexFireball = PRECACHE_MODEL ("sprites/zerogxplode.spr"); // fireball
g_sModelIndexFireball = PRECACHE_MODELINDEX("sprites/zerogxplode.spr"); // fireball
// random explosions
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
@@ -193,7 +198,7 @@ void CMApache :: DyingThink( void )
MESSAGE_END();
if (g_sModelIndexSmoke == 0)
g_sModelIndexSmoke = PRECACHE_MODEL ("sprites/steam1.spr"); // smoke
g_sModelIndexSmoke = PRECACHE_MODELINDEX("sprites/steam1.spr"); // smoke
// lots of smoke
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
@@ -419,7 +424,7 @@ void CMApache :: HuntThink( void )
// if (m_hEnemy == NULL)
{
Look( 4092 );
Look( 4096 );
m_hEnemy = BestVisibleEnemy( );
}
@@ -930,7 +935,7 @@ void CMApacheHVR :: Spawn( void )
void CMApacheHVR :: Precache( void )
{
PRECACHE_MODEL("models/HVR.mdl");
m_iTrail = PRECACHE_MODEL("sprites/smoke.spr");
m_iTrail = PRECACHE_MODELINDEX("sprites/smoke.spr");
PRECACHE_SOUND ("weapons/rocket1.wav");
}

View File

@@ -345,12 +345,12 @@ void CMBarney :: Spawn()
// when a level is loaded, nobody will talk (time is reset to 0)
TalkInit();
SET_MODEL(ENT(pev), "models/barney.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/barney.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_RED;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_RED : m_bloodColor;
pev->health = gSkillData.barneyHealth;
pev->view_ofs = Vector ( 0, 0, 50 );// position of the eyes relative to monster's origin.
m_flFieldOfView = VIEW_FIELD_FULL;

View File

@@ -603,13 +603,13 @@ void CMBigMomma :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/big_mom.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/big_mom.mdl"));
// UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
UTIL_SetSize( pev, Vector( -64, -64, 0 ), Vector( 64, 64, 128 ) );
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->health = 150 * gSkillData.bigmommaHealthFactor;
pev->view_ofs = Vector ( 0, 0, 128 );// position of the eyes relative to monster's origin.
m_flFieldOfView = 0.3;// indicates the width of this monster's forward view cone ( as a dotproduct result )
@@ -648,8 +648,8 @@ void CMBigMomma :: Precache()
// TEMP: Squid
PRECACHE_MODEL("sprites/mommaspit.spr");// spit projectile.
gSpitSprite = PRECACHE_MODEL("sprites/mommaspout.spr");// client side spittle.
gSpitDebrisSprite = PRECACHE_MODEL("sprites/mommablob.spr" );
gSpitSprite = PRECACHE_MODELINDEX("sprites/mommaspout.spr");// client side spittle.
gSpitDebrisSprite = PRECACHE_MODELINDEX("sprites/mommablob.spr" );
PRECACHE_SOUND( "bullchicken/bc_acid1.wav" );
PRECACHE_SOUND( "bullchicken/bc_spithit1.wav" );

View File

@@ -166,6 +166,8 @@ void CSquidSpit :: SpitTouch ( edict_t *pOther )
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
pMonster->TakeDamage ( pev, pev, gSkillData.bullsquidDmgSpit, DMG_GENERIC );
}
else
UTIL_TakeDamageExternal( pOther, pev, pev, gSkillData.bullsquidDmgSpit, DMG_GENERIC );
}
SetThink ( &CSquidSpit::SUB_Remove );
@@ -608,12 +610,12 @@ void CMBullsquid :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/bullsquid.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/bullsquid.mdl"));
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->effects = 0;
pev->health = gSkillData.bullsquidHealth;
m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
@@ -640,7 +642,7 @@ void CMBullsquid :: Precache()
PRECACHE_MODEL("sprites/bigspit.spr");// spit projectile.
iSquidSpitSprite = PRECACHE_MODEL("sprites/tinyspit.spr");// client side spittle.
iSquidSpitSprite = PRECACHE_MODELINDEX("sprites/tinyspit.spr");// client side spittle.
PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event

View File

@@ -17,6 +17,8 @@
#include "cmbase.h"
#include "decals.h"
void EntvarsKeyvalue( entvars_t *pev, KeyValueData *pkvd );
extern Vector VecBModelOrigin( entvars_t* pevBModel );
extern DLL_GLOBAL Vector g_vecAttackDir;
@@ -103,6 +105,15 @@ edict_t *CMBaseEntity::CreateEntity(char *classname)
return pent;
}
// process entvar keyvalue
void CMBaseEntity :: KeyValue( KeyValueData* pkvd )
{
if ( !pev || !pkvd )
return;
EntvarsKeyvalue( pev, pkvd );
}
// give health
int CMBaseEntity :: TakeHealth( float flHealth, int bitsDamageType )
{

View File

@@ -134,7 +134,7 @@ public:
// initialization functions
virtual void Spawn( void ) { return; }
virtual void Precache( void ) { return; }
virtual void KeyValue( KeyValueData* pkvd) { pkvd->fHandled = FALSE; }
virtual void KeyValue( KeyValueData* pkvd );
virtual int ObjectCaps( void ) { return FCAP_ACROSS_TRANSITION; }
virtual void Activate( void ) {}
@@ -601,7 +601,6 @@ template <class T> T * CreateClassPtr( T *a )
// store the class pointer in the array here!!!
monsters[monster_index].monster_index = edict_index;
monsters[monster_index].monster_pent = temp_edict;
monsters[monster_index].respawn_index = -1;
monsters[monster_index].pMonster = (CMBaseMonster *)a;
// get the private data
@@ -609,3 +608,7 @@ template <class T> T * CreateClassPtr( T *a )
return a;
}
#ifndef GLOBALREPLACE_H
#include "globalreplace.h"
#endif

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

@@ -0,0 +1,51 @@
#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_iMonsterBlood;//blood color of spawned monsters.
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?
};
//=========================================================
// Ambient Music - Plays an mp3 music file to players.
//=========================================================
class CMAmbientMusic : public CMBaseMonster
{
public:
void Spawn(void);
//void Precache(void); // accessed before entvars are valid, manual precache in monster_config.cpp
void KeyValue(KeyValueData* pkvd);
void EXPORT MusicUse(edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value);
BOOL m_fPlaying; // music is active
};
#endif // BASEEXTRA_H

View File

@@ -105,6 +105,11 @@ public:
string_t m_szMonsterName; // Monster name to display on HUD
int m_iClassifyOverride; // Overriden classification for this monster
REPLACER::REPLACER *m_srSoundList; // individual sound replacements for this monster
int m_isrSounds; // number of replaced sounds
float m_flLastYawTime;
void KeyValue( KeyValueData *pkvd );
// monster use function
@@ -729,6 +734,7 @@ public:
static const char *pAttackHitSounds[];
static const char *pAttackMissSounds[];
static const char *pAttackSounds[];
static const char *pFireSounds[];
static const char *pDieSounds[];
static const char *pPainSounds[];
static const char *pIdleSounds[];
@@ -1332,6 +1338,11 @@ public:
void DeathSound(void);
void PainSound(void);
void IdleSound(void);
void SetActivity(Activity NewActivity);
Schedule_t *GetScheduleOfType( int Type );
CUSTOM_SCHEDULES;
};
//=========================================================
@@ -1371,6 +1382,8 @@ public:
void EXPORT SpikeTouch(edict_t *pOther);
void EXPORT StartTrail();
static edict_t *Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, Vector vecAngles);
EHANDLE m_hOwner;
};
//=========================================================
@@ -1434,6 +1447,7 @@ public:
void AlertSound(void);
void MonsterThink(void);
void StartTask(Task_t* pTask);
void HandleAnimEvent(MonsterEvent_t *pEvent);
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
static const char *pIdleSounds[];
@@ -1546,6 +1560,7 @@ public:
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);
void UpdateOnRemove();
CUSTOM_SCHEDULES
@@ -1667,8 +1682,6 @@ public:
void Minigun(void);
CUSTOM_SCHEDULES
float m_flMinigunSpinTime;
};
//=========================================================
@@ -1679,14 +1692,11 @@ 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 );
@@ -1704,6 +1714,8 @@ public:
float m_flActiveDischarge;
int m_iBodyGibs;
static const char *pRobotSentences[];
};
//=========================================================

View File

@@ -130,7 +130,7 @@ public:
int FIdleStare( void );
int FIdleHello( void );
void IdleHeadTurn( Vector &vecFriend );
int FOkToSpeak( void );
virtual int FOkToSpeak( void );
void TrySmellTalk( void );
edict_t *EnumFriends( edict_t *pentPrevious, int listNumber, BOOL bTrace );
void AlertFriends( void );

View File

@@ -30,8 +30,7 @@
#include "weapons.h"
#include "func_break.h"
const Vector g_vecZero = Vector(0,0,0);
Vector g_vecAttackDir;
extern DLL_GLOBAL Vector g_vecAttackDir;
entvars_t *g_pevLastInflictor;
#define HUMAN_GIB_COUNT 6
@@ -618,14 +617,11 @@ void CMBaseMonster :: Killed( entvars_t *pevAttacker, int iGib )
SetConditions( bits_COND_LIGHT_DAMAGE );
// tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality.
/*jlb monstermaker
CMBaseEntity *pOwner = CMBaseEntity::Instance(pev->owner);
if ( pOwner )
{
//jlb it crashes here sometimes!!!
pOwner->DeathNotice( pev );
}
jlb*/
if ( ShouldGibMonster( iGib ) )
{
@@ -1154,11 +1150,8 @@ void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacke
}
}
}
else if (!UTIL_IsPlayer(pEntity))
else
{
// I'm doing really bad copypastes instead of making the code clean!
// Remind me to refactor this, this is not how this is supposed to be written!
// -Giegue
edict_t *pMonster = pEntity;
if ( iClassIgnore != CLASS_NONE && pMonster->v.iuser4 == iClassIgnore )
@@ -1260,7 +1253,7 @@ edict_t* CMBaseMonster :: CheckTraceHullAttack( float flDist, int iDamage, int i
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
pMonster->TakeDamage( pev, pev, iDamage, iDmgType );
}
else if (!UTIL_IsPlayer(pEntity))
else
UTIL_TakeDamageExternal( pEntity, pev, pev, iDamage, iDmgType );
}
@@ -1329,32 +1322,32 @@ void CMBaseMonster :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vecto
{
m_LastHitGroup = ptr->iHitgroup;
/*jlb
// do we need the hitboxes to be customizable? use vanilla HL skill.cfg for now
switch ( ptr->iHitgroup )
{
case HITGROUP_GENERIC:
break;
case HITGROUP_HEAD:
flDamage *= gSkillData.monHead;
flDamage *= 3; //gSkillData.monHead;
break;
case HITGROUP_CHEST:
flDamage *= gSkillData.monChest;
flDamage *= 1; //gSkillData.monChest;
break;
case HITGROUP_STOMACH:
flDamage *= gSkillData.monStomach;
flDamage *= 1; //gSkillData.monStomach;
break;
case HITGROUP_LEFTARM:
case HITGROUP_RIGHTARM:
flDamage *= gSkillData.monArm;
flDamage *= 1; //gSkillData.monArm;
break;
case HITGROUP_LEFTLEG:
case HITGROUP_RIGHTLEG:
flDamage *= gSkillData.monLeg;
flDamage *= 1; //gSkillData.monLeg;
break;
default:
break;
}
jlb*/
SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage);// a little surface blood.
TraceBleed( flDamage, vecDir, ptr, bitsDamageType );
AddMultiDamage( pevAttacker, this->edict(), flDamage, bitsDamageType );
@@ -1571,7 +1564,7 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
}
}
}
else if (!UTIL_IsPlayer(tr.pHit)) // normal game monster
else // normal game entity
{
edict_t *pMonster = tr.pHit;

View File

@@ -302,13 +302,13 @@ void CMController :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/controller.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/controller.mdl"));
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ));
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_FLY;
pev->flags |= FL_FLY;
m_bloodColor = BLOOD_COLOR_GREEN;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->health = gSkillData.controllerHealth;
pev->view_ofs = Vector( 0, 0, -2 );// position of the eyes relative to monster's origin.
m_flFieldOfView = VIEW_FIELD_FULL;// indicates the width of this monster's forward view cone ( as a dotproduct result )
@@ -1185,6 +1185,8 @@ void CMControllerHeadBall :: HuntThink( void )
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit));
pMonster->TraceAttack( VARS(m_hOwner), gSkillData.controllerDmgZap, pev->velocity, &tr, DMG_SHOCK );
}
else
UTIL_TraceAttack( tr.pHit, VARS(m_hOwner), gSkillData.controllerDmgZap, pev->velocity, &tr, DMG_SHOCK );
ApplyMultiDamage( pev, VARS(m_hOwner) );
}
@@ -1361,6 +1363,8 @@ void CMControllerZapBall::ExplodeTouch( edict_t *pOther )
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
pMonster->TraceAttack(pevOwner, gSkillData.controllerDmgBall, pev->velocity.Normalize(), &tr, DMG_ENERGYBEAM );
}
else
UTIL_TraceAttack(pOther, pevOwner, gSkillData.controllerDmgBall, pev->velocity.Normalize(), &tr, DMG_ENERGYBEAM );
ApplyMultiDamage( pevOwner, pevOwner );
UTIL_EmitAmbientSound( ENT(pev), tr.vecEndPos, "weapons/electro4.wav", 0.3, ATTN_NORM, 0, RANDOM_LONG( 90, 99 ) );

View File

@@ -42,6 +42,7 @@
#include "cmbase.h"
#include "cmbasemonster.h"
#include "cmbaseextra.h"
#include "monsters.h"
#include "weapons.h"
#include "hornet.h"
@@ -61,18 +62,15 @@ extern cvar_t *monster_spawn;
extern cvar_t *monster_show_deaths;
extern cvar_t *monster_show_info;
// compiler does not like util.h being included here so i'll just extern the function
extern void SENTENCEG_Init();
// Player TakeDamage and Killed
int g_DamageMsg;
bool g_DamageActive;
int g_DamageVictim;
edict_t *g_DamageAttacker[33];
int g_DamageBits[33];
bool g_PlayerKilled[33];
float g_flWaitTillMessage[33];
// DeathMsg
int g_DeathMsg;
bool g_DeathActive;
// TE_TEXTMESSAGE
float g_NextMessage[33];
@@ -138,7 +136,7 @@ monster_type_t monster_types[]=
"monster_apache", FALSE,
"monster_barney", FALSE,
"monster_bigmomma", FALSE,
"monster_bullsquid", FALSE,
"monster_bullchicken", FALSE,
"monster_alien_controller", FALSE,
"monster_human_assassin", FALSE,
"monster_headcrab", FALSE,
@@ -158,14 +156,17 @@ monster_type_t monster_types[]=
"monster_pitdrone", FALSE,
"monster_shockroach", FALSE,
"monster_shocktrooper", FALSE,
"monster_voltigore", FALSE,
"monster_baby_voltigore", FALSE,
"monster_alien_voltigore", FALSE,
"monster_alien_babyvoltigore", FALSE,
"monster_babygarg", FALSE, // Sven Co-op Monsters
"monster_hwgrunt", FALSE,
"monster_robogrunt", FALSE,
"monster_stukabat", FALSE,
"info_node", FALSE, // Nodes
"info_node_air", FALSE,
"monstermaker", FALSE, // Extra entities
"ambient_music", FALSE,
"squadmaker", FALSE, // Aliases
"", FALSE
};
@@ -179,6 +180,7 @@ node_spawnpoint_t node_spawnpoint[MAX_NODES];
int node_spawn_count = 0;
float check_respawn_time;
float check_graph_time;
bool process_monster_cfg(void);
bool process_monster_precache_cfg(void);
@@ -209,15 +211,6 @@ int GetMonsterIndex(void)
void FreeMonsterIndex(int index)
{
int idx = monsters[index].respawn_index;
if (idx != -1)
{
monster_spawnpoint[idx].need_to_respawn = TRUE;
monster_spawnpoint[idx].respawn_time = gpGlobals->time +
monster_spawnpoint[idx].delay;
}
delete monsters[index].pMonster;
monsters[index].monster_index = 0;
@@ -309,7 +302,7 @@ void check_monster_hurt(edict_t *pAttacker)
vecSpot = vecSrc + gpGlobals->v_forward * distance;
// trace a line ignoring enemies body...
UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, pent, &tr );
UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, pAttacker, &tr );
damage = pent->v.fuser4 - pent->v.health;
@@ -317,7 +310,7 @@ void check_monster_hurt(edict_t *pAttacker)
pent->v.health = pent->v.fuser4;
ClearMultiDamage( );
monsters[index].pMonster->TraceAttack( VARS(pAttacker), damage, (tr.vecEndPos - vecSrc).Normalize( ), &tr, DMG_BULLET );
monsters[index].pMonster->TraceAttack( VARS(pAttacker), damage, (tr.vecEndPos - vecSrc).Normalize( ), &tr, DMG_BULLET|DMG_NEVERGIB );
ApplyMultiDamage( VARS(pAttacker), VARS(pAttacker) );
}
@@ -400,41 +393,81 @@ void check_player_dead( edict_t *pPlayer )
// Killed by a monster?
if ( pAttacker->v.flags & FL_MONSTER )
{
// Check the first character for 'aeiou'.
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pAttacker));
char szCheck[2];
strncpy( szCheck, STRING( pMonster->m_szMonsterName ), 1 );
// Try to get the name of the monster
char szName[129], szCheck[2];
// Make the first character lowercase
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pAttacker));
if ( pMonster != NULL )
{
// One of our monsters
strcpy(szName, STRING( pMonster->m_szMonsterName ));
}
else
{
// Does this monster have a name?
if ( !FStringNull( pAttacker->v.netname ) )
strcpy(szName, STRING( pAttacker->v.netname ));
else
{
// No name, use class
strcpy(szName, STRING( pAttacker->v.classname ));
}
}
// Now, copy the first character to check for 'aeiou'.
strncpy( szCheck, szName, 1 );
// Make this character lowercase and inspect it. Select which message.
szCheck[0] = tolower( szCheck[ 0 ] );
if ( strncmp( szCheck, "a", 1 ) == 0 || strncmp( szCheck, "e", 1 ) == 0 || strncmp( szCheck, "i", 1 ) == 0 || strncmp( szCheck, "o", 1 ) == 0 || strncmp( szCheck, "u", 1 ) == 0 )
sprintf( szMessage, "* %s was killed by an %s.\n", szPlayerName, STRING( pMonster->m_szMonsterName ) );
sprintf( szMessage, "* %s was killed by an %s.\n", szPlayerName, szName );
else
sprintf( szMessage, "* %s was killed by a %s.\n", szPlayerName, STRING( pMonster->m_szMonsterName ) );
sprintf( szMessage, "* %s was killed by a %s.\n", szPlayerName, szName );
}
else
{
// Suicide?
if ( pAttacker == pPlayer )
sprintf( szMessage, "* %s commited suicide.\n", szPlayerName );
// Player killed by another player
else if ( UTIL_IsPlayer( pAttacker ) )
{
// Get attacker name
char szAttackerName[33];
strcpy(szAttackerName, STRING(pAttacker->v.netname));
// Print a very basic death message until we can detect teamkills
sprintf( szMessage, "* %s was killed by %s.\n", szPlayerName, szAttackerName );
}
// An entity killed this player.
else if ( ENTINDEX( pAttacker ) > 0 )
{
// HLSDK: "int visibleDamageBits = m_bitsDamageType & DMG_SHOWNHUD;"
// When the game sends a "Damage" NetworkMessage, the damageBits
// gets filtered so only damages that can be shown on HUD will pass
// through, otherwise it gets neutered to DMG_GENERIC.
// This means that players will only see a few death messages
// instead of all possibilities that are written here.
// If you want to make use of all possible death messages, you
// will have to override the NetworkMessage. -Giegue
// Gather damage type and format death message
if ( g_DamageBits[ iPlayerIndex ] == DMG_GENERIC )
sprintf( szMessage, "* %s died mysteriously.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_CRUSH )
sprintf( szMessage, "* %s was smashed.\n", szPlayerName );
sprintf( szMessage, "* %s was crushed.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_BULLET )
sprintf( szMessage, "* %s was shot.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLASH )
sprintf( szMessage, "* %s lost its jelly.\n", szPlayerName );
sprintf( szMessage, "* %s has been chopped.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_BURN )
sprintf( szMessage, "* %s burned to death.\n", szPlayerName );
sprintf( szMessage, "* %s burned down.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_FREEZE )
sprintf( szMessage, "* %s froze to death.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_FALL )
sprintf( szMessage, "* %s broke its bones.\n", szPlayerName );
sprintf( szMessage, "* %s fell.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_BLAST )
sprintf( szMessage, "* %s blew up.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_CLUB )
@@ -444,33 +477,29 @@ void check_player_dead( edict_t *pPlayer )
else if ( g_DamageBits[ iPlayerIndex ] & DMG_SONIC )
sprintf( szMessage, "* %s ears popped.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_ENERGYBEAM )
sprintf( szMessage, "* %s saw the pretty lights.\n", szPlayerName );
sprintf( szMessage, "* %s was cut by a laser.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] == DMG_NEVERGIB )
sprintf( szMessage, "* %s had a painful death.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] == DMG_ALWAYSGIB )
sprintf( szMessage, "* %s was gibbed.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_DROWN )
sprintf( szMessage, "* %s became too drunk.\n", szPlayerName );
sprintf( szMessage, "* %s drowned.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_PARALYZE )
sprintf( szMessage, "* %s was paralyzed.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_NERVEGAS )
sprintf( szMessage, "* %s lost its brain.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_POISON )
sprintf( szMessage, "* %s had a slow death.\n", szPlayerName );
sprintf( szMessage, "* %s has been poisoned.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_RADIATION )
sprintf( szMessage, "* %s went nuclear.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_DROWNRECOVER )
sprintf( szMessage, "* %s used too much flex tape.\n", szPlayerName ); // is this type of death even possible?
else if ( g_DamageBits[ iPlayerIndex ] & DMG_ACID )
sprintf( szMessage, "* %s was melted.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLOWBURN )
sprintf( szMessage, "* %s became a cake.\n", szPlayerName );
sprintf( szMessage, "* %s was baked like a cake.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLOWFREEZE )
sprintf( szMessage, "* %s died of hypothermia.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] & DMG_MORTAR )
sprintf( szMessage, "* %s blew its missile pet.\n", szPlayerName );
else if ( g_DamageBits[ iPlayerIndex ] == (1 << 30) ) // (1 << 30) = 1073741824. For custom death messages
sprintf( szMessage, "* %s %s.\n", szPlayerName, STRING( pAttacker->v.noise ) );
sprintf( szMessage, "* %s was killed by a Mortar Shell.\n", szPlayerName );
else // other mods could have more DMG_ variants that aren't registered here.
sprintf( szMessage, "* %s deadly died.\n", szPlayerName );
}
@@ -520,14 +549,46 @@ void check_monster_info( edict_t *pPlayer )
// Hit an entity?
if (tr.pHit != NULL)
{
// Must be a monster
if (tr.pHit->v.flags & FL_MONSTER)
// It should be alive
if ( UTIL_IsAlive( tr.pHit ) )
{
// Must be a monster (and strictly a monster!)
if (strncmp( STRING( tr.pHit->v.classname ), "monster_", 8 ) == 0 && tr.pHit->v.flags & FL_MONSTER)
{
char szName[129];
float monsterHealth, monsterFrags;
int classify;
BOOL isAlly = FALSE;
// Get monster info
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit));
if ( pMonster != NULL )
{
strcpy(szName, STRING( pMonster->m_szMonsterName ));
classify = pMonster->Classify();
}
else
{
// A monster that we do not recognize, use its netname
if ( !FStringNull( tr.pHit->v.netname ) )
strcpy(szName, STRING( tr.pHit->v.netname ));
else
{
// If all else fails, use classname as monster name
strcpy(szName, STRING( tr.pHit->v.classname ));
}
classify = tr.pHit->v.iuser4;
}
monsterHealth = tr.pHit->v.health;
monsterFrags = tr.pHit->v.frags;
char szInfo[512];
sprintf(szInfo, "Enemy: %s\nHealth: %.0f\nFrags: %.0f\n", STRING( pMonster->m_szMonsterName ), pMonster->pev->health, pMonster->pev->frags );
// Unless it is strictly ally to us, treat as enemy monster
if ( classify == CLASS_HUMAN_PASSIVE || classify == CLASS_PLAYER_ALLY )
isAlly = TRUE;
// Prepare the message
char szInfo[257];
sprintf(szInfo, "%s: %s\nHealth: %.0f\nFrags: %.0f\n", ( isAlly ? "Friend" : "Enemy" ), szName, monsterHealth, monsterFrags );
// Create a TE_TEXTMESSAGE and show the monster information
MESSAGE_BEGIN( MSG_ONE, SVC_TEMPENTITY, NULL, pPlayer );
@@ -536,9 +597,18 @@ void check_monster_info( edict_t *pPlayer )
WRITE_SHORT( 327 ); // X
WRITE_SHORT( 4771 ); // Y
WRITE_BYTE( 0 ); // Effect
if ( isAlly )
{
WRITE_BYTE( 9 ); // R1
WRITE_BYTE( 172 ); // G1
WRITE_BYTE( 96 ); // B1
}
else
{
WRITE_BYTE( 171 ); // R1
WRITE_BYTE( 23 ); // G1
WRITE_BYTE( 7 ); // B1
}
WRITE_BYTE( 0 ); // A1
WRITE_BYTE( 207 ); // R2
WRITE_BYTE( 23 ); // G2
@@ -551,13 +621,14 @@ void check_monster_info( edict_t *pPlayer )
MESSAGE_END();
// Delay till next scan
g_NextMessage[ ENTINDEX( pPlayer ) ] = gpGlobals->time + 0.8;
g_NextMessage[ ENTINDEX( pPlayer ) ] = gpGlobals->time + 0.30;
}
}
}
}
}
bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_index, int spawnflags, pKVD *keyvalue)
edict_t* spawn_monster(int monster_type, Vector origin, Vector angles, int spawnflags, pKVD *keyvalue)
{
int monster_index;
edict_t *monster_pent;
@@ -567,7 +638,7 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i
{
//META_CONS("[MONSTER] ERROR: No FREE Monster edicts!");
LOG_MESSAGE(PLID, "ERROR: No FREE Monster edicts!");
return TRUE;
return NULL;
}
// was this monster NOT precached?
@@ -601,11 +672,12 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i
LOG_MESSAGE(PLID, "%s", msg);
}
return TRUE;
return NULL;
}
switch (monster_type)
{
// Monsters
case 0: monsters[monster_index].pMonster = CreateClassPtr((CMAGrunt *)NULL); break;
case 1: monsters[monster_index].pMonster = CreateClassPtr((CMApache *)NULL); break;
case 2: monsters[monster_index].pMonster = CreateClassPtr((CMBarney *)NULL); break;
@@ -636,17 +708,18 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i
case 27: monsters[monster_index].pMonster = CreateClassPtr((CMHWGrunt *)NULL); break;
case 28: monsters[monster_index].pMonster = CreateClassPtr((CMRGrunt *)NULL); break;
case 29: monsters[monster_index].pMonster = CreateClassPtr((CMStukabat *)NULL); break;
// Extra entities
case 32: monsters[monster_index].pMonster = CreateClassPtr((CMMonsterMaker *)NULL); break;
case 33: monsters[monster_index].pMonster = CreateClassPtr((CMAmbientMusic *)NULL); break;
}
if (monsters[monster_index].pMonster == NULL)
{
//META_CONS("[MONSTER] ERROR: Error Creating Monster!" );
LOG_MESSAGE(PLID, "ERROR: Error Creating Monster!");
return TRUE;
return NULL;
}
monsters[monster_index].respawn_index = respawn_index;
monster_pent = ENT(monsters[monster_index].pMonster->pev);
monsters[monster_index].monster_pent = monster_pent;
@@ -655,6 +728,9 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i
monster_pent->v.origin = origin;
monster_pent->v.angles = angles;
// Pass spawnflags first if no keyvalue data exists for it
monster_pent->v.spawnflags = spawnflags;
// Keyvalue data
if (keyvalue != NULL)
{
@@ -669,19 +745,21 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i
}
}
monster_pent->v.spawnflags = spawnflags;
monsters[monster_index].pMonster->Spawn();
// Only modify starting spawnflags for monsters, not for entities!
if ( monster_index <= 29 )
{
// Reverse fadecorpse behaviour
if ( ( spawnflags & SF_MONSTER_FADECORPSE ) )
monster_pent->v.spawnflags &= ~SF_MONSTER_FADECORPSE;
else
monster_pent->v.spawnflags |= SF_MONSTER_FADECORPSE;
}
monster_pent->v.fuser4 = monster_pent->v.health; // save the original health
return FALSE;
return monster_pent;
}
@@ -698,8 +776,7 @@ void check_respawn(void)
for (int index=0; index < monster_spawn_count; index++)
{
if (monster_spawnpoint[index].need_to_respawn &&
(monster_spawnpoint[index].respawn_time <= gpGlobals->time))
if (monster_spawnpoint[index].need_to_respawn)
{
monster_spawnpoint[index].need_to_respawn = FALSE;
@@ -713,18 +790,15 @@ void check_respawn(void)
keyvalue = monster_spawnpoint[index].keyvalue;
if (spawn_monster(monster_type, origin, angles, index, spawnflags, keyvalue))
if (spawn_monster(monster_type, origin, angles, spawnflags, keyvalue) == NULL)
{
// spawn_monster failed, retry again after delay...
monster_spawnpoint[index].need_to_respawn = TRUE;
monster_spawnpoint[index].respawn_time = gpGlobals->time +
monster_spawnpoint[index].delay;
// spawn_monster failed
ALERT( at_error, "[MONSTER] Failed to spawn %s at origin %f %f %f\n", monster_types[monster_type].name, origin.x, origin.y, origin.z );
}
}
}
}
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_sModelIndexTinySpit;// holds the index for the spore grenade explosion
@@ -736,23 +810,27 @@ DLL_GLOBAL short g_sModelIndexLaser;// holds the index for the laser beam
DLL_GLOBAL const char *g_pModelNameLaser = "sprites/laserbeam.spr";
DLL_GLOBAL short g_sModelIndexLaserDot;// holds the index for the laser beam dot
// globals.cpp
DLL_GLOBAL const Vector g_vecZero = Vector(0, 0, 0); // null vector
DLL_GLOBAL Vector g_vecAttackDir; // attack direction
void world_precache(void)
{
g_sModelIndexFireball = PRECACHE_MODEL ("sprites/zerogxplode.spr");// fireball
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_sModelIndexBubbles = PRECACHE_MODEL ("sprites/bubble.spr");//bubbles
g_sModelIndexBloodSpray = PRECACHE_MODEL ("sprites/bloodspray.spr"); // initial blood
g_sModelIndexBloodDrop = PRECACHE_MODEL ("sprites/blood.spr"); // splattered blood
g_sModelIndexFireball = PRECACHE_MODELINDEX("sprites/zerogxplode.spr");// fireball
g_sModelIndexSmoke = PRECACHE_MODELINDEX("sprites/steam1.spr");// smoke
g_sModelIndexTinySpit = PRECACHE_MODELINDEX("sprites/tinyspit.spr");// spore
g_sModelIndexWExplosion = PRECACHE_MODELINDEX("sprites/WXplo1.spr");// underwater fireball
g_sModelIndexBubbles = PRECACHE_MODELINDEX("sprites/bubble.spr");//bubbles
g_sModelIndexBloodSpray = PRECACHE_MODELINDEX("sprites/bloodspray.spr"); // initial blood
g_sModelIndexBloodDrop = PRECACHE_MODELINDEX("sprites/blood.spr"); // splattered blood
g_sModelIndexLaser = PRECACHE_MODEL( (char *)g_pModelNameLaser );
g_sModelIndexLaserDot = PRECACHE_MODEL("sprites/laserdot.spr");
g_sModelIndexLaser = PRECACHE_MODELINDEX( (char *)g_pModelNameLaser );
g_sModelIndexLaserDot = PRECACHE_MODELINDEX("sprites/laserdot.spr");
PRECACHE_MODEL("models/w_grenade.mdl");
}
void MonsterCommand(void)
{
int index;
@@ -886,7 +964,7 @@ void MonsterCommand(void)
if (monster_angle.y < 0)
monster_angle.y += 360;
spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL);
spawn_monster(monster_type, v_src, monster_angle, spawnflags, NULL);
return;
}
@@ -914,7 +992,7 @@ void MonsterCommand(void)
if (monster_angle.y < 0)
monster_angle.y += 360;
spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL);
spawn_monster(monster_type, v_src, monster_angle, spawnflags, NULL);
return;
}
@@ -942,7 +1020,7 @@ void MonsterCommand(void)
if (monster_angle.y < 0)
monster_angle.y += 360;
spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL);
spawn_monster(monster_type, v_src, monster_angle, spawnflags, NULL);
return;
}
@@ -969,7 +1047,7 @@ void MonsterCommand(void)
if (monster_angle.y < 0)
monster_angle.y += 360;
spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL);
spawn_monster(monster_type, v_src, monster_angle, spawnflags, NULL);
return;
}
@@ -996,7 +1074,7 @@ void MonsterCommand(void)
if (monster_angle.y < 0)
monster_angle.y += 360;
spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL);
spawn_monster(monster_type, v_src, monster_angle, spawnflags, NULL);
return;
}
@@ -1023,7 +1101,7 @@ void MonsterCommand(void)
if (monster_angle.y < 0)
monster_angle.y += 360;
spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL);
spawn_monster(monster_type, v_src, monster_angle, spawnflags, NULL);
return;
}
@@ -1184,10 +1262,11 @@ int mmDispatchSpawn( edict_t *pent )
}
// free any allocated keyvalue memory
for (index = 0; index < monster_spawn_count; index++)
for (index = 0; index < MAX_MONSTERS; index++)
{
if (monster_spawnpoint[index].keyvalue)
if (monster_spawnpoint[index].keyvalue != NULL)
free(monster_spawnpoint[index].keyvalue);
monster_spawnpoint[index].keyvalue = NULL;
}
// do level initialization stuff here...
@@ -1195,7 +1274,9 @@ int mmDispatchSpawn( edict_t *pent )
for (index = 0; monster_types[index].name[0]; index++)
monster_types[index].need_to_precache = FALSE;
world_precache();
CVAR_SET_STRING("monster_gmr", "");
CVAR_SET_STRING("monster_gsr", "");
REPLACER::Init();
monster_spawn_count = 0;
node_spawn_count = 0;
@@ -1206,32 +1287,16 @@ int mmDispatchSpawn( edict_t *pent )
process_monster_cfg();
// precache last in the event of a GMR being present
world_precache();
SENTENCEG_Init();
// node support. -Giegue
// init the WorldGraph.
WorldGraph.InitGraph();
check_graph_time = gpGlobals->time + 2.00; // give enough gap
// make sure the .NOD file is newer than the .BSP file.
if ( !WorldGraph.CheckNODFile ( ( char * )STRING( gpGlobals->mapname ) ) )
{
// NOD file is not present, or is older than the BSP file.
WorldGraph.AllocNodes();
}
else
{
// Load the node graph for this level
if ( !WorldGraph.FLoadGraph ( (char *)STRING( gpGlobals->mapname ) ) )
{
// couldn't load, so alloc and prepare to build a graph.
ALERT ( at_console, "*Error opening .NOD file\n" );
WorldGraph.AllocNodes();
}
else
{
ALERT ( at_console, "\n*Graph Loaded!\n" );
}
}
check_respawn_time = 0.0;
check_respawn_time = gpGlobals->time + 4.00;
for (index = 0; index < MAX_MONSTER_ENTS; index++)
{
@@ -1290,12 +1355,53 @@ void mmDispatchTouch( edict_t *pentTouched, edict_t *pentOther )
RETURN_META(MRES_IGNORED);
}
// pfnUse has been deprecated so the only way to trigger a monstermod
// entity from the outside is to do it manually. ARRGHH! -Giegue
void mmDispatchUse( void )
{
if ( CMD_ARGC() >= 6 ) // the command itself is an argument, we need 5. so argc == 6
{
edict_t *entity = INDEXENT( atoi( CMD_ARGV( 1 ) ) );
edict_t *caller = INDEXENT( atoi( CMD_ARGV( 2 ) ) );
edict_t *activator = INDEXENT( atoi( CMD_ARGV( 3 ) ) );
USE_TYPE useType = USE_TYPE( atoi( CMD_ARGV( 4 ) ) );
float flValue = atof( CMD_ARGV( 5 ) );
// nevermind the unoptimization that this brings... >C
for (int index=0; index < monster_ents_used; index++)
{
if ((entity != NULL) && (entity == monsters[index].monster_pent))
{
if ( FNullEnt( caller ) ) caller = NULL;
if ( FNullEnt( activator ) ) activator = NULL;
monsters[index].pMonster->Use( activator, caller, useType, flValue );
return;
}
}
}
}
void mmDispatchKeyValue( edict_t *pentKeyvalue, KeyValueData *pkvd )
{
for (int index=0; index < monster_ents_used; index++)
{
if ((pentKeyvalue != NULL) && (pentKeyvalue == monsters[index].monster_pent))
{
monsters[index].pMonster->KeyValue( pkvd );
RETURN_META(MRES_SUPERCEDE);
}
}
RETURN_META(MRES_IGNORED);
}
void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
{
int index;
CMAGrunt agrunt;
// Monsters
CMAGrunt agrunt; // 0
CMApache apache;
CMBarney barney;
CMBigMomma bigmomma;
@@ -1324,12 +1430,17 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
CMBabyGargantua babygargantua;
CMHWGrunt hwgrunt;
CMRGrunt rgrunt;
CMStukabat stukabat;
CMStukabat stukabat; // 29
// Extra entities
CMMonsterMaker monstermaker; // 32
CMAmbientMusic ambientmusic;
g_psv_gravity = CVAR_GET_POINTER( "sv_gravity" );
(g_engfuncs.pfnAddServerCommand)("monster", MonsterCommand);
(g_engfuncs.pfnAddServerCommand)("node_viewer", SpawnViewerCommand);
(g_engfuncs.pfnAddServerCommand)("_use", mmDispatchUse);
for (index = 0; monster_types[index].name[0]; index++)
{
@@ -1372,6 +1483,8 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
case 27: hwgrunt.Precache(); break;
case 28: rgrunt.Precache(); break;
case 29: stukabat.Precache(); break;
case 32: monstermaker.Precache(); break;
//case 33: ambientmusic.Precache(); break;
}
}
}
@@ -1384,9 +1497,61 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
monsters[index].pMonster = NULL;
}
for (index = 0; index < 33; index++)
{
g_DamageBits[index] = 0;
g_PlayerKilled[index] = false;
g_NextMessage[index] = 0.0;
}
monster_ents_used = 0;
RETURN_META(MRES_IGNORED);
}
void mmStartFrame( void )
{
// Don't generate node graph right away
if (check_graph_time != -1 && check_graph_time <= gpGlobals->time)
{
BOOL generateNodes = FALSE;
check_graph_time = -1; // only once
// it could be possible that the mod can generate the node graph
// on it's own, so we wait a bit before attempting to create ours.
// if we can use the game's generated graph, stick to that one.
// if not, then do standard node allocation and spawns. -Giegue
// make sure the .NOD file is newer than the .BSP file.
if ( !WorldGraph.CheckNODFile ( ( char * )STRING( gpGlobals->mapname ) ) )
{
// NOD file is not present, or is older than the BSP file.
generateNodes = TRUE;
WorldGraph.AllocNodes();
}
else
{
// Load the node graph for this level
if ( !WorldGraph.FLoadGraph ( (char *)STRING( gpGlobals->mapname ) ) )
{
// couldn't load, so alloc and prepare to build a graph.
generateNodes = TRUE;
ALERT ( at_console, "*Error opening .NOD file\n" );
WorldGraph.AllocNodes();
}
else
{
// node graph is OK, we can spawn the monsters instantly
check_respawn_time = 0.0;
ALERT ( at_console, "\n[MONSTER] Graph Loaded!\n" );
}
}
if ( generateNodes )
{
// spawn nodes
int index;
for (index = 0; index < node_spawn_count; index++)
{
CMBaseEntity *pNode;
@@ -1409,12 +1574,10 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
pNode->Spawn();
}
}
RETURN_META(MRES_IGNORED);
}
}
void mmStartFrame( void )
{
// Wait for node graph before spawning the monsters
if (check_respawn_time <= gpGlobals->time)
{
check_respawn_time = gpGlobals->time + 1.0;
@@ -1425,22 +1588,34 @@ void mmStartFrame( void )
RETURN_META(MRES_IGNORED);
}
void mmClientKill( edict_t *pPlayer )
void mmClientKill_Post( edict_t *pPlayer )
{
// Just to let the system know the player commited suicide
// Show "commited suicide" message
pPlayer->v.dmg_inflictor = pPlayer;
check_player_dead( pPlayer );
RETURN_META(MRES_IGNORED);
}
BOOL mmClientConnect( edict_t *pPlayer, const char *pszName, const char *pszAddress, char *szRejectReason )
{
// stop any ambient_music that is playing
MESSAGE_BEGIN(MSG_ONE, SVC_STUFFTEXT, NULL, pPlayer);
WRITE_STRING("mp3 stop\n");
MESSAGE_END();
RETURN_META_VALUE( MRES_IGNORED, TRUE );
}
static DLL_FUNCTIONS gFunctionTable =
{
mmGameDLLInit, //! pfnGameInit() Initialize the game (one-time call after loading of game .dll)
mmDispatchSpawn, //! pfnSpawn()
mmDispatchThink, //! pfnThink
NULL, // pfnUse
NULL, // pfnUse [DEPRECATED]
mmDispatchTouch, //! pfnTouch
NULL, // pfnBlocked
NULL, // pfnKeyValue
mmDispatchKeyValue, //! pfnKeyValue
NULL, // pfnSave
NULL, // pfnRestore
NULL, // pfnSetAbsBox
@@ -1452,9 +1627,9 @@ static DLL_FUNCTIONS gFunctionTable =
NULL, // pfnRestoreGlobalState
NULL, // pfnResetGlobalState
NULL, // pfnClientConnect
mmClientConnect, //! pfnClientConnect
NULL, // pfnClientDisconnect
mmClientKill, //! pfnClientKill
NULL, // pfnClientKill
NULL, // pfnClientPutInServer
NULL, // pfnClientCommand
NULL, // pfnClientUserInfoChanged
@@ -1526,7 +1701,7 @@ void mmPlayerPostThink_Post( edict_t *pEntity )
{
check_monster_hurt(pEntity);
check_monster_dead(pEntity);
check_player_dead(pEntity);
//check_player_dead(pEntity); // too early for damageBits
check_monster_info(pEntity);
RETURN_META(MRES_IGNORED);
@@ -1537,7 +1712,7 @@ static DLL_FUNCTIONS gFunctionTable_Post =
NULL, // pfnGameInit() Initialize the game (one-time call after loading of game .dll)
NULL, // pfnSpawn()
mmDispatchThink_Post, //! pfnThink
NULL, // pfnUse
NULL, // pfnUse [DEPRECATED]
NULL, // pfnTouch
NULL, // pfnBlocked
NULL, // pfnKeyValue
@@ -1554,7 +1729,7 @@ static DLL_FUNCTIONS gFunctionTable_Post =
NULL, // pfnClientConnect
NULL, // pfnClientDisconnect
NULL, // pfnClientKill
mmClientKill_Post, //! pfnClientKill
NULL, // pfnClientPutInServer
NULL, // pfnClientCommand
NULL, // pfnClientUserInfoChanged
@@ -1631,8 +1806,6 @@ int mmRegUserMsg_Post( const char *pName, int iSize )
if ( strcmp( pName, "Damage" ) == 0 )
g_DamageMsg = META_RESULT_ORIG_RET( int ) - cs_server;
else if ( strcmp( pName, "DeathMsg" ) == 0 )
g_DeathMsg = META_RESULT_ORIG_RET( int );// - cs_server;
RETURN_META_VALUE( MRES_IGNORED, 0 );
}
@@ -1646,14 +1819,8 @@ void mmMessageBegin_Post( int msg_dest, int msg_type, const float *pOrigin, edic
{
g_DamageActive = true;
g_DamageVictim = ENTINDEX( ed );
g_DamageAttacker[ g_DamageVictim ] = ed->v.dmg_inflictor;
}
}
else if ( msg_type == g_DeathMsg )
{
// Prepare to update deathmsg
g_DeathActive = true;
}
RETURN_META( MRES_IGNORED );
}
@@ -1666,35 +1833,17 @@ void mmWriteLong_Post( int iValue )
RETURN_META( MRES_IGNORED );
}
// This cannot be done on post!
void mmWriteString( const char *szValue )
{
if ( g_DeathActive )
{
// Prevent recursion
g_DeathActive = false;
// Ensure whatever killed the player is a valid entity
if (g_DamageAttacker[ g_DamageVictim ] != NULL)
{
// Send a new WriteString with the killer's classname
WRITE_STRING( STRING( g_DamageAttacker[ g_DamageVictim ]->v.classname ) );
// Supercede the old message
RETURN_META( MRES_SUPERCEDE );
}
}
RETURN_META( MRES_IGNORED );
}
void mmMessageEnd_Post( void )
{
if ( g_DamageActive )
{
check_player_dead( INDEXENT( g_DamageVictim ) );
}
g_DamageActive = false;
RETURN_META( MRES_IGNORED );
}
/*
enginefuncs_t meta_engfuncs =
{
NULL, // pfnPrecacheModel()
@@ -1764,7 +1913,7 @@ enginefuncs_t meta_engfuncs =
NULL, // pfnWriteLong()
NULL, // pfnWriteAngle()
NULL, // pfnWriteCoord()
mmWriteString, //! pfnWriteString()
NULL, // pfnWriteString()
NULL, // pfnWriteEntity()
NULL, // pfnCVarRegister()
@@ -1918,7 +2067,7 @@ C_DLLEXPORT int GetEngineFunctions(enginefuncs_t *pengfuncsFromEngine, int *inte
memcpy(pengfuncsFromEngine, &meta_engfuncs, sizeof(enginefuncs_t));
return TRUE;
}
*/
enginefuncs_t meta_engfuncs_post =
{
NULL, // pfnPrecacheModel()

View File

@@ -114,7 +114,7 @@ void CMBeam::BeamInit( const char *pSpriteName, int width )
SetFrame( 0 );
SetScrollRate( 0 );
pev->model = MAKE_STRING( pSpriteName );
SetTexture( PRECACHE_MODEL( (char *)pSpriteName ) );
SetTexture( PRECACHE_MODELINDEX( (char *)pSpriteName ) );
SetWidth( width );
pev->skin = 0;
pev->sequence = 0;

View File

@@ -23,10 +23,10 @@ extern enginefuncs_t g_engfuncs;
// The actual engine callbacks
#define GETPLAYERUSERID (*g_engfuncs.pfnGetPlayerUserId)
#define PRECACHE_MODEL (*g_engfuncs.pfnPrecacheModel)
#define PRECACHE_SOUND (*g_engfuncs.pfnPrecacheSound)
#define PRECACHE_MODEL2 (*g_engfuncs.pfnPrecacheModel)
#define PRECACHE_SOUND2 (*g_engfuncs.pfnPrecacheSound)
#define PRECACHE_GENERIC (*g_engfuncs.pfnPrecacheGeneric)
#define SET_MODEL (*g_engfuncs.pfnSetModel)
#define SET_MODEL2 (*g_engfuncs.pfnSetModel)
#define MODEL_INDEX (*g_engfuncs.pfnModelIndex)
#define MODEL_FRAMES (*g_engfuncs.pfnModelFrames)
#define SET_SIZE (*g_engfuncs.pfnSetSize)

View File

@@ -54,7 +54,7 @@ void CMShower::Spawn( void )
pev->speed = RANDOM_FLOAT( 0.5, 1.5 );
pev->angles = g_vecZero;
pev->classname = MAKE_STRING( "_spark_shower" );
pev->classname = MAKE_STRING( "spark_shower" );
}
@@ -172,7 +172,7 @@ void CMEnvExplosion::Spawn( void )
}
m_spriteScale = (int)flSpriteScale;
pev->classname = MAKE_STRING( "_env_explosion" );
pev->classname = MAKE_STRING( "env_explosion" );
}
void CMEnvExplosion::DelayUse( void )

View File

@@ -26,12 +26,15 @@
#endif
// Silence certain warnings
// PS: All warnings to be silenced until T5 milestone. -Giegue
#pragma warning(disable : 4244) // int or float down-conversion
#pragma warning(disable : 4305) // int or float data truncation
#pragma warning(disable : 4201) // nameless struct/union
#pragma warning(disable : 4514) // unreferenced inline function removed
#pragma warning(disable : 4100) // unreferenced formal parameter
#pragma warning(disable : 4390) // empty controlled statement (seems to work fine? monster_api.cpp[101/115])
#pragma warning(disable : 4091) // nameless typedef
#pragma warning(disable : 4996) // unsafe string operations
// Prevent tons of unused windows definitions
#ifdef _WIN32

View File

@@ -130,13 +130,23 @@ void CStomp::Think( void )
if ( tr.pHit && tr.pHit != pev->owner )
{
CMBaseEntity *pEntity = CMBaseEntity::Instance( tr.pHit );
edict_t *pEntity = tr.pHit;
entvars_t *pevOwner = pev;
if ( pev->owner )
pevOwner = VARS(pev->owner);
if ( pEntity )
pEntity->TakeDamage( pev, pevOwner, pev->dmg, DMG_SONIC );
if (pEntity->v.takedamage)
{
if (UTIL_IsPlayer(pEntity))
UTIL_TakeDamage(pEntity, pev, pevOwner, pev->dmg, DMG_SONIC);
else if (pEntity->v.euser4 != NULL)
{
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
pMonster->TakeDamage(pev, pevOwner, pev->dmg, DMG_SONIC);
}
else
UTIL_TakeDamageExternal(pEntity, pev, pevOwner, pev->dmg, DMG_SONIC);
}
}
// Accelerate the effect
@@ -564,6 +574,8 @@ void CMGargantua :: FlameDamage( Vector vecStart, Vector vecEnd, entvars_t *pevI
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
pMonster->TraceAttack( pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize(), &tr, bitsDamageType );
}
else
UTIL_TraceAttack( pEntity, pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize(), &tr, bitsDamageType );
ApplyMultiDamage( pevInflictor, pevAttacker );
}
else
@@ -575,6 +587,8 @@ void CMGargantua :: FlameDamage( Vector vecStart, Vector vecEnd, entvars_t *pevI
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
pMonster->TakeDamage( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType );
}
else
UTIL_TakeDamageExternal( pEntity, pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType );
}
}
}
@@ -664,12 +678,12 @@ void CMGargantua :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/garg.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/garg.mdl"));
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->health = gSkillData.gargantuaHealth;
//pev->view_ofs = Vector ( 0, 0, 96 );// taken from mdl file
m_flFieldOfView = -0.2;// width of forward view cone ( as a dotproduct result )
@@ -698,48 +712,25 @@ void CMGargantua :: Spawn()
//=========================================================
void CMGargantua :: Precache()
{
int i;
PRECACHE_MODEL("models/garg.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 );
gStompSprite = PRECACHE_MODELINDEX( GARG_STOMP_SPRITE_NAME );
gGargGibModel = PRECACHE_MODELINDEX( 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]);
PRECACHE_SOUND_ARRAY(pAttackHitSounds);
PRECACHE_SOUND_ARRAY(pBeamAttackSounds);
PRECACHE_SOUND_ARRAY(pAttackMissSounds);
PRECACHE_SOUND_ARRAY(pRicSounds);
PRECACHE_SOUND_ARRAY(pFootSounds);
PRECACHE_SOUND_ARRAY(pIdleSounds);
PRECACHE_SOUND_ARRAY(pAlertSounds);
PRECACHE_SOUND_ARRAY(pPainSounds);
PRECACHE_SOUND_ARRAY(pAttackSounds);
PRECACHE_SOUND_ARRAY(pStompSounds);
PRECACHE_SOUND_ARRAY(pBreatheSounds);
}
@@ -823,10 +814,13 @@ void CMGargantua::DeathEffect( void )
void CMGargantua::Killed( entvars_t *pevAttacker, int iGib )
{
if ( m_pEyeGlow )
{
EyeOff();
UTIL_Remove( m_pEyeGlow->edict() );
m_pEyeGlow = NULL;
}
CMBaseMonster::Killed( pevAttacker, GIB_NEVER );
}
@@ -976,6 +970,8 @@ edict_t *CMGargantua::GargantuaCheckTraceHullAttack(float flDist, int iDamage, i
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit));
pMonster->TakeDamage( pev, pev, iDamage, iDmgType );
}
else
UTIL_TakeDamageExternal( tr.pHit, pev, pev, iDamage, iDmgType );
}
return tr.pHit;
@@ -1327,12 +1323,12 @@ void CMBabyGargantua::Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/babygarg.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/babygarg.mdl"));
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
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 )
@@ -1363,51 +1359,26 @@ void CMBabyGargantua::Spawn()
//=========================================================
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 );
gStompSprite = PRECACHE_MODELINDEX( GARG_STOMP_SPRITE_NAME );
gGargGibModel = PRECACHE_MODELINDEX( 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]);
PRECACHE_SOUND_ARRAY(pAttackHitSounds);
PRECACHE_SOUND_ARRAY(pBeamAttackSounds);
PRECACHE_SOUND_ARRAY(pAttackMissSounds);
PRECACHE_SOUND_ARRAY(pRicSounds);
PRECACHE_SOUND_ARRAY(pFootSounds);
PRECACHE_SOUND_ARRAY(pIdleSounds);
PRECACHE_SOUND_ARRAY(pAlertSounds);
PRECACHE_SOUND_ARRAY(pPainSounds);
PRECACHE_SOUND_ARRAY(pAttackSounds);
PRECACHE_SOUND_ARRAY(pStompSounds);
PRECACHE_SOUND_ARRAY(pBreatheSounds);
PRECACHE_SOUND_ARRAY(pDieSounds);
}
void CMBabyGargantua::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
@@ -1791,6 +1762,8 @@ edict_t *CMBabyGargantua::BabyGargCheckTraceHullAttack(float flDist, int iDamage
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit));
pMonster->TakeDamage( pev, pev, iDamage, iDmgType );
}
else
UTIL_TakeDamageExternal( tr.pHit, pev, pev, iDamage, iDmgType );
}
return tr.pHit;

View File

@@ -48,6 +48,10 @@ void CMGrenade::Explode( Vector vecSrc, Vector vecAim )
// UNDONE: temporary scorching for PreAlpha - find a less sleazy permenant solution.
void CMGrenade::Explode( TraceResult *pTrace, int bitsDamageType )
{
// CRITICAL - always ensure owner of grenade is valid
if (m_hOwner == NULL)
pev->owner = NULL;
float flRndSound;// sound randomizer
pev->model = iStringNull;//invisible
@@ -234,6 +238,8 @@ void CMGrenade::BounceTouch( edict_t *pOther )
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
pMonster->TraceAttack(pevOwner, 1, gpGlobals->v_forward, &tr, DMG_CLUB );
}
else
UTIL_TraceAttack(pOther, pevOwner, 1, gpGlobals->v_forward, &tr, DMG_CLUB); // lmao
ApplyMultiDamage( pev, pevOwner);
}
@@ -277,7 +283,6 @@ void CMGrenade::BounceTouch( edict_t *pOther )
pev->framerate = 1;
else if (pev->framerate < 0.5)
pev->framerate = 0;
}
@@ -308,6 +313,10 @@ void CMGrenade::SlideTouch( edict_t *pOther )
void CMGrenade :: BounceSound( void )
{
// CRITICAL - always ensure owner of grenade is valid
if (m_hOwner == NULL)
pev->owner = NULL;
switch ( RANDOM_LONG( 0, 2 ) )
{
case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/grenade_hit1.wav", 0.25, ATTN_NORM); break;
@@ -368,6 +377,7 @@ CMGrenade *CMGrenade::ShootContact( entvars_t *pevOwner, Vector vecStart, Vector
pGrenade->pev->velocity = vecVelocity;
pGrenade->pev->angles = UTIL_VecToAngles (pGrenade->pev->velocity);
pGrenade->pev->owner = ENT(pevOwner);
pGrenade->m_hOwner = ENT(pevOwner);
// make monsters afaid of it while in the air
pGrenade->SetThink( &CMGrenade::DangerSoundThink );
@@ -397,6 +407,7 @@ CMGrenade * CMGrenade:: ShootTimed( entvars_t *pevOwner, Vector vecStart, Vector
pGrenade->pev->velocity = vecVelocity;
pGrenade->pev->angles = UTIL_VecToAngles(pGrenade->pev->velocity);
pGrenade->pev->owner = ENT(pevOwner);
pGrenade->m_hOwner = ENT(pevOwner);
pGrenade->SetTouch( &CMGrenade::BounceTouch ); // Bounce if touched
@@ -450,6 +461,7 @@ CMGrenade * CMGrenade :: ShootSatchelCharge( entvars_t *pevOwner, Vector vecStar
pGrenade->pev->velocity = vecVelocity;
pGrenade->pev->angles = g_vecZero;
pGrenade->pev->owner = ENT(pevOwner);
pGrenade->m_hOwner = ENT(pevOwner);
// Detonate in "time" seconds
pGrenade->SetThink( &CMGrenade::SUB_DoNothing );

164
src/dlls/globalreplace.cpp Normal file
View File

@@ -0,0 +1,164 @@
//=========================================================
// Global Replacement:
// Tool to replace all default models/sounds with a
// customized list.
//=========================================================
#include "extdll.h"
#include "dllapi.h"
#include "meta_api.h"
#include "cmbase.h"
#include "cmbasemonster.h"
namespace REPLACER
{
REPLACER *GMR;
REPLACER *GSR;
int numModels;
int numSounds;
void Init(void)
{
if ( GMR != NULL )
{
free( GMR );
GMR = NULL;
}
if ( GSR != NULL )
{
free( GSR );
GSR = NULL;
}
numModels = 0;
numSounds = 0;
}
bool AddGlobalModel(const char *from, const char *to)
{
if (numModels < MAX_REPLACEMENTS)
{
// allocate for the first time
if (!numModels)
GMR = (REPLACER*)calloc(MAX_REPLACEMENTS, sizeof(*GMR));
strcpy(GMR[numModels].source, from);
strcpy(GMR[numModels].destination, to);
numModels++;
return true;
}
LOG_MESSAGE(PLID, "Can't replace model '%s', too many models in GMR.", from);
return false;
}
bool AddGlobalSound(const char *from, const char *to)
{
if (numSounds < MAX_REPLACEMENTS)
{
// allocate for the first time
if (!numSounds)
GSR = (REPLACER*)calloc(MAX_REPLACEMENTS, sizeof(*GSR));
strcpy(GSR[numSounds].source, from);
strcpy(GSR[numSounds].destination, to);
numSounds++;
return true;
}
LOG_MESSAGE(PLID, "Can't replace sound '%s', too many sounds in GSR.", from);
return false;
}
bool AddIndividualSound(edict_t *pMonster, const char *from, const char *to)
{
CMBaseMonster *castMonster = GetClassPtr((CMBaseMonster *)VARS(pMonster));
if ( castMonster == NULL )
return false;
int m_iSounds = castMonster->m_isrSounds;
if (m_iSounds < MAX_REPLACEMENTS)
{
// allocate for the first time
if (!m_iSounds)
castMonster->m_srSoundList = (REPLACER*)calloc(MAX_REPLACEMENTS, sizeof(*castMonster->m_srSoundList));
strcpy(castMonster->m_srSoundList[m_iSounds].source, from);
strcpy(castMonster->m_srSoundList[m_iSounds].destination, to);
castMonster->m_isrSounds++;
return true;
}
LOG_MESSAGE(PLID, "Can't replace sound '%s', too many sounds in list.", from);
return false;
}
const char* FindModelReplacement( edict_t *pMonster, const char *from )
{
// Individually set models takes priority!
if (UTIL_IsValidEntity(pMonster) && !FStringNull(pMonster->v.model))
return STRING(pMonster->v.model);
// Find the model
for (int model = 0; model < numModels; model++)
{
if (strcmp(GMR[model].source, from) == 0)
{
// If found, use that model instead
return GMR[model].destination;
}
}
// Nothing found, stick with default
return from;
}
const char* FindSoundReplacement( edict_t *pMonster, const char *from )
{
// Individually set sounds takes priority!
if (UTIL_IsValidEntity(pMonster))
{
CMBaseMonster *castMonster = NULL;
// Check if this is really a monster or not
if (pMonster->v.flags & FL_MONSTER)
castMonster = GetClassPtr((CMBaseMonster *)VARS(pMonster));
else
{
// This is probably a monster-owned projectile of sorts
if (UTIL_IsValidEntity(pMonster->v.owner))
castMonster = GetClassPtr((CMBaseMonster *)VARS(pMonster->v.owner));
}
// If still no valid BaseMonster pointer, full stop, use GSR.
if (castMonster != NULL && castMonster->m_srSoundList != NULL && castMonster->m_isrSounds > 0)
{
for (int sound = 0; sound < castMonster->m_isrSounds; sound++)
{
if (strcmp(castMonster->m_srSoundList[sound].source, from) == 0)
{
// If found, use it
return castMonster->m_srSoundList[sound].destination;
}
}
// If nothing is found stick to GSR if available
}
}
for (int sound = 0; sound < numSounds; sound++)
{
if (strcmp(GSR[sound].source, from) == 0)
{
// If found, use that sound instead
return GSR[sound].destination;
}
}
// Nothing found, stick with default
return from;
}
}

26
src/dlls/globalreplace.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef GLOBALREPLACE_H
#define GLOBALREPLACE_H
#define MAX_REPLACEMENTS 255
namespace REPLACER
{
typedef struct
{
char source[128];
char destination[128];
} REPLACER;
void Init(void);
bool AddGlobalModel(const char *from, const char *to);
bool AddGlobalSound(const char *from, const char *to);
bool AddIndividualSound(edict_t *pMonster, const char *from, const char *to);
const char* FindModelReplacement( edict_t *pMonster, const char *from );
inline const char* FindModelReplacement( const char *from ) { return FindModelReplacement( NULL, from ); }
const char* FindSoundReplacement( edict_t *pMonster, const char *from );
inline const char* FindSoundReplacement( const char *from ) { return FindSoundReplacement( NULL, from ); }
}
#endif

View File

@@ -134,12 +134,14 @@ void CGonomeGuts :: GutsTouch( edict_t *pOther )
else
{
if (UTIL_IsPlayer(pOther))
UTIL_TakeDamage( pOther, pev, pev, gSkillData.gonomeDmgGuts, DMG_GENERIC );
UTIL_TakeDamage( pOther, pev, VARS(pev->owner), gSkillData.gonomeDmgGuts, DMG_GENERIC );
else if (pOther->v.euser4 != NULL)
{
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
pMonster->TakeDamage ( pev, pev, gSkillData.gonomeDmgGuts, DMG_GENERIC );
pMonster->TakeDamage ( pev, VARS(pev->owner), gSkillData.gonomeDmgGuts, DMG_GENERIC );
}
else
UTIL_TakeDamageExternal( pOther, pev, VARS(pev->owner), gSkillData.gonomeDmgGuts, DMG_GENERIC );
}
SetThink( &CGonomeGuts::SUB_Remove );
@@ -349,16 +351,6 @@ int CMGonome::Classify(void)
//=========================================================
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();
@@ -616,12 +608,12 @@ void CMGonome::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/gonome.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/gonome.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
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 )

View File

@@ -215,12 +215,12 @@ void CMHAssassin :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/hassassin.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/hassassin.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_RED;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_RED : m_bloodColor;
pev->effects = 0;
pev->health = gSkillData.hassassinHealth;
m_flFieldOfView = VIEW_FIELD_WIDE; // indicates the width of this monster's forward view cone ( as a dotproduct result )
@@ -256,7 +256,7 @@ void CMHAssassin :: Precache()
PRECACHE_SOUND("debris/beamstart1.wav");
m_iShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell
m_iShell = PRECACHE_MODELINDEX("models/shell.mdl");// brass shell
}

View File

@@ -247,12 +247,12 @@ void CMHeadCrab :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/headcrab.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/headcrab.mdl"));
UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24));
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->effects = 0;
pev->health = gSkillData.headcrabHealth;
pev->view_ofs = Vector ( 0, 0, 20 );// position of the eyes relative to monster's origin.
@@ -461,7 +461,7 @@ Schedule_t* CMHeadCrab :: GetScheduleOfType ( int Type )
void CMBabyCrab :: Spawn( void )
{
CMHeadCrab::Spawn();
SET_MODEL(ENT(pev), "models/baby_headcrab.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/baby_headcrab.mdl"));
pev->rendermode = kRenderTransTexture;
pev->renderamt = 192;
UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24));

View File

@@ -163,7 +163,7 @@ void CMHGrunt :: SpeakSentence( void )
if (FOkToSpeak())
{
SENTENCEG_PlayRndSz( ENT(pev), pGruntSentences[ m_iSentence ], HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch);
SENTENCEG_PlayRndSz( ENT(pev), !FClassnameIs(pev, "monster_robogrunt") ? pGruntSentences[ m_iSentence ] : CMRGrunt::pRobotSentences[ m_iSentence ], HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch);
JustSpoke();
}
}
@@ -546,21 +546,22 @@ void CMHGrunt :: IdleSound( void )
{
if (FOkToSpeak() && (g_fGruntQuestion || RANDOM_LONG(0,1)))
{
// there has to be a better way than spamming ternary operators... -Giegue
if (!g_fGruntQuestion)
{
// ask question or make statement
switch (RANDOM_LONG(0,2))
{
case 0: // check in
SENTENCEG_PlayRndSz(ENT(pev), "HG_CHECK", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
SENTENCEG_PlayRndSz(ENT(pev), !FClassnameIs(pev, "monster_robogrunt") ? "HG_CHECK" : "RB_CHECK", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
g_fGruntQuestion = 1;
break;
case 1: // question
SENTENCEG_PlayRndSz(ENT(pev), "HG_QUEST", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
SENTENCEG_PlayRndSz(ENT(pev), !FClassnameIs(pev, "monster_robogrunt") ? "HG_QUEST" : "RB_QUEST", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
g_fGruntQuestion = 2;
break;
case 2: // statement
SENTENCEG_PlayRndSz(ENT(pev), "HG_IDLE", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
SENTENCEG_PlayRndSz(ENT(pev), !FClassnameIs(pev, "monster_robogrunt") ? "HG_IDLE" : "RB_IDLE", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
break;
}
}
@@ -569,10 +570,10 @@ void CMHGrunt :: IdleSound( void )
switch (g_fGruntQuestion)
{
case 1: // check in
SENTENCEG_PlayRndSz(ENT(pev), "HG_CLEAR", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
SENTENCEG_PlayRndSz(ENT(pev), !FClassnameIs(pev, "monster_robogrunt") ? "HG_CLEAR" : "RB_CLEAR", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
break;
case 2: // question
SENTENCEG_PlayRndSz(ENT(pev), "HG_ANSWER", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
SENTENCEG_PlayRndSz(ENT(pev), !FClassnameIs(pev, "monster_robogrunt") ? "HG_ANSWER" : "RB_ANSWER", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
break;
}
g_fGruntQuestion = 0;
@@ -663,9 +664,7 @@ void CMHGrunt :: Shoot ( void )
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!
m_cAmmoLoaded--;// take away a bullet!
Vector angDir = UTIL_VecToAngles( vecShootDir );
SetBlending( 0, angDir.x );
@@ -692,9 +691,7 @@ void CMHGrunt :: Shotgun ( void )
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!
m_cAmmoLoaded--;// take away a bullet!
Vector angDir = UTIL_VecToAngles( vecShootDir );
SetBlending( 0, angDir.x );
@@ -804,6 +801,8 @@ void CMHGrunt :: HandleAnimEvent( MonsterEvent_t *pEvent )
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt));
pMonster->TakeDamage(pev, pev, gSkillData.hgruntDmgKick, DMG_CLUB);
}
else
UTIL_TakeDamageExternal(pHurt, pev, pev, gSkillData.hgruntDmgKick, DMG_CLUB);
}
}
break;
@@ -812,7 +811,7 @@ void CMHGrunt :: HandleAnimEvent( MonsterEvent_t *pEvent )
{
if ( FOkToSpeak() )
{
SENTENCEG_PlayRndSz(ENT(pev), "HG_ALERT", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch);
SENTENCEG_PlayRndSz(ENT(pev), !FClassnameIs(pev, "monster_robogrunt") ? "HG_ALERT" : "RB_ALERT", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch);
JustSpoke();
}
@@ -831,12 +830,12 @@ void CMHGrunt :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/hgrunt.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/hgrunt.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_RED;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_RED : m_bloodColor;
pev->effects = 0;
pev->health = gSkillData.hgruntHealth;
m_flFieldOfView = VIEW_FIELD_FULL; // indicates the width of this monster's forward view cone ( as a dotproduct result )
@@ -941,8 +940,8 @@ void CMHGrunt :: Precache()
else
m_voicePitch = 100;
m_iBrassShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell
m_iShotgunShell = PRECACHE_MODEL ("models/shotgunshell.mdl");
m_iBrassShell = PRECACHE_MODELINDEX("models/shell.mdl");// brass shell
m_iShotgunShell = PRECACHE_MODELINDEX("models/shotgunshell.mdl");
}
//=========================================================
@@ -1677,6 +1676,37 @@ Schedule_t slGruntRepelLand[] =
},
};
//=========================================================
// Chase enemy failure schedule
//=========================================================
Task_t tlGruntChaseEnemyFailed[] =
{
{ TASK_STOP_MOVING, (float)0 },
{ TASK_WAIT, (float)0.2 },
{ TASK_FIND_COVER_FROM_ENEMY, (float)0 },
{ TASK_RUN_PATH, (float)0 },
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
{ TASK_REMEMBER, (float)bits_MEMORY_INCOVER },
// { TASK_TURN_LEFT, (float)179 },
{ TASK_FACE_ENEMY, (float)0 },
{ TASK_WAIT, (float)1 },
};
Schedule_t slGruntChaseEnemyFailed[] =
{
{
tlGruntChaseEnemyFailed,
ARRAYSIZE ( tlGruntChaseEnemyFailed ),
bits_COND_NEW_ENEMY |
bits_COND_CAN_RANGE_ATTACK1 |
bits_COND_CAN_MELEE_ATTACK1 |
bits_COND_CAN_RANGE_ATTACK2 |
bits_COND_CAN_MELEE_ATTACK2 |
bits_COND_HEAR_SOUND,
0,
"GruntChaseEnemyFailed"
},
};
DEFINE_CUSTOM_SCHEDULES( CMHGrunt )
{
@@ -1701,6 +1731,7 @@ DEFINE_CUSTOM_SCHEDULES( CMHGrunt )
slGruntRepel,
slGruntRepelAttack,
slGruntRepelLand,
slGruntChaseEnemyFailed,
};
IMPLEMENT_CUSTOM_SCHEDULES( CMHGrunt, CMBaseMonster );
@@ -1856,6 +1887,8 @@ Schedule_t *CMHGrunt :: GetSchedule( void )
// new enemy
if ( HasConditions(bits_COND_NEW_ENEMY) )
{
// none of this should take place as CSquadMonster functions were completely stripped. -Giegue
/*
{
{
//!!!KELLY - the leader of a squad of grunts has just seen the player or a
@@ -1871,14 +1904,14 @@ Schedule_t *CMHGrunt :: GetSchedule( void )
if ((m_hEnemy != NULL) && UTIL_IsPlayer(m_hEnemy))
// player
SENTENCEG_PlayRndSz( ENT(pev), "HG_ALERT", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch);
/*jlb
else if ((m_hEnemy != NULL) &&
(m_hEnemy->Classify() != CLASS_PLAYER_ALLY) &&
(m_hEnemy->Classify() != CLASS_HUMAN_PASSIVE) &&
(m_hEnemy->Classify() != CLASS_MACHINE))
// monster
SENTENCEG_PlayRndSz( ENT(pev), "HG_MONST", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch);
jlb*/
JustSpoke();
}
@@ -1892,6 +1925,7 @@ jlb*/
}
}
}
*/
}
// no ammo
else if ( HasConditions ( bits_COND_NO_AMMO_LOADED ) )
@@ -1917,9 +1951,9 @@ jlb*/
//!!!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);
SENTENCEG_PlayRndSz( ENT(pev), !FClassnameIs(pev, "monster_robogrunt") ? "HG_COVER" : "RB_COVER", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch);
m_iSentence = HGRUNT_SENT_COVER;
//JustSpoke();
JustSpoke();
}
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY );
}
@@ -1943,6 +1977,10 @@ jlb*/
// can shoot
else if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) )
{
// lack of CSquadMonster functionality makes hgrunt behave erraticaly as is removes
// core conditions to make them attack (OccupySlot). -Giegue
// check if a grenade can be thrown, otherwise force weapon fire.
if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) )
{
// throw a grenade if can and no engage slots are available
@@ -1950,24 +1988,34 @@ jlb*/
}
else
{
return GetScheduleOfType( SCHED_RANGE_ATTACK1 );
// hide!
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY );
//return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY );
}
}
// can't see enemy
else if ( HasConditions( bits_COND_ENEMY_OCCLUDED ) )
{
// missing CSquadMonster functions means that the monster will stand still if its enemy is out of sight
// AND if it is impossible to throw a grenade. force it to chase the enemy if attack isn't possible
// -Giegue
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), "HG_THROW", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch);
SENTENCEG_PlayRndSz( ENT(pev), !FClassnameIs(pev, "monster_robogrunt") ? "HG_THROW" : "RB_THROW", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch);
JustSpoke();
}
return GetScheduleOfType( SCHED_RANGE_ATTACK2 );
}
else
{
return GetScheduleOfType( SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE );
}
/*
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
@@ -1979,6 +2027,7 @@ jlb*/
}
return GetScheduleOfType( SCHED_STANDOFF );
}
*/
}
if ( HasConditions( bits_COND_SEE_ENEMY ) && !HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) )
@@ -2113,6 +2162,11 @@ Schedule_t* CMHGrunt :: GetScheduleOfType ( int Type )
{
return &slGruntRepelLand[ 0 ];
}
case SCHED_CHASE_ENEMY_FAILED:
{
// add missing schedule from squadmonster.cpp
return &slGruntChaseEnemyFailed[ 0 ];
}
default:
{
return CMBaseMonster :: GetScheduleOfType ( Type );

View File

@@ -104,8 +104,8 @@ void CMHornet :: Precache()
PRECACHE_SOUND( "hornet/ag_hornethit2.wav" );
PRECACHE_SOUND( "hornet/ag_hornethit3.wav" );
iHornetPuff = PRECACHE_MODEL( "sprites/muz1.spr" );
iHornetTrail = PRECACHE_MODEL("sprites/laserbeam.spr");
iHornetPuff = PRECACHE_MODELINDEX( "sprites/muz1.spr" );
iHornetTrail = PRECACHE_MODELINDEX("sprites/laserbeam.spr");
}
//=========================================================
@@ -126,24 +126,15 @@ int CMHornet::IRelationship ( CMBaseEntity *pTarget )
//=========================================================
int CMHornet::Classify ( void )
{
/*
if ( pev->owner && pev->owner->v.flags & FL_CLIENT)
{
return CLASS_PLAYER_BIOWEAPON;
}
return CLASS_ALIEN_BIOWEAPON;
*/
// Ensure classify is consistent with the owner, in the event
// it's classification was overriden.
if ( pev->owner == NULL )
return CLASS_ALIEN_BIOWEAPON;
// Ain't this going to make the hornets code "slow"?
if (UTIL_IsValidEntity(pev->owner))
{
CMBaseMonster *pOwner = GetClassPtr((CMBaseMonster *)VARS(pev->owner));
return pOwner->Classify();
}
return CLASS_ALIEN_BIOWEAPON;
}
//=========================================================
// StartTrack - starts a hornet out tracking its target
@@ -326,11 +317,10 @@ void CMHornet :: TrackTouch ( edict_t *pOther )
}
// is this NOT a player and IS a monster?
if (!UTIL_IsPlayer(pOther) && (pOther->v.euser4 != NULL))
if (!UTIL_IsPlayer(pOther) && (pOther->v.flags & FL_MONSTER))
{
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
if ( IRelationship( pMonster ) <= R_NO )
if ( pMonster != NULL && IRelationship( pMonster ) <= R_NO || IRelationshipByClass( pOther->v.iuser4 ) <= R_NO )
{
// hit something we don't want to hurt, so turn around.
@@ -373,6 +363,8 @@ void CMHornet::DieTouch ( edict_t *pOther )
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
pMonster->TakeDamage( pev, VARS( pev->owner ), pev->dmg, DMG_BULLET );
}
else
UTIL_TakeDamageExternal( pOther, pev, VARS( pev->owner ), pev->dmg, DMG_BULLET );
}
pev->modelindex = 0;// so will disappear for the 0.1 secs we wait until NEXTTHINK gets rid

View File

@@ -267,12 +267,12 @@ void CMHoundeye :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/houndeye.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/houndeye.mdl"));
UTIL_SetSize(pev, Vector ( -16, -16, 0 ), Vector ( 16, 16, 36 ) );
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_YELLOW;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->effects = 0;
pev->health = gSkillData.houndeyeHealth;
pev->yaw_speed = 5;//!!! should we put this in the monster's changeanim function since turn rates may vary with state/anim?
@@ -326,7 +326,7 @@ void CMHoundeye :: Precache()
PRECACHE_SOUND("houndeye/he_blast2.wav");
PRECACHE_SOUND("houndeye/he_blast3.wav");
m_iSpriteTexture = PRECACHE_MODEL( "sprites/shockwave.spr" );
m_iSpriteTexture = PRECACHE_MODELINDEX( "sprites/shockwave.spr" );
}
//=========================================================
@@ -524,7 +524,8 @@ void CMHoundeye :: SonicAttack ( void )
{
if ( pEntity->v.takedamage != DAMAGE_NO )
{
if ( strcmp(STRING(pEntity->v.model), "models/houndeye.mdl") != 0 )
// don't compare by model because a mapper might change it
if ( strcmp(STRING(pEntity->v.classname), "monster_houndeye") != 0 )
{// houndeyes don't hurt other houndeyes with their attack
// houndeyes do FULL damage if the ent in question is visible. Half damage otherwise.
@@ -565,6 +566,8 @@ void CMHoundeye :: SonicAttack ( void )
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
pMonster->TakeDamage( pev, pev, flAdjustedDamage, DMG_SONIC | DMG_ALWAYSGIB );
}
else
UTIL_TakeDamageExternal( pEntity, pev, pev, flAdjustedDamage, DMG_SONIC | DMG_ALWAYSGIB );
}
}
}

View File

@@ -32,46 +32,38 @@
//=========================================================
// 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_ESTABLISH_LINE_OF_FIRE = LAST_COMMON_SCHEDULE + 1,// move to a location to set up an attack against the enemy.
SCHED_HWGRUNT_REPEL,
SCHED_HWGRUNT_REPEL_ATTACK,
SCHED_HWGRUNT_REPEL_LAND,
SCHED_HWGRUNT_WAIT_FACE_ENEMY,
SCHED_HWGRUNT_TAKECOVER_FAILED,// force analysis of conditions and pick the best possible schedule to recover from failure.
SCHED_HWGRUNT_ELOF_FAIL,
};
//=========================================================
// monster-specific conditions
//=========================================================
#define bits_MEMORY_HWGRUNT_SPINUP ( bits_MEMORY_CUSTOM1 )
//=========================================================
// Monster's Anim Events Go Here
//=========================================================
#define HWGRUNT_AE_DEATH ( 11 )
#define HWGRUNT_AE_MINIGUN ( 5001 )
//=========================================================
// Classify - indicates this monster's place in the
// relationship table.
@@ -202,19 +194,19 @@ void CMHWGrunt::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/hwgrunt.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/hwgrunt.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_RED;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_RED : m_bloodColor;
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_flMinigunSpinTime = 0; // be able to spin up/down minigun right away
m_iSentence = -1;
m_fStanding = TRUE;
@@ -260,13 +252,11 @@ void CMHWGrunt::Precache()
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
m_voicePitch = 95 + RANDOM_LONG(0, 3); // slighly lower than normal grunt
CMHGrunt hgrunt;
hgrunt.Precache();
}
//=========================================================
@@ -274,7 +264,7 @@ void CMHWGrunt::Precache()
//=========================================================
//=========================================================
// GruntFail
// Fail
//=========================================================
Task_t tlHWGruntFail[] =
{
@@ -296,7 +286,7 @@ Schedule_t slHWGruntFail[] =
};
//=========================================================
// Grunt Combat Fail
// Combat Fail
//=========================================================
Task_t tlHWGruntCombatFail[] =
{
@@ -318,7 +308,7 @@ Schedule_t slHWGruntCombatFail[] =
};
//=========================================================
// Victory dance!
// Not really victory dance
//=========================================================
Task_t tlHWGruntVictoryDance[] =
{
@@ -328,7 +318,8 @@ Task_t tlHWGruntVictoryDance[] =
{ TASK_GET_PATH_TO_ENEMY_CORPSE, (float)0 },
{ TASK_WALK_PATH, (float)0 },
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
{ TASK_FACE_ENEMY, (float)0 }
{ TASK_FACE_ENEMY, (float)0 },
// { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE },
};
Schedule_t slHWGruntVictoryDance[] =
@@ -336,38 +327,14 @@ Schedule_t slHWGruntVictoryDance[] =
{
tlHWGruntVictoryDance,
ARRAYSIZE ( tlHWGruntVictoryDance ),
bits_COND_NEW_ENEMY,
bits_COND_NEW_ENEMY |
bits_COND_LIGHT_DAMAGE |
bits_COND_HEAVY_DAMAGE,
0,
"HWGrunt Victory Dance"
},
};
//=========================================================
// 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.
@@ -376,6 +343,7 @@ Task_t tlHWGruntEstablishLineOfFire[] =
{
{ TASK_SET_FAIL_SCHEDULE, (float)SCHED_HWGRUNT_ELOF_FAIL },
{ TASK_GET_PATH_TO_ENEMY, (float)0 },
// { TASK_GRUNT_SPEAK_SENTENCE,(float)0 },
{ TASK_RUN_PATH, (float)0 },
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
};
@@ -395,61 +363,9 @@ Schedule_t slHWGruntEstablishLineOfFire[] =
};
//=========================================================
// 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.
// 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[] =
{
@@ -471,61 +387,78 @@ Schedule_t slHWGruntWaitInCover[] =
},
};
//=========================================================
// Do a turning sweep of the area
// run to cover.
//=========================================================
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_t tlHWGruntTakeCover[] =
{
{ 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 },
{ TASK_SET_FAIL_SCHEDULE, (float)SCHED_HWGRUNT_TAKECOVER_FAILED },
{ TASK_WAIT, (float)0.2 },
{ TASK_FIND_COVER_FROM_ENEMY, (float)0 },
// { TASK_GRUNT_SPEAK_SENTENCE, (float)0 },
{ TASK_RUN_PATH, (float)0 },
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
{ TASK_REMEMBER, (float)bits_MEMORY_INCOVER },
{ TASK_SET_SCHEDULE, (float)SCHED_HWGRUNT_WAIT_FACE_ENEMY },
};
Schedule_t slHWGruntRangeAttack1B[] =
Schedule_t slHWGruntTakeCover[] =
{
{
tlHWGruntRangeAttack1B,
ARRAYSIZE ( tlHWGruntRangeAttack1B ),
bits_COND_NEW_ENEMY |
bits_COND_ENEMY_DEAD |
bits_COND_ENEMY_OCCLUDED,
tlHWGruntTakeCover,
ARRAYSIZE ( tlHWGruntTakeCover ),
0,
"HWRange Attack1B"
0,
"HWGrunt Take Cover"
},
};
//=========================================================
// minigun spinup
//=========================================================
Task_t tlHWGruntMinigunSpinUp[] =
{
{ TASK_STOP_MOVING, (float)0 },
{ TASK_SET_ACTIVITY, (float)ACT_THREAT_DISPLAY },
{ TASK_WAIT_FACE_ENEMY, (float)1 },
{ TASK_REMEMBER, (float)bits_MEMORY_HWGRUNT_SPINUP },
};
Schedule_t slHWGruntMinigunSpinUp[] =
{
{
tlHWGruntMinigunSpinUp,
ARRAYSIZE ( tlHWGruntMinigunSpinUp ),
0, // nothing should interrupt this
0,
"HWGrunt Minigun Spin Up"
},
};
//=========================================================
// minigun attack
//=========================================================
Task_t tlHWGruntMinigunAttack[] =
{
{ TASK_STOP_MOVING, (float)0 },
{ TASK_PLAY_SEQUENCE_FACE_ENEMY, (float)ACT_RANGE_ATTACK1 },
{ TASK_RANGE_ATTACK1, (float)0 },
};
Schedule_t slHWGruntMinigunAttack[] =
{
{
tlHWGruntMinigunAttack,
ARRAYSIZE ( tlHWGruntMinigunAttack ),
bits_COND_NEW_ENEMY |
bits_COND_ENEMY_DEAD |
bits_COND_ENEMY_OCCLUDED |
bits_COND_HEAR_SOUND,
0,
"HWGrunt Minigun Attack"
},
};
//=========================================================
// repel
@@ -543,31 +476,12 @@ Schedule_t slHWGruntRepel[] =
tlHWGruntRepel,
ARRAYSIZE ( tlHWGruntRepel ),
bits_COND_SEE_ENEMY |
bits_COND_NEW_ENEMY,
bits_COND_NEW_ENEMY |
bits_COND_LIGHT_DAMAGE |
bits_COND_HEAVY_DAMAGE |
bits_COND_HEAR_SOUND,
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"
"HWGrunt Repel"
},
};
@@ -590,9 +504,41 @@ Schedule_t slHWGruntRepelLand[] =
tlHWGruntRepelLand,
ARRAYSIZE ( tlHWGruntRepelLand ),
bits_COND_SEE_ENEMY |
bits_COND_NEW_ENEMY,
bits_COND_NEW_ENEMY |
bits_COND_LIGHT_DAMAGE |
bits_COND_HEAVY_DAMAGE |
bits_COND_HEAR_SOUND,
0,
"HWRepel Land"
"HWGrunt Repel Land"
},
};
//=========================================================
// Chase enemy failure
//=========================================================
Task_t tlHWGruntChaseEnemyFailed[] =
{
{ TASK_STOP_MOVING, (float)0 },
{ TASK_WAIT, (float)0.2 },
{ TASK_FIND_COVER_FROM_ENEMY, (float)0 },
{ TASK_RUN_PATH, (float)0 },
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
{ TASK_REMEMBER, (float)bits_MEMORY_INCOVER },
// { TASK_TURN_LEFT, (float)179 },
{ TASK_FACE_ENEMY, (float)0 },
{ TASK_WAIT, (float)1 },
};
Schedule_t slHWGruntChaseEnemyFailed[] =
{
{
tlHWGruntChaseEnemyFailed,
ARRAYSIZE ( tlHWGruntChaseEnemyFailed ),
bits_COND_NEW_ENEMY |
bits_COND_CAN_RANGE_ATTACK1 |
bits_COND_HEAR_SOUND,
0,
"HWGrunt Chase Enemy Failed"
},
};
@@ -602,110 +548,43 @@ DEFINE_CUSTOM_SCHEDULES( CMHWGrunt )
slHWGruntFail,
slHWGruntCombatFail,
slHWGruntVictoryDance,
slHWGruntELOFFail,
slHWGruntEstablishLineOfFire,
slHWGruntCombatFace,
slHWGruntSuppress,
slHWGruntWaitInCover,
slHWGruntSweep,
slHWGruntRangeAttack1B,
slHWGruntTakeCover,
slHWGruntMinigunSpinUp,
slHWGruntMinigunAttack,
slHWGruntRepel,
slHWGruntRepelAttack,
slHWGruntRepelLand,
slHWGruntChaseEnemyFailed,
};
IMPLEMENT_CUSTOM_SCHEDULES( CMHWGrunt, CMBaseMonster );
//=========================================================
// SetActivity - different set than normal hgrunt, adapt
// SetActivity
//=========================================================
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:
iSequence = LookupSequence( "run" );
break;
case ACT_WALK:
iSequence = LookupSequence( "creeping_walk" );
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 )
@@ -719,7 +598,7 @@ void CMHWGrunt :: SetActivity ( Activity NewActivity )
ResetSequenceInfo( );
SetYawSpeed();
}
else if (refreshActivity)
else
{
// Not available try to get default anim
ALERT ( at_console, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity );
@@ -732,6 +611,9 @@ void CMHWGrunt :: SetActivity ( Activity NewActivity )
//=========================================================
Schedule_t *CMHWGrunt :: GetSchedule( void )
{
// clear old sentence
m_iSentence = -1; // we don't care about sounds for now.
// flying? If PRONE, barnacle has me. IF not, it's assumed I am rapelling.
if ( pev->movetype == MOVETYPE_FLY && m_MonsterState != MONSTERSTATE_PRONE )
{
@@ -743,10 +625,7 @@ Schedule_t *CMHWGrunt :: GetSchedule( void )
}
else
{
// repel down a rope,
if ( m_MonsterState == MONSTERSTATE_COMBAT )
return GetScheduleOfType ( SCHED_HWGRUNT_REPEL_ATTACK );
else
// can not attack while holding a minigun in rapel
return GetScheduleOfType ( SCHED_HWGRUNT_REPEL );
}
}
@@ -758,6 +637,13 @@ Schedule_t *CMHWGrunt :: GetSchedule( void )
// dead enemy
if ( HasConditions( bits_COND_ENEMY_DEAD ) )
{
// was attacking, spin down
if ( HasMemory( bits_MEMORY_HWGRUNT_SPINUP ) )
{
Forget( bits_MEMORY_HWGRUNT_SPINUP );
EMIT_SOUND(ENT(pev), CHAN_ITEM, "hassault/hw_spindown.wav", 0.8, ATTN_NORM);
}
// call base class, all code to handle dead enemies is centralized there.
return CMBaseMonster :: GetSchedule();
}
@@ -765,36 +651,72 @@ Schedule_t *CMHWGrunt :: GetSchedule( void )
// new enemy
if ( HasConditions(bits_COND_NEW_ENEMY) )
{
// none of this should take place as CSquadMonster functions were completely stripped. -Giegue
/*
{
{
//!!!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 != NULL) && UTIL_IsPlayer(m_hEnemy))
// player
SENTENCEG_PlayRndSz( ENT(pev), "HG_ALERT", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch);
else if ((m_hEnemy != NULL) &&
(m_hEnemy->Classify() != CLASS_PLAYER_ALLY) &&
(m_hEnemy->Classify() != CLASS_HUMAN_PASSIVE) &&
(m_hEnemy->Classify() != CLASS_MACHINE))
// monster
SENTENCEG_PlayRndSz( ENT(pev), "HG_MONST", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch);
JustSpoke();
}
if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) )
{
return GetScheduleOfType ( SCHED_HWGRUNT_SUPPRESS );
return GetScheduleOfType ( SCHED_GRUNT_SUPPRESS );
}
else
{
return GetScheduleOfType ( SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE );
return GetScheduleOfType ( SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE );
}
}
// no ammo
else if ( HasConditions ( bits_COND_NO_AMMO_LOADED ) )
}
*/
}
// damaged just a little
else if ( HasConditions( bits_COND_LIGHT_DAMAGE ) )
{
// 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 );
}
// we don't want the monster to take cover when hurt while attacking, clear this
ClearConditions( bits_COND_LIGHT_DAMAGE );
}
// can shoot
else if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) )
{
// Force attack!
return GetScheduleOfType ( SCHED_HWGRUNT_SUPPRESS );
// can fire? shoot. destroy without a care
return GetScheduleOfType( SCHED_RANGE_ATTACK1 );
}
// can't see enemy
else if ( HasConditions( bits_COND_ENEMY_OCCLUDED ) )
{
// do sound
if ( HasMemory( bits_MEMORY_HWGRUNT_SPINUP ) )
{
Forget( bits_MEMORY_HWGRUNT_SPINUP );
EMIT_SOUND(ENT(pev), CHAN_ITEM, "hassault/hw_spindown.wav", 0.8, ATTN_NORM);
}
// then go kamikaze and chase the enemy
return GetScheduleOfType( SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE );
}
if ( HasConditions( bits_COND_SEE_ENEMY ) && !HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) )
{
@@ -813,56 +735,55 @@ Schedule_t* CMHWGrunt :: GetScheduleOfType ( int Type )
{
switch ( Type )
{
case SCHED_TAKE_COVER_FROM_ENEMY:
{
return &slHWGruntTakeCover[ 0 ];
}
case SCHED_HWGRUNT_TAKECOVER_FAILED:
{
if ( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) )
{
return GetScheduleOfType( SCHED_RANGE_ATTACK1 );
}
return GetScheduleOfType ( SCHED_FAIL );
}
break;
case SCHED_HWGRUNT_ELOF_FAIL:
{
// human grunt is unable to move to a position that allows him to attack the enemy.
UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_HWGRUNT_ELOF_FAIL\n" );
return &slHWGruntELOFFail[ 0 ];
// unable to move to a position that allows attacking the enemy.
return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY );
}
break;
case SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE:
{
UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE\n" );
return &slHWGruntEstablishLineOfFire[ 0 ];
}
break;
case SCHED_RANGE_ATTACK1:
{
// no pistols yet, always do standing attack
UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_RANGE_ATTACK1\n" );
return &slHWGruntRangeAttack1B[ 0 ];
}
case SCHED_COMBAT_FACE:
// minigun should spin up first
if ( !HasMemory( bits_MEMORY_HWGRUNT_SPINUP ) )
{
UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_COMBAT_FACE\n" );
return &slHWGruntCombatFace[ 0 ];
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "hassault/hw_spinup.wav", 0.8, ATTN_NORM);
return &slHWGruntMinigunSpinUp[ 0 ];
}
else
return &slHWGruntMinigunAttack[ 0 ];
}
case SCHED_HWGRUNT_WAIT_FACE_ENEMY:
{
UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_HWGRUNT_WAIT_FACE_ENEMY\n" );
return &slHWGruntWaitInCover[ 0 ];
}
case SCHED_HWGRUNT_SWEEP:
{
UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_HWGRUNT_SWEEP\n" );
return &slHWGruntSweep[ 0 ];
}
case SCHED_VICTORY_DANCE:
{
UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_VICTORY_DANCE\n" );
return &slHWGruntVictoryDance[ 0 ];
}
case SCHED_HWGRUNT_SUPPRESS:
{
UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_HWGRUNT_SUPPRESS\n" );
return &slHWGruntSuppress[ 0 ];
}
case SCHED_FAIL:
{
UTIL_ClientPrintAll( HUD_PRINTTALK, "* DEBUG: SCHED_FAIL\n" );
if ( m_hEnemy != NULL )
{
// grunt has an enemy, so pick a different default fail schedule most likely to help recover.
// has an enemy, so pick a different default fail schedule most likely to help recover.
return &slHWGruntCombatFail[ 0 ];
}
@@ -874,19 +795,19 @@ Schedule_t* CMHWGrunt :: GetScheduleOfType ( int Type )
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 ];
}
case SCHED_CHASE_ENEMY_FAILED:
{
// add missing schedule from squadmonster.cpp
return &slHWGruntChaseEnemyFailed[ 0 ];
}
default:
{
return CMBaseMonster :: GetScheduleOfType ( Type );
}
}
}

View File

@@ -416,12 +416,12 @@ void CMISlave :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/islave.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/islave.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->effects = 0;
pev->health = gSkillData.slaveHealth;
pev->view_ofs = Vector ( 0, 0, 64 );// position of the eyes relative to monster's origin.
@@ -451,8 +451,6 @@ void CMISlave :: Spawn()
//=========================================================
void CMISlave :: Precache()
{
int i;
PRECACHE_MODEL("models/islave.mdl");
PRECACHE_MODEL("sprites/lgtning.spr");
PRECACHE_SOUND("debris/zap1.wav");
@@ -463,17 +461,10 @@ void CMISlave :: Precache()
PRECACHE_SOUND("headcrab/hc_headbite.wav");
PRECACHE_SOUND("weapons/cbar_miss1.wav");
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ )
PRECACHE_SOUND((char *)pAttackHitSounds[i]);
for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ )
PRECACHE_SOUND((char *)pAttackMissSounds[i]);
for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ )
PRECACHE_SOUND((char *)pPainSounds[i]);
for ( i = 0; i < ARRAYSIZE( pDeathSounds ); i++ )
PRECACHE_SOUND((char *)pDeathSounds[i]);
PRECACHE_SOUND_ARRAY(pAttackHitSounds);
PRECACHE_SOUND_ARRAY(pAttackMissSounds);
PRECACHE_SOUND_ARRAY(pPainSounds);
PRECACHE_SOUND_ARRAY(pDeathSounds);
}
@@ -737,6 +728,8 @@ void CMISlave :: ZapBeam( int side )
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
pMonster->TraceAttack( pev, gSkillData.slaveDmgZap, vecAim, &tr, DMG_SHOCK );
}
else
UTIL_TraceAttack( pEntity, pev, gSkillData.slaveDmgZap, vecAim, &tr, DMG_SHOCK );
}
UTIL_EmitAmbientSound( ENT(pev), tr.vecEndPos, "weapons/electro4.wav", 0.5, ATTN_NORM, 0, RANDOM_LONG( 140, 160 ) );

View File

@@ -31,6 +31,8 @@
#include "effects.h"
#include "customentity.h"
extern cvar_t *monster_default_maxrange;
//=========================================================
// monster-specific DEFINE's
//=========================================================
@@ -61,6 +63,8 @@
//=========================================================
#define MASSN_AE_KICK ( 3 )
#define MASSN_AE_BURST1 ( 4 )
#define MASSN_AE_BURST2 ( 5 )
#define MASSN_AE_BURST3 ( 6 )
#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.
@@ -120,9 +124,7 @@ void CMMassn::Sniperrifle(void)
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!
m_cAmmoLoaded--;// take away a bullet!
Vector angDir = UTIL_VecToAngles(vecShootDir);
SetBlending(0, angDir.x);
@@ -178,6 +180,11 @@ void CMMassn::HandleAnimEvent(MonsterEvent_t *pEvent)
}
break;
case MASSN_AE_BURST2:
case MASSN_AE_BURST3:
Shoot();
break;
case MASSN_AE_KICK:
{
edict_t *pHurt = Kick();
@@ -195,6 +202,8 @@ void CMMassn::HandleAnimEvent(MonsterEvent_t *pEvent)
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt));
pMonster->TakeDamage( pev, pev, gSkillData.massnDmgKick, DMG_CLUB );
}
else
UTIL_TakeDamageExternal( pHurt, pev, pev, gSkillData.massnDmgKick, DMG_CLUB );
}
}
break;
@@ -215,12 +224,12 @@ void CMMassn::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/massn.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/massn.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_RED;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_RED : m_bloodColor;
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 )
@@ -258,6 +267,10 @@ void CMMassn::Spawn()
{
SetBodygroup(GUN_GROUP, GUN_SNIPERRIFLE);
m_cClipSize = 5;
// if no attack range set, set 2x default
if (!m_flDistLook)
m_flDistLook = monster_default_maxrange->value * 2;
}
else
{
@@ -273,6 +286,11 @@ void CMMassn::Spawn()
CMTalkMonster::g_talkWaitTime = 0;
MonsterInit();
if (FBitSet(pev->weapons, MASSN_SNIPERRIFLE))
{
// override for snipers
m_flDistTooFar = m_flDistLook / 1.33;
}
pev->classname = MAKE_STRING( "monster_male_assassin" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
@@ -307,5 +325,118 @@ void CMMassn::Precache()
else
m_voicePitch = 100;
m_iBrassShell = PRECACHE_MODEL("models/shell.mdl");// brass shell
m_iBrassShell = PRECACHE_MODELINDEX("models/shell.mdl");// brass shell
}
//=========================================================
// Chase enemy failure schedule
//=========================================================
Task_t tlMassnSniperAttack[] =
{
{ TASK_STOP_MOVING, (float)0 },
{ TASK_SET_ACTIVITY, (float)ACT_IDLE_ANGRY },
{ TASK_WAIT_FACE_ENEMY , (float)1 },
{ TASK_SET_ACTIVITY, (float)ACT_RANGE_ATTACK1 },
{ TASK_WAIT_FACE_ENEMY, (float)1 },
};
Schedule_t slMassnSniperAttack[] =
{
{
tlMassnSniperAttack,
ARRAYSIZE ( tlMassnSniperAttack ),
bits_COND_HEAR_SOUND,
0,
"MassnSniperAttack"
},
};
DEFINE_CUSTOM_SCHEDULES( CMMassn )
{
slMassnSniperAttack,
};
IMPLEMENT_CUSTOM_SCHEDULES( CMMassn, CMHGrunt );
//=========================================================
// SetActivity
//=========================================================
void CMMassn :: SetActivity ( Activity NewActivity )
{
int iSequence = ACTIVITY_NOT_AVAILABLE;
void *pmodel = GET_MODEL_PTR( ENT(pev) );
switch ( NewActivity )
{
case ACT_RANGE_ATTACK1:
// shooting standing or shooting crouched
if (FBitSet( pev->weapons, MASSN_SNIPERRIFLE))
{
// Always standing
iSequence = LookupSequence( "standing_m40a1" );
}
else
{
if ( m_fStanding )
{
// get aimable sequence
iSequence = LookupSequence( "standing_mp5" );
}
else
{
// get crouching shoot
iSequence = LookupSequence( "crouching_mp5" );
}
}
break;
default:
CMHGrunt::SetActivity(NewActivity);
return;
}
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 - Override schedule for sniper attack
//=========================================================
Schedule_t* CMMassn :: GetScheduleOfType ( int Type )
{
switch ( Type )
{
case SCHED_RANGE_ATTACK1:
{
if (FBitSet(pev->weapons, MASSN_SNIPERRIFLE))
{
// sniper attack is always standing
m_fStanding = TRUE;
return &slMassnSniperAttack[ 0 ];
}
return CMHGrunt :: GetScheduleOfType ( Type );
}
default:
{
return CMHGrunt :: GetScheduleOfType ( Type );
}
}
}

View File

@@ -48,7 +48,7 @@ static META_FUNCTIONS gMetaFunctionTable =
GetEntityAPI2_Post, // pfnGetEntityAPI2_Post META; called after game DLL
NULL, // pfnGetNewDLLFunctions HL SDK2; called before game DLL
NULL, // pfnGetNewDLLFunctions_Post META; called after game DLL
GetEngineFunctions, // pfnGetEngineFunctions META; called before HL engine
NULL, // pfnGetEngineFunctions META; called before HL engine
GetEngineFunctions_Post, // pfnGetEngineFunctions_Post META; called after HL engine
};
@@ -56,8 +56,8 @@ static META_FUNCTIONS gMetaFunctionTable =
plugin_info_t Plugin_info = {
META_INTERFACE_VERSION, // interface version
"MonsterMod", // name
"2.0", // version
"03/06/2020", // date in DD/MM/YYYY format
"4.0", // version
"14/07/2023", // date in DD/MM/YYYY format
"botman, Rick90, Giegue", // original authors + recreation by...
"https://github.com/JulianR0/monstermod-redo", // url
"MONSTER", // logtag
@@ -87,6 +87,16 @@ cvar_t init_monster_show_deaths = {"monster_show_deaths", "1", FCVAR_EXTDLL, 0,
cvar_t *monster_show_deaths = NULL;
cvar_t init_monster_show_info = {"monster_show_info", "1", FCVAR_EXTDLL, 0, NULL};
cvar_t *monster_show_info = NULL;
cvar_t init_monster_turn_coeficient = {"monster_turn_coeficient", "1.75", FCVAR_EXTDLL, 0, NULL};
cvar_t *monster_turn_coeficient = NULL;
cvar_t init_monster_entity_config = {"monster_entity_config", "1", FCVAR_EXTDLL, 0, NULL};
cvar_t *monster_entity_config = NULL;
cvar_t init_globalmodellist = {"monster_gmr", "", FCVAR_EXTDLL, 0, NULL};
cvar_t *globalmodellist = NULL;
cvar_t init_globalsoundlist = {"monster_gsr", "", FCVAR_EXTDLL, 0, NULL};
cvar_t *globalsoundlist = NULL;
cvar_t init_monster_default_maxrange = {"monster_default_maxrange", "2048", FCVAR_EXTDLL, 0, NULL};
cvar_t *monster_default_maxrange = NULL;
// Metamod requesting info about this plugin:
@@ -146,6 +156,20 @@ C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, m
CVAR_REGISTER(&init_monster_show_info);
monster_show_info = CVAR_GET_POINTER("monster_show_info");
CVAR_REGISTER(&init_monster_turn_coeficient);
monster_turn_coeficient = CVAR_GET_POINTER("monster_turn_coeficient");
CVAR_REGISTER(&init_monster_entity_config);
monster_entity_config = CVAR_GET_POINTER("monster_entity_config");
CVAR_REGISTER(&init_globalmodellist);
globalmodellist = CVAR_GET_POINTER("monster_gmr");
CVAR_REGISTER(&init_globalsoundlist);
globalsoundlist = CVAR_GET_POINTER("monster_gsr");
CVAR_REGISTER(&init_monster_default_maxrange);
monster_default_maxrange = CVAR_GET_POINTER("monster_default_maxrange");
return(TRUE);
}

View File

@@ -13,8 +13,14 @@
#include "meta_api.h"
#include "monster_plugin.h"
#include "ripent.h"
#include "globalreplace.h"
extern cvar_t *dllapi_log;
extern cvar_t *monster_entity_config;
extern cvar_t *globalmodellist;
extern cvar_t *globalsoundlist;
extern monster_type_t monster_types[];
extern int monster_spawn_count;
@@ -62,6 +68,84 @@ bool get_input(FILE *fp, char *input)
return FALSE; // no input found
}
void scan_monster_sound(FILE *fp, edict_t *pMonster )
{
char input[1024];
while (get_input(fp, input))
{
// might slip through
if (strlen(input) == 0)
continue;
char *source = strtok(input, " \t");
char *destination = strtok(NULL, " \t");
// Remove all quotes
char parse[128] = {0};
int skip;
// source
skip = 0;
for (unsigned i = 0; i < strlen(source); i++)
{
if (source[i] == '"')
{
skip++;
continue;
}
parse[i-skip] = source[i];
}
parse[strlen(parse)] = '\0';
strcpy(source, parse);
// destination
memset(parse, 0, sizeof(parse));
skip = 0;
for (unsigned i = 0; i < strlen(destination); i++)
{
if (destination[i] == '"')
{
skip++;
continue;
}
parse[i-skip] = destination[i];
}
parse[strlen(parse)] = '\0';
strcpy(destination, parse);
if ( pMonster )
REPLACER::AddIndividualSound( pMonster, source, destination );
else
PRECACHE_SOUND2(destination);
}
}
void process_monster_sound(edict_t *pMonster, char *fileName)
{
char game_dir[256];
FILE *fp = NULL;
// find the directory name of the currently running MOD...
(*g_engfuncs.pfnGetGameDir)(game_dir);
char srPath[192];
// SC soundlist path starts from sound/MAPNAME
sprintf(srPath, "%s/sound/%s/%s", game_dir, STRING(gpGlobals->mapname), fileName);
if (access(srPath, 0) == 0)
{
if ((fp = fopen(srPath, "r")) != NULL)
{
scan_monster_sound(fp, pMonster);
fclose(fp);
}
}
return;
}
void scan_monster_cfg(FILE *fp)
{
// Let's make a full rework of this. -Giegue
@@ -94,7 +178,7 @@ void scan_monster_cfg(FILE *fp)
// Now that I think about it this looks slow and bad code >.>
// A match is found. What is this?
if (strncmp(monster_types[mIndex].name, "monster", 7) == 0)
if (strncmp(monster_types[mIndex].name, "monster_", 8) == 0)
{
// It's a monster, add it to the list
if (monster_spawn_count == MAX_MONSTERS)
@@ -110,6 +194,37 @@ void scan_monster_cfg(FILE *fp)
monster = TRUE;
}
}
else if (strcmp(monster_types[mIndex].name, "monstermaker") == 0 || strcmp(monster_types[mIndex].name, "squadmaker") == 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 = 32; // monstermaker index is fixed at 32
monster_types[32].need_to_precache = TRUE;
monster = TRUE;
}
}
else if (strcmp(monster_types[mIndex].name, "ambient_music") == 0)
{
// TODO - Extra entities should go towards a separate counter like nodes
if (monster_spawn_count == MAX_MONSTERS)
{
LOG_MESSAGE(PLID, "ERROR: can't add ambient_music, reached MAX_MONSTERS!");
badent = TRUE;
}
else
{
monster_spawnpoint[monster_spawn_count].monster = mIndex;
monster_types[mIndex].need_to_precache = TRUE;
monster = TRUE;
}
}
else if (strcmp(monster_types[mIndex].name, "info_node") == 0)
{
// Normal node
@@ -142,7 +257,7 @@ void scan_monster_cfg(FILE *fp)
}
if (monster_types[mIndex].name[0] == 0)
{
LOG_MESSAGE(PLID, "ERROR: unknown classname: %s", input); // print conflictive line
LOG_MESSAGE(PLID, "ERROR: unknown classname: %s", data[kvd_index-1].value); // print conflictive line
LOG_MESSAGE(PLID, "ERROR: nothing will spawn here!");
badent = TRUE;
}
@@ -150,8 +265,8 @@ void scan_monster_cfg(FILE *fp)
else
{
// What are you doing?!
LOG_MESSAGE(PLID, "ERROR: BAD ENTITY STRUCTURE! Last line was %s", input); // print conflictive line
LOG_MESSAGE(PLID, "ERROR: nothing will spawn here!");
LOG_MESSAGE(PLID, "ERROR: BAD ENTITY STRUCTURE! Last line was %s", data[kvd_index-1].key); // print conflictive line
LOG_MESSAGE(PLID, "ERROR: classname MUST be the last entry of the entity!" );
badent = TRUE;
}
@@ -192,23 +307,6 @@ void scan_monster_cfg(FILE *fp)
node_spawnpoint[node_spawn_count].origin[2] = z;
}
}
else if (strcmp(data[i].key, "delay") == 0)
{
// ToDo: Remove this keyvalue.
// Monsters spawned directly should not respawn.
if (monster)
{
if (sscanf(data[i].value, "%f", &x) != 1)
{
LOG_MESSAGE(PLID, "ERROR: invalid delay: %s", input); // print conflictive line
// default to 30 seconds
LOG_MESSAGE(PLID, "ERROR: entity respawn frequency will be set to 30 seconds");
x = 30;
}
monster_spawnpoint[monster_spawn_count].delay = x;
}
}
else if (strcmp(data[i].key, "angles") == 0)
{
if (monster)
@@ -241,6 +339,94 @@ void scan_monster_cfg(FILE *fp)
monster_spawnpoint[monster_spawn_count].spawnflags = x;
}
}
else if (strcmp(data[i].key, "model") == 0)
{
if (monster)
{
// only applicable for normal monsters
if (strcmp(data[kvd_index-1].value, "monstermaker") != 0 && strcmp(data[kvd_index-1].value, "squadmaker") != 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, "soundlist") == 0)
{
if (monster)
{
// file handling, string must not be empty
if (strlen(data[i].value))
{
// process and precache the replacement sounds
process_monster_sound( NULL, 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 monstermaker entity
if (strcmp(data[kvd_index-1].value, "monstermaker") == 0 || strcmp(data[kvd_index-1].value, "squadmaker") == 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 || strcmp(data[kvd_index-1].value, "squadmaker") == 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 if (strcmp(data[i].key, "message") == 0)
{
if (monster)
{
// only applicable for ambient_music
if (strcmp(data[kvd_index - 1].value, "ambient_music") == 0)
{
// precache the sound here
PRECACHE_GENERIC(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
{
// We do not know this keyvalue, but an specific entity might use it.
@@ -255,8 +441,7 @@ void scan_monster_cfg(FILE *fp)
if (monster)
{
// Init monster
monster_spawnpoint[monster_spawn_count].respawn_time = gpGlobals->time + 0.1; // spawn (nearly) right away
// Spawn right away
monster_spawnpoint[monster_spawn_count].need_to_respawn = TRUE;
monster_spawn_count++;
}
@@ -317,39 +502,502 @@ void scan_monster_cfg(FILE *fp)
}
}
void scan_monster_bsp(void)
{
// TODO: code duplication galore! optimize this for T5 milestone. -Giegue
epair_t *kv_pair;
pKVD data[MAX_KEYVALUES];
int kvd_index;
bool use_monstermod_explicit;
bool use_monstermod;
int classname_kvdI, mIndex;
float x, y, z;
bool badent, monster, node;
// go through all entities
for (int ent = 1; ent < num_entities; ent++)
{
kv_pair = entities[ent].epairs;
kvd_index = 0;
use_monstermod_explicit = false;
use_monstermod = true;
classname_kvdI = 0;
badent = monster = node = false;
// examine all keys
while (kv_pair != NULL)
{
// entities cannot be this big!
if (kvd_index >= MAX_KEYVALUES)
{
LOG_MESSAGE(PLID, "WARNING: can't process entity #%i - too many keyvalues", ent);
use_monstermod = false;
use_monstermod_explicit = false;
break;
}
if (strcmp(kv_pair->key, "classname") == 0)
{
// the entity we are trying to spawn could already exist within the game
// use the engine's CREATE_NAMED_ENTITY to see if it's valid or not
edict_t *existsGAME = CREATE_NAMED_ENTITY( MAKE_STRING( kv_pair->value ) );
if ( !FNullEnt( existsGAME ) )
{
// this entity already exists and we should ignore it
// use REMOVE_ENTITY instead of UTIL_Remove!
// UTIL_Remove sets FL_KILLME to remove the entity on the next frame, that won't do.
// REMOVE_ENTITY instead removes it instantly, which is needed to prevent server crashes
// due to "ED_Alloc: no free edicts" error.
REMOVE_ENTITY( existsGAME ); // get rid of the temporary entity
use_monstermod = false; // stick with game entity
}
}
else if (strcmp(kv_pair->key, "use_monstermod") == 0)
{
if (atoi(kv_pair->value) == 1)
{
// The extra plugin must be available to handle the old entity!
if (CVAR_GET_FLOAT("_hl_explicit"))
{
// EXPLICITY requested to use monstermod for this entity
use_monstermod_explicit = true;
}
else
LOG_MESSAGE(PLID, "WARNING: can't explicity add entity #%i - add-on plugin unavailable", ent);
}
}
strcpy(data[kvd_index].key, kv_pair->key);
strcpy(data[kvd_index].value, kv_pair->value);
kvd_index++;
kv_pair = kv_pair->next;
}
// spawn a monstermod entity?
if (use_monstermod_explicit || use_monstermod)
{
// find classname keyvalue
for (int i = 0; i < kvd_index; i++)
{
if (strcmp(data[i].key, "classname") == 0)
{
for (mIndex = 0; monster_types[mIndex].name[0]; mIndex++)
{
if (strcmp(data[i].value, monster_types[mIndex].name) == 0)
{
// Match found, check if it's a node
if (strcmp(monster_types[mIndex].name, "info_node") == 0)
{
// Normal node
if (node_spawn_count == MAX_NODES)
{
LOG_MESSAGE(PLID, "ERROR: can't add node, reached MAX_NODES!");
badent = true;
}
else
node = true;
}
else if (strcmp(monster_types[mIndex].name, "info_node_air") == 0)
{
// Aerial node
if (node_spawn_count == MAX_NODES)
{
LOG_MESSAGE(PLID, "ERROR: can't add node, reached MAX_NODES!");
badent = true;
}
else
{
node_spawnpoint[node_spawn_count].is_air_node = true;
node = true;
}
}
else
{
// Assume it's a monster and add it to the list
// (Extra entities are built as CMBaseMonster)
if (monster_spawn_count == MAX_MONSTERS)
{
LOG_MESSAGE(PLID, "ERROR: can't add entity, reached MAX_MONSTERS!");
badent = true;
}
else
{
// Aliases
if (strcmp(monster_types[mIndex].name, "squadmaker") == 0)
mIndex = 32; // monstermaker
monster_spawnpoint[monster_spawn_count].monster = mIndex;
monster_types[mIndex].need_to_precache = true;
monster = true;
}
}
classname_kvdI = i;
break;
}
}
if (monster_types[mIndex].name[0] == 0)
{
LOG_MESSAGE(PLID, "unknown classname: %s", data[i].value);
badent = true;
}
}
}
if (!badent)
{
// Make room for entity-specific keyvalues.
if (monster)
{
// Can I use malloc/calloc again or you are going to crash cuz you feel like it? >.>
monster_spawnpoint[monster_spawn_count].keyvalue = (pKVD*)calloc(MAX_KEYVALUES, sizeof(*monster_spawnpoint[monster_spawn_count].keyvalue));
}
// process entity keyvalues
for (int i = 0; i < kvd_index; i++)
{
// duplicates are overwritten
if (strcmp(data[i].key, "origin") == 0)
{
if (sscanf(data[i].value, "%f %f %f", &x, &y, &z) != 3)
{
LOG_MESSAGE(PLID, "ERROR: invalid origin: %s", data[i].value); // print conflictive line
// reset origin to g_vecZero
LOG_MESSAGE(PLID, "ERROR: entity will spawn at 0 0 0");
x = y = z = 0;
}
if (monster)
{
monster_spawnpoint[monster_spawn_count].origin[0] = x;
monster_spawnpoint[monster_spawn_count].origin[1] = y;
monster_spawnpoint[monster_spawn_count].origin[2] = z;
}
else if (node)
{
node_spawnpoint[node_spawn_count].origin[0] = x;
node_spawnpoint[node_spawn_count].origin[1] = y;
node_spawnpoint[node_spawn_count].origin[2] = z;
}
}
else if (strcmp(data[i].key, "angles") == 0)
{
if (monster)
{
if (sscanf(data[i].value, "%f %f %f", &x, &y, &z) != 3)
{
LOG_MESSAGE(PLID, "ERROR: invalid angles: %s", data[i].value); // print conflictive line
// reset angles to g_vecZero
LOG_MESSAGE(PLID, "ERROR: entity angles will be set to 0 0 0");
x = y = z = 0;
}
monster_spawnpoint[monster_spawn_count].angles[0] = x;
monster_spawnpoint[monster_spawn_count].angles[1] = y;
monster_spawnpoint[monster_spawn_count].angles[2] = z;
}
}
else if (strcmp(data[i].key, "spawnflags") == 0)
{
if (monster)
{
if (sscanf(data[i].value, "%f", &x) != 1)
{
LOG_MESSAGE(PLID, "ERROR: invalid spawnflags: %s", data[i].value); // print conflictive line
// default to no spawnflags
LOG_MESSAGE(PLID, "ERROR: entity spawnflags will be set to none (0)");
x = 0;
}
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[classname_kvdI].value, "monstermaker") != 0 && strcmp(data[classname_kvdI].value, "squadmaker") != 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, "soundlist") == 0)
{
if (monster)
{
// file handling, string must not be empty
if (strlen(data[i].value))
{
// process and precache the replacement sounds
process_monster_sound( NULL, 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 monstermaker entity
if (strcmp(data[classname_kvdI].value, "monstermaker") == 0 || strcmp(data[classname_kvdI].value, "squadmaker") == 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[classname_kvdI].value, "monstermaker") == 0 || strcmp(data[classname_kvdI].value, "squadmaker") == 0)
{
// process the entity precache here
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 if (strcmp(data[i].key, "message") == 0)
{
if (monster)
{
// only applicable for ambient_music
if (strcmp(data[classname_kvdI].value, "ambient_music") == 0)
{
// precache the sound here
PRECACHE_GENERIC(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
{
// We do not know this keyvalue, but an specific entity might use it.
// Save it for later
if (monster)
{
strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].key, data[i].key);
strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].value, data[i].value);
}
}
}
if (monster)
{
// Spawn right away
monster_spawnpoint[monster_spawn_count].need_to_respawn = TRUE;
monster_spawn_count++;
}
else if (node)
{
// Increase node count
node_spawn_count++;
}
// Log on? Print all the entities that were added
if (dllapi_log->value)
{
// Classname only, or we will flood the server!
LOG_CONSOLE(PLID, "[DEBUG] Added entity: %s", data[classname_kvdI].value);
}
}
}
}
}
void scan_extra_cfg(FILE *fp)
{
char input[1024];
while (get_input(fp, input))
{
char *cmd = strtok(input, " ");
char *value = strtok(NULL, " ");
if (value == NULL)
continue; // command with no value, skip
// Remove all quotes from "value"
char parse[128] = {0};
int skip = 0;
for (unsigned i = 0; i < strlen(value); i++)
{
if (value[i] == '"')
{
skip++;
continue;
}
parse[i-skip] = value[i];
}
parse[strlen(parse)] = '\0';
strcpy(value, parse);
if (strcmp(cmd, "globalmodellist") == 0)
{
// ugh...
//globalmodellist->string = value;
CVAR_SET_STRING( "monster_gmr", value );
// Verbose if logging is enabled
if (dllapi_log->value)
LOG_CONSOLE(PLID, "[DEBUG] Using global model replacement file: %s", value);
}
if (strcmp(cmd, "globalsoundlist") == 0)
{
//globalsoundlist->string = value;
CVAR_SET_STRING( "monster_gsr", value );
// Verbose if logging is enabled
if (dllapi_log->value)
LOG_CONSOLE(PLID, "[DEBUG] Using global sound replacement file: %s", value);
}
}
}
void scan_monster_replace(FILE *fp, bool toGSR )
{
char input[1024];
while (get_input(fp, input))
{
// might slip through
if (strlen(input) == 0)
continue;
char *source = strtok(input, " \t");
char *destination = strtok(NULL, " \t");
// Remove all quotes
char parse[128] = {0};
int skip;
// source
skip = 0;
for (unsigned i = 0; i < strlen(source); i++)
{
if (source[i] == '"')
{
skip++;
continue;
}
parse[i-skip] = source[i];
}
parse[strlen(parse)] = '\0';
strcpy(source, parse);
// destination
memset(parse, 0, sizeof(parse));
skip = 0;
for (unsigned i = 0; i < strlen(destination); i++)
{
if (destination[i] == '"')
{
skip++;
continue;
}
parse[i-skip] = destination[i];
}
parse[strlen(parse)] = '\0';
strcpy(destination, parse);
if ( toGSR )
REPLACER::AddGlobalSound( source, destination );
else
REPLACER::AddGlobalModel( source, destination );
}
}
bool process_monster_cfg(void)
{
char game_dir[256];
char filename[256];
char BSPfilename[256]; // to read ents directly from BSP
char CFGfilename[256]; // read ents from MAPNAME_monster.cfg file
char EXTfilename[256]; // extra map configs from MAPNAME.cfg
FILE *fp = NULL;
monster_spawn_count = 0;
node_spawn_count = 0;
// find the directory name of the currently running MOD...
(*g_engfuncs.pfnGetGameDir)(game_dir);
strcpy(filename, game_dir);
#ifdef __linux__
strcat(filename, "/maps/");
#else
strcat(filename, "\\maps\\");
#endif
strcat(filename, STRING(gpGlobals->mapname));
strcat(filename, "_monster.cfg");
// build route...
strcpy(CFGfilename, game_dir);
strcat(CFGfilename, "/maps/");
strcat(CFGfilename, STRING(gpGlobals->mapname));
strcpy(BSPfilename, CFGfilename);
strcpy(EXTfilename, CFGfilename);
strcat(BSPfilename, ".bsp");
strcat(CFGfilename, "_monster.cfg");
strcat(EXTfilename, ".cfg");
// process config files?
// -1 = don't process monster config, dynamic spawns only
// 0 = read entities from BSP file
// 1 = read entities from CFG file
// 2 = read entities from both, BSP first, then CFG file
if (monster_entity_config->value >= 0)
{
// read from bsp? (mode 0 or 2)
if (monster_entity_config->value != 1)
{
LoadBSPFile(BSPfilename);
ParseEntities();
scan_monster_bsp();
}
// read from cfg? (mode 1 or 2)
if (monster_entity_config->value > 0)
{
// check if the map specific filename exists...
if (access(filename, 0) == 0)
if (access(CFGfilename, 0) == 0)
{
if (dllapi_log->value)
{
//META_CONS("[MONSTER] Processing config file=%s", filename);
LOG_MESSAGE(PLID, "Processing config file=%s", filename);
LOG_MESSAGE(PLID, "Processing config file '%s'", CFGfilename);
}
if ((fp = fopen(filename, "r")) == NULL)
if ((fp = fopen(CFGfilename, "r")) == NULL)
{
//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!", CFGfilename);
return TRUE; // error
}
@@ -357,11 +1005,61 @@ bool process_monster_cfg(void)
fclose(fp);
}
}
}
/* The code is only getting worse from here, I have to finish T4 quickly
* so I can move into making actual clean and optimized code for the final tier...
* -Giegue */
// extra map configs
if (access(EXTfilename, 0) == 0)
{
// first read configs
if ((fp = fopen(EXTfilename, "r")) != NULL)
{
scan_extra_cfg(fp);
fclose(fp);
}
// then process them here
if (strlen(globalmodellist->string))
{
char gmrPath[192];
// SC globalmodellist path starts from models/MAPNAME
sprintf(gmrPath, "%s/models/%s/%s", game_dir, STRING(gpGlobals->mapname), globalmodellist->string);
if (access(gmrPath, 0) == 0)
{
if ((fp = fopen(gmrPath, "r")) != NULL)
{
scan_monster_replace(fp, false);
fclose(fp);
}
}
}
if (strlen(globalsoundlist->string))
{
char gsrPath[192];
// SC globalsoundlist path starts from sound/MAPNAME
sprintf(gsrPath, "%s/sound/%s/%s", game_dir, STRING(gpGlobals->mapname), globalsoundlist->string);
if (access(gsrPath, 0) == 0)
{
if ((fp = fopen(gsrPath, "r")) != NULL)
{
scan_monster_replace(fp, true);
fclose(fp);
}
}
}
}
return FALSE; // all ok
}
bool scan_monster_precache_cfg(FILE *fp)
{
char input[1024];

View File

@@ -1,425 +0,0 @@
# Microsoft Developer Studio Project File - Name="monster_mm" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
CFG=monster_mm - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "monster_mm.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "monster_mm.mak" CFG="monster_mm - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "monster_mm - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE "monster_mm - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
MTL=midl.exe
RSC=rc.exe
!IF "$(CFG)" == "monster_mm - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# 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 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
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
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 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"
!ELSEIF "$(CFG)" == "monster_mm - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# 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 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
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
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 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
!ENDIF
# Begin Target
# Name "monster_mm - Win32 Release"
# Name "monster_mm - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\agrunt.cpp
# End Source File
# Begin Source File
SOURCE=.\AI_BaseNPC_Schedule.cpp
# End Source File
# Begin Source File
SOURCE=.\animating.cpp
# End Source File
# Begin Source File
SOURCE=.\animation.cpp
# End Source File
# Begin Source File
SOURCE=.\apache.cpp
# End Source File
# Begin Source File
SOURCE=.\barney.cpp
# End Source File
# Begin Source File
SOURCE=.\bigmomma.cpp
# End Source File
# Begin Source File
SOURCE=.\bullsquid.cpp
# End Source File
# Begin Source File
SOURCE=.\cmbase.cpp
# End Source File
# Begin Source File
SOURCE=.\combat.cpp
# End Source File
# Begin Source File
SOURCE=.\controller.cpp
# End Source File
# Begin Source File
SOURCE=.\defaultai.cpp
# End Source File
# Begin Source File
SOURCE=.\dllapi.cpp
# End Source File
# Begin Source File
SOURCE=.\effects.cpp
# End Source File
# Begin Source File
SOURCE=.\explode.cpp
# End Source File
# Begin Source File
SOURCE=.\flyingmonster.cpp
# End Source File
# Begin Source File
SOURCE=.\gargantua.cpp
# End Source File
# Begin Source File
SOURCE=.\ggrenade.cpp
# End Source File
# Begin Source File
SOURCE=.\gonome.cpp
# End Source File
# Begin Source File
SOURCE=.\h_ai.cpp
# End Source File
# Begin Source File
SOURCE=.\h_export.cpp
# End Source File
# Begin Source File
SOURCE=.\hassassin.cpp
# End Source File
# Begin Source File
SOURCE=.\headcrab.cpp
# End Source File
# Begin Source File
SOURCE=.\hgrunt.cpp
# End Source File
# Begin Source File
SOURCE=.\hornet.cpp
# End Source File
# Begin Source File
SOURCE=.\houndeye.cpp
# End Source File
# Begin Source File
SOURCE=.\hwgrunt.cpp
# End Source File
# Begin Source File
SOURCE=.\islave.cpp
# End Source File
# Begin Source File
SOURCE=.\massn.cpp
# End Source File
# Begin Source File
SOURCE=.\monster_api.cpp
# End Source File
# Begin Source File
SOURCE=.\monster_config.cpp
# End Source File
# Begin Source File
SOURCE=.\monsters.cpp
# End Source File
# Begin Source File
SOURCE=.\monsterstate.cpp
# End Source File
# Begin Source File
SOURCE=.\nodes.cpp
# End 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
# End 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
# End Source File
# Begin Source File
SOURCE=.\sound.cpp
# End Source File
# Begin Source File
SOURCE=.\sporegrenade.cpp
# End Source File
# Begin Source File
SOURCE=.\squeakgrenade.cpp
# End Source File
# Begin Source File
SOURCE=.\strooper.cpp
# End Source File
# Begin Source File
SOURCE=.\subs.cpp
# End Source File
# Begin Source File
SOURCE=.\talkmonster.cpp
# End Source File
# Begin Source File
SOURCE=.\turret.cpp
# End Source File
# Begin Source File
SOURCE=.\util.cpp
# End Source File
# Begin Source File
SOURCE=.\voltigore.cpp
# End Source File
# Begin Source File
SOURCE=.\weapons.cpp
# End Source File
# Begin Source File
SOURCE=.\zombie.cpp
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=.\activity.h
# End Source File
# Begin Source File
SOURCE=.\activitymap.h
# End Source File
# Begin Source File
SOURCE=.\animation.h
# End Source File
# Begin Source File
SOURCE=.\cdll_dll.h
# End Source File
# Begin Source File
SOURCE=.\cmbase.h
# End Source File
# Begin Source File
SOURCE=.\cmbasemonster.h
# End Source File
# Begin Source File
SOURCE=.\cmflyingmonster.h
# End Source File
# Begin Source File
SOURCE=.\cmtalkmonster.h
# End Source File
# Begin Source File
SOURCE=.\decals.h
# End Source File
# Begin Source File
SOURCE=.\defaultai.h
# End Source File
# Begin Source File
SOURCE=.\doors.h
# End Source File
# Begin Source File
SOURCE=.\effects.h
# End Source File
# Begin Source File
SOURCE=.\enginecallback.h
# End Source File
# Begin Source File
SOURCE=.\explode.h
# End Source File
# Begin Source File
SOURCE=.\extdll.h
# End Source File
# Begin Source File
SOURCE=.\func_break.h
# End Source File
# Begin Source File
SOURCE=.\hornet.h
# End Source File
# Begin Source File
SOURCE=.\monster_plugin.h
# End Source File
# Begin Source File
SOURCE=.\monsterevent.h
# End Source File
# Begin Source File
SOURCE=.\monsters.h
# End Source File
# Begin Source File
SOURCE=.\nodes.h
# End Source File
# Begin Source File
SOURCE=.\plane.h
# End Source File
# Begin Source File
SOURCE=.\schedule.h
# End Source File
# Begin Source File
SOURCE=.\shock.h
# End Source File
# Begin Source File
SOURCE=.\skill.h
# End Source File
# Begin Source File
SOURCE=.\util.h
# End Source File
# Begin Source File
SOURCE=.\vector.h
# End Source File
# Begin Source File
SOURCE=.\weapons.h
# End Source File
# End Group
# Begin Group "Resource Files"
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# End Target
# End Project

25
src/dlls/monster_mm.sln Normal file
View File

@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.1433
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "monster_mm", "monster_mm.vcxproj", "{E4F36B30-6406-4D6E-90F6-DE34744D2434}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E4F36B30-6406-4D6E-90F6-DE34744D2434}.Debug|x86.ActiveCfg = Debug|Win32
{E4F36B30-6406-4D6E-90F6-DE34744D2434}.Debug|x86.Build.0 = Debug|Win32
{E4F36B30-6406-4D6E-90F6-DE34744D2434}.Release|x86.ActiveCfg = Release|Win32
{E4F36B30-6406-4D6E-90F6-DE34744D2434}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {32C2F544-BCE4-4787-9C16-F85467CD69DA}
EndGlobalSection
EndGlobal

227
src/dlls/monster_mm.vcxproj Normal file
View File

@@ -0,0 +1,227 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{E4F36B30-6406-4D6E-90F6-DE34744D2434}</ProjectGuid>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
<PlatformToolset>v141_xp</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
<PlatformToolset>v141_xp</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
<PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</PostBuildEventUseInBuild>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Midl>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MkTypLibCompatible>true</MkTypLibCompatible>
<SuppressStartupBanner>true</SuppressStartupBanner>
<TargetEnvironment>Win32</TargetEnvironment>
<TypeLibraryName>.\Debug/monster_mm.tlb</TypeLibraryName>
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\dlls;..\common;..\engine;..\pm_shared;..\metamod;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;monster_mm_EXPORTS;strcasecmp=stricmp;strncasecmp=_strnicmp;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeaderOutputFile>.\Debug/monster_mm.pch</PrecompiledHeaderOutputFile>
<AssemblerListingLocation>.\Debug/</AssemblerListingLocation>
<ObjectFileName>.\Debug/</ObjectFileName>
<ProgramDataBaseFileName>.\Debug/</ProgramDataBaseFileName>
<WarningLevel>Level3</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<CompileAs>Default</CompileAs>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0409</Culture>
</ResourceCompile>
<Link>
<AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions>
<AdditionalDependencies>odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>.\Debug/monster_mm.dll</OutputFile>
<SuppressStartupBanner>true</SuppressStartupBanner>
<ModuleDefinitionFile>.\monster_mm.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>.\Debug/monster_mm.pdb</ProgramDatabaseFile>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention>
</DataExecutionPrevention>
<ImportLibrary>.\Debug/monster_mm.lib</ImportLibrary>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Midl>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MkTypLibCompatible>true</MkTypLibCompatible>
<SuppressStartupBanner>true</SuppressStartupBanner>
<TargetEnvironment>Win32</TargetEnvironment>
<TypeLibraryName>.\Release/monster_mm.tlb</TypeLibraryName>
</Midl>
<ClCompile>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>..\dlls;..\common;..\engine;..\pm_shared;..\metamod;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;monster_mm_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeaderOutputFile>.\Release/monster_mm.pch</PrecompiledHeaderOutputFile>
<AssemblerListingLocation>.\Release/</AssemblerListingLocation>
<ObjectFileName>.\Release/</ObjectFileName>
<ProgramDataBaseFileName>.\Release/</ProgramDataBaseFileName>
<WarningLevel>Level3</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
<CompileAs>Default</CompileAs>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0409</Culture>
</ResourceCompile>
<Link>
<AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions>
<AdditionalDependencies>odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>.\Release/monster_mm.dll</OutputFile>
<SuppressStartupBanner>true</SuppressStartupBanner>
<ModuleDefinitionFile>.\monster_mm.def</ModuleDefinitionFile>
<ProgramDatabaseFile>.\Release/monster_mm.pdb</ProgramDatabaseFile>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention>
</DataExecutionPrevention>
<ImportLibrary>.\Release/monster_mm.lib</ImportLibrary>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="agrunt.cpp" />
<ClCompile Include="AI_BaseNPC_Schedule.cpp" />
<ClCompile Include="animating.cpp" />
<ClCompile Include="animation.cpp" />
<ClCompile Include="apache.cpp" />
<ClCompile Include="barney.cpp" />
<ClCompile Include="bigmomma.cpp" />
<ClCompile Include="bullsquid.cpp" />
<ClCompile Include="cmbase.cpp" />
<ClCompile Include="combat.cpp" />
<ClCompile Include="controller.cpp" />
<ClCompile Include="defaultai.cpp" />
<ClCompile Include="dllapi.cpp" />
<ClCompile Include="effects.cpp" />
<ClCompile Include="explode.cpp" />
<ClCompile Include="flyingmonster.cpp" />
<ClCompile Include="gargantua.cpp" />
<ClCompile Include="ggrenade.cpp" />
<ClCompile Include="globalreplace.cpp" />
<ClCompile Include="gonome.cpp" />
<ClCompile Include="h_ai.cpp" />
<ClCompile Include="h_export.cpp" />
<ClCompile Include="hassassin.cpp" />
<ClCompile Include="headcrab.cpp" />
<ClCompile Include="hgrunt.cpp" />
<ClCompile Include="hornet.cpp" />
<ClCompile Include="houndeye.cpp" />
<ClCompile Include="hwgrunt.cpp" />
<ClCompile Include="islave.cpp" />
<ClCompile Include="massn.cpp" />
<ClCompile Include="monster_api.cpp" />
<ClCompile Include="monster_config.cpp" />
<ClCompile Include="monstermaker.cpp" />
<ClCompile Include="monsters.cpp" />
<ClCompile Include="monsterstate.cpp" />
<ClCompile Include="music.cpp" />
<ClCompile Include="nodes.cpp" />
<ClCompile Include="otis.cpp" />
<ClCompile Include="pitdrone.cpp" />
<ClCompile Include="rgrunt.cpp" />
<ClCompile Include="ripent.cpp" />
<ClCompile Include="scientist.cpp" />
<ClCompile Include="shock.cpp" />
<ClCompile Include="shockroach.cpp" />
<ClCompile Include="skill.cpp" />
<ClCompile Include="sound.cpp" />
<ClCompile Include="sporegrenade.cpp" />
<ClCompile Include="squeakgrenade.cpp" />
<ClCompile Include="strooper.cpp" />
<ClCompile Include="stukabat.cpp" />
<ClCompile Include="subs.cpp" />
<ClCompile Include="talkmonster.cpp" />
<ClCompile Include="turret.cpp" />
<ClCompile Include="util.cpp" />
<ClCompile Include="voltigore.cpp" />
<ClCompile Include="weapons.cpp" />
<ClCompile Include="zombie.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="activity.h" />
<ClInclude Include="activitymap.h" />
<ClInclude Include="animation.h" />
<ClInclude Include="cdll_dll.h" />
<ClInclude Include="cmbase.h" />
<ClInclude Include="cmbaseextra.h" />
<ClInclude Include="cmbasemonster.h" />
<ClInclude Include="cmflyingmonster.h" />
<ClInclude Include="cmtalkmonster.h" />
<ClInclude Include="decals.h" />
<ClInclude Include="defaultai.h" />
<ClInclude Include="doors.h" />
<ClInclude Include="effects.h" />
<ClInclude Include="enginecallback.h" />
<ClInclude Include="explode.h" />
<ClInclude Include="extdll.h" />
<ClInclude Include="func_break.h" />
<ClInclude Include="globalreplace.h" />
<ClInclude Include="hornet.h" />
<ClInclude Include="monster_plugin.h" />
<ClInclude Include="monsterevent.h" />
<ClInclude Include="monsters.h" />
<ClInclude Include="nodes.h" />
<ClInclude Include="plane.h" />
<ClInclude Include="ripent.h" />
<ClInclude Include="schedule.h" />
<ClInclude Include="shock.h" />
<ClInclude Include="skill.h" />
<ClInclude Include="util.h" />
<ClInclude Include="vector.h" />
<ClInclude Include="weapons.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,285 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{c6c9a5d3-53c7-4dda-a7f7-a278343d08f6}</UniqueIdentifier>
<Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{7a512719-d6e9-4695-acde-c7239717ece9}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{af74e8b8-3ee2-4d62-b8cb-fa3952c4afed}</UniqueIdentifier>
<Extensions>ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="agrunt.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="AI_BaseNPC_Schedule.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="animating.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="animation.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="apache.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="barney.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bigmomma.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bullsquid.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="cmbase.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="combat.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="controller.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="defaultai.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="dllapi.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="effects.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="explode.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="flyingmonster.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="gargantua.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ggrenade.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="gonome.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="h_ai.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="h_export.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="hassassin.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="headcrab.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="hgrunt.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="hornet.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="houndeye.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="hwgrunt.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="islave.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="massn.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="monster_api.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="monster_config.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="monstermaker.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="monsters.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="monsterstate.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="nodes.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="otis.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pitdrone.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="rgrunt.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="scientist.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="shock.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="shockroach.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="skill.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="sound.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="sporegrenade.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="squeakgrenade.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="strooper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="stukabat.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="subs.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="talkmonster.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="turret.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="util.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="voltigore.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="weapons.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="zombie.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ripent.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="music.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="globalreplace.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="activity.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="activitymap.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="animation.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="cdll_dll.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="cmbase.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="cmbaseextra.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="cmbasemonster.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="cmflyingmonster.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="cmtalkmonster.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="decals.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="defaultai.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="doors.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="effects.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="enginecallback.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="explode.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="extdll.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="func_break.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="hornet.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="monster_plugin.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="monsterevent.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="monsters.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="nodes.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="plane.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="schedule.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="shock.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="skill.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="util.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vector.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="weapons.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ripent.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="globalreplace.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@@ -8,7 +8,7 @@
typedef struct
{
char key[33];
char value[33];
char value[481];
} pKVD;
#define MAX_KEYVALUES 32
@@ -27,26 +27,23 @@ typedef struct
int monster_index;
edict_t *monster_pent;
bool killed;
int respawn_index;
CMBaseMonster *pMonster;
} monster_t;
#define MAX_MONSTER_ENTS 400 // increased from 200 so it can hold non-monster entities
#define MAX_MONSTER_ENTS 400
extern monster_t monsters[MAX_MONSTER_ENTS];
typedef struct {
Vector origin;
Vector angles;
float delay;
unsigned char monster;
int spawnflags;
pKVD *keyvalue;
float respawn_time;
bool need_to_respawn;
} monster_spawnpoint_t;
#define MAX_MONSTERS 100
#define MAX_MONSTERS 200
extern monster_spawnpoint_t monster_spawnpoint[MAX_MONSTERS];
// this is here to store if a node we want to spawn is an ordinary one, or a flying one

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

@@ -0,0 +1,355 @@
/***
*
* 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
}
}
if (monster_types[mIndex].name[0] == 0)
{
ALERT ( at_logged, "[MONSTER] MonsterMaker - %s is not a valid monster type!\n", pkvd->szValue );
m_iMonsterIndex = -1;
}
pkvd->fHandled = TRUE;
}
else if ( FStrEq(pkvd->szKeyName, "new_model") )
{
m_iszCustomModel = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if ( FStrEq(pkvd->szKeyName, "bloodcolor") )
{
m_iMonsterBlood = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if ( FStrEq(pkvd->szKeyName, "respawn_as_playerally") )
{
if (atoi(pkvd->szValue))
m_iClassifyOverride = CLASS_PLAYER_ALLY;
pkvd->fHandled = TRUE;
}
// These are to keep consistency with Sven Co-op's squadmaker entity.
// CMBaseMonster::KeyValue will process TriggerCondition/TriggerTarget
// keyvalues in the same way.
else if ( FStrEq(pkvd->szKeyName, "trigger_condition") )
{
m_iTriggerCondition = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if ( FStrEq(pkvd->szKeyName, "trigger_target") )
{
m_iszTriggerTarget = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else
CMBaseMonster::KeyValue( pkvd );
}
void CMMonsterMaker :: Spawn( )
{
// likely omitted keyvalue, but it could truly be an alien grunt spawn
if ( m_iMonsterIndex == 0 )
{
if ( !monster_types[0].need_to_precache )
{
// monstertype was not defined
ALERT ( at_logged, "[MONSTER] Spawned a monstermaker entity without a monstertype! targetname: \"%s\"\n", STRING(pev->targetname) );
m_iMonsterIndex = -1;
}
}
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;
pev->classname = MAKE_STRING("monstermaker");
}
void CMMonsterMaker :: Precache( void )
{
CMBaseMonster::Precache();
// choosen monster is auto-precached
}
//=========================================================
// MakeMonster- this is the code that drops the monster
//=========================================================
void CMMonsterMaker::MakeMonster( void )
{
// monstermaker incorrectly setup or intentionally empty
if ( m_iMonsterIndex == -1 )
{
ALERT ( at_console, "[MONSTER] NULL Ent in MonsterMaker!\n" );
return;
}
edict_t *pent;
pKVD keyvalue[MAX_KEYVALUES]; // 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;
/* KEYVALUES */
// 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 ));
}
// Override monster blood color?
if ( m_iMonsterBlood )
{
// setup blood keyvalue
strcpy(keyvalue[1].key, "bloodcolor");
sprintf(keyvalue[1].value, "%i", m_iMonsterBlood);
}
// Trigger conditions set?
if ( !FStringNull( m_iszTriggerTarget ) )
{
// setup trigger keyvalues
strcpy(keyvalue[2].key, "TriggerCondition");
sprintf(keyvalue[2].value, "%i", m_iTriggerCondition);
strcpy(keyvalue[3].key, "TriggerTarget");
strcpy(keyvalue[3].value, STRING( m_iszTriggerTarget ));
}
// Weapons (For hgrunt/massn/rgrunt/etc...)
if ( pev->weapons )
{
strcpy(keyvalue[4].key, "weapons");
sprintf(keyvalue[4].value, "%i", pev->weapons);
}
// Monster's Name
if ( !FStringNull( m_szMonsterName ) )
{
strcpy(keyvalue[5].key, "displayname");
strcpy(keyvalue[5].value, STRING( m_szMonsterName ));
}
// Classify override
if ( m_iClassifyOverride )
{
strcpy(keyvalue[6].key, "classify");
sprintf(keyvalue[6].value, "%i", m_iClassifyOverride);
}
// Attempt to spawn monster
pent = spawn_monster(m_iMonsterIndex, pev->origin, pev->angles, createSF, keyvalue);
if ( pent == NULL )
{
ALERT ( at_console, "[MONSTER] MonsterMaker - failed to spawn monster! targetname: \"%s\"\n", STRING(pev->targetname) );
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;
}
// Pass parent's rendering effects to child
pent->v.rendermode = pev->rendermode;
pent->v.renderfx = pev->renderfx;
pent->v.renderamt = pev->renderamt;
pent->v.rendercolor = pev->rendercolor;
// Soundlist isn't "exactly" a keyvalue so pass it here
if ( m_srSoundList != NULL )
{
// it needs to be allocated first
CMBaseMonster *pChild = GetClassPtr((CMBaseMonster *)VARS(pent));
pChild->m_srSoundList = (REPLACER::REPLACER*)calloc(MAX_REPLACEMENTS, sizeof(*pChild->m_srSoundList));
memcpy(pChild->m_srSoundList, m_srSoundList, sizeof(REPLACER::REPLACER));
pChild->m_isrSounds = m_isrSounds;
}
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

@@ -39,7 +39,10 @@ extern DLL_GLOBAL BOOL g_fDrawLines;
extern CGraph WorldGraph;// the world node graph
extern cvar_t *monster_turn_coeficient;
extern cvar_t *monster_default_maxrange;
extern void process_monster_sound(edict_t *pMonster, char *fileName);
//=========================================================
// Eat - makes a monster full for a little while.
@@ -207,7 +210,7 @@ void CMBaseMonster :: Look ( int iDistance )
}
}
}
else if (!UTIL_IsPlayer(pSightEnt))
else
{
/* MonsterMod monster looking at a NON-MonsterMod monster */
@@ -1552,7 +1555,7 @@ void CMBaseMonster :: Move ( float flInterval )
}
else
{
//jlb TaskFail();
TaskFail();
ALERT( at_aiconsole, "%s Failed to move (%d)!\n", STRING(pev->classname), HasMemory( bits_MEMORY_MOVE_FAILED ) );
//ALERT( at_aiconsole, "%f, %f, %f\n", pev->origin.z, (pev->origin + (vecDir * flCheckDist)).z, m_Route[m_iRouteIndex].vecLocation.z );
}
@@ -1661,8 +1664,9 @@ void CMBaseMonster :: MonsterInit ( void )
for (int i=0; i < MAX_OLD_ENEMIES; i++)
m_hOldEnemy[ i ] = NULL;
m_flDistTooFar = 1024.0;
m_flDistLook = 2048.0;
if (!m_flDistLook)
m_flDistLook = monster_default_maxrange->value;
m_flDistTooFar = m_flDistLook / 2; // always 50%
// set eye position
SetEyePosition();
@@ -2085,15 +2089,28 @@ edict_t *CMBaseMonster :: BestVisibleEnemy ( void )
if ( UTIL_IsPlayer(pEnt) )
{
// it's a player...
if ( UTIL_IsAlive(pEnt) )
{
// repeat2
if ( IRelationshipByClass( CLASS_PLAYER ) > iBestRelationship )
{
iBestRelationship = IRelationshipByClass( CLASS_PLAYER );
iNearest = ( pEnt->v.origin - pev->origin ).Length();
pReturn = pEnt;
}
else if ( IRelationshipByClass( CLASS_PLAYER ) == iBestRelationship )
{
iDist = ( pEnt->v.origin - pev->origin ).Length();
if ( iDist <= iNearest )
{
iNearest = iDist;
iBestRelationship = R_NM; // player is always nemesis
iBestRelationship = IRelationshipByClass( CLASS_PLAYER );
pReturn = pEnt;
}
}
}
}
else if (pEnt->v.euser4 != NULL)
{
// it's a monstermod monster...
@@ -2125,12 +2142,12 @@ edict_t *CMBaseMonster :: BestVisibleEnemy ( void )
}
}
}
else if (!UTIL_IsPlayer(pEnt))
else
{
// it's a game default monster...
// it's a normal game entity...
if ( UTIL_IsAlive(pEnt) )
{
//repeat2
//repeat3
if ( IRelationship( pEnt->v.iuser4 ) > iBestRelationship )
{
iBestRelationship = IRelationship( pEnt->v.iuser4 );
@@ -2221,7 +2238,22 @@ float CMBaseMonster::ChangeYaw ( int yawSpeed )
ideal = pev->ideal_yaw;
if (current != ideal)
{
speed = (float)yawSpeed * gpGlobals->frametime * 10;
// -SamVanheer
if ( m_flLastYawTime == 0 )
{
m_flLastYawTime = gpGlobals->time - gpGlobals->frametime;
}
float delta = gpGlobals->time - m_flLastYawTime;
if ( delta > 0.25 )
delta = 0.25;
// let server operators modify the multiplier coeficient -Giegue
float multiplier = monster_turn_coeficient->value;
if ( multiplier < 0.1 || multiplier > 10.0 )
multiplier = 1.75;
speed = (float)yawSpeed * delta * multiplier;
move = ideal - current;
if (ideal > current)
@@ -2596,6 +2628,36 @@ void CMBaseMonster :: KeyValue( KeyValueData *pkvd )
m_iClassifyOverride = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "bloodcolor"))
{
switch ( atoi( pkvd->szValue ) )
{
case -1: m_bloodColor = DONT_BLEED; break;
case 1: m_bloodColor = BLOOD_COLOR_RED; break;
case 2: m_bloodColor = BLOOD_COLOR_YELLOW; break;
case 3: m_bloodColor = BLOOD_COLOR_BLUE; break;
case 4: m_bloodColor = BLOOD_COLOR_PINK; break;
case 5: m_bloodColor = BLOOD_COLOR_WHITE; break;
case 6: m_bloodColor = BLOOD_COLOR_ORANGE; break;
case 7: m_bloodColor = BLOOD_COLOR_BLACK; break;
case 8: m_bloodColor = BLOOD_COLOR_GREEN; break;
default: m_bloodColor = 0; break; // Invalid, set default
}
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "soundlist"))
{
if (strlen( pkvd->szValue ))
{
process_monster_sound(edict(), pkvd->szValue);
}
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "attackrange"))
{
m_flDistLook = atof(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else
{
CMBaseToggle::KeyValue( pkvd );
@@ -2959,9 +3021,14 @@ BOOL CMBaseMonster :: GetEnemy ( void )
if (HasConditions(bits_COND_SEE_CLIENT) && (m_hEnemy == NULL))
{
m_hEnemy = BestVisibleEnemy();
// the player we've just seen might not always be our enemy
if ( m_hEnemy != NULL )
{
m_hTargetEnt = m_hEnemy;
m_vecEnemyLKP = m_hEnemy->v.origin;
}
}
// remember old enemies
if (m_hEnemy == NULL && PopEnemy( ))

61
src/dlls/music.cpp Normal file
View File

@@ -0,0 +1,61 @@
//=========================================================
// Ambient Music - when triggered, it will play an mp3
// music file locally to players, looped or once.
//=========================================================
#include "extdll.h"
#include "util.h"
#include "cmbase.h"
#include "cmbasemonster.h"
#include "cmbaseextra.h"
// spawnflags
#define SF_MUSIC_LOOP 2 // music will loop instead of playing once.
#define SF_MUSIC_ACTIVATOR_ONLY 4 // only play to the one that activates this entity.
void CMAmbientMusic::KeyValue(KeyValueData *pkvd)
{
CMBaseMonster::KeyValue(pkvd);
}
void CMAmbientMusic::Spawn()
{
pev->solid = SOLID_NOT;
SetUse(&CMAmbientMusic::MusicUse);
pev->classname = MAKE_STRING("ambient_music");
}
void CMAmbientMusic::MusicUse(edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value)
{
// no music
if (FStringNull(pev->message))
return;
// not a player (if not to everyone)
if (pev->spawnflags & SF_MUSIC_ACTIVATOR_ONLY && !UTIL_IsPlayer(pActivator))
return;
if (pev->spawnflags & SF_MUSIC_ACTIVATOR_ONLY)
MESSAGE_BEGIN(MSG_ONE, SVC_STUFFTEXT, NULL, pActivator);
else
MESSAGE_BEGIN(MSG_ALL, SVC_STUFFTEXT);
// triggering off
if (useType == USE_OFF || m_fPlaying && useType != USE_ON)
{
WRITE_STRING("mp3 stop\n");
m_fPlaying = FALSE;
}
else // USE_ON / USE_TOGGLE
{
char szPath[256];
if (pev->spawnflags & SF_MUSIC_LOOP)
sprintf(szPath, "mp3 loop %s\n", STRING(pev->message));
else
sprintf(szPath, "mp3 play %s\n", STRING(pev->message));
WRITE_STRING(szPath);
m_fPlaying = TRUE;
}
MESSAGE_END();
}

View File

@@ -212,6 +212,10 @@ entvars_t* CGraph :: LinkEntForLink ( CLink *pLink, CNode *pNode )
//=========================================================
int CGraph :: HandleLinkEnt ( int iNode, entvars_t *pevLinkEnt, int afCapMask, NODEQUERY queryType )
{
// NULL pointers are becoming a nightmare... -Giegue
return FALSE;
#if 0
edict_t *pentWorld;
CMBaseEntity *pDoor;
TraceResult tr;
@@ -282,6 +286,7 @@ int CGraph :: HandleLinkEnt ( int iNode, entvars_t *pevLinkEnt, int afCapMask, N
}
return FALSE;
#endif
}
#if 0
@@ -1278,7 +1283,7 @@ int CGraph :: LinkVisibleNodes ( CLink *pLinkPool, FILE *file, int *piBadNode )
fprintf ( file, " Entity on connection: %s, name: %s Model: %s", STRING( VARS( pTraceEnt )->classname ), STRING ( VARS( pTraceEnt )->targetname ), STRING ( VARS(tr.pHit)->model ) );
}
fprintf ( file, "\n", j );
fprintf ( file, "\n" );
}
pLinkPool [ cTotalLinks ].m_iDestNode = j;
@@ -1889,17 +1894,17 @@ void CTestHull :: BuildNodeGraph( void )
switch ( hull )
{
case NODE_SMALL_HULL: // if this hull can't fit, nothing can, so drop the connection
fprintf ( file, "NODE_SMALL_HULL step %f\n", step );
fprintf ( file, "NODE_SMALL_HULL step %i\n", step );
pTempPool[ pSrcNode->m_iFirstLink + j ].m_afLinkInfo &= ~(bits_LINK_SMALL_HULL | bits_LINK_HUMAN_HULL | bits_LINK_LARGE_HULL);
fSkipRemainingHulls = TRUE;// don't bother checking larger hulls
break;
case NODE_HUMAN_HULL:
fprintf ( file, "NODE_HUMAN_HULL step %f\n", step );
fprintf ( file, "NODE_HUMAN_HULL step %i\n", step );
pTempPool[ pSrcNode->m_iFirstLink + j ].m_afLinkInfo &= ~(bits_LINK_HUMAN_HULL | bits_LINK_LARGE_HULL);
fSkipRemainingHulls = TRUE;// don't bother checking larger hulls
break;
case NODE_LARGE_HULL:
fprintf ( file, "NODE_LARGE_HULL step %f\n", step );
fprintf ( file, "NODE_LARGE_HULL step %i\n", step );
pTempPool[ pSrcNode->m_iFirstLink + j ].m_afLinkInfo &= ~bits_LINK_LARGE_HULL;
break;
}
@@ -3437,7 +3442,7 @@ void CGraph :: TestRoutingTables( void )
#endif
ALERT(at_aiconsole, "Routing is inconsistent!!!\n");
ALERT(at_aiconsole, "(%d to %d |%d/%d)1:", iFrom, iTo, iHull, iCap);
for (int i = 0; i < cPathSize1; i++)
for (i = 0; i < cPathSize1; i++)
{
ALERT(at_aiconsole, "%d ", pMyPath[i]);
}

View File

@@ -137,12 +137,12 @@ void CMOtis::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/otis.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/otis.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_RED;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_RED : m_bloodColor;
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

View File

@@ -60,6 +60,9 @@ void CPitdroneSpike::Spawn(void)
void CPitdroneSpike::SpikeTouch(edict_t *pOther)
{
if (m_hOwner == NULL)
pev->owner = NULL;
int iPitch;
// splat sound
@@ -89,6 +92,8 @@ void CPitdroneSpike::SpikeTouch(edict_t *pOther)
else
{
entvars_t *pevOwner = VARS(pev->owner);
if (pevOwner == NULL)
pevOwner = pev;
if ( UTIL_IsPlayer( pOther ) )
UTIL_TakeDamage( pOther, pev, pevOwner, gSkillData.pitdroneDmgSpit, DMG_GENERIC | DMG_NEVERGIB );
@@ -97,6 +102,8 @@ void CPitdroneSpike::SpikeTouch(edict_t *pOther)
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
pMonster->TakeDamage( pev, pevOwner, gSkillData.pitdroneDmgSpit, DMG_GENERIC | DMG_NEVERGIB );
}
else
UTIL_TakeDamageExternal( pOther, pev, pevOwner, gSkillData.pitdroneDmgSpit, DMG_GENERIC | DMG_NEVERGIB );
if (RANDOM_LONG(0,1))
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "weapons/xbow_hitbod1.wav", 1, ATTN_NORM, 0, iPitch);
@@ -139,6 +146,7 @@ edict_t *CPitdroneSpike::Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecV
pSpit->pev->velocity = vecVelocity;
pSpit->pev->angles = vecAngles;
pSpit->pev->owner = ENT( pevOwner );
pSpit->m_hOwner = ENT( pevOwner );
pSpit->SetThink(&CPitdroneSpike::StartTrail);
pSpit->pev->nextthink = gpGlobals->time + 0.1;
@@ -563,12 +571,12 @@ void CMPitdrone::Spawn()
{
Precache();
SET_MODEL( ENT(pev), "models/pit_drone.mdl" );
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/pit_drone.mdl"));
UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 48));
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->effects = 0;
pev->health = gSkillData.pitdroneHealth;
m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
@@ -603,7 +611,7 @@ void CMPitdrone::Precache()
{
PRECACHE_MODEL("models/pit_drone.mdl");
PRECACHE_MODEL("models/pit_drone_gibs.mdl");
iPitdroneSpitSprite = PRECACHE_MODEL("sprites/tinyspit.spr");// client side spittle.
iPitdroneSpitSprite = PRECACHE_MODELINDEX("sprites/tinyspit.spr");// client side spittle.
PRECACHE_SOUND_ARRAY(pAttackMissSounds);
PRECACHE_SOUND_ARRAY(pIdleSounds);
@@ -640,7 +648,7 @@ void CMPitdrone::Precache()
PRECACHE_SOUND("weapons/xbow_hitbod2.wav");
PRECACHE_SOUND("weapons/xbow_hit1.wav");
#if FEATURE_PITDRONE_SPIKE_TRAIL
iSpikeTrail = PRECACHE_MODEL("sprites/spike_trail.spr");
iSpikeTrail = PRECACHE_MODELINDEX("sprites/spike_trail.spr");
#endif
}

View File

@@ -53,21 +53,26 @@
#define RGRUNT_MAX_SPARKS 5
//=========================================================
// These sounds are muted for Robo Grunts
// This sound is muted for Robo Grunts
//=========================================================
BOOL CMRGrunt::FOkToSpeak(void)
{
return FALSE;
}
void CMRGrunt::IdleSound(void)
{
}
void CMRGrunt::PainSound(void)
{
}
// This is a gross hack: RGRUNT inherits from HGRUNT, so...
// to avoid duplicating code, I'm going to define it here
// then use it in hgrunt whenever speech is needed. -Giegue
const char *CMRGrunt::pRobotSentences[] =
{
"RB_GREN", // Sven Co-op uses "RB_" for rgrunt sentences
"RB_ALERT",
"RB_MONSTER",
"RB_COVER",
"RB_THROW",
"RB_CHARGE",
"RB_TAUNT",
};
//=========================================================
// DeathSound
//=========================================================
@@ -312,7 +317,7 @@ void CMRGrunt::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/rgrunt.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/rgrunt.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;
@@ -387,7 +392,7 @@ void CMRGrunt::Precache()
{
PRECACHE_MODEL("models/rgrunt.mdl");
m_iBodyGibs = PRECACHE_MODEL( "models/metalplategibs_green.mdl" );
m_iBodyGibs = PRECACHE_MODELINDEX( "models/metalplategibs_green.mdl" );
PRECACHE_SOUND("hgrunt/gr_mgun1.wav");
PRECACHE_SOUND("hgrunt/gr_mgun2.wav");
@@ -411,13 +416,8 @@ void CMRGrunt::Precache()
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_voicePitch = 115; // always the same
m_iBrassShell = PRECACHE_MODEL("models/shell.mdl");// brass shell
m_iBrassShell = PRECACHE_MODELINDEX("models/shell.mdl");// brass shell
}

456
src/dlls/ripent.cpp Normal file
View File

@@ -0,0 +1,456 @@
/***
*
* Copyright (c) 1996-2002, 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 is a stripped down "bspfile.c" file containing only entity data similar
// to using a ripent tool to read ents from a bsp, hence the name.
// -Giegue
#include "extdll.h"
#include "ripent.h"
#include "meta_api.h"
#if defined linux
#include <sys/stat.h>
#endif
//=============================================================================
int entdatasize;
char dentdata[MAX_MAP_ENTSTRING];
int num_entities;
entity_t entities[MAX_MAP_ENTITIES];
//=============================================================================
dheader_t *header;
int CopyLump(int lump, void *dest, int size)
{
int length, ofs;
length = header->lumps[lump].filelen;
ofs = header->lumps[lump].fileofs;
if (length % size)
{
LOG_MESSAGE(PLID, "LoadBSPFile: odd lump size");
return 0;
}
memcpy(dest, (byte *)header + ofs, length);
return length / size;
}
/*
=============
LoadBSPFile
=============
*/
void LoadBSPFile(char *filename)
{
//int i;
// reset these values
entdatasize = 0;
num_entities = 0;
memset(dentdata, 0, sizeof(dentdata));
memset(entities, 0, sizeof(entities));
//
// load the file header
//
if (LoadFile(filename, (void **)&header) == -1)
return;
// UNDONE: there is no need to swap it...? -Giegue
// swap the header
/*for (i = 0; i < sizeof(dheader_t) / 4; i++)
((int *)header)[i] = LittleLong(((int *)header)[i]);*/
// game will not load the BSP if it's invalid.
// so if this is called, it means something went really wrong loading it
if (header->version != BSPVERSION)
{
LOG_MESSAGE(PLID, "%s is version %i, not %i", filename, header->version, BSPVERSION);
return;
}
entdatasize = CopyLump(LUMP_ENTITIES, dentdata, 1);
free(header); // everything has been copied out
}
//============================================================================
/*
=================
ParseEpair
=================
*/
epair_t *ParseEpair(void)
{
epair_t *e;
e = (epair_t*)malloc(sizeof(epair_t));
memset(e, 0, sizeof(epair_t));
if (strlen(token) >= MAX_KEY - 1)
{
LOG_MESSAGE(PLID, "ParseEpar: token key too long");
return NULL;
}
e->key = copystring(token);
GetToken(false);
if (strlen(token) >= MAX_VALUE - 1)
{
LOG_MESSAGE(PLID, "ParseEpar: token value too long");
return NULL;
}
e->value = copystring(token);
return e;
}
/*
================
ParseEntity
================
*/
bool ParseEntity(void)
{
epair_t *e;
entity_t *mapent;
if (!GetToken(true))
return false;
if (strcmp(token, "{"))
{
LOG_MESSAGE(PLID, "ParseEntity: { not found");
return false;
}
if (num_entities == MAX_MAP_ENTITIES)
{
LOG_MESSAGE(PLID, "num_entities == MAX_MAP_ENTITIES");
return false;
}
mapent = &entities[num_entities];
num_entities++;
do
{
if (!GetToken(true))
{
LOG_MESSAGE(PLID, "ParseEntity: EOF without closing brace");
return false;
}
if (!strcmp(token, "}"))
break;
e = ParseEpair();
e->next = mapent->epairs;
mapent->epairs = e;
} while (1);
return true;
}
/*
================
ParseEntities
Parses the dentdata string into entities
================
*/
void ParseEntities(void)
{
num_entities = 0;
ParseFromMemory(dentdata, entdatasize);
while (ParseEntity())
{
}
}
// --
/* MERGE cmdlib.c AND scriplib.c INTO ripent.cpp */
/* Only add needed functions. */
// --
// -- cmdlib.c --
char qdir[1024] = { '\0' };
int LoadFile(char *filename, void **bufferptr)
{
FILE *f;
int length;
void *buffer;
f = SafeOpenRead(filename);
if (f == NULL)
return -1; // error
#if defined (_WIN32)
length = filelength(fileno(f));
#else
struct stat st; stat(filename, &st);
length = st.st_size;
#endif
buffer = malloc(length + 1);
((char *)buffer)[length] = 0;
SafeRead(f, buffer, length);
fclose(f);
*bufferptr = buffer;
return length;
}
int LittleLong(int l)
{
byte b1, b2, b3, b4;
b1 = l & 255;
b2 = (l >> 8) & 255;
b3 = (l >> 16) & 255;
b4 = (l >> 24) & 255;
return ((int)b1 << 24) + ((int)b2 << 16) + ((int)b3 << 8) + b4;
}
char *copystring(char *s)
{
char *b;
b = (char*)malloc(strlen(s) + 1);
strcpy(b, s);
return b;
}
FILE *SafeOpenRead(char *filename)
{
FILE *f;
f = fopen(filename, "rb");
if (!f)
{
LOG_MESSAGE(PLID, "Error opening %s: %s", filename, strerror(errno));
return NULL;
}
return f;
}
void SafeRead(FILE *f, void *buffer, int count)
{
if (fread(buffer, 1, count, f) != (size_t)count)
{
LOG_MESSAGE(PLID, "File read failure");
return;
}
}
char *ExpandPath(char *path)
{
char *psz;
static char full[1024];
if (!qdir)
{
LOG_MESSAGE(PLID, "ExpandPath called without qdir set");
return NULL;
}
if (path[0] == '/' || path[0] == '\\' || path[1] == ':')
return path;
psz = strstr(path, qdir);
if (psz)
strcpy(full, path);
else
sprintf(full, "%s%s", qdir, path);
return full;
}
// -- scriplib.c --
typedef struct
{
char filename[1024];
char *buffer, *script_p, *end_p;
int line;
} script_t;
#define MAX_INCLUDES 8
script_t scriptstack[MAX_INCLUDES];
script_t *script;
int scriptline;
char token[MAXTOKEN];
bool endofscript;
bool tokenready; // only true if UnGetToken was just called
void ParseFromMemory(char *buffer, int size)
{
script = scriptstack;
script++;
if (script == &scriptstack[MAX_INCLUDES])
{
LOG_MESSAGE(PLID, "script file exceeded MAX_INCLUDES");
return;
}
strcpy(script->filename, "memory buffer");
script->buffer = buffer;
script->line = 1;
script->script_p = script->buffer;
script->end_p = script->buffer + size;
endofscript = false;
tokenready = false;
}
bool EndOfScript(bool crossline)
{
if (!crossline)
{
LOG_MESSAGE(PLID, "Line %i is incomplete", scriptline);
return false;
}
if (!strcmp(script->filename, "memory buffer"))
{
endofscript = true;
return false;
}
free(script->buffer);
if (script == scriptstack + 1)
{
endofscript = true;
return false;
}
script--;
scriptline = script->line;
//printf("returning to %s\n", script->filename);
return GetToken(crossline);
}
void AddScriptToStack(char *filename)
{
int size;
script++;
if (script == &scriptstack[MAX_INCLUDES])
{
LOG_MESSAGE(PLID, "script file exceeded MAX_INCLUDES");
return;
}
strcpy(script->filename, ExpandPath(filename));
size = LoadFile(script->filename, (void **)&script->buffer);
//printf("entering %s\n", script->filename);
script->line = 1;
script->script_p = script->buffer;
script->end_p = script->buffer + size;
}
bool GetToken(bool crossline)
{
char *token_p;
if (tokenready) // is a token allready waiting?
{
tokenready = false;
return true;
}
if (script->script_p >= script->end_p)
return EndOfScript(crossline);
//
// skip space
//
skipspace:
while (*script->script_p <= 32)
{
if (script->script_p >= script->end_p)
return EndOfScript(crossline);
if (*script->script_p++ == '\n')
{
if (!crossline)
{
LOG_MESSAGE(PLID, "Line %i is incomplete", scriptline);
return false;
}
scriptline = script->line++;
}
}
if (script->script_p >= script->end_p)
return EndOfScript(crossline);
if (*script->script_p == ';' || *script->script_p == '#' || // semicolon and # is comment field
(*script->script_p == '/' && *((script->script_p) + 1) == '/')) // also make // a comment field
{
if (!crossline)
{
LOG_MESSAGE(PLID, "Line %i is incomplete", scriptline);
return false;
}
while (*script->script_p++ != '\n')
if (script->script_p >= script->end_p)
return EndOfScript(crossline);
goto skipspace;
}
//
// copy token
//
token_p = token;
if (*script->script_p == '"')
{
// quoted token
script->script_p++;
while (*script->script_p != '"')
{
*token_p++ = *script->script_p++;
if (script->script_p == script->end_p)
break;
if (token_p == &token[MAXTOKEN])
{
// if the server does not crash before this happens, then monstermod will.
// simulate a fatal error and be verbose on why it happens.
ALERT(at_logged, "FATAL ERROR (shutting down): ReadEntsFromBSP: Line %i is too long (length > %i).", scriptline, MAXTOKEN);
}
}
script->script_p++;
}
else // regular token
while (*script->script_p > 32 && *script->script_p != ';')
{
*token_p++ = *script->script_p++;
if (script->script_p == script->end_p)
break;
if (token_p == &token[MAXTOKEN])
{
// ditto
ALERT(at_logged, "FATAL ERROR (shutting down): ReadEntsFromBSP: Line %i is too long (length > %i).", scriptline, MAXTOKEN);
}
}
*token_p = 0;
if (!strcmp(token, "$include"))
{
GetToken(false);
AddScriptToStack(token);
return GetToken(crossline);
}
return true;
}

97
src/dlls/ripent.h Normal file
View File

@@ -0,0 +1,97 @@
/***
*
* Copyright (c) 1996-2002, 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 is a stripped down "bspfile.h". contains only entity data similar
// to using a ripent tool to read ents from a bsp, hence the name.
// -Giegue
#include "extdll.h"
// upper design bounds
// some ents are info holders for compiler tools (also -num_edicts is customizable)
#define MAX_MAP_ENTITIES 2048
#define MAX_MAP_ENTSTRING (128*2048)
// key / value pair sizes
#define MAX_KEY 32
#define MAX_VALUE 1024
//=============================================================================
#define BSPVERSION 30
typedef struct
{
int fileofs, filelen;
} lump_t;
#define LUMP_ENTITIES 0
#define HEADER_LUMPS 15
typedef struct
{
int version;
lump_t lumps[HEADER_LUMPS];
} dheader_t;
//============================================================================
// the utilities get to be lazy and just use large static arrays
extern int entdatasize;
extern char dentdata[MAX_MAP_ENTSTRING];
void LoadBSPFile(char *filename);
//===============
typedef struct epair_s
{
struct epair_s *next;
char *key;
char *value;
} epair_t;
typedef struct
{
vec3_t origin;
int firstbrush;
int numbrushes;
epair_t *epairs;
} entity_t;
extern int num_entities;
extern entity_t entities[MAX_MAP_ENTITIES];
void ParseEntities(void);
epair_t *ParseEpair(void);
// --
/* MERGE cmdlib.h AND scriplib.h INTO ripent.h */
/* Only add needed functions. */
// --
// -- cmdlib.h --
int LoadFile(char *filename, void **bufferptr);
int LittleLong(int l);
char *copystring(char *s);
FILE *SafeOpenRead(char *filename);
void SafeRead(FILE *f, void *buffer, int count);
char *ExpandPath(char *path); // from scripts
// -- scriplib.h --
#define MAXTOKEN 4096
extern char token[MAXTOKEN];
bool GetToken(bool crossline);
void ParseFromMemory(char *buffer, int size);

View File

@@ -596,12 +596,12 @@ void CMScientist :: Spawn( void )
// when a level is loaded, nobody will talk (time is reset to 0)
TalkInit();
SET_MODEL(ENT(pev), "models/scientist.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/scientist.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_RED;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_RED : m_bloodColor;
pev->health = gSkillData.scientistHealth;
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 scientists will notice player and say hello

View File

@@ -142,6 +142,8 @@ void CMShock::Touch(edict_t *pOther)
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
pMonster->TraceAttack( pevAttacker, pev->dmg, pev->velocity.Normalize(), &tr, damageType );
}
else
UTIL_TraceAttack( pOther, pevAttacker, pev->dmg, pev->velocity.Normalize(), &tr, damageType );
ApplyMultiDamage(pev, pevAttacker);
}

View File

@@ -26,6 +26,8 @@
#include "schedule.h"
#include "weapons.h"
#define SR_AE_JUMPATTACK ( 2 )
const char *CMShockRoach::pIdleSounds[] =
{
"shockroach/shock_idle1.wav",
@@ -69,7 +71,7 @@ void CMShockRoach::Spawn()
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_FLY;
m_bloodColor = BLOOD_COLOR_GREEN;
m_bloodColor = BLOOD_COLOR_YELLOW;
pev->effects = 0;
pev->health = gSkillData.roachHealth;
@@ -131,6 +133,8 @@ void CMShockRoach::LeapTouch(edict_t *pOther)
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
pMonster->TakeDamage( pev, pev, GetDamageAmount(), DMG_SLASH );
}
else
UTIL_TakeDamageExternal( pOther, pev, pev, GetDamageAmount(), DMG_SLASH );
}
SetTouch(NULL);
@@ -153,7 +157,7 @@ void CMShockRoach::MonsterThink(void)
{
pev->health = -1;
Killed(pev, 0);
return;
//return; // it still needs to think
}
CMHeadCrab::MonsterThink();
@@ -217,7 +221,26 @@ int CMShockRoach::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, f
return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
}
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//=========================================================
void CMShockRoach::HandleAnimEvent(MonsterEvent_t *pEvent)
{
CMHeadCrab::HandleAnimEvent(pEvent);
switch (pEvent->event)
{
case SR_AE_JUMPATTACK:
{
// Overwrite attack noise
AttackSound();
}
break;
}
}
void CMShockRoach::AttackSound()
{
EMIT_SOUND_DYN(edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAttackSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
}

View File

@@ -540,8 +540,7 @@ int SENTENCEG_Lookup(const char *sample, char *sentencenum)
return -1;
}
void EMIT_SOUND_DYN(edict_t *entity, int channel, const char *sample, float volume, float attenuation,
int flags, int pitch)
void EMIT_SOUND_DYN(edict_t *entity, int channel, const char *sample, float volume, float attenuation, int flags, int pitch)
{
if (sample && *sample == '!')
{
@@ -552,7 +551,7 @@ void EMIT_SOUND_DYN(edict_t *entity, int channel, const char *sample, float volu
ALERT( at_aiconsole, "Unable to find %s in sentences.txt\n", sample );
}
else
EMIT_SOUND_DYN2(entity, channel, sample, volume, attenuation, flags, pitch);
EMIT_SOUND_DYN2(entity, channel, REPLACER::FindSoundReplacement( entity, sample ), volume, attenuation, flags, pitch);
}
// play a specific sentence over the HEV suit speaker - just pass player entity, and !sentencename

View File

@@ -31,15 +31,18 @@ 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");
g_sModelIndexTinySpit = PRECACHE_MODELINDEX("sprites/tinyspit.spr");
gSporeExplode = PRECACHE_MODELINDEX("sprites/spore_exp_01.spr");
gSporeExplodeC = PRECACHE_MODELINDEX("sprites/spore_exp_c_01.spr");
PRECACHE_SOUND("weapons/splauncher_bounce.wav");
PRECACHE_SOUND("weapons/splauncher_impact.wav");
}
void CMSporeGrenade::Explode(TraceResult *pTrace)
{
if (m_hOwner == NULL)
pev->owner = NULL;
pev->solid = SOLID_NOT;// intangible
pev->takedamage = DAMAGE_NO;
@@ -125,6 +128,9 @@ void CMSporeGrenade::Detonate()
void CMSporeGrenade::BounceSound()
{
if (m_hOwner == NULL)
pev->owner = NULL;
EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/splauncher_bounce.wav", 0.25, ATTN_NORM);
}
@@ -252,6 +258,7 @@ CMSporeGrenade* CMSporeGrenade::ShootTimed(entvars_t *pevOwner, Vector vecStart,
pGrenade->pev->velocity = vecVelocity;
pGrenade->pev->angles = UTIL_VecToAngles(pGrenade->pev->velocity);
pGrenade->pev->owner = ENT(pevOwner);
pGrenade->m_hOwner = ENT(pevOwner);
pGrenade->SetTouch(&CMSporeGrenade::BounceTouch); // Bounce if touched
@@ -286,6 +293,7 @@ CMSporeGrenade *CMSporeGrenade::ShootContact(entvars_t *pevOwner, Vector vecStar
pGrenade->pev->velocity = vecVelocity;
pGrenade->pev->angles = UTIL_VecToAngles(pGrenade->pev->velocity);
pGrenade->pev->owner = ENT(pevOwner);
pGrenade->m_hOwner = ENT(pevOwner);
// make monsters afraid of it while in the air
pGrenade->SetThink(&CMSporeGrenade::DangerSoundThink);

View File

@@ -302,6 +302,8 @@ void CMSqueakGrenade::SuperBounceTouch( edict_t *pOther )
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
pMonster->TraceAttack(pev, gSkillData.snarkDmgBite, gpGlobals->v_forward, &tr, DMG_SLASH );
}
else
UTIL_TraceAttack(pOther, pev, gSkillData.snarkDmgBite, gpGlobals->v_forward, &tr, DMG_SLASH );
ApplyMultiDamage( pev, pev );

View File

@@ -216,7 +216,7 @@ int CMStrooper::Classify()
BOOL CMStrooper::CheckRangeAttack1(float flDot, float flDist)
{
return m_cAmmoLoaded >= 1;// && CMHGrunt::CheckRangeAttack1(flDot, flDist);
return (m_cAmmoLoaded >= 1) && CMHGrunt::CheckRangeAttack1(flDot, flDist);
}
BOOL CMStrooper::CheckRangeAttack2( float flDot, float flDist )
@@ -289,34 +289,24 @@ void CMStrooper::HandleAnimEvent(MonsterEvent_t *pEvent)
Vector vecShootOrigin = vecGunPos + gpGlobals->v_forward * 32;
Vector vecShootDir = ShootAtEnemy( vecShootOrigin );
vecGunAngles = UTIL_VecToAngles( vecShootDir );
vecGunAngles.z += RANDOM_FLOAT( -0.05, 0 );
//CBaseEntity *pShock = CBaseEntity::Create("shock_beam", vecShootOrigin, vecGunAngles, edict());
CMShock *pShock = CreateClassPtr((CMShock *)NULL);
Vector vecVelocity = vecShootDir * 2000;
edict_t *pShock = CMShock::Shoot( pev, vecGunAngles, vecShootOrigin, vecVelocity );
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);
}
else
{
ALERT( at_console, "Cannot create shock_beam!\n" );
}
}
}
break;
@@ -340,6 +330,8 @@ void CMStrooper::HandleAnimEvent(MonsterEvent_t *pEvent)
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt));
pMonster->TakeDamage( pev, pev, gSkillData.strooperDmgKick, DMG_CLUB );
}
else
UTIL_TakeDamageExternal( pHurt, pev, pev, gSkillData.strooperDmgKick, DMG_CLUB );
}
m_fRightClaw = !m_fRightClaw;
@@ -370,12 +362,12 @@ void CMStrooper::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/strooper.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/strooper.mdl"));
UTIL_SetSize( pev, Vector(-24, -24, 0), Vector(24, 24, 72) );
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
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 )
@@ -451,7 +443,7 @@ void CMStrooper::Precache()
{
PRECACHE_MODEL("models/strooper.mdl");
PRECACHE_MODEL("models/strooper_gibs.mdl");
iStrooperMuzzleFlash = PRECACHE_MODEL(STROOPER_MUZZLEFLASH);
iStrooperMuzzleFlash = PRECACHE_MODELINDEX(STROOPER_MUZZLEFLASH);
PRECACHE_SOUND("shocktrooper/shock_trooper_attack.wav");
PRECACHE_SOUND("shocktrooper/shock_trooper_die1.wav");
@@ -488,7 +480,7 @@ void CMStrooper::Precache()
else
m_voicePitch = 100;
m_iBrassShell = PRECACHE_MODEL("models/shell.mdl");// brass shell
m_iBrassShell = PRECACHE_MODELINDEX("models/shell.mdl");// brass shell
}
@@ -590,12 +582,8 @@ void CMStrooper::DropShockRoach(bool gibbed)
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);
UTIL_SetOrigin(roach->pev, vecPos);
roach->pev->angles = vecDropAngles;
roach->Spawn();
@@ -744,6 +732,8 @@ Schedule_t *CMStrooper::GetSchedule(void)
// new enemy
if (HasConditions(bits_COND_NEW_ENEMY))
{
// pretty much a copypaste of hgrunt and so the same issues. -Giegue
/*
//!!!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
@@ -757,14 +747,14 @@ Schedule_t *CMStrooper::GetSchedule(void)
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();
}
@@ -776,6 +766,7 @@ Schedule_t *CMStrooper::GetSchedule(void)
{
return GetScheduleOfType(SCHED_STROOPER_ESTABLISH_LINE_OF_FIRE);
}
*/
}
// no ammo
else if (HasConditions(bits_COND_NO_AMMO_LOADED))
@@ -801,9 +792,9 @@ Schedule_t *CMStrooper::GetSchedule(void)
//!!!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);
SENTENCEG_PlayRndSz( ENT(pev), "ST_COVER", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch);
m_iSentence = STROOPER_SENT_COVER;
//JustSpoke();
JustSpoke();
}
return GetScheduleOfType(SCHED_TAKE_COVER_FROM_ENEMY);
}
@@ -828,8 +819,10 @@ Schedule_t *CMStrooper::GetSchedule(void)
}
else
{
return GetScheduleOfType(SCHED_RANGE_ATTACK1);
// hide!
return GetScheduleOfType(SCHED_TAKE_COVER_FROM_ENEMY);
//return GetScheduleOfType(SCHED_TAKE_COVER_FROM_ENEMY);
}
}
// can't see enemy
@@ -846,6 +839,11 @@ Schedule_t *CMStrooper::GetSchedule(void)
return GetScheduleOfType(SCHED_RANGE_ATTACK2);
}
else
{
return GetScheduleOfType(SCHED_STROOPER_ESTABLISH_LINE_OF_FIRE);
}
/*
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
@@ -857,6 +855,7 @@ Schedule_t *CMStrooper::GetSchedule(void)
}
return GetScheduleOfType(SCHED_STANDOFF);
}
*/
}
if (HasConditions(bits_COND_SEE_ENEMY) && !HasConditions(bits_COND_CAN_RANGE_ATTACK1))

View File

@@ -99,19 +99,19 @@ void CMStukabat :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/stukabat.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/stukabat.mdl"));
UTIL_SetSize( pev, Vector( -12, -12, 0 ), Vector( 12, 12, 24 ) );
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_FLY;
pev->flags |= FL_FLY;
m_bloodColor = BLOOD_COLOR_YELLOW;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
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";
m_pFlapSound = REPLACER::FindSoundReplacement( "stukabat/stukabat_flap1.wav" );
MonsterInit();
@@ -138,40 +138,70 @@ void CMStukabat :: Precache()
// AI Schedules Specific to this monster
//=========================================================
/* Chase */
// Chase enemy
Task_t tlStukabatChaseEnemy[] =
{
{ TASK_GET_PATH_TO_ENEMY, (float)128 }, // is the 128 number really used?
{ TASK_SET_ACTIVITY, (float)ACT_FLY },
{ TASK_SET_FAIL_SCHEDULE, (float)SCHED_CHASE_ENEMY_FAILED },
{ TASK_GET_PATH_TO_ENEMY, (float)0 },
{ TASK_WALK_PATH, (float)0 }, // flying monster, use walk
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
};
Schedule_t slStukabatChaseEnemy[] =
{
{
tlStukabatChaseEnemy,
ARRAYSIZE ( tlStukabatChaseEnemy ),
bits_COND_NEW_ENEMY |
bits_COND_CAN_RANGE_ATTACK1 |
bits_COND_TASK_FAILED,
0,
"Stukabat Chase Enemy"
},
};
/* Fail */
// Chase failed
Task_t tlStukabatChaseEnemyFailed[] =
{
{ TASK_STOP_MOVING, (float)0 },
{ TASK_WAIT, (float)0.2 },
{ TASK_FIND_COVER_FROM_ENEMY, (float)0 },
{ TASK_WALK_PATH, (float)0 },
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
{ TASK_REMEMBER, (float)bits_MEMORY_INCOVER },
// { TASK_TURN_LEFT, (float)179 },
{ TASK_FACE_ENEMY, (float)0 },
{ TASK_WAIT, (float)1 },
};
Schedule_t slStukabatChaseEnemyFailed[] =
{
{
tlStukabatChaseEnemyFailed,
ARRAYSIZE ( tlStukabatChaseEnemyFailed ),
bits_COND_NEW_ENEMY |
bits_COND_CAN_RANGE_ATTACK1,
0,
"Stukabat Chase Failed"
},
};
// Fail
Task_t tlStukabatFail[] =
{
{ TASK_STOP_MOVING, 0 },
{ TASK_STOP_MOVING, (float)0 },
{ TASK_SET_ACTIVITY, (float)ACT_HOVER },
{ TASK_WAIT, (float)2 },
{ TASK_WAIT_PVS, (float)0 },
};
Schedule_t slStukabatFail[] =
{
{
tlStukabatFail,
ARRAYSIZE ( tlStukabatFail ),
0,
bits_COND_CAN_ATTACK,
0,
"Stukabat Fail"
},
@@ -181,6 +211,7 @@ Schedule_t slStukabatFail[] =
DEFINE_CUSTOM_SCHEDULES( CMStukabat )
{
slStukabatChaseEnemy,
slStukabatChaseEnemyFailed,
slStukabatFail,
};
@@ -198,9 +229,8 @@ void CMStukabat :: SetActivity ( Activity NewActivity )
{
case ACT_IDLE:
return; // refuse
case ACT_HOVER:
case ACT_FLY:
iSequence = LookupActivity ( NewActivity );
break;
default:
iSequence = LookupActivity ( NewActivity );
break;
@@ -238,6 +268,8 @@ Schedule_t* CMStukabat :: GetScheduleOfType ( int Type )
{
case SCHED_CHASE_ENEMY:
return slStukabatChaseEnemy;
case SCHED_CHASE_ENEMY_FAILED:
return slStukabatChaseEnemyFailed;
case SCHED_FAIL:
return slStukabatFail;
}

View File

@@ -145,12 +145,25 @@ void FireTargets( const char *targetName, edict_t *pActivator, edict_t *pCaller,
if (FNullEnt(pentTarget))
break;
// MonsterMod entity
CMBaseEntity *pTarget = CMBaseEntity::Instance( pentTarget );
if ( pTarget && !(pTarget->pev->flags & FL_KILLME) ) // Don't use dying ents
if ( pTarget && !(pTarget->pev->flags & FL_KILLME) )
{
ALERT( at_aiconsole, "Found: %s, firing (%s)\n", STRING(pTarget->pev->classname), targetName );
pTarget->Use( pActivator, pCaller, useType, value );
}
// Valid entity but not recognized by monstermod, must be a normal entity
else if (!(pentTarget->v.flags & FL_KILLME))
{
if (CVAR_GET_FLOAT("_glb_use")) // avoid "unknown command" spam
{
ALERT( at_aiconsole, "Found: %s, firing (%s)\n", STRING(pentTarget->v.classname), targetName );
char extCmd[64];
sprintf( extCmd, "_trigger %i %i %i %i %f\n", ENTINDEX( pentTarget ), ENTINDEX( ENT( pActivator ) ), ENTINDEX( ENT( pCaller ) ), useType, value );
SERVER_COMMAND( extCmd );
}
}
}
}

View File

@@ -92,6 +92,9 @@ void CMBaseTurret::Spawn()
m_iAutoStart = TRUE;
}
if (!m_flDistLook)
m_flDistLook = TURRET_RANGE;
ResetSequenceInfo( );
SetBoneController( 0, 0 );
SetBoneController( 1, 0 );
@@ -121,7 +124,7 @@ void CMBaseTurret::Precache( )
void CMTurret::Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/turret.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/turret.mdl"));
pev->health = gSkillData.turretHealth;
m_HackedGunPos = Vector( 0, 0, 12.75 );
m_flMaxSpin = TURRET_MAXSPIN;
@@ -161,7 +164,7 @@ void CMTurret::Precache()
void CMMiniTurret::Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/miniturret.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/miniturret.mdl"));
pev->health = gSkillData.miniturretHealth;
m_HackedGunPos = Vector( 0, 0, 12.75 );
m_flMaxSpin = 0;
@@ -348,7 +351,7 @@ void CMBaseTurret::ActiveThink(void)
Vector vec = UTIL_VecToAngles(vecMidEnemy - vecMid);
// Current enemy is not visible.
if (!fEnemyVisible || (flDistToEnemy > TURRET_RANGE))
if (!fEnemyVisible || (flDistToEnemy > m_flDistLook))
{
if (!m_flLastSight)
m_flLastSight = gpGlobals->time + 0.5;
@@ -459,7 +462,7 @@ void CMBaseTurret::ActiveThink(void)
void CMTurret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy)
{
FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, BULLET_MONSTER_12MM, 1 );
FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, m_flDistLook, BULLET_MONSTER_12MM, 1 );
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "turret/tu_fire1.wav", 1, 0.6);
pev->effects = pev->effects | EF_MUZZLEFLASH;
}
@@ -467,7 +470,7 @@ void CMTurret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy)
void CMMiniTurret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy)
{
FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, BULLET_MONSTER_9MM, 1 );
FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, m_flDistLook, BULLET_MONSTER_9MM, 1 );
switch(RANDOM_LONG(0,2))
{
@@ -721,7 +724,7 @@ void CMBaseTurret::SearchThink(void)
// Acquire Target
if (m_hEnemy == NULL)
{
Look(TURRET_RANGE);
Look(m_flDistLook);
m_hEnemy = BestVisibleEnemy();
}
@@ -779,7 +782,7 @@ void CMBaseTurret::AutoSearchThink(void)
if (m_hEnemy == NULL)
{
Look( TURRET_RANGE );
Look( m_flDistLook );
m_hEnemy = BestVisibleEnemy();
}
@@ -801,6 +804,7 @@ void CMBaseTurret :: TurretDeath( void )
if (pev->deadflag != DEAD_DEAD)
{
pev->deadflag = DEAD_DEAD;
FCheckAITrigger(); // trigger death condition
float flRndSound = RANDOM_FLOAT ( 0 , 1 );
@@ -1014,7 +1018,7 @@ void CMSentry::Precache()
void CMSentry::Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/sentry.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/sentry.mdl"));
pev->health = gSkillData.sentryHealth;
m_HackedGunPos = Vector( 0, 0, 48 );
pev->view_ofs.z = 48;
@@ -1041,7 +1045,7 @@ void CMSentry::Spawn()
void CMSentry::Shoot(Vector &vecSrc, Vector &vecDirToEnemy)
{
FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, BULLET_MONSTER_MP5, 1 );
FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, m_flDistLook, BULLET_MONSTER_MP5, 1 );
switch(RANDOM_LONG(0,2))
{
@@ -1105,6 +1109,7 @@ void CMSentry::SentryDeath( void )
if (pev->deadflag != DEAD_DEAD)
{
pev->deadflag = DEAD_DEAD;
FCheckAITrigger(); // trigger death condition
float flRndSound = RANDOM_FLOAT ( 0 , 1 );

View File

@@ -39,7 +39,7 @@ typedef struct {
} gamedll_funcs_t;
extern gamedll_funcs_t *gpGamedllFuncs;
extern void check_player_dead( edict_t *pPlayer );
// Print to console.
void META_CONS(char *fmt, ...) {
@@ -226,6 +226,120 @@ UTIL_GroupTrace::~UTIL_GroupTrace( void )
}
TYPEDESCRIPTION gEntvarsDescription[] =
{
DEFINE_ENTITY_FIELD( classname, FIELD_STRING ),
DEFINE_ENTITY_GLOBAL_FIELD( globalname, FIELD_STRING ),
DEFINE_ENTITY_FIELD( origin, FIELD_POSITION_VECTOR ),
DEFINE_ENTITY_FIELD( oldorigin, FIELD_POSITION_VECTOR ),
DEFINE_ENTITY_FIELD( velocity, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( basevelocity, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( movedir, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( angles, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( avelocity, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( punchangle, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( v_angle, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( fixangle, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( idealpitch, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( pitch_speed, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( ideal_yaw, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( yaw_speed, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( modelindex, FIELD_INTEGER ),
DEFINE_ENTITY_GLOBAL_FIELD( model, FIELD_MODELNAME ),
DEFINE_ENTITY_FIELD( viewmodel, FIELD_MODELNAME ),
DEFINE_ENTITY_FIELD( weaponmodel, FIELD_MODELNAME ),
DEFINE_ENTITY_FIELD( absmin, FIELD_POSITION_VECTOR ),
DEFINE_ENTITY_FIELD( absmax, FIELD_POSITION_VECTOR ),
DEFINE_ENTITY_GLOBAL_FIELD( mins, FIELD_VECTOR ),
DEFINE_ENTITY_GLOBAL_FIELD( maxs, FIELD_VECTOR ),
DEFINE_ENTITY_GLOBAL_FIELD( size, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( ltime, FIELD_TIME ),
DEFINE_ENTITY_FIELD( nextthink, FIELD_TIME ),
DEFINE_ENTITY_FIELD( solid, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( movetype, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( skin, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( body, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( effects, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( gravity, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( friction, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( light_level, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( frame, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( scale, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( sequence, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( animtime, FIELD_TIME ),
DEFINE_ENTITY_FIELD( framerate, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( controller, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( blending, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( rendermode, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( renderamt, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( rendercolor, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( renderfx, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( health, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( frags, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( weapons, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( takedamage, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( deadflag, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( view_ofs, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( button, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( impulse, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( chain, FIELD_EDICT ),
DEFINE_ENTITY_FIELD( dmg_inflictor, FIELD_EDICT ),
DEFINE_ENTITY_FIELD( enemy, FIELD_EDICT ),
DEFINE_ENTITY_FIELD( aiment, FIELD_EDICT ),
DEFINE_ENTITY_FIELD( owner, FIELD_EDICT ),
DEFINE_ENTITY_FIELD( groundentity, FIELD_EDICT ),
DEFINE_ENTITY_FIELD( spawnflags, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( flags, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( colormap, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( team, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( max_health, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( teleport_time, FIELD_TIME ),
DEFINE_ENTITY_FIELD( armortype, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( armorvalue, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( waterlevel, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( watertype, FIELD_INTEGER ),
// Having these fields be local to the individual levels makes it easier to test those levels individually.
DEFINE_ENTITY_GLOBAL_FIELD( target, FIELD_STRING ),
DEFINE_ENTITY_GLOBAL_FIELD( targetname, FIELD_STRING ),
DEFINE_ENTITY_FIELD( netname, FIELD_STRING ),
DEFINE_ENTITY_FIELD( message, FIELD_STRING ),
DEFINE_ENTITY_FIELD( dmg_take, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( dmg_save, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( dmg, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( dmgtime, FIELD_TIME ),
DEFINE_ENTITY_FIELD( noise, FIELD_SOUNDNAME ),
DEFINE_ENTITY_FIELD( noise1, FIELD_SOUNDNAME ),
DEFINE_ENTITY_FIELD( noise2, FIELD_SOUNDNAME ),
DEFINE_ENTITY_FIELD( noise3, FIELD_SOUNDNAME ),
DEFINE_ENTITY_FIELD( speed, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( air_finished, FIELD_TIME ),
DEFINE_ENTITY_FIELD( pain_finished, FIELD_TIME ),
DEFINE_ENTITY_FIELD( radsuit_finished, FIELD_TIME ),
};
#define ENTVARS_COUNT (sizeof(gEntvarsDescription)/sizeof(gEntvarsDescription[0]))
#ifdef DEBUG
edict_t *DBG_EntOfVars( const entvars_t *pev )
{
@@ -1512,6 +1626,55 @@ void UTIL_StripToken( const char *pKey, char *pDest )
}
void EntvarsKeyvalue( entvars_t *pev, KeyValueData *pkvd )
{
int i;
TYPEDESCRIPTION *pField;
for ( i = 0; i < ENTVARS_COUNT; i++ )
{
pField = &gEntvarsDescription[i];
if ( !stricmp( pField->fieldName, pkvd->szKeyName ) )
{
switch( pField->fieldType )
{
case FIELD_MODELNAME:
case FIELD_SOUNDNAME:
case FIELD_STRING:
(*(int *)((char *)pev + pField->fieldOffset)) = ALLOC_STRING( pkvd->szValue );
break;
case FIELD_TIME:
case FIELD_FLOAT:
(*(float *)((char *)pev + pField->fieldOffset)) = atof( pkvd->szValue );
break;
case FIELD_INTEGER:
(*(int *)((char *)pev + pField->fieldOffset)) = atoi( pkvd->szValue );
break;
case FIELD_POSITION_VECTOR:
case FIELD_VECTOR:
UTIL_StringToVector( (float *)((char *)pev + pField->fieldOffset), pkvd->szValue );
break;
default:
case FIELD_EVARS:
case FIELD_CLASSPTR:
case FIELD_EDICT:
case FIELD_ENTITY:
case FIELD_POINTER:
ALERT( at_error, "Bad field in entity!!\n" );
break;
}
pkvd->fHandled = TRUE;
return;
}
}
}
Vector VecBModelOrigin( entvars_t* pevBModel )
{
return pevBModel->absmin + ( pevBModel->size * 0.5 );
@@ -1520,15 +1683,13 @@ Vector VecBModelOrigin( entvars_t* pevBModel )
bool UTIL_IsAlive(entvars_t *pev)
{
return ((pev->deadflag == DEAD_NO) && (pev->health > 0) &&
((pev->flags & FL_NOTARGET) == 0) && (pev->takedamage != 0));
return ((pev->deadflag == DEAD_NO) && (pev->health > 0));
}
bool UTIL_IsAlive(edict_t *pEdict)
{
return ((pEdict->v.deadflag == DEAD_NO) && (pEdict->v.health > 0) &&
((pEdict->v.flags & FL_NOTARGET) == 0) && (pEdict->v.takedamage != 0));
return ((pEdict->v.deadflag == DEAD_NO) && (pEdict->v.health > 0));
}
@@ -1677,8 +1838,8 @@ int UTIL_TakeDamage( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAtt
flBonus = ARMOR_BONUS;
flRatio = ARMOR_RATIO;
if (!pEdict->v.takedamage)
return 0;
if (!pEdict->v.takedamage || (pEdict->v.flags & FL_GODMODE))
return 0; // refuse the damage
if ( ( bitsDamageType & DMG_BLAST ) )
{
@@ -1762,6 +1923,7 @@ int UTIL_TakeDamage( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAtt
{
pEdict->v.health = 1; // can't suicide if already dead!
gpGamedllFuncs->dllapi_table->pfnClientKill(pEdict);
check_player_dead(pEdict); // will you just fucking work?
// Add 1 score to the monster that killed this player
if ( pevAttacker->flags & FL_MONSTER )
@@ -1875,7 +2037,11 @@ void UTIL_TraceBleed( edict_t *pEdict, float flDamage, Vector vecDir, TraceResul
if ( Bloodtr.flFraction != 1.0 )
{
UTIL_BloodDecalTrace( &Bloodtr, BLOOD_COLOR_RED );
int bloodColor = pEdict->v.iuser3;
if ( !bloodColor )
bloodColor = BLOOD_COLOR_RED;
UTIL_BloodDecalTrace( &Bloodtr, bloodColor );
}
}
}
@@ -1886,9 +2052,13 @@ void UTIL_TraceAttack( edict_t *pEdict, entvars_t *pevAttacker, float flDamage,
if ( pEdict->v.takedamage )
{
int bloodColor = pEdict->v.iuser3;
if ( !bloodColor )
bloodColor = BLOOD_COLOR_RED;
AddMultiDamage( pevAttacker, pEdict, flDamage, bitsDamageType );
SpawnBlood(ptr->vecEndPos, BLOOD_COLOR_RED, flDamage);// a little surface blood.
SpawnBlood(ptr->vecEndPos, bloodColor, flDamage);// a little surface blood.
UTIL_TraceBleed( pEdict, flDamage, vecDir, ptr, bitsDamageType );
}
@@ -1971,8 +2141,11 @@ bool UTIL_IsBSPModel( edict_t *pent )
void UTIL_TakeDamageExternal( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
// Tell AMXX to call TakeDamage for us.
// Tell AMXX to call TakeDamage for us if it can.
if (CVAR_GET_FLOAT("_glb_takedamage"))
{
char extCmd[64];
sprintf( extCmd, "monster_hurt_entity %i %i %i %f %i\n", ENTINDEX( pEdict ), ENTINDEX( ENT( pevInflictor ) ), ENTINDEX( ENT( pevAttacker ) ), flDamage, bitsDamageType );
sprintf( extCmd, "_takedamage %i %i %i %f %i\n", ENTINDEX( pEdict ), ENTINDEX( ENT( pevInflictor ) ), ENTINDEX( ENT( pevAttacker ) ), flDamage, bitsDamageType );
SERVER_COMMAND( extCmd );
}
}

View File

@@ -149,7 +149,12 @@ inline BOOL FStringNull(int iString) { return iString == iStringNull; }
#define DONT_BLEED -1
#define BLOOD_COLOR_RED (BYTE)247
#define BLOOD_COLOR_YELLOW (BYTE)195
#define BLOOD_COLOR_GREEN BLOOD_COLOR_YELLOW
#define BLOOD_COLOR_BLUE (BYTE)211 // custom colors
#define BLOOD_COLOR_PINK (BYTE)147
#define BLOOD_COLOR_WHITE (BYTE)11
#define BLOOD_COLOR_ORANGE (BYTE)231
#define BLOOD_COLOR_BLACK (BYTE)49 // not 100% accurate but close enough
#define BLOOD_COLOR_GREEN (BYTE)181 // ^
typedef enum
{
@@ -400,6 +405,9 @@ extern DLL_GLOBAL const Vector g_vecZero;
#define SVC_ROOMTYPE 37
#define SVC_HLTV 50
// Added to stuff text to the clients
#define SVC_STUFFTEXT 9 // [string] stuffed into client's console buffer
// prxoy director stuff
#define DRC_EVENT 3 // informs the dircetor about ann important game event
@@ -433,6 +441,18 @@ extern DLL_GLOBAL const Vector g_vecZero;
#define SF_TRIG_PUSH_ONCE 1
// Override a few engine callbacks for model replacements
#include "globalreplace.h"
#define SET_MODEL( entity, model ) \
{ SET_MODEL2( entity, REPLACER::FindModelReplacement( entity, model ) ); }
#define PRECACHE_MODEL( model ) \
{ PRECACHE_MODEL2( (char*)REPLACER::FindModelReplacement( model ) ); }
#define PRECACHE_SOUND( sound ) \
{ PRECACHE_SOUND2( (char*)REPLACER::FindSoundReplacement( sound ) ); }
inline int PRECACHE_MODELINDEX( char* model )
{
return PRECACHE_MODEL2( (char*)REPLACER::FindModelReplacement( model ) );
}
// Sound Utilities
@@ -466,9 +486,7 @@ float TEXTURETYPE_PlaySound(TraceResult *ptr, Vector vecSrc, Vector vecEnd, int
// EMIT_SOUND_DYN with pitch != 100 should be used sparingly, as it's not quite as
// fast as EMIT_SOUND (the pitchshift mixer is not native coded).
void EMIT_SOUND_DYN(edict_t *entity, int channel, const char *sample, float volume, float attenuation,
int flags, int pitch);
void EMIT_SOUND_DYN(edict_t *entity, int channel, const char *sample, float volume, float attenuation, int flags, int pitch);
inline void EMIT_SOUND(edict_t *entity, int channel, const char *sample, float volume, float attenuation)
{
@@ -485,7 +503,7 @@ void EMIT_GROUPID_SUIT(edict_t *entity, int isentenceg);
void EMIT_GROUPNAME_SUIT(edict_t *entity, const char *groupname);
#define PRECACHE_SOUND_ARRAY( a ) \
{ for (int i = 0; i < ARRAYSIZE( a ); i++ ) PRECACHE_SOUND((char *) a [i]); }
{ for (int i = 0; i < ARRAYSIZE( a ); i++ ) PRECACHE_SOUND2((char *) REPLACER::FindSoundReplacement(a[i])); }
#define EMIT_SOUND_ARRAY_DYN( chan, array ) \
EMIT_SOUND_DYN ( ENT(pev), chan , array [ RANDOM_LONG(0,ARRAYSIZE( array )-1) ], 1.0, ATTN_NORM, 0, RANDOM_LONG(95,105) );

View File

@@ -33,9 +33,6 @@
#define VOLTIGORE_MAX_BEAMS 8
#define VOLTIGORE_CLASSNAME "monster_alien_voltigore"
#define VOLTIGORE_BABY_CLASSNAME "monster_alien_babyvoltigore"
#define VOLTIGORE_ZAP_RED 180
#define VOLTIGORE_ZAP_GREEN 16
#define VOLTIGORE_ZAP_BLUE 255
@@ -130,6 +127,8 @@ void CMVoltigoreEnergyBall::BallTouch(edict_t *pOther)
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
pMonster->TakeDamage( pev, VARS( pev->owner ), gSkillData.voltigoreDmgBeam, DMG_SHOCK|DMG_ALWAYSGIB );
}
else
UTIL_TakeDamageExternal( pOther, pev, VARS(pev->owner), gSkillData.voltigoreDmgBeam, DMG_SHOCK | DMG_ALWAYSGIB );
}
pev->velocity = Vector(0,0,0);
@@ -157,6 +156,8 @@ void CMVoltigoreEnergyBall::FlyThink(void)
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
pMonster->TakeDamage( pev, pev, gSkillData.voltigoreDmgBeam/5, DMG_SHOCK );
}
else
UTIL_TakeDamageExternal( pEntity, pev, pev, gSkillData.voltigoreDmgBeam / 5, DMG_SHOCK );
}
}
@@ -394,11 +395,11 @@ BOOL CMVoltigore::CheckRangeAttack1(float flDot, float flDist)
return FALSE;
}
if (flDist > 64 && flDist <= 784 && flDot >= 0.5 && gpGlobals->time >= m_flNextZapTime)
if (flDist > 64 && flDist <= m_flDistTooFar && flDot >= 0.5 && gpGlobals->time >= m_flNextZapTime)
{
if (m_hEnemy != 0)
{
if (fabs(pev->origin.z - m_hEnemy->v.origin.z) > 256)
if (fabs(pev->origin.z - m_hEnemy->v.origin.z) > 512)
{
// don't try to spit at someone up really high or down really low.
return FALSE;
@@ -448,6 +449,13 @@ void CMVoltigore::GibMonster()
pev->nextthink = gpGlobals->time;
}
void CMVoltigore::UpdateOnRemove()
{
CMBaseMonster::UpdateOnRemove();
DestroyBeams();
DestroyGlow();
}
//=========================================================
// CheckMeleeAttack1 - voltigore is a big guy, so has a longer
// melee range than most monsters. This is the tailwhip attack
@@ -629,12 +637,12 @@ void CMVoltigore::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/voltigore.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/voltigore.mdl"));
UTIL_SetSize(pev, Vector(-80, -80, 0), Vector(80, 80, 90));
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->effects = 0;
pev->health = gSkillData.voltigoreHealth;
m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
@@ -691,7 +699,7 @@ void CMVoltigore::PrecacheImpl(char *modelName)
PRECACHE_SOUND("debris/beamstart1.wav");
m_beamTexture = PRECACHE_MODEL(VOLTIGORE_ZAP_BEAM);
m_beamTexture = PRECACHE_MODELINDEX(VOLTIGORE_ZAP_BEAM);
PRECACHE_MODEL(VOLTIGORE_GLOW_SPRITE);
PRECACHE_MODEL("sprites/lgtning.spr");
@@ -1023,6 +1031,8 @@ void CMVoltigore::GibBeamDamage()
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
pMonster->TakeDamage( pev, pev, flAdjustedDamage, DMG_SHOCK );
}
else
UTIL_TakeDamageExternal( pEntity, pev, pev, flAdjustedDamage, DMG_SHOCK );
}
}
}
@@ -1135,12 +1145,12 @@ void CMBabyVoltigore::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/baby_voltigore.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/baby_voltigore.mdl"));
UTIL_SetSize(pev, Vector(-32, -32, 0), Vector(32, 32, 36));
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->effects = 0;
pev->health = gSkillData.babyVoltigoreHealth;
m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )

View File

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

View File

@@ -47,6 +47,7 @@ public:
virtual void Killed( entvars_t *pevAttacker, int iGib );
BOOL m_fRegisteredSound;// whether or not this grenade has issued its DANGER sound to the world sound list yet.
EHANDLE m_hOwner;
};
// Contact/Timed spore grenade
@@ -75,6 +76,7 @@ public:
void UpdateOnRemove();
CMSprite* m_pSporeGlow;
EHANDLE m_hOwner;
};
// constant items

View File

@@ -111,16 +111,6 @@ void CMZombie :: SetYawSpeed ( void )
int CMZombie :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
// Take 30% 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.3;
}
// HACK HACK -- until we fix this.
if ( IsAlive() )
PainSound();
@@ -246,12 +236,12 @@ void CMZombie :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/zombie.mdl");
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/zombie.mdl"));
UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX );
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->health = gSkillData.zombieHealth;
pev->view_ofs = VEC_VIEW;// 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 )
@@ -273,27 +263,14 @@ void CMZombie :: Spawn()
//=========================================================
void CMZombie :: Precache()
{
int i;
PRECACHE_MODEL("models/zombie.mdl");
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ )
PRECACHE_SOUND((char *)pAttackHitSounds[i]);
for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ )
PRECACHE_SOUND((char *)pAttackMissSounds[i]);
for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ )
PRECACHE_SOUND((char *)pAttackSounds[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]);
PRECACHE_SOUND_ARRAY(pAttackHitSounds);
PRECACHE_SOUND_ARRAY(pAttackMissSounds);
PRECACHE_SOUND_ARRAY(pAttackSounds);
PRECACHE_SOUND_ARRAY(pIdleSounds);
PRECACHE_SOUND_ARRAY(pAlertSounds);
PRECACHE_SOUND_ARRAY(pPainSounds);
}
//=========================================================

View File

@@ -420,11 +420,10 @@ typedef enum _fieldtypes
FIELD_TYPECOUNT, // MUST BE LAST
} FIELDTYPE;
#ifndef linux
#ifndef offsetof
#if !defined(offsetof) && !defined(GNUC)
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#endif
#endif
#define _FIELD(type,name,fieldtype,count,flags) { fieldtype, #name, offsetof(type, name), count, flags }
#define DEFINE_FIELD(type,name,fieldtype) _FIELD(type, name, fieldtype, 1, 0)

View File

@@ -74,16 +74,19 @@
#endif //defined WIN32
#endif
#if defined (_WIN32) && defined (_MSC_VER)
// VS2015 and newer already has va_copy defined
#if defined (_WIN32) && defined (_MSC_VER) && _MSC_VER < 1900
// On x86 va_list is just a pointer.
#define va_copy(dst,src) ((dst)=(src))
#else
#if (linux)
// Some systems that do not supply va_copy have __va_copy instead, since
// that was the name used in the draft proposal.
#if !defined(__GNUC__) || __GNUC__ < 3
#define va_copy __va_copy
#endif
#endif
#endif
// Manual branch optimization for GCC 3.0.0 and newer
#if !defined(__GNUC__) || __GNUC__ < 3

View File

@@ -1,9 +1,14 @@
/**
* This file has no copyright assigned and is placed in the Public Domain.
* This file was originally part of the w64 mingw-runtime package.
*/
/* 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>
* Modified for libusb/MSVC: Pete Batard <pbatard@gmail.com>
*
* This source code is offered for use in the public domain. You may
* use, modify or distribute it freely.
@@ -13,51 +18,72 @@
* 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.
* Date: 2010-04-02
*/
#ifndef _MSC_VER
#error This header should only be used with Microsoft compilers
#endif
#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
#ifndef _INTPTR_T_DEFINED
#define _INTPTR_T_DEFINED
#ifndef __intptr_t_defined
#define __intptr_t_defined
#undef intptr_t
#ifdef _WIN64
typedef __int64 intptr_t;
#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 */
typedef int intptr_t;
#endif /* _WIN64 */
#endif /* __intptr_t_defined */
#endif /* _INTPTR_T_DEFINED */
#ifndef _UINTPTR_T_DEFINED
#define _UINTPTR_T_DEFINED
#ifndef __uintptr_t_defined
#define __uintptr_t_defined
#undef uintptr_t
#ifdef _WIN64
typedef unsigned __int64 uintptr_t;
#else
typedef unsigned int uintptr_t;
#endif /* _WIN64 */
#endif /* __uintptr_t_defined */
#endif /* _UINTPTR_T_DEFINED */
#ifndef _PTRDIFF_T_DEFINED
#define _PTRDIFF_T_DEFINED
#ifndef _PTRDIFF_T_
#define _PTRDIFF_T_
#undef ptrdiff_t
#ifdef _WIN64
typedef __int64 ptrdiff_t;
#else
typedef int ptrdiff_t;
#endif /* _WIN64 */
#endif /* _PTRDIFF_T_ */
#endif /* _PTRDIFF_T_DEFINED */
#ifndef _WCHAR_T_DEFINED
#define _WCHAR_T_DEFINED
#ifndef __cplusplus
typedef unsigned short wchar_t;
#endif /* C++ */
#endif /* _WCHAR_T_DEFINED */
#ifndef _WCTYPE_T_DEFINED
#define _WCTYPE_T_DEFINED
#ifndef _WINT_T
#define _WINT_T
typedef unsigned short wint_t;
typedef unsigned short wctype_t;
#endif /* _WINT_T */
#endif /* _WCTYPE_T_DEFINED */
/* 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;
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
/* 7.18.1.2 Minimum-width integer types */
typedef signed char int_least8_t;
typedef unsigned char uint_least8_t;
@@ -65,97 +91,65 @@ 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;
typedef __int64 int_least64_t;
typedef unsigned __int64 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 */
typedef __int8 int_fast8_t;
typedef unsigned __int8 uint_fast8_t;
typedef __int16 int_fast16_t;
typedef unsigned __int16 uint_fast16_t;
typedef __int32 int_fast32_t;
typedef unsigned __int32 uint_fast32_t;
typedef __int64 int_fast64_t;
typedef unsigned __int64 uint_fast64_t;
/* 7.18.1.5 Greatest-width integer types */
typedef __STDINT_LONGLONG intmax_t;
typedef unsigned __STDINT_LONGLONG uintmax_t;
typedef __int64 intmax_t;
typedef unsigned __int64 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 INT64_MIN (-9223372036854775807LL - 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 */
#define INT64_MAX 9223372036854775807LL
#define UINT8_MAX 255
#define UINT16_MAX 65535
#define UINT32_MAX 0xffffffffU /* 4294967295U */
#define UINT64_MAX 0xffffffffffffffffULL /* 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 */
/* 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
@@ -164,41 +158,39 @@ typedef unsigned __STDINT_LONGLONG uintmax_t;
#define INTPTR_MIN INT32_MIN
#define INTPTR_MAX INT32_MAX
#define UINTPTR_MAX UINT32_MAX
#endif /* _WIN64 */
#endif
/* 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 */
#ifdef _WIN64
#define PTRDIFF_MIN INT64_MIN
#define PTRDIFF_MAX INT64_MAX
#else
#define PTRDIFF_MIN INT32_MIN
#define PTRDIFF_MAX INT32_MAX
#endif
#define SIG_ATOMIC_MIN INT32_MIN
#define SIG_ATOMIC_MAX INT32_MAX
#ifndef SIZE_MAX
#define SIZE_MAX UINTPTR_MAX
#ifdef _WIN64
#define SIZE_MAX UINT64_MAX
#else
#define SIZE_MAX UINT32_MAX
#endif
#endif
#ifndef WCHAR_MIN /* also in wchar.h */
#define WCHAR_MIN 0
#define WCHAR_MAX ((wchar_t)-1) /* UINT16_MAX */
#define WCHAR_MIN 0U
#define WCHAR_MAX 0xffffU
#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 */
/*
* wint_t is unsigned short for compatibility with MS runtime
*/
#define WINT_MIN 0U
#define WINT_MAX 0xffffU
/* 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
@@ -208,22 +200,19 @@ typedef unsigned __STDINT_LONGLONG uintmax_t;
an integer constant with width less than that of type int.
TC1 changed this to require just an integer constant
*expression* with *promoted* type."
The trick used here is from Clive D W Feather.
*/
#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))
#define INT8_C(val) (INT_LEAST8_MAX-INT_LEAST8_MAX+(val))
#define INT16_C(val) (INT_LEAST16_MAX-INT_LEAST16_MAX+(val))
#define INT32_C(val) (INT_LEAST32_MAX-INT_LEAST32_MAX+(val))
/* The 'trick' doesn't work in C89 for long long because, without
suffix, (val) will be evaluated as int, not intmax_t */
#define INT64_C(val) val##i64
#define UINT8_C(val) (val)
#define UINT16_C(val) (val)
#define UINT32_C(val) (val##i32)
#define UINT64_C(val) val##ui64
/* 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 */
#define INTMAX_C(val) val##i64
#define UINTMAX_C(val) val##ui64
#endif