97 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
Giegue
6e1081d793 Start making monstermod aware of normal HL monsters.
This is still heavily experimental and its use is not yet recommended.
2023-02-15 21:19:57 -03:00
Giegue
5830ac7908 Tweaked hwgrunt to be less crappy. 2023-02-15 21:14:13 -03:00
Giegue
2f80da1e15 Fix linux compilation.
Fix windows compilation, again.
2023-02-14 20:44:13 -03:00
Giegue
c32827b4cb Update milestones, again. 2023-02-07 18:32:55 -03:00
Giegue
5f7abc2df0 Add monster_stukabat. 2023-02-07 18:19:52 -03:00
Giegue
875d6610ae Add monster_robogrunt. 2023-02-05 17:13:39 -03:00
Giegue
94fc64226c Update README and milestones. 2023-02-03 03:04:08 -03:00
Giegue
19b7c69b14 Add monster_hwgrunt. 2023-02-03 02:37:30 -03:00
Giegue
0fd2d27b6f Fix windows compilation. 2023-02-01 01:07:35 -03:00
Julian
87210e5f6c Update readme to add information about precache. 2020-07-30 00:04:08 -03:00
Julian
9df28b9d3f Added monster_babygarg. 2020-07-29 22:35:01 -03:00
Julian
9602f862c9 Added monster_voltigore and monster_baby_voltigore. 2020-07-19 12:31:52 -03:00
Julian
5b0ced8e7d Add monster_shocktrooper. 2020-06-30 23:17:53 -03:00
Julian
63779c0471 Randomize Male Assassin weapons. 2020-06-12 04:26:25 -03:00
Julian
433bb24dcd Add monster_pitdrone. 2020-06-11 01:30:29 -03:00
Julian
ba25f7710c Add monster_otis. 2020-06-10 01:12:58 -03:00
Julian
bf607621b3 Add monster_male_assassin. 2020-06-09 18:21:51 -03:00
Julian
769ec8852f Update monster IRelationship to match Sven Co-op 5.x table. 2020-06-08 03:52:27 -03:00
Julian
7fcf39b6d3 Add monster_gonome. 2020-06-07 18:52:55 -03:00
Julian
2762dbc0b2 Update README. 2020-06-04 15:06:47 -03:00
Giegue
0f2bd95275 Merge pull request #1 from JulianR0/tier2
Tier 2 milestone reached!
2020-06-03 14:38:21 -03:00
Giegue
be8df87e82 Merge branch 'master' into tier2 2020-06-03 14:37:16 -03:00
Julian
aa99d801c7 Merge branch 'tier2' of github.com:JulianR0/monstermod-redo into tier2 2020-06-03 14:32:37 -03:00
Julian
aa57599c3c Tier 2 milestone reached! 2020-06-03 14:31:55 -03:00
Julian
b2beda8f68 Tier 2 milestone reached! 2020-06-03 14:28:58 -03:00
Julian
6d4a608793 Add "classify" keyvalue to override monster classification. 2020-06-03 13:57:06 -03:00
Giegue
612ce63050 Update README.md 2020-06-03 01:42:33 -03:00
Julian
1e68434df5 Add "displayname" keyvalue for custom monster names. 2020-06-03 01:36:04 -03:00
Julian
4d61695e6b Merge branch 'tier2' of github.com:JulianR0/monstermod-redo into tier2 2020-06-02 01:56:04 -03:00
Julian
4912f6d23d Display basic monster info on aiment. 2020-06-02 01:54:48 -03:00
Giegue
50fd9aa13a Update README.md 2020-06-01 17:20:23 -03:00
Giegue
d72aba3b6f Update README.md 2020-06-01 17:18:06 -03:00
Julian
d39bc85ab6 Use monster classname for player deaths. 2020-06-01 17:10:52 -03:00
Julian
5c275e5b62 Added death messages. 2020-06-01 03:53:42 -03:00
Julian
b4ec347170 Fixed TextMsg message crashes. 2020-06-01 03:53:12 -03:00
Julian
8f899d2c6d Add install instructions. 2020-03-17 22:48:56 -03:00
Giegue
d63e0af548 Tier 1 milestone reached! 2020-03-17 22:41:10 -03:00
178 changed files with 65679 additions and 51895 deletions

View File

@@ -21,27 +21,57 @@ A small light is seen at the distance...
## What is this? ## What is this?
MonsterMod is a MetaMod plugin. It's purpose was to allow support for monsters to spawn in multiplayer games where they are not. The project updates became incredibly obscure, getting up-to-date versions and the new additions was very difficult. And the only one who had even futher progress kept the plugin private. MonsterMod is a MetaMod plugin. Its purpose was to allow multiplayer games to add monsters, where it wasn't possible to do so by normal means. The updates of the project became incredibly obscure: Getting the "up-to-date" versions containing the new additions (opposing force monsters, for example) were very difficult. And the only one who managed to bring the plugin even futher kept the progress of the plugin private.
Nearly 18 years after botman's original plugin was released, the future of the project became nothing but a forgotten, ancient relic of the past. After 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... Not anymore...
This project aims towards the recreation of the new features of the "obscured and updated" Monster Mod plugin. Taking botman's original 2002 plugin and working up from the ground, and to rebuild it with the new features and monsters that only few were able to see. The first goal of this project aims towards the recreation of the new features of the "obscured and updated" Monster Mod plugin. Taking botman's original 2002 plugin and working from the ground up, the mission is to rebuild it with the new features and monsters that only few were able to see.
The source code is completely free to use for everyone: In the event that the development of this new project falls and becomes stagnant again, the plugin will live on, as the project's second goal is it's preservation. The original botman's page where you can download the 2002 plugin will not stay up forever. The source code is completely free for everyone to use: In the event that the development of this project falls and becomes stagnant again, the plugin will live on, as the project's second goal is its preservation. ~~The original botman's page where you can download the 2002 plugin will not stay up forever.~~ **April 2023 update**: The page is gone, the original 2002 plugin can no longer be found.
Under no circumstances we shall allow this project to fade away and become lost amidst the gears of time. Under no circumstances shall we allow this project to fade away and become lost amidst the gears of time.
## Installation
The plugin -should- be able to be used out-of-the-box by simply downloading the binary and adding the appropiate entry in metamod's plugin list.
**Windows:**
`win32 addons\monstermod\monster_mm.dll`
**Linux:**
`linux addons/monstermod/monster_mm_i386.so`
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.
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.
## 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
Usage of ReHLDS is highly recommended, as you can use the command `rescount` which will reveal the current number of precached models and sounds. You can also use `reslist model` and `reslist sound` to see the entire list of precached content.
Keeping track of the number of precached content will allow you to maximize the number of monsters you can use without risking going over the limits.
## 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:
- Rarely, Stukabats will become unable to fly towards their target, standing in air doing seemingly nothing.
- Monsters are very prone to gibbing when hurt during death animations.
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 ## Milestones
Attempting to recreate everything in one go is a daunting task. Attempting to recreate everything in one go is a daunting task.
Let it be known that the original 2002 source code will NOT compile on today's compilers, and does NOT contain all the necessary files for compilation. The preliminary was to rewrite and provide as many files or lines of code to ensure it can compile again, and be usable on an actual HLDS installation. Let it be known that the original 2002 source code will NOT compile on today's compilers, and does NOT contain all the necessary files for compilation. The preliminary was to rewrite and provide as many files or lines of code to ensure it can compile again, and be usable on an actual HLDS installation.
Currently, I aim working this for linux only. While the original 2002 Visual C++ DSP file exists in the repository, it has not been updated to newer formats. Who knows if it can even open on newer Visual Studio versions, let alone, to compile?
Even so, in linux, I had to resort to compile under GCC 4.8 for the plugin to work. While I could use a newer compiler (GCC 9 at the time of this writing), whenever or not the plugin will run is dependant on the HLDS installation and the system libraries is it hosted with. If you compile with newer GCC compilers, be warned that it may refuse to load, as it will complain about libstdc++ not having CXXABI_1.X.X or similar.
Current milestones are separated by "Tiers", which are as follows: Current milestones are separated by "Tiers", which are as follows:
### Tier 0 ### Tier 0
@@ -53,29 +83,41 @@ Current milestones are separated by "Tiers", which are as follows:
- Rework config processing to use a "key --> value" format. **[DONE]** - Rework config processing to use a "key --> value" format. **[DONE]**
- Add info_node and info_node_air support for AI navigation. **[DONE]** - Add info_node and info_node_air support for AI navigation. **[DONE]**
- Add node_viewer to debug node graph generation. **[DONE]** - Add node_viewer to debug node graph generation. **[DONE]**
- Implement Gargantua and all Turrets from Half-Life. - Implement Gargantua and all Turrets from Half-Life. **[DONE]**
### Tier 2 ### Tier 2
- Add *-at least minimal-* death messages. *-Example: "\<player\> was killed by a \<monster\>".-* - Add *-at least minimal-* death messages. *-Example: "\<player\> was killed by a \<monster\>".-* **[DONE]**
- Implement HUD info about the monsters, along with the "displayname" keyvalue. - Implement HUD info about the monsters, along with the "displayname" keyvalue. **[DONE]**
- Implement custom monster classification, the "classify" keyvalue. - Implement custom monster classification, the "classify" keyvalue. **[DONE]**
### Tier 3 ### Tier 3
- Implement *-almost-* all Opposing Force monsters. - Update source code so it can compile AND run **ON WINDOWS**. **[DONE]**
- Implement *-almost-* all default Sven Co-op monsters. - Implement *-almost-* all Opposing Force monsters. **[DONE]**
- Add configurations to change AI behaviour. - Implement *-almost-* all default Sven Co-op monsters. **[DONE]**
- Make MonsterMod aware of normal game entities. *-For those wanting to use this in vanilla HL.-* **[DONE]**
- Custom model support. **[DONE]**
### Tier 4 ### Tier 4
- Custom model support, along with scaling. - Implement reading entities within the BSP itself. **[DONE]**
- Custom sound support. - Add build instructions. **[DONE]**
- Implement extra entities to enhance map gameplay. - 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 ### Tier 5
- Update source code so it can compile AND run **ON WINDOWS**. - Enhance the AI.
- Add configurations to change AI behaviour.
- 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? What will the future hold after all Tiers has been completed?

View File

@@ -2,20 +2,21 @@
// Make a copy of this file and rename it to "<mapname>_monster.cfg". // Make a copy of this file and rename it to "<mapname>_monster.cfg".
// Send this new file to your maps folder. // 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. // For more detailed instructions, check the wiki at
// https://github.com/JulianR0/monstermod-redo/wiki
{ {
"origin" "90 -180 150" "origin" "90 -180 150"
"delay" "30" "displayname" "Example Custom Name"
"orientation" "1" "orientation" "1"
"spawnflags" "32" "spawnflags" "32"
"classname" "monster_miniturret" "classname" "monster_miniturret"
} }
{ {
"origin" "123 456 789" "origin" "123 456 789"
"delay" "5" "classify" "7"
"angles" "0 45 0" "angles" "0 45 0"
"classname" "monster_houndeye" "classname" "monster_houndeye"
} }

View File

@@ -1,21 +1,25 @@
// 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.
// //
// (just remove the comment characters at the beginning of the line for the // Install this file to your mod's base directory.
// monsters that you always want to precache.) // 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
// server due to the age-old 512 limit.
//monster_alien_grunt //monster_alien_grunt
//monster_apache //monster_apache
//monster_barney //monster_barney
//monster_bigmomma //monster_bigmomma
//monster_bullsquid //monster_bullchicken
//monster_gargantua //monster_gargantua
//monster_hassassin //monster_human_assassin
//monster_headcrab //monster_headcrab
//monster_human_grunt //monster_human_grunt
//monster_houndeye //monster_houndeye
//monster_alien_slave //monster_alien_slave
//monster_icthyosaur
//monster_alien_controller //monster_alien_controller
//monster_scientist //monster_scientist
//monster_snark //monster_snark
@@ -23,3 +27,14 @@
//monster_turret //monster_turret
//monster_miniturret //monster_miniturret
//monster_sentry //monster_sentry
//monster_gonome
//monster_male_assassin
//monster_otis
//monster_pitdrone
//monster_shocktrooper
//monster_alien_voltigore
//monster_alien_babyvoltigore
//monster_babygarg
//monster_hwgrunt
//monster_robogrunt
//monster_stukabat

View File

@@ -1,6 +1,9 @@
// MONSTERS health and damage amounts // MONSTERS health and damage amounts
// //
// (Note: Some of these aren't currently used. They're in here for future use.) // Install this file to your mod's base directory.
// ORIGINAL HALF-LIFE MONSTERS
// Alien Grunt // Alien Grunt
sk_agrunt_health 90 sk_agrunt_health 90
@@ -55,10 +58,6 @@ sk_islave_dmg_claw 10
sk_islave_dmg_clawrake 25 sk_islave_dmg_clawrake 25
sk_islave_dmg_zap 10 sk_islave_dmg_zap 10
// Icthyosaur
sk_ichthyosaur_health 200
sk_ichthyosaur_shake 35
// Controller // Controller
sk_controller_health 60 sk_controller_health 60
sk_controller_dmgzap 25 sk_controller_dmgzap 25
@@ -88,8 +87,75 @@ sk_miniturret_health 40
// Sentry Turret // Sentry Turret
sk_sentry_health 40 sk_sentry_health 40
// OPPOSING FORCE MONSTERS
// Gonome
sk_gonome_health 85
sk_gonome_dmg_one_slash 20
sk_gonome_dmg_guts 10
sk_gonome_dmg_one_bite 14
// Male Assassin
sk_massassin_health 50
sk_massassin_kick 25
// Otis
sk_otis_health 35
// Pit Drone
sk_pitdrone_health 40
sk_pitdrone_dmg_bite 25
sk_pitdrone_dmg_whip 35
sk_pitdrone_dmg_spit 10
// Shock Roach
sk_shockroach_health 10
sk_shockroach_lifespan 10
// ShockTrooper
sk_shocktrooper_health 50
sk_shocktrooper_kick 10
sk_shocktrooper_maxcharge 8
sk_shocktrooper_rchgspeed 1
sk_shock_dmg 15
sk_spore_dmg 50
// Voltigore
sk_voltigore_health 320
sk_voltigore_dmg_beam 50
sk_voltigore_dmg_punch 40
// Baby Voltigore
sk_babyvoltigore_health 60
sk_babyvoltigore_dmg_punch 15
// SVEN CO-OP MONSTERS
// Baby Gargantua
sk_babygarg_health 640
sk_babygarg_dmg_slash 24
sk_babygarg_dmg_fire 4
sk_babygarg_dmg_stomp 80
// Heavy Weapons Grunt
sk_hwgrunt_health 100
// Robo Grunt
sk_rgrunt_health 50
sk_rgrunt_armor 0.75
// Stukabat
sk_stukabat_health 80
sk_stukabat_dmg_bite 11
sk_stukabat_speed 400
// MONSTER WEAPON DAMAGE // MONSTER WEAPON DAMAGE
sk_9mm_bullet 5 sk_9mm_bullet 5
sk_9mmAR_bullet 4 sk_9mmAR_bullet 4
sk_9mmAR_grenade 100 sk_9mmAR_grenade 100
sk_12mm_bullet 10 sk_12mm_bullet 10
sk_762_bullet 100
sk_357_bullet 40

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/ msgs/
opt.*/ opt.*/
debug.*/ debug.*/
*.o *.o
*.so *.so
*.suo
*.sdf

View File

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

View File

@@ -1,5 +1,5 @@
CPP = g++-7 CPP = g++
BASEFLAGS = -Dstricmp=strcasecmp -Dstrcmpi=strcasecmp -m32 BASEFLAGS = -Dstricmp=strcasecmp -Dstrcmpi=strcasecmp -m32 -fPIC
OPTFLAGS = -O2 OPTFLAGS = -O2
CPPFLAGS = ${BASEFLAGS} ${OPTFLAGS} -w -I. -I../engine -I../common -I../pm_shared -I../metamod CPPFLAGS = ${BASEFLAGS} ${OPTFLAGS} -w -I. -I../engine -I../common -I../pm_shared -I../metamod
@@ -17,10 +17,13 @@ OBJ = \
controller.o \ controller.o \
defaultai.o \ defaultai.o \
dllapi.o \ dllapi.o \
explode.o \
effects.o \ effects.o \
explode.o \
flyingmonster.o \
gargantua.o \ gargantua.o \
ggrenade.o \ ggrenade.o \
globalreplace.o \
gonome.o \
h_ai.o \ h_ai.o \
h_export.o \ h_export.o \
hassassin.o \ hassassin.o \
@@ -28,20 +31,34 @@ OBJ = \
hgrunt.o \ hgrunt.o \
hornet.o \ hornet.o \
houndeye.o \ houndeye.o \
hwgrunt.o \
islave.o \ islave.o \
massn.o \
monster_api.o \ monster_api.o \
monster_config.o \ monster_config.o \
monstermaker.o \
monsters.o \ monsters.o \
monsterstate.o \ monsterstate.o \
music.o \
nodes.o \ nodes.o \
otis.o \
pitdrone.o \
rgrunt.o \
ripent.o \
scientist.o \ scientist.o \
shock.o \
shockroach.o \
skill.o \ skill.o \
sound.o \ sound.o \
sporegrenade.o \
squeakgrenade.o \ squeakgrenade.o \
strooper.o \
stukabat.o \
subs.o \ subs.o \
talkmonster.o \ talkmonster.o \
turret.o \ turret.o \
util.o \ util.o \
voltigore.o \
weapons.o \ weapons.o \
zombie.o zombie.o

View File

@@ -19,12 +19,12 @@
#include "extdll.h" #include "extdll.h"
#include "util.h" #include "util.h"
#include "cmbase.h" #include "cmbase.h"
#include "cmbasemonster.h" #include "cmbasemonster.h"
#include "monsters.h" #include "monsters.h"
#include "schedule.h" #include "schedule.h"
#include "weapons.h" #include "weapons.h"
#include "hornet.h" #include "hornet.h"
#include "skill.h" #include "skill.h"
//========================================================= //=========================================================
@@ -87,6 +87,13 @@ const char *CMAGrunt::pAttackSounds[] =
"agrunt/ag_attack3.wav", "agrunt/ag_attack3.wav",
}; };
const char *CMAGrunt::pFireSounds[] =
{
"agrunt/ag_fire1.wav",
"agrunt/ag_fire2.wav",
"agrunt/ag_fire3.wav",
};
const char *CMAGrunt::pDieSounds[] = const char *CMAGrunt::pDieSounds[] =
{ {
"agrunt/ag_die1.wav", "agrunt/ag_die1.wav",
@@ -125,10 +132,13 @@ const char *CMAGrunt::pAlertSounds[] =
//========================================================= //=========================================================
int CMAGrunt::IRelationship ( CMBaseEntity *pTarget ) int CMAGrunt::IRelationship ( CMBaseEntity *pTarget )
{ {
// ditto hgrunt.cpp
/*
if ( strcmp(STRING(pTarget->pev->model), "models/hgrunt.mdl") == 0 ) if ( strcmp(STRING(pTarget->pev->model), "models/hgrunt.mdl") == 0 )
{ {
return R_NM; return R_NM;
} }
*/
return CMBaseMonster :: IRelationship( pTarget ); return CMBaseMonster :: IRelationship( pTarget );
} }
@@ -181,13 +191,8 @@ void CMAGrunt :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vec
if (flDamage <= 0) if (flDamage <= 0)
flDamage = 0.1;// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated 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);
} }
//========================================================= //=========================================================
@@ -313,6 +318,11 @@ void CMAGrunt :: PainSound ( void )
//========================================================= //=========================================================
int CMAGrunt :: Classify ( void ) int CMAGrunt :: Classify ( void )
{ {
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_ALIEN_MILITARY; return CLASS_ALIEN_MILITARY;
} }
@@ -410,6 +420,8 @@ void CMAGrunt :: HandleAnimEvent( MonsterEvent_t *pEvent )
UTIL_MakeVectors ( pHornet->pev->angles ); UTIL_MakeVectors ( pHornet->pev->angles );
pHornet->pev->velocity = gpGlobals->v_forward * 300; 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(); CMBaseMonster *pHornetMonster = pHornet->MyMonsterPointer();
if ( pHornetMonster ) if ( pHornetMonster )
@@ -524,12 +536,12 @@ void CMAGrunt :: Spawn()
{ {
Precache( ); 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)); UTIL_SetSize(pev, Vector(-32, -32, 0), Vector(32, 32, 64));
pev->solid = SOLID_SLIDEBOX; pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP; pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN; m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->effects = 0; pev->effects = 0;
pev->health = gSkillData.agruntHealth; pev->health = gSkillData.agruntHealth;
m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
@@ -541,6 +553,13 @@ void CMAGrunt :: Spawn()
m_flNextSpeakTime = m_flNextWordTime = gpGlobals->time + 10 + RANDOM_LONG(0, 10); m_flNextSpeakTime = m_flNextWordTime = gpGlobals->time + 10 + RANDOM_LONG(0, 10);
MonsterInit(); MonsterInit();
pev->classname = MAKE_STRING( "monster_alien_grunt" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Alien Grunt" );
}
} }
//========================================================= //=========================================================
@@ -548,38 +567,21 @@ void CMAGrunt :: Spawn()
//========================================================= //=========================================================
void CMAGrunt :: Precache() void CMAGrunt :: Precache()
{ {
int i;
PRECACHE_MODEL("models/agrunt.mdl"); PRECACHE_MODEL("models/agrunt.mdl");
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) PRECACHE_SOUND_ARRAY( pAttackHitSounds );
PRECACHE_SOUND((char *)pAttackHitSounds[i]); 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++ ) iAgruntMuzzleFlash = PRECACHE_MODELINDEX("sprites/muz4.spr");
PRECACHE_SOUND((char *)pAttackMissSounds[i]);
for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ ) CMHornet hornet;
PRECACHE_SOUND((char *)pIdleSounds[i]); hornet.Precache();
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" );
CMHornet hornet;
hornet.Precache();
} }
//========================================================= //=========================================================
@@ -1015,7 +1017,7 @@ Schedule_t *CMAGrunt :: GetSchedule ( void )
// zap player! // zap player!
if ( HasConditions ( bits_COND_CAN_MELEE_ATTACK1 ) ) if ( HasConditions ( bits_COND_CAN_MELEE_ATTACK1 ) )
{ {
AttackSound();// this is a total hack. Should be parto f the schedule AttackSound();// this is a total hack. Should be part of the schedule
return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); return GetScheduleOfType ( SCHED_MELEE_ATTACK1 );
} }

View File

@@ -163,7 +163,11 @@ int LookupSequence( void *pmodel, const char *label )
for (int i = 0; i < pstudiohdr->numseq; i++) 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) if (stricmp( pseqdesc[i].label, label ) == 0)
#endif
return i; 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 ); 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) ) );
} }
} }
} }
@@ -297,9 +301,14 @@ int GetAnimationEvent( void *pmodel, entvars_t *pev, MonsterEvent_t *pMonsterEve
for (; index < pseqdesc->numevents; index++) for (; index < pseqdesc->numevents; index++)
{ {
// Some Sven Co-op monsters rely on client-side events.
// Let those be sent to the server AI, even if it's wrong. -Giegue
/*
// Don't send client-side events to the server AI // Don't send client-side events to the server AI
if ( pevent[index].event >= EVENT_CLIENT ) if ( pevent[index].event >= EVENT_CLIENT )
continue; continue;
*/
if ( (pevent[index].frame >= flStart && pevent[index].frame < flEnd) || if ( (pevent[index].frame >= flStart && pevent[index].frame < flEnd) ||
((pseqdesc->flags & STUDIO_LOOPING) && flEnd >= pseqdesc->numframes - 1 && pevent[index].frame < flEnd - pseqdesc->numframes + 1) ) ((pseqdesc->flags & STUDIO_LOOPING) && flEnd >= pseqdesc->numframes - 1 && pevent[index].frame < flEnd - pseqdesc->numframes + 1) )

View File

@@ -35,7 +35,7 @@ void CMApache :: Spawn( void )
pev->movetype = MOVETYPE_FLY; pev->movetype = MOVETYPE_FLY;
pev->solid = SOLID_BBOX; pev->solid = SOLID_BBOX;
SET_MODEL(ENT(pev), "models/apache.mdl"); SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/apache.mdl"));
UTIL_SetSize( pev, Vector( -32, -32, -64 ), Vector( 32, 32, 0 ) ); UTIL_SetSize( pev, Vector( -32, -32, -64 ), Vector( 32, 32, 0 ) );
UTIL_SetOrigin( pev, pev->origin ); UTIL_SetOrigin( pev, pev->origin );
@@ -84,6 +84,13 @@ void CMApache :: Spawn( void )
m_flPrevSeen = 0.0f; m_flPrevSeen = 0.0f;
m_iSoundState = 0; m_iSoundState = 0;
pev->classname = MAKE_STRING( "monster_apache" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Apache" );
}
} }
@@ -98,20 +105,28 @@ void CMApache::Precache( void )
PRECACHE_SOUND("weapons/mortarhit.wav"); 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_SOUND("turret/tu_fire1.wav");
PRECACHE_MODEL("sprites/lgtning.spr"); PRECACHE_MODEL("sprites/lgtning.spr");
m_iExplode = PRECACHE_MODEL( "sprites/fexplo.spr" ); m_iExplode = PRECACHE_MODELINDEX( "sprites/fexplo.spr" );
m_iBodyGibs = PRECACHE_MODEL( "models/metalplategibs_green.mdl" ); m_iBodyGibs = PRECACHE_MODELINDEX( "models/metalplategibs_green.mdl" );
CMApacheHVR apache_rocket; CMApacheHVR apache_rocket;
apache_rocket.Precache(); apache_rocket.Precache();
} }
int CMApache :: Classify ( void )
{
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_HUMAN_MILITARY;
}
void CMApache::NullThink( void ) void CMApache::NullThink( void )
{ {
@@ -142,6 +157,11 @@ void CMApache :: Killed( entvars_t *pevAttacker, int iGib )
pev->health = 0; pev->health = 0;
pev->takedamage = DAMAGE_NO; 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) if (pev->spawnflags & SF_NOWRECKAGE)
{ {
m_flNextRocket = gpGlobals->time + 4.0; m_flNextRocket = gpGlobals->time + 4.0;
@@ -163,7 +183,7 @@ void CMApache :: DyingThink( void )
if (m_flNextRocket > gpGlobals->time ) if (m_flNextRocket > gpGlobals->time )
{ {
if (g_sModelIndexFireball == 0) if (g_sModelIndexFireball == 0)
g_sModelIndexFireball = PRECACHE_MODEL ("sprites/zerogxplode.spr"); // fireball g_sModelIndexFireball = PRECACHE_MODELINDEX("sprites/zerogxplode.spr"); // fireball
// random explosions // random explosions
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
@@ -178,7 +198,7 @@ void CMApache :: DyingThink( void )
MESSAGE_END(); MESSAGE_END();
if (g_sModelIndexSmoke == 0) if (g_sModelIndexSmoke == 0)
g_sModelIndexSmoke = PRECACHE_MODEL ("sprites/steam1.spr"); // smoke g_sModelIndexSmoke = PRECACHE_MODELINDEX("sprites/steam1.spr"); // smoke
// lots of smoke // lots of smoke
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
@@ -404,7 +424,7 @@ void CMApache :: HuntThink( void )
// if (m_hEnemy == NULL) // if (m_hEnemy == NULL)
{ {
Look( 4092 ); Look( 4096 );
m_hEnemy = BestVisibleEnemy( ); m_hEnemy = BestVisibleEnemy( );
} }
@@ -915,7 +935,7 @@ void CMApacheHVR :: Spawn( void )
void CMApacheHVR :: Precache( void ) void CMApacheHVR :: Precache( void )
{ {
PRECACHE_MODEL("models/HVR.mdl"); PRECACHE_MODEL("models/HVR.mdl");
m_iTrail = PRECACHE_MODEL("sprites/smoke.spr"); m_iTrail = PRECACHE_MODELINDEX("sprites/smoke.spr");
PRECACHE_SOUND ("weapons/rocket1.wav"); PRECACHE_SOUND ("weapons/rocket1.wav");
} }

View File

@@ -186,6 +186,11 @@ int CMBarney :: ISoundMask ( void)
//========================================================= //=========================================================
int CMBarney :: Classify ( void ) int CMBarney :: Classify ( void )
{ {
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_PLAYER_ALLY; return CLASS_PLAYER_ALLY;
} }
@@ -340,12 +345,12 @@ void CMBarney :: Spawn()
// when a level is loaded, nobody will talk (time is reset to 0) // when a level is loaded, nobody will talk (time is reset to 0)
TalkInit(); TalkInit();
SET_MODEL(ENT(pev), "models/barney.mdl"); SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/barney.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX; pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP; pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_RED; m_bloodColor = !m_bloodColor ? BLOOD_COLOR_RED : m_bloodColor;
pev->health = gSkillData.barneyHealth; pev->health = gSkillData.barneyHealth;
pev->view_ofs = Vector ( 0, 0, 50 );// position of the eyes relative to monster's origin. pev->view_ofs = Vector ( 0, 0, 50 );// position of the eyes relative to monster's origin.
m_flFieldOfView = VIEW_FIELD_FULL; m_flFieldOfView = VIEW_FIELD_FULL;
@@ -357,6 +362,13 @@ void CMBarney :: Spawn()
m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
MonsterInit(); MonsterInit();
pev->classname = MAKE_STRING( "monster_barney" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Barney" );
}
} }
//========================================================= //=========================================================

View File

@@ -48,6 +48,7 @@ public:
void CMInfoBM::Spawn( void ) void CMInfoBM::Spawn( void )
{ {
pev->classname = MAKE_STRING( "info_bigmomma" );
} }
void CMInfoBM::KeyValue( KeyValueData* pkvd ) void CMInfoBM::KeyValue( KeyValueData* pkvd )
@@ -298,6 +299,11 @@ void CMBigMomma :: KeyValue( KeyValueData *pkvd )
//========================================================= //=========================================================
int CMBigMomma :: Classify ( void ) int CMBigMomma :: Classify ( void )
{ {
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_ALIEN_MONSTER; return CLASS_ALIEN_MONSTER;
} }
@@ -597,19 +603,26 @@ void CMBigMomma :: Spawn()
{ {
Precache( ); Precache( );
SET_MODEL(ENT(pev), "models/big_mom.mdl"); SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/big_mom.mdl"));
// UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); // UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
UTIL_SetSize( pev, Vector( -64, -64, 0 ), Vector( 64, 64, 128 ) ); UTIL_SetSize( pev, Vector( -64, -64, 0 ), Vector( 64, 64, 128 ) );
pev->solid = SOLID_SLIDEBOX; pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP; pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN; m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->health = 150 * gSkillData.bigmommaHealthFactor; pev->health = 150 * gSkillData.bigmommaHealthFactor;
pev->view_ofs = Vector ( 0, 0, 128 );// position of the eyes relative to monster's origin. 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 ) m_flFieldOfView = 0.3;// indicates the width of this monster's forward view cone ( as a dotproduct result )
m_MonsterState = MONSTERSTATE_NONE; m_MonsterState = MONSTERSTATE_NONE;
MonsterInit(); MonsterInit();
pev->classname = MAKE_STRING( "monster_bigmomma" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Big Momma" );
}
} }
//========================================================= //=========================================================
@@ -635,8 +648,8 @@ void CMBigMomma :: Precache()
// TEMP: Squid // TEMP: Squid
PRECACHE_MODEL("sprites/mommaspit.spr");// spit projectile. PRECACHE_MODEL("sprites/mommaspit.spr");// spit projectile.
gSpitSprite = PRECACHE_MODEL("sprites/mommaspout.spr");// client side spittle. gSpitSprite = PRECACHE_MODELINDEX("sprites/mommaspout.spr");// client side spittle.
gSpitDebrisSprite = PRECACHE_MODEL("sprites/mommablob.spr" ); gSpitDebrisSprite = PRECACHE_MODELINDEX("sprites/mommablob.spr" );
PRECACHE_SOUND( "bullchicken/bc_acid1.wav" ); PRECACHE_SOUND( "bullchicken/bc_acid1.wav" );
PRECACHE_SOUND( "bullchicken/bc_spithit1.wav" ); PRECACHE_SOUND( "bullchicken/bc_spithit1.wav" );
@@ -1134,7 +1147,7 @@ CMBMortar *CMBMortar::Shoot( edict_t *pOwner, Vector vecStart, Vector vecVelocit
{ {
CMBMortar *pSpit = CreateClassPtr( (CMBMortar *)NULL ); CMBMortar *pSpit = CreateClassPtr( (CMBMortar *)NULL );
if (pSpit) if (pSpit)
{ {
pSpit->Spawn(); pSpit->Spawn();
UTIL_SetOrigin( pSpit->pev, vecStart ); UTIL_SetOrigin( pSpit->pev, vecStart );

View File

@@ -113,7 +113,7 @@ void CSquidSpit::Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity
pSpit->SetThink ( &CSquidSpit::Animate ); pSpit->SetThink ( &CSquidSpit::Animate );
pSpit->pev->nextthink = gpGlobals->time + 0.1; pSpit->pev->nextthink = gpGlobals->time + 0.1;
pSpit->SetTouch ( &CSquidSpit::SpitTouch ); pSpit->SetTouch ( &CSquidSpit::SpitTouch );
} }
void CSquidSpit :: SpitTouch ( edict_t *pOther ) void CSquidSpit :: SpitTouch ( edict_t *pOther )
@@ -166,6 +166,8 @@ void CSquidSpit :: SpitTouch ( edict_t *pOther )
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
pMonster->TakeDamage ( pev, pev, gSkillData.bullsquidDmgSpit, DMG_GENERIC ); pMonster->TakeDamage ( pev, pev, gSkillData.bullsquidDmgSpit, DMG_GENERIC );
} }
else
UTIL_TakeDamageExternal( pOther, pev, pev, gSkillData.bullsquidDmgSpit, DMG_GENERIC );
} }
SetThink ( &CSquidSpit::SUB_Remove ); SetThink ( &CSquidSpit::SUB_Remove );
@@ -357,6 +359,11 @@ BOOL CMBullsquid :: FValidateHintType ( short sHint )
//========================================================= //=========================================================
int CMBullsquid :: Classify ( void ) int CMBullsquid :: Classify ( void )
{ {
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_ALIEN_MONSTER; return CLASS_ALIEN_MONSTER;
} }
@@ -603,12 +610,12 @@ void CMBullsquid :: Spawn()
{ {
Precache( ); Precache( );
SET_MODEL(ENT(pev), "models/bullsquid.mdl"); SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/bullsquid.mdl"));
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
pev->solid = SOLID_SLIDEBOX; pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP; pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN; m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->effects = 0; pev->effects = 0;
pev->health = gSkillData.bullsquidHealth; pev->health = gSkillData.bullsquidHealth;
m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
@@ -617,6 +624,13 @@ void CMBullsquid :: Spawn()
m_flNextSpitTime = gpGlobals->time; m_flNextSpitTime = gpGlobals->time;
MonsterInit(); MonsterInit();
pev->classname = MAKE_STRING( "monster_bullchicken" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Bullsquid" );
}
} }
//========================================================= //=========================================================
@@ -628,7 +642,7 @@ void CMBullsquid :: Precache()
PRECACHE_MODEL("sprites/bigspit.spr");// spit projectile. 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 PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event

View File

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

View File

@@ -50,7 +50,7 @@ CMBaseEntity
// C functions for external declarations that call the appropriate C++ methods // C functions for external declarations that call the appropriate C++ methods
#ifdef _WIN32 #ifdef _WIN32
#define EXPORT _declspec( dllexport ) #define EXPORT __declspec( dllexport )
#else #else
#define EXPORT /* */ #define EXPORT /* */
#endif #endif
@@ -80,6 +80,8 @@ typedef void (CMBaseEntity::*USEPTR)( edict_t *pActivator, edict_t *pCaller, USE
#define CLASS_PLAYER_ALLY 11 #define CLASS_PLAYER_ALLY 11
#define CLASS_PLAYER_BIOWEAPON 12 // hornets and snarks.launched by players #define CLASS_PLAYER_BIOWEAPON 12 // hornets and snarks.launched by players
#define CLASS_ALIEN_BIOWEAPON 13 // hornets and snarks.launched by the alien menace #define CLASS_ALIEN_BIOWEAPON 13 // hornets and snarks.launched by the alien menace
#define CLASS_RACEX_PITDRONE 14
#define CLASS_RACEX_SHOCK 15
#define CLASS_BARNACLE 99 // special because no one pays attention to it, and it eats a wide cross-section of creatures. #define CLASS_BARNACLE 99 // special because no one pays attention to it, and it eats a wide cross-section of creatures.
class CMBaseEntity; class CMBaseEntity;
@@ -132,7 +134,7 @@ public:
// initialization functions // initialization functions
virtual void Spawn( void ) { return; } virtual void Spawn( void ) { return; }
virtual void Precache( 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 int ObjectCaps( void ) { return FCAP_ACROSS_TRANSITION; }
virtual void Activate( void ) {} virtual void Activate( void ) {}
@@ -220,7 +222,7 @@ public:
pent = ENT(0); pent = ENT(0);
if ( pent->v.euser4 == NULL ) if ( pent->v.euser4 == NULL )
return (CMBaseEntity *)NULL; return (CMBaseEntity *)NULL;
CMBaseEntity *pEnt = GetClassPtr((CMBaseEntity *)VARS(pent)); CMBaseEntity *pEnt = GetClassPtr((CMBaseEntity *)VARS(pent));
return pEnt; return pEnt;
} }
@@ -540,7 +542,6 @@ public:
class CMBaseMonster; class CMBaseMonster;
extern int GetMonsterIndex(void); extern int GetMonsterIndex(void);
// //
@@ -600,7 +601,6 @@ template <class T> T * CreateClassPtr( T *a )
// store the class pointer in the array here!!! // store the class pointer in the array here!!!
monsters[monster_index].monster_index = edict_index; monsters[monster_index].monster_index = edict_index;
monsters[monster_index].monster_pent = temp_edict; monsters[monster_index].monster_pent = temp_edict;
monsters[monster_index].respawn_index = -1;
monsters[monster_index].pMonster = (CMBaseMonster *)a; monsters[monster_index].pMonster = (CMBaseMonster *)a;
// get the private data // get the private data
@@ -608,3 +608,7 @@ template <class T> T * CreateClassPtr( T *a )
return 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

@@ -102,6 +102,14 @@ public:
Vector m_HackedGunPos; // HACK until we can query end of gun Vector m_HackedGunPos; // HACK until we can query end of gun
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 ); void KeyValue( KeyValueData *pkvd );
// monster use function // monster use function
@@ -131,6 +139,8 @@ public:
virtual void MonsterThink( void ); virtual void MonsterThink( void );
void EXPORT CallMonsterThink( void ) { this->MonsterThink(); } void EXPORT CallMonsterThink( void ) { this->MonsterThink(); }
virtual int IRelationship ( CMBaseEntity *pTarget ); virtual int IRelationship ( CMBaseEntity *pTarget );
virtual int IRelationship ( int iTargetClass );
int IRelationshipByClass ( int iClass );
virtual void MonsterInit ( void ); virtual void MonsterInit ( void );
virtual void MonsterInitDead( void ); // Call after animation/pose is set up virtual void MonsterInitDead( void ); // Call after animation/pose is set up
virtual void BecomeDead( void ); virtual void BecomeDead( void );
@@ -210,7 +220,7 @@ public:
void AdvanceRoute ( float distance ); void AdvanceRoute ( float distance );
virtual BOOL FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, edict_t *pTargetEnt, Vector *pApex ); virtual BOOL FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, edict_t *pTargetEnt, Vector *pApex );
void MakeIdealYaw( Vector vecTarget ); void MakeIdealYaw( Vector vecTarget );
virtual void SetYawSpeed ( void ) { return; };// allows different yaw_speeds for each activity virtual void SetYawSpeed ( void ) { return; }; // allows different yaw_speeds for each activity
BOOL BuildRoute ( const Vector &vecGoal, int iMoveFlag, edict_t *pTarget ); BOOL BuildRoute ( const Vector &vecGoal, int iMoveFlag, edict_t *pTarget );
virtual BOOL BuildNearestRoute ( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist ); virtual BOOL BuildNearestRoute ( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist );
int RouteClassify( int iMoveFlag ); int RouteClassify( int iMoveFlag );
@@ -344,7 +354,7 @@ public:
virtual float GetDamageAmount( void ) { return gSkillData.headcrabDmgBite; } virtual float GetDamageAmount( void ) { return gSkillData.headcrabDmgBite; }
virtual int GetVoicePitch( void ) { return 100; } virtual int GetVoicePitch( void ) { return 100; }
virtual float GetSoundVolue( void ) { return 1.0; } virtual float GetSoundVolume( void ) { return 1.0; }
Schedule_t* GetScheduleOfType ( int Type ); Schedule_t* GetScheduleOfType ( int Type );
CUSTOM_SCHEDULES; CUSTOM_SCHEDULES;
@@ -367,7 +377,7 @@ public:
BOOL CheckRangeAttack1 ( float flDot, float flDist ); BOOL CheckRangeAttack1 ( float flDot, float flDist );
Schedule_t* GetScheduleOfType ( int Type ); Schedule_t* GetScheduleOfType ( int Type );
virtual int GetVoicePitch( void ) { return PITCH_NORM + RANDOM_LONG(40,50); } virtual int GetVoicePitch( void ) { return PITCH_NORM + RANDOM_LONG(40,50); }
virtual float GetSoundVolue( void ) { return 0.8; } virtual float GetSoundVolume( void ) { return 0.8; }
}; };
@@ -724,6 +734,7 @@ public:
static const char *pAttackHitSounds[]; static const char *pAttackHitSounds[];
static const char *pAttackMissSounds[]; static const char *pAttackMissSounds[];
static const char *pAttackSounds[]; static const char *pAttackSounds[];
static const char *pFireSounds[];
static const char *pDieSounds[]; static const char *pDieSounds[];
static const char *pPainSounds[]; static const char *pPainSounds[];
static const char *pIdleSounds[]; static const char *pIdleSounds[];
@@ -792,7 +803,7 @@ class CMApache : public CMBaseMonster
public: public:
void Spawn( void ); void Spawn( void );
void Precache( void ); void Precache( void );
int Classify( void ) { return CLASS_HUMAN_MILITARY; }; int Classify( void );
int BloodColor( void ) { return DONT_BLEED; } int BloodColor( void ) { return DONT_BLEED; }
void Killed( entvars_t *pevAttacker, int iGib ); void Killed( entvars_t *pevAttacker, int iGib );
void GibMonster( void ); void GibMonster( void );
@@ -1084,6 +1095,7 @@ public:
private: private:
edict_t *GargantuaCheckTraceHullAttack(float flDist, int iDamage, int iDmgType); edict_t *GargantuaCheckTraceHullAttack(float flDist, int iDamage, int iDmgType);
protected:
CMSprite *m_pEyeGlow; // Glow around the eyes CMSprite *m_pEyeGlow; // Glow around the eyes
CMBeam *m_pFlame[4]; // Flame beams CMBeam *m_pFlame[4]; // Flame beams
@@ -1234,4 +1246,480 @@ public:
}; };
//
// opposing force monsters
//
//=========================================================
// Gonome's guts projectile
//=========================================================
class CGonomeGuts : public CMBaseEntity
{
public:
void Spawn( void );
static edict_t *Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity );
void GutsTouch( edict_t *pOther );
void EXPORT Animate( void );
int m_maxFrame;
};
//=========================================================
// Gonome
//=========================================================
class CMGonome : public CMBaseMonster
{
public:
void Spawn(void);
void Precache(void);
int Classify(void);
void SetYawSpeed();
void HandleAnimEvent(MonsterEvent_t *pEvent);
int IgnoreConditions();
void IdleSound( void );
void PainSound( void );
void DeathSound( void );
void AlertSound( void );
void StartTask(Task_t *pTask);
BOOL CheckMeleeAttack2(float flDot, float flDist);
BOOL CheckRangeAttack1(float flDot, float flDist);
void SetActivity( Activity NewActivity );
Schedule_t *GetSchedule();
Schedule_t *GetScheduleOfType( int Type );
void RunTask(Task_t* pTask);
int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType);
void Killed(entvars_t *pevAttacker, int iGib);
void UnlockPlayer();
CGonomeGuts* GetGonomeGuts(entvars_t *pevOwner, const Vector& pos);
void ClearGuts();
CUSTOM_SCHEDULES;
static const char* pPainSounds[];
static const char* pIdleSounds[];
static const char* pDeathSounds[];
static const char *pAttackHitSounds[];
static const char *pAttackMissSounds[];
protected:
float m_flNextFlinch;
float m_flNextThrowTime;// last time the gonome used the guts attack.
CGonomeGuts* m_pGonomeGuts;
BOOL m_fPlayerLocked;
EHANDLE m_lockedPlayer;
bool m_meleeAttack2;
bool m_playedAttackSound;
};
//=========================================================
// Male Assassin
//=========================================================
class CMMassn : public CMHGrunt
{
public:
int Classify(void);
void HandleAnimEvent(MonsterEvent_t *pEvent);
void Sniperrifle(void);
BOOL FOkToSpeak(void);
void Spawn( void );
void Precache( void );
void DeathSound(void);
void PainSound(void);
void IdleSound(void);
void SetActivity(Activity NewActivity);
Schedule_t *GetScheduleOfType( int Type );
CUSTOM_SCHEDULES;
};
//=========================================================
// Otis
//=========================================================
class CMOtis : public CMBarney
{
public:
void KeyValue(KeyValueData *pkvd);
void Spawn(void);
void Precache(void);
void BarneyFirePistol(void);
void AlertSound(void);
void HandleAnimEvent(MonsterEvent_t *pEvent);
int TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType);
// Override these to set behavior
Schedule_t *GetSchedule(void);
void TalkInit(void);
void TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
void Killed(entvars_t *pevAttacker, int iGib);
int head;
int bodystate;
};
//=========================================================
// Pit Drone's spit projectile
//=========================================================
class CPitdroneSpike : public CMBaseEntity
{
public:
void Spawn(void);
void EXPORT SpikeTouch(edict_t *pOther);
void EXPORT StartTrail();
static edict_t *Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, Vector vecAngles);
EHANDLE m_hOwner;
};
//=========================================================
// Pit Drone
//=========================================================
class CMPitdrone : public CMBaseMonster
{
public:
void Spawn(void);
void Precache(void);
void HandleAnimEvent(MonsterEvent_t *pEvent);
void SetYawSpeed(void);
int ISoundMask();
void KeyValue(KeyValueData *pkvd);
int Classify(void);
BOOL CheckMeleeAttack1(float flDot, float flDist);
BOOL CheckRangeAttack1(float flDot, float flDist);
void IdleSound(void);
void PainSound(void);
void AlertSound(void);
void DeathSound(void);
void BodyChange(float spikes);
int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType);
int IgnoreConditions(void);
Schedule_t* GetSchedule(void);
Schedule_t* GetScheduleOfType(int Type);
void StartTask(Task_t *pTask);
void RunTask(Task_t *pTask);
void RunAI(void);
void CheckAmmo();
void GibMonster();
CUSTOM_SCHEDULES;
float m_flLastHurtTime;
float m_flNextSpitTime;// last time the PitDrone used the spit attack.
float m_flNextFlinch;
int m_iInitialAmmo;
bool shouldAttackWithLeftClaw;
static const char *pIdleSounds[];
static const char *pAlertSounds[];
static const char *pPainSounds[];
static const char *pDieSounds[];
static const char *pAttackMissSounds[];
};
//=========================================================
// Shock Roach
//=========================================================
class CMShockRoach : public CMHeadCrab
{
public:
void Spawn(void);
void Precache(void);
void EXPORT LeapTouch(edict_t *pOther);
void PainSound(void);
void DeathSound(void);
void IdleSound(void);
void AlertSound(void);
void MonsterThink(void);
void StartTask(Task_t* pTask);
void HandleAnimEvent(MonsterEvent_t *pEvent);
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
static const char *pIdleSounds[];
static const char *pAlertSounds[];
static const char *pPainSounds[];
static const char *pAttackSounds[];
static const char *pDeathSounds[];
static const char *pBiteSounds[];
float m_flBirthTime;
BOOL m_fRoachSolid;
protected:
void AttackSound();
};
//=========================================================
// Shock Trooper
//=========================================================
class CMStrooper : public CMHGrunt
{
public:
void Spawn(void);
void MonsterThink();
void Precache(void);
int Classify(void);
BOOL CheckRangeAttack1(float flDot, float flDist);
BOOL CheckRangeAttack2(float flDot, float flDist);
void HandleAnimEvent(MonsterEvent_t *pEvent);
void SetObjectCollisionBox( void )
{
pev->absmin = pev->origin + Vector( -24, -24, 0 );
pev->absmax = pev->origin + Vector( 24, 24, 72 );
}
void SetActivity(Activity NewActivity);
void DeathSound(void);
void PainSound(void);
void IdleSound(void);
void GibMonster(void);
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
void DropShockRoach(bool gibbed);
Schedule_t *GetSchedule(void);
Schedule_t *GetScheduleOfType(int Type);
void SpeakSentence();
BOOL m_fRightClaw;
float m_rechargeTime;
float m_blinkTime;
float m_eyeChangeTime;
static const char *pGruntSentences[];
};
//=========================================================
// Voltigore's energy ball projectile
//=========================================================
#define VOLTIGORE_MAX_BEAMS 8
class CMVoltigoreEnergyBall : public CMBaseEntity
{
public:
void Spawn(void);
static edict_t *Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity);
void EXPORT BallTouch(edict_t *pOther);
void EXPORT FlyThink(void);
void CreateBeams();
void ClearBeams();
void UpdateBeams();
CMBeam* m_pBeam[VOLTIGORE_MAX_BEAMS];
int m_iBeams;
float m_timeToDie;
protected:
void CreateBeam(int nIndex, const Vector& vecPos, int width, int brightness);
void UpdateBeam(int nIndex, const Vector& vecPos, bool show);
void ClearBeam(int nIndex);
};
//=========================================================
// Voltigore
//=========================================================
class CMVoltigore : public CMBaseMonster
{
public:
virtual void Spawn(void);
virtual void Precache(void);
void SetYawSpeed(void);
virtual int Classify(void);
virtual void HandleAnimEvent(MonsterEvent_t *pEvent);
virtual void IdleSound(void);
virtual void PainSound(void);
virtual void DeathSound(void);
virtual void AlertSound(void);
void AttackSound(void);
virtual void StartTask(Task_t *pTask);
virtual BOOL CheckMeleeAttack1(float flDot, float flDist);
virtual BOOL CheckRangeAttack1(float flDot, float flDist);
virtual void RunAI(void);
virtual void GibMonster();
Schedule_t *GetSchedule(void);
Schedule_t *GetScheduleOfType(int Type);
virtual int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType);
virtual void Killed(entvars_t *pevAttacker, int iGib);
void UpdateOnRemove();
CUSTOM_SCHEDULES
float m_flNextZapTime; // last time the voltigore used the spit attack.
BOOL m_fShouldUpdateBeam;
CMBeam* m_pBeam[3];
CMSprite* m_pBeamGlow;
int m_glowBrightness;
static const char* pAlertSounds[];
static const char* pAttackMeleeSounds[];
static const char* pMeleeHitSounds[];
static const char* pMeleeMissSounds[];
static const char* pComSounds[];
static const char* pDeathSounds[];
static const char* pFootstepSounds[];
static const char* pIdleSounds[];
static const char* pPainSounds[];
static const char* pGruntSounds[];
void CreateBeams();
void DestroyBeams();
void UpdateBeams();
void CreateGlow();
void DestroyGlow();
void GlowUpdate();
void GlowOff(void);
void GlowOn(int level);
protected:
void GibBeamDamage();
void PrecacheImpl(char* modelName);
int m_beamTexture;
};
//=========================================================
// Baby Voltigore
//=========================================================
class CMBabyVoltigore : public CMVoltigore
{
public:
void Spawn(void);
void Precache(void);
void HandleAnimEvent(MonsterEvent_t* pEvent);
BOOL CheckMeleeAttack1(float flDot, float flDist);
BOOL CheckRangeAttack1(float flDot, float flDist);
void StartTask(Task_t *pTask);
void Killed(entvars_t *pevAttacker, int iGib);
void GibMonster();
Schedule_t* GetSchedule();
Schedule_t* GetScheduleOfType(int Type);
};
//
// sven co-op monsters
//
//=========================================================
// Baby Gargantua
//=========================================================
class CMBabyGargantua : public CMGargantua
{
public:
void Spawn( void );
void Precache( void );
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType );
void HandleAnimEvent( MonsterEvent_t *pEvent );
BOOL CheckMeleeAttack1( float flDot, float flDist ); // Swipe
BOOL CheckMeleeAttack2( float flDot, float flDist ); // Flames
BOOL CheckRangeAttack1( float flDot, float flDist ); // Stomp attack
void StartTask( Task_t *pTask );
void RunTask( Task_t *pTask );
void StompAttack( void );
void FlameCreate( void );
void FlameUpdate( void );
void FlameDestroy( void );
static const char *pBeamAttackSounds[];
static const char *pFootSounds[];
static const char *pIdleSounds[];
static const char *pAlertSounds[];
static const char *pPainSounds[];
static const char *pAttackSounds[];
static const char *pStompSounds[];
static const char *pBreatheSounds[];
static const char *pDieSounds[];
private:
edict_t *BabyGargCheckTraceHullAttack(float flDist, int iDamage, int iDmgType);
};
//=========================================================
// Heavy Weapons Grunt
//=========================================================
class CMHWGrunt : public CMHGrunt
{
public:
void Spawn( void );
void Precache( void );
int Classify(void);
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType );
void HandleAnimEvent(MonsterEvent_t *pEvent);
void SetActivity(Activity NewActivity);
Schedule_t *GetSchedule( void );
Schedule_t *GetScheduleOfType ( int Type );
BOOL CheckRangeAttack1( float flDot, float flDist );
BOOL CheckMeleeAttack1( float flDot, float flDist );
BOOL CheckRangeAttack2( float flDot, float flDist );
void Minigun(void);
CUSTOM_SCHEDULES
};
//=========================================================
// Robo Grunt
//=========================================================
class CMRGrunt : public CMHGrunt
{
public:
int Classify(void);
void Spawn( void );
void Precache( void );
void DeathSound(void);
void PainSound(void);
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType );
void RunAI(void);
void Killed(entvars_t *pevAttacker, int iGib);
void GibMonster();
void EXPORT SparkTouch(edict_t *pOther);
void EXPORT StartGib(void);
float m_flNextSpark;
float m_flNextDischarge;
float m_flActiveDischarge;
int m_iBodyGibs;
static const char *pRobotSentences[];
};
//=========================================================
// Looking for Stukabat? It's located in cmflyingmonster.h
//=========================================================
#endif // BASEMONSTER_H #endif // BASEMONSTER_H

View File

@@ -17,6 +17,8 @@
#ifndef FLYINGMONSTER_H #ifndef FLYINGMONSTER_H
#define FLYINGMONSTER_H #define FLYINGMONSTER_H
#include "cmbasemonster.h"
class CMFlyingMonster : public CMBaseMonster class CMFlyingMonster : public CMBaseMonster
{ {
public: public:
@@ -49,5 +51,31 @@ protected:
}; };
//=========================================================
// Stukabat
//=========================================================
class CMStukabat : public CMFlyingMonster
{
public:
void Spawn( void );
void Precache( void );
void SetYawSpeed( void );
int Classify ( void );
void SetActivity ( Activity NewActivity );
void HandleAnimEvent( MonsterEvent_t *pEvent );
Schedule_t *GetScheduleOfType ( int Type );
int GetBitePitch( void ) { return PITCH_NORM + RANDOM_LONG( 40, 50 ); }
BOOL CheckRangeAttack1 ( float flDot, float flDist );
// Not used
BOOL CheckRangeAttack2 ( float flDot, float flDist ) { return FALSE; }
BOOL CheckMeleeAttack1 ( float flDot, float flDist ) { return FALSE; }
BOOL CheckMeleeAttack2 ( float flDot, float flDist ) { return FALSE; }
CUSTOM_SCHEDULES
};
#endif //FLYINGMONSTER_H #endif //FLYINGMONSTER_H

View File

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

View File

@@ -30,8 +30,7 @@
#include "weapons.h" #include "weapons.h"
#include "func_break.h" #include "func_break.h"
const Vector g_vecZero = Vector(0,0,0); extern DLL_GLOBAL Vector g_vecAttackDir;
Vector g_vecAttackDir;
entvars_t *g_pevLastInflictor; entvars_t *g_pevLastInflictor;
#define HUMAN_GIB_COUNT 6 #define HUMAN_GIB_COUNT 6
@@ -177,7 +176,15 @@ void CMGib :: SpawnHeadGib( entvars_t *pevVictim )
pGib->LimitVelocity(); pGib->LimitVelocity();
} }
// Overload
void CMGib :: SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human ) void CMGib :: SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human )
{
if ( human )
CMGib::SpawnRandomGibs( pevVictim, cGibs, "models/hgibs.mdl", human );
else
CMGib::SpawnRandomGibs( pevVictim, cGibs, "models/agibs.mdl", human );
}
void CMGib :: SpawnRandomGibs( entvars_t *pevVictim, int cGibs, const char *pGibModel, int human )
{ {
int cSplat; int cSplat;
@@ -191,13 +198,13 @@ void CMGib :: SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human )
if ( human ) if ( human )
{ {
// human pieces // human pieces
pGib->Spawn( "models/hgibs.mdl" ); pGib->Spawn( pGibModel );
pGib->pev->body = RANDOM_LONG(1,HUMAN_GIB_COUNT-1);// start at one to avoid throwing random amounts of skulls (0th gib) pGib->pev->body = RANDOM_LONG(1,HUMAN_GIB_COUNT-1);// start at one to avoid throwing random amounts of skulls (0th gib)
} }
else else
{ {
// aliens // aliens
pGib->Spawn( "models/agibs.mdl" ); pGib->Spawn( pGibModel );
pGib->pev->body = RANDOM_LONG(0,ALIEN_GIB_COUNT-1); pGib->pev->body = RANDOM_LONG(0,ALIEN_GIB_COUNT-1);
} }
@@ -572,7 +579,7 @@ void CMBaseMonster::CallGibMonster( void )
if (pev->health < -99) if (pev->health < -99)
{ {
pev->health = 0; pev->health = 0;
pev->fuser4 = pev->health; pev->fuser4 = pev->health;
} }
if ( ShouldFadeOnDeath() && !fade ) if ( ShouldFadeOnDeath() && !fade )
@@ -590,6 +597,10 @@ void CMBaseMonster :: Killed( entvars_t *pevAttacker, int iGib )
unsigned int cCount = 0; unsigned int cCount = 0;
BOOL fDone = FALSE; BOOL fDone = FALSE;
// If a player killed this monster, add score
if ( UTIL_IsPlayer( ENT( pevAttacker ) ) )
pevAttacker->frags += 1.0;
if ( HasMemory( bits_MEMORY_KILLED ) ) if ( HasMemory( bits_MEMORY_KILLED ) )
{ {
if ( ShouldGibMonster( iGib ) ) if ( ShouldGibMonster( iGib ) )
@@ -606,14 +617,11 @@ void CMBaseMonster :: Killed( entvars_t *pevAttacker, int iGib )
SetConditions( bits_COND_LIGHT_DAMAGE ); SetConditions( bits_COND_LIGHT_DAMAGE );
// tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality. // tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality.
/*jlb monstermaker
CMBaseEntity *pOwner = CMBaseEntity::Instance(pev->owner); CMBaseEntity *pOwner = CMBaseEntity::Instance(pev->owner);
if ( pOwner ) if ( pOwner )
{ {
//jlb it crashes here sometimes!!!
pOwner->DeathNotice( pev ); pOwner->DeathNotice( pev );
} }
jlb*/
if ( ShouldGibMonster( iGib ) ) if ( ShouldGibMonster( iGib ) )
{ {
@@ -630,7 +638,7 @@ jlb*/
if (pev->health < -99) if (pev->health < -99)
{ {
pev->health = 0; pev->health = 0;
pev->fuser4 = pev->health; pev->fuser4 = pev->health;
} }
//pev->enemy = ENT( pevAttacker );//why? (sjb) //pev->enemy = ENT( pevAttacker );//why? (sjb)
@@ -882,8 +890,8 @@ int CMBaseMonster :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker
// do the damage // do the damage
pev->health -= flTake; pev->health -= flTake;
if (pev->flags & FL_MONSTER) if (pev->flags & FL_MONSTER)
pev->fuser4 = pev->health; pev->fuser4 = pev->health;
// HACKHACK Don't kill monsters in a script. Let them break their scripts first // HACKHACK Don't kill monsters in a script. Let them break their scripts first
if ( m_MonsterState == MONSTERSTATE_SCRIPT ) if ( m_MonsterState == MONSTERSTATE_SCRIPT )
@@ -993,13 +1001,13 @@ int CMBaseMonster :: DeadTakeDamage( entvars_t *pevInflictor, entvars_t *pevAtta
if ( pev->health <= flDamage ) if ( pev->health <= flDamage )
{ {
pev->health = -50; pev->health = -50;
pev->fuser4 = pev->health; pev->fuser4 = pev->health;
Killed( pevAttacker, GIB_ALWAYS ); Killed( pevAttacker, GIB_ALWAYS );
return 0; return 0;
} }
// Accumulate corpse gibbing damage, so you can gib with multiple hits // Accumulate corpse gibbing damage, so you can gib with multiple hits
pev->health -= flDamage * 0.1; pev->health -= flDamage * 0.1;
pev->fuser4 = pev->health; pev->fuser4 = pev->health;
} }
return 1; return 1;
@@ -1048,8 +1056,8 @@ void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacke
{ {
if ( pEntity->v.takedamage != DAMAGE_NO ) if ( pEntity->v.takedamage != DAMAGE_NO )
{ {
if (UTIL_IsPlayer(pEntity)) if (UTIL_IsPlayer(pEntity))
{ {
// blast's don't tavel into or out of water // blast's don't tavel into or out of water
if (bInWater && pEntity->v.waterlevel == 0) if (bInWater && pEntity->v.waterlevel == 0)
continue; continue;
@@ -1088,11 +1096,11 @@ void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacke
else else
{ {
UTIL_TakeDamage ( pEntity, pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ); UTIL_TakeDamage ( pEntity, pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType );
} }
} }
} }
else if (pEntity->v.euser4 != NULL) else if (pEntity->v.euser4 != NULL)
{ {
// UNDONE: this should check a damage mask, not an ignore // UNDONE: this should check a damage mask, not an ignore
CMBaseEntity *pMonster = GetClassPtr((CMBaseEntity *)VARS(pEntity)); CMBaseEntity *pMonster = GetClassPtr((CMBaseEntity *)VARS(pEntity));
@@ -1139,7 +1147,57 @@ void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacke
else else
{ {
pMonster->TakeDamage ( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ); pMonster->TakeDamage ( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType );
} }
}
}
else
{
edict_t *pMonster = pEntity;
if ( iClassIgnore != CLASS_NONE && pMonster->v.iuser4 == iClassIgnore )
{// houndeyes don't hurt other houndeyes with their attack
continue;
}
// blast's don't tavel into or out of water
if (bInWater && pEntity->v.waterlevel == 0)
continue;
if (!bInWater && pEntity->v.waterlevel == 3)
continue;
vecSpot = UTIL_BodyTarget( pMonster, vecSrc );
UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, ENT(pevInflictor), &tr );
if ( tr.flFraction == 1.0 || tr.pHit == pEntity )
{// the explosion can 'see' this entity, so hurt them!
if (tr.fStartSolid)
{
// if we're stuck inside them, fixup the position and distance
tr.vecEndPos = vecSrc;
tr.flFraction = 0.0;
}
// decrease damage for an ent that's farther from the bomb.
flAdjustedDamage = ( vecSrc - tr.vecEndPos ).Length() * falloff;
flAdjustedDamage = flDamage - flAdjustedDamage;
if ( flAdjustedDamage < 0 )
{
flAdjustedDamage = 0;
}
// ALERT( at_console, "hit %s\n", STRING( pEntity->pev->classname ) );
if (tr.flFraction != 1.0)
{
ClearMultiDamage( );
UTIL_TraceAttack( pMonster, pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize( ), &tr, bitsDamageType );
ApplyMultiDamage( pevInflictor, pevAttacker );
}
else
{
UTIL_TakeDamageExternal( pMonster, pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType );
}
} }
} }
} }
@@ -1195,6 +1253,8 @@ edict_t* CMBaseMonster :: CheckTraceHullAttack( float flDist, int iDamage, int i
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
pMonster->TakeDamage( pev, pev, iDamage, iDmgType ); pMonster->TakeDamage( pev, pev, iDamage, iDmgType );
} }
else
UTIL_TakeDamageExternal( pEntity, pev, pev, iDamage, iDmgType );
} }
return pEntity; return pEntity;
@@ -1262,32 +1322,32 @@ void CMBaseMonster :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vecto
{ {
m_LastHitGroup = ptr->iHitgroup; m_LastHitGroup = ptr->iHitgroup;
/*jlb // do we need the hitboxes to be customizable? use vanilla HL skill.cfg for now
switch ( ptr->iHitgroup ) switch ( ptr->iHitgroup )
{ {
case HITGROUP_GENERIC: case HITGROUP_GENERIC:
break; break;
case HITGROUP_HEAD: case HITGROUP_HEAD:
flDamage *= gSkillData.monHead; flDamage *= 3; //gSkillData.monHead;
break; break;
case HITGROUP_CHEST: case HITGROUP_CHEST:
flDamage *= gSkillData.monChest; flDamage *= 1; //gSkillData.monChest;
break; break;
case HITGROUP_STOMACH: case HITGROUP_STOMACH:
flDamage *= gSkillData.monStomach; flDamage *= 1; //gSkillData.monStomach;
break; break;
case HITGROUP_LEFTARM: case HITGROUP_LEFTARM:
case HITGROUP_RIGHTARM: case HITGROUP_RIGHTARM:
flDamage *= gSkillData.monArm; flDamage *= 1; //gSkillData.monArm;
break; break;
case HITGROUP_LEFTLEG: case HITGROUP_LEFTLEG:
case HITGROUP_RIGHTLEG: case HITGROUP_RIGHTLEG:
flDamage *= gSkillData.monLeg; flDamage *= 1; //gSkillData.monLeg;
break; break;
default: default:
break; break;
} }
jlb*/
SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage);// a little surface blood. SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage);// a little surface blood.
TraceBleed( flDamage, vecDir, ptr, bitsDamageType ); TraceBleed( flDamage, vecDir, ptr, bitsDamageType );
AddMultiDamage( pevAttacker, this->edict(), flDamage, bitsDamageType ); AddMultiDamage( pevAttacker, this->edict(), flDamage, bitsDamageType );
@@ -1356,6 +1416,8 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
case BULLET_MONSTER_MP5: case BULLET_MONSTER_MP5:
case BULLET_MONSTER_9MM: case BULLET_MONSTER_9MM:
case BULLET_MONSTER_12MM: case BULLET_MONSTER_12MM:
case BULLET_MONSTER_762:
case BULLET_MONSTER_357:
default: default:
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, vecTracerSrc ); MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, vecTracerSrc );
WRITE_BYTE( TE_TRACER ); WRITE_BYTE( TE_TRACER );
@@ -1372,8 +1434,8 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
// do damage, paint decals // do damage, paint decals
if (tr.flFraction != 1.0) if (tr.flFraction != 1.0)
{ {
if (UTIL_IsPlayer(tr.pHit)) // is this a player? if (UTIL_IsPlayer(tr.pHit)) // is this a player?
{ {
edict_t *pPlayer = tr.pHit; edict_t *pPlayer = tr.pHit;
if ( iDamage ) if ( iDamage )
@@ -1382,46 +1444,63 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType ); DecalGunshot( &tr, iBulletType );
} }
else switch(iBulletType) else
{ {
default: switch(iBulletType)
case BULLET_MONSTER_9MM:
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_MP5:
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_12MM:
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET);
if ( !tracer )
{ {
default:
case BULLET_MONSTER_9MM:
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType ); DecalGunshot( &tr, iBulletType );
}
break;
case BULLET_NONE: // FIX break;
UTIL_TraceAttack(pPlayer, pevAttacker, 50, vecDir, &tr, DMG_CLUB);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
// only decal glass
if ( !FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0)
{
UTIL_DecalTrace( &tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0,2) );
}
break; case BULLET_MONSTER_MP5:
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_12MM:
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET);
if ( !tracer )
{
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
}
break;
case BULLET_MONSTER_762:
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg762, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_357:
UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg357, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_NONE: // FIX
UTIL_TraceAttack(pPlayer, pevAttacker, 50, vecDir, &tr, DMG_CLUB);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
// only decal glass
if ( !FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0)
{
UTIL_DecalTrace( &tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0,2) );
}
break;
}
} }
} }
else if (tr.pHit->v.euser4 != NULL) else if (tr.pHit->v.euser4 != NULL) // monstermod monster
{ {
CMBaseEntity *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit)); CMBaseEntity *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit));
if ( iDamage ) if ( iDamage )
@@ -1430,42 +1509,124 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType ); DecalGunshot( &tr, iBulletType );
} }
else switch(iBulletType) else
{ {
default: switch(iBulletType)
case BULLET_MONSTER_9MM:
pMonster->TraceAttack(pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_MP5:
pMonster->TraceAttack(pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_12MM:
pMonster->TraceAttack(pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET);
if ( !tracer )
{ {
default:
case BULLET_MONSTER_9MM:
pMonster->TraceAttack(pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType ); DecalGunshot( &tr, iBulletType );
}
break;
case BULLET_NONE: // FIX break;
pMonster->TraceAttack(pevAttacker, 50, vecDir, &tr, DMG_CLUB);
case BULLET_MONSTER_MP5:
pMonster->TraceAttack(pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_12MM:
pMonster->TraceAttack(pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET);
if ( !tracer )
{
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
}
break;
case BULLET_MONSTER_762:
pMonster->TraceAttack(pevAttacker, gSkillData.monDmg762, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_357:
pMonster->TraceAttack(pevAttacker, gSkillData.monDmg357, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_NONE: // FIX
pMonster->TraceAttack(pevAttacker, 50, vecDir, &tr, DMG_CLUB);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
// only decal glass
if ( !FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0)
{
UTIL_DecalTrace( &tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0,2) );
}
break;
}
}
}
else // normal game entity
{
edict_t *pMonster = tr.pHit;
if ( iDamage )
{
UTIL_TraceAttack(pMonster, pevAttacker, iDamage, vecDir, &tr, DMG_BULLET | ((iDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) );
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
// only decal glass DecalGunshot( &tr, iBulletType );
if ( !FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0) }
else
{
switch(iBulletType)
{ {
UTIL_DecalTrace( &tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0,2) ); default:
} case BULLET_MONSTER_9MM:
UTIL_TraceAttack(pMonster, pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break; break;
case BULLET_MONSTER_MP5:
UTIL_TraceAttack(pMonster, pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_12MM:
UTIL_TraceAttack(pMonster, pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET);
if ( !tracer )
{
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
}
break;
case BULLET_MONSTER_762:
UTIL_TraceAttack(pMonster, pevAttacker, gSkillData.monDmg762, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MONSTER_357:
UTIL_TraceAttack(pMonster, pevAttacker, gSkillData.monDmg357, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_NONE: // FIX
UTIL_TraceAttack(pMonster, pevAttacker, 50, vecDir, &tr, DMG_CLUB);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
// only decal glass
if ( !FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0)
{
UTIL_DecalTrace( &tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0,2) );
}
break;
}
} }
} }
} }

View File

@@ -81,6 +81,11 @@ const char *CMController::pDeathSounds[] =
//========================================================= //=========================================================
int CMController :: Classify ( void ) int CMController :: Classify ( void )
{ {
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_ALIEN_MILITARY; return CLASS_ALIEN_MILITARY;
} }
@@ -297,19 +302,26 @@ void CMController :: Spawn()
{ {
Precache( ); Precache( );
SET_MODEL(ENT(pev), "models/controller.mdl"); SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/controller.mdl"));
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 )); UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ));
pev->solid = SOLID_SLIDEBOX; pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_FLY; pev->movetype = MOVETYPE_FLY;
pev->flags |= FL_FLY; pev->flags |= FL_FLY;
m_bloodColor = BLOOD_COLOR_GREEN; m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->health = gSkillData.controllerHealth; pev->health = gSkillData.controllerHealth;
pev->view_ofs = Vector( 0, 0, -2 );// position of the eyes relative to monster's origin. 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 ) m_flFieldOfView = VIEW_FIELD_FULL;// indicates the width of this monster's forward view cone ( as a dotproduct result )
m_MonsterState = MONSTERSTATE_NONE; m_MonsterState = MONSTERSTATE_NONE;
MonsterInit(); MonsterInit();
pev->classname = MAKE_STRING( "monster_alien_controller" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Alien Controller" );
}
} }
//========================================================= //=========================================================
@@ -1113,6 +1125,8 @@ void CMControllerHeadBall :: Spawn( void )
m_hOwner = pev->owner; m_hOwner = pev->owner;
pev->dmgtime = gpGlobals->time; pev->dmgtime = gpGlobals->time;
pev->classname = MAKE_STRING( "controller_head_ball" );
} }
@@ -1171,6 +1185,8 @@ void CMControllerHeadBall :: HuntThink( void )
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit)); CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit));
pMonster->TraceAttack( VARS(m_hOwner), gSkillData.controllerDmgZap, pev->velocity, &tr, DMG_SHOCK ); 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) ); ApplyMultiDamage( pev, VARS(m_hOwner) );
} }
@@ -1297,8 +1313,9 @@ void CMControllerZapBall :: Spawn( void )
m_hOwner = pev->owner; m_hOwner = pev->owner;
pev->dmgtime = gpGlobals->time; // keep track of when ball spawned pev->dmgtime = gpGlobals->time; // keep track of when ball spawned
pev->nextthink = gpGlobals->time + 0.1; pev->nextthink = gpGlobals->time + 0.1;
}
pev->classname = MAKE_STRING( "controller_energy_ball" );
}
void CMControllerZapBall :: Precache( void ) void CMControllerZapBall :: Precache( void )
{ {
@@ -1346,6 +1363,8 @@ void CMControllerZapBall::ExplodeTouch( edict_t *pOther )
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
pMonster->TraceAttack(pevOwner, gSkillData.controllerDmgBall, pev->velocity.Normalize(), &tr, DMG_ENERGYBEAM ); 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 ); ApplyMultiDamage( pevOwner, pevOwner );
UTIL_EmitAmbientSound( ENT(pev), tr.vecEndPos, "weapons/electro4.wav", 0.3, ATTN_NORM, 0, RANDOM_LONG( 90, 99 ) ); UTIL_EmitAmbientSound( ENT(pev), tr.vecEndPos, "weapons/electro4.wav", 0.3, ATTN_NORM, 0, RANDOM_LONG( 90, 99 ) );

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -23,10 +23,10 @@ extern enginefuncs_t g_engfuncs;
// The actual engine callbacks // The actual engine callbacks
#define GETPLAYERUSERID (*g_engfuncs.pfnGetPlayerUserId) #define GETPLAYERUSERID (*g_engfuncs.pfnGetPlayerUserId)
#define PRECACHE_MODEL (*g_engfuncs.pfnPrecacheModel) #define PRECACHE_MODEL2 (*g_engfuncs.pfnPrecacheModel)
#define PRECACHE_SOUND (*g_engfuncs.pfnPrecacheSound) #define PRECACHE_SOUND2 (*g_engfuncs.pfnPrecacheSound)
#define PRECACHE_GENERIC (*g_engfuncs.pfnPrecacheGeneric) #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_INDEX (*g_engfuncs.pfnModelIndex)
#define MODEL_FRAMES (*g_engfuncs.pfnModelFrames) #define MODEL_FRAMES (*g_engfuncs.pfnModelFrames)
#define SET_SIZE (*g_engfuncs.pfnSetSize) #define SET_SIZE (*g_engfuncs.pfnSetSize)

72
src/dlls/explode.cpp Executable file → Normal file
View File

@@ -54,7 +54,7 @@ void CMShower::Spawn( void )
pev->speed = RANDOM_FLOAT( 0.5, 1.5 ); pev->speed = RANDOM_FLOAT( 0.5, 1.5 );
pev->angles = g_vecZero; pev->angles = g_vecZero;
pev->classname = MAKE_STRING( "_spark_shower" ); pev->classname = MAKE_STRING( "spark_shower" );
} }
@@ -81,13 +81,52 @@ void CMShower::Touch( CMBaseEntity *pOther )
pev->speed = 0; pev->speed = 0;
} }
// Puff of Smoke
class CSmoker : public CMBaseEntity
{
public:
void Spawn( void );
void Think( void );
};
void CSmoker::Spawn( void )
{
pev->movetype = MOVETYPE_NONE;
pev->nextthink = gpGlobals->time;
pev->solid = SOLID_NOT;
UTIL_SetSize(pev, g_vecZero, g_vecZero );
pev->effects |= EF_NODRAW;
pev->angles = g_vecZero;
}
void CSmoker::Think( void )
{
// lots of smoke
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
WRITE_BYTE( TE_SMOKE );
WRITE_COORD( pev->origin.x + RANDOM_FLOAT( -pev->dmg, pev->dmg ));
WRITE_COORD( pev->origin.y + RANDOM_FLOAT( -pev->dmg, pev->dmg ));
WRITE_COORD( pev->origin.z);
WRITE_SHORT( g_sModelIndexSmoke );
WRITE_BYTE( RANDOM_LONG(pev->scale, pev->scale * 1.1) );
WRITE_BYTE( RANDOM_LONG(8,14) ); // framerate
MESSAGE_END();
pev->health--;
if ( pev->health > 0 )
pev->nextthink = gpGlobals->time + RANDOM_FLOAT(0.1, 0.2);
else
UTIL_Remove( this->edict() );
}
// Explosion
class CMEnvExplosion : public CMBaseMonster class CMEnvExplosion : public CMBaseMonster
{ {
public: public:
void Spawn( ); void Spawn( );
void EXPORT Smoke ( void ); void EXPORT Smoke ( void );
void KeyValue( KeyValueData *pkvd ); void KeyValue( KeyValueData *pkvd );
void DelayUse( void ); void EXPORT DelayUse( void );
void Use( CMBaseEntity *pActivator, CMBaseEntity *pCaller, USE_TYPE useType, float value ); void Use( CMBaseEntity *pActivator, CMBaseEntity *pCaller, USE_TYPE useType, float value );
int m_iMagnitude;// how large is the fireball? how much damage? int m_iMagnitude;// how large is the fireball? how much damage?
@@ -133,7 +172,7 @@ void CMEnvExplosion::Spawn( void )
} }
m_spriteScale = (int)flSpriteScale; m_spriteScale = (int)flSpriteScale;
pev->classname = MAKE_STRING( "_env_explosion" ); pev->classname = MAKE_STRING( "env_explosion" );
} }
void CMEnvExplosion::DelayUse( void ) void CMEnvExplosion::DelayUse( void )
@@ -259,7 +298,8 @@ void CMEnvExplosion::Smoke( void )
} }
// Stock to quickly create a one-time explosion // Stocks:
// Create a one-time explosion
void ExplosionCreate( const Vector &center, const Vector &angles, edict_t *pOwner, int magnitude, int flags, float delay ) void ExplosionCreate( const Vector &center, const Vector &angles, edict_t *pOwner, int magnitude, int flags, float delay )
{ {
KeyValueData kvd; KeyValueData kvd;
@@ -287,15 +327,19 @@ void ExplosionCreate( const Vector &center, const Vector &angles, edict_t *pOwne
pExplosion->pev->spawnflags &= ~SF_ENVEXPLOSION_REPEATABLE; pExplosion->pev->spawnflags &= ~SF_ENVEXPLOSION_REPEATABLE;
pExplosion->Spawn(); pExplosion->Spawn();
if ( delay > 0.0f ) pExplosion->SetThink( &CMEnvExplosion::DelayUse );
{ pExplosion->pev->nextthink = gpGlobals->time + delay;
//pExplosion->SetThink( &CMBaseEntity::SUB_CallUseToggle ); // i don't trust you
pExplosion->SetThink( &CMEnvExplosion::DelayUse );
pExplosion->pev->nextthink = gpGlobals->time + delay;
}
else
{
pExplosion->Use( NULL, NULL, USE_TOGGLE, 0 );
}
} }
} }
// Emit smoke
void SmokeCreate( const Vector &origin, int amount, int size, int radius, float delay )
{
CMBaseEntity *pSmoker = CreateClassPtr((CSmoker *)NULL); // CMBaseEntity::Create( "env_smoker", pev->origin, g_vecZero, NULL );
UTIL_SetOrigin( pSmoker->pev, origin );
pSmoker->Spawn();
pSmoker->pev->health = amount; // number of smoke balls
pSmoker->pev->scale = size; // size in 0.1x - size 10 = x1.0
pSmoker->pev->dmg = radius; // radial distribution
pSmoker->pev->nextthink = gpGlobals->time + delay; // Start in ... seconds
}

View File

@@ -23,10 +23,11 @@
#define SF_ENVEXPLOSION_NODECAL ( 1 << 4 ) // don't make a scorch mark #define SF_ENVEXPLOSION_NODECAL ( 1 << 4 ) // don't make a scorch mark
#define SF_ENVEXPLOSION_NOSPARKS ( 1 << 5 ) // don't make a scorch mark #define SF_ENVEXPLOSION_NOSPARKS ( 1 << 5 ) // don't make a scorch mark
extern DLL_GLOBAL short g_sModelIndexFireball; extern DLL_GLOBAL short g_sModelIndexFireball;
extern DLL_GLOBAL short g_sModelIndexSmoke; extern DLL_GLOBAL short g_sModelIndexSmoke;
extern DLL_GLOBAL short g_sModelIndexTinySpit;
extern void ExplosionCreate( const Vector &center, const Vector &angles, edict_t *pOwner, int magnitude, int flags, float delay ); extern void ExplosionCreate( const Vector &center, const Vector &angles, edict_t *pOwner, int magnitude, int flags, float delay );
extern void SmokeCreate( const Vector &origin, int amount, int size, int radius, float delay );
#endif //EXPLODE_H #endif //EXPLODE_H

View File

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

View File

@@ -16,15 +16,14 @@
#include "util.h" #include "util.h"
#include "cmbase.h" #include "cmbase.h"
#include "cmbasemonster.h" #include "cmbasemonster.h"
#include "cmflyingmonster.h"
#include "monsters.h" #include "monsters.h"
#include "schedule.h" #include "schedule.h"
#define FLYING_AE_FLAP (8) #define FLYING_AE_FLAP (8)
#define FLYING_AE_FLAPSOUND (9) #define FLYING_AE_FLAPSOUND (9)
extern DLL_GLOBAL edict_t *g_pBodyQueueHead;
int CMFlyingMonster :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, edict_t *pTarget, float *pflDist ) int CMFlyingMonster :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, edict_t *pTarget, float *pflDist )
{ {
// UNDONE: need to check more than the endpoint // UNDONE: need to check more than the endpoint

700
src/dlls/gargantua.cpp Executable file → Normal file
View File

@@ -62,13 +62,6 @@ const float GARG_ATTACKDIST = 80.0;
int gStompSprite = 0, gGargGibModel = 0; int gStompSprite = 0, gGargGibModel = 0;
void SpawnExplosion( Vector center, float randomRange, float time, int magnitude, edict_t *owner ); void SpawnExplosion( Vector center, float randomRange, float time, int magnitude, edict_t *owner );
class CSmoker : public CMBaseEntity
{
public:
void Spawn( void );
void Think( void );
};
// Spiral Effect // Spiral Effect
class CSpiral : public CMBaseEntity class CSpiral : public CMBaseEntity
{ {
@@ -84,14 +77,14 @@ class CStomp : public CMBaseEntity
public: public:
void Spawn( void ); void Spawn( void );
void Think( void ); void Think( void );
static CStomp *StompCreate( const Vector &origin, const Vector &end, float speed ); static CStomp *StompCreate( const Vector &origin, const Vector &end, float speed, float damage );
private: private:
// UNDONE: re-use this sprite list instead of creating new ones all the time // UNDONE: re-use this sprite list instead of creating new ones all the time
// CSprite *m_pSprites[ STOMP_SPRITE_COUNT ]; // CSprite *m_pSprites[ STOMP_SPRITE_COUNT ];
}; };
CStomp *CStomp::StompCreate( const Vector &origin, const Vector &end, float speed ) CStomp *CStomp::StompCreate( const Vector &origin, const Vector &end, float speed, float damage )
{ {
CStomp *pStomp = CreateClassPtr( (CStomp *)NULL ); CStomp *pStomp = CreateClassPtr( (CStomp *)NULL );
@@ -100,6 +93,7 @@ CStomp *CStomp::StompCreate( const Vector &origin, const Vector &end, float spee
pStomp->pev->scale = dir.Length(); pStomp->pev->scale = dir.Length();
pStomp->pev->movedir = dir.Normalize(); pStomp->pev->movedir = dir.Normalize();
pStomp->pev->speed = speed; pStomp->pev->speed = speed;
pStomp->pev->dmg = damage;
pStomp->Spawn(); pStomp->Spawn();
return pStomp; return pStomp;
@@ -136,13 +130,23 @@ void CStomp::Think( void )
if ( tr.pHit && tr.pHit != pev->owner ) if ( tr.pHit && tr.pHit != pev->owner )
{ {
CMBaseEntity *pEntity = CMBaseEntity::Instance( tr.pHit ); edict_t *pEntity = tr.pHit;
entvars_t *pevOwner = pev; entvars_t *pevOwner = pev;
if ( pev->owner ) if ( pev->owner )
pevOwner = VARS(pev->owner); pevOwner = VARS(pev->owner);
if ( pEntity ) if (pEntity->v.takedamage)
pEntity->TakeDamage( pev, pevOwner, gSkillData.gargantuaDmgStomp, DMG_SONIC ); {
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 // Accelerate the effect
@@ -388,7 +392,7 @@ void CMGargantua::StompAttack( void )
Vector vecEnd = (vecAim * 1024) + vecStart; Vector vecEnd = (vecAim * 1024) + vecStart;
UTIL_TraceLine( vecStart, vecEnd, ignore_monsters, edict(), &trace ); UTIL_TraceLine( vecStart, vecEnd, ignore_monsters, edict(), &trace );
CStomp::StompCreate( vecStart, trace.vecEndPos, 0 ); CStomp::StompCreate( vecStart, trace.vecEndPos, 0, gSkillData.gargantuaDmgStomp );
UTIL_ScreenShake( pev->origin, 12.0, 100.0, 2.0, 1000 ); UTIL_ScreenShake( pev->origin, 12.0, 100.0, 2.0, 1000 );
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pStompSounds[ RANDOM_LONG(0,ARRAYSIZE(pStompSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) ); EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pStompSounds[ RANDOM_LONG(0,ARRAYSIZE(pStompSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) );
@@ -489,8 +493,8 @@ void CMGargantua :: FlameUpdate( void )
streaks = TRUE; streaks = TRUE;
UTIL_DecalTrace( &trace, DECAL_SMALLSCORCH1 + RANDOM_LONG(0,2) ); UTIL_DecalTrace( &trace, DECAL_SMALLSCORCH1 + RANDOM_LONG(0,2) );
} }
// RadiusDamage( trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, CLASS_ALIEN_MONSTER, DMG_BURN ); // RadiusDamage( trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, Classify(), DMG_BURN );
FlameDamage( vecStart, trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, CLASS_ALIEN_MONSTER, DMG_BURN ); FlameDamage( vecStart, trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, Classify(), DMG_BURN );
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
WRITE_BYTE( TE_ELIGHT ); WRITE_BYTE( TE_ELIGHT );
@@ -570,6 +574,8 @@ void CMGargantua :: FlameDamage( Vector vecStart, Vector vecEnd, entvars_t *pevI
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
pMonster->TraceAttack( pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize(), &tr, bitsDamageType ); 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 ); ApplyMultiDamage( pevInflictor, pevAttacker );
} }
else else
@@ -581,6 +587,8 @@ void CMGargantua :: FlameDamage( Vector vecStart, Vector vecEnd, entvars_t *pevI
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
pMonster->TakeDamage( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ); pMonster->TakeDamage( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType );
} }
else
UTIL_TakeDamageExternal( pEntity, pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType );
} }
} }
} }
@@ -624,6 +632,11 @@ void CMGargantua :: PrescheduleThink( void )
//========================================================= //=========================================================
int CMGargantua :: Classify ( void ) int CMGargantua :: Classify ( void )
{ {
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_ALIEN_MONSTER; return CLASS_ALIEN_MONSTER;
} }
@@ -665,12 +678,12 @@ void CMGargantua :: Spawn()
{ {
Precache( ); Precache( );
SET_MODEL(ENT(pev), "models/garg.mdl"); SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/garg.mdl"));
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
pev->solid = SOLID_SLIDEBOX; pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP; pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN; m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->health = gSkillData.gargantuaHealth; pev->health = gSkillData.gargantuaHealth;
//pev->view_ofs = Vector ( 0, 0, 96 );// taken from mdl file //pev->view_ofs = Vector ( 0, 0, 96 );// taken from mdl file
m_flFieldOfView = -0.2;// width of forward view cone ( as a dotproduct result ) m_flFieldOfView = -0.2;// width of forward view cone ( as a dotproduct result )
@@ -684,6 +697,13 @@ void CMGargantua :: Spawn()
EyeOff(); EyeOff();
m_seeTime = gpGlobals->time + 5; m_seeTime = gpGlobals->time + 5;
m_flameTime = gpGlobals->time + 2; m_flameTime = gpGlobals->time + 2;
pev->classname = MAKE_STRING( "monster_gargantua" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Gargantua" );
}
} }
@@ -692,48 +712,25 @@ void CMGargantua :: Spawn()
//========================================================= //=========================================================
void CMGargantua :: Precache() void CMGargantua :: Precache()
{ {
int i;
PRECACHE_MODEL("models/garg.mdl"); PRECACHE_MODEL("models/garg.mdl");
PRECACHE_MODEL( GARG_EYE_SPRITE_NAME ); PRECACHE_MODEL( GARG_EYE_SPRITE_NAME );
PRECACHE_MODEL( GARG_BEAM_SPRITE_NAME ); PRECACHE_MODEL( GARG_BEAM_SPRITE_NAME );
PRECACHE_MODEL( GARG_BEAM_SPRITE2 ); PRECACHE_MODEL( GARG_BEAM_SPRITE2 );
gStompSprite = PRECACHE_MODEL( GARG_STOMP_SPRITE_NAME ); gStompSprite = PRECACHE_MODELINDEX( GARG_STOMP_SPRITE_NAME );
gGargGibModel = PRECACHE_MODEL( GARG_GIB_MODEL ); gGargGibModel = PRECACHE_MODELINDEX( GARG_GIB_MODEL );
PRECACHE_SOUND( GARG_STOMP_BUZZ_SOUND ); PRECACHE_SOUND( GARG_STOMP_BUZZ_SOUND );
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) PRECACHE_SOUND_ARRAY(pAttackHitSounds);
PRECACHE_SOUND((char *)pAttackHitSounds[i]); PRECACHE_SOUND_ARRAY(pBeamAttackSounds);
PRECACHE_SOUND_ARRAY(pAttackMissSounds);
for ( i = 0; i < ARRAYSIZE( pBeamAttackSounds ); i++ ) PRECACHE_SOUND_ARRAY(pRicSounds);
PRECACHE_SOUND((char *)pBeamAttackSounds[i]); PRECACHE_SOUND_ARRAY(pFootSounds);
PRECACHE_SOUND_ARRAY(pIdleSounds);
for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) PRECACHE_SOUND_ARRAY(pAlertSounds);
PRECACHE_SOUND((char *)pAttackMissSounds[i]); PRECACHE_SOUND_ARRAY(pPainSounds);
PRECACHE_SOUND_ARRAY(pAttackSounds);
for ( i = 0; i < ARRAYSIZE( pRicSounds ); i++ ) PRECACHE_SOUND_ARRAY(pStompSounds);
PRECACHE_SOUND((char *)pRicSounds[i]); PRECACHE_SOUND_ARRAY(pBreatheSounds);
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]);
} }
@@ -768,7 +765,7 @@ void CMGargantua::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector ve
// if ( RANDOM_LONG(0,100) < 25 ) // if ( RANDOM_LONG(0,100) < 25 )
// EMIT_SOUND_DYN( ENT(pev), CHAN_BODY, pRicSounds[ RANDOM_LONG(0,ARRAYSIZE(pRicSounds)-1) ], 1.0, ATTN_NORM, 0, PITCH_NORM ); // EMIT_SOUND_DYN( ENT(pev), CHAN_BODY, pRicSounds[ RANDOM_LONG(0,ARRAYSIZE(pRicSounds)-1) ], 1.0, ATTN_NORM, 0, PITCH_NORM );
} }
flDamage *= (1.01f - gSkillData.gargantuaArmor); // Again, for mods (see below) // flDamage *= (1.00f - gSkillData.gargantuaArmor); // in here...?
} }
CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
@@ -776,13 +773,12 @@ void CMGargantua::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector ve
} }
int CMGargantua::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) int CMGargantua::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{ {
if ( IsAlive() ) if ( IsAlive() )
{ {
if ( !(bitsDamageType & GARG_DAMAGE) ) if ( !(bitsDamageType & GARG_DAMAGE) )
flDamage *= (1.01f - gSkillData.gargantuaArmor); // This is for mods that don't use explosives of any kind or do not work with the gargantua. flDamage *= (1.00f - gSkillData.gargantuaArmor); // This is for mods that don't use explosives of any kind or do not work with the gargantua.
// Always set // Always set
SetConditions( bits_COND_LIGHT_DAMAGE ); SetConditions( bits_COND_LIGHT_DAMAGE );
@@ -809,21 +805,22 @@ void CMGargantua::DeathEffect( void )
position.z += 15; position.z += 15;
} }
CMBaseEntity *pSmoker = CreateClassPtr((CSmoker *)NULL); // CMBaseEntity::Create( "env_smoker", pev->origin, g_vecZero, NULL ); // 1 smoke balls
UTIL_SetOrigin( pSmoker->pev, pev->origin ); // 4.6X normal size
pSmoker->Spawn(); // 0 radial distribution
pSmoker->pev->health = 1; // 1 smoke balls // start in 2.5 seconds
pSmoker->pev->scale = 46; // 4.6X normal size SmokeCreate( pev->origin, 1, 46, 0, 2.5 );
pSmoker->pev->dmg = 0; // 0 radial distribution
pSmoker->pev->nextthink = gpGlobals->time + 2.5; // Start in 2.5 seconds
} }
void CMGargantua::Killed( entvars_t *pevAttacker, int iGib ) void CMGargantua::Killed( entvars_t *pevAttacker, int iGib )
{ {
EyeOff(); if ( m_pEyeGlow )
UTIL_Remove( m_pEyeGlow->edict() ); {
m_pEyeGlow = NULL; EyeOff();
UTIL_Remove( m_pEyeGlow->edict() );
m_pEyeGlow = NULL;
}
CMBaseMonster::Killed( pevAttacker, GIB_NEVER ); CMBaseMonster::Killed( pevAttacker, GIB_NEVER );
} }
@@ -973,6 +970,8 @@ edict_t *CMGargantua::GargantuaCheckTraceHullAttack(float flDist, int iDamage, i
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit)); CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit));
pMonster->TakeDamage( pev, pev, iDamage, iDmgType ); pMonster->TakeDamage( pev, pev, iDamage, iDmgType );
} }
else
UTIL_TakeDamageExternal( tr.pHit, pev, pev, iDamage, iDmgType );
} }
return tr.pHit; return tr.pHit;
@@ -1154,38 +1153,6 @@ void CMGargantua::RunTask( Task_t *pTask )
} }
} }
void CSmoker::Spawn( void )
{
pev->movetype = MOVETYPE_NONE;
pev->nextthink = gpGlobals->time;
pev->solid = SOLID_NOT;
UTIL_SetSize(pev, g_vecZero, g_vecZero );
pev->effects |= EF_NODRAW;
pev->angles = g_vecZero;
}
void CSmoker::Think( void )
{
// lots of smoke
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
WRITE_BYTE( TE_SMOKE );
WRITE_COORD( pev->origin.x + RANDOM_FLOAT( -pev->dmg, pev->dmg ));
WRITE_COORD( pev->origin.y + RANDOM_FLOAT( -pev->dmg, pev->dmg ));
WRITE_COORD( pev->origin.z);
WRITE_SHORT( g_sModelIndexSmoke );
WRITE_BYTE( RANDOM_LONG(pev->scale, pev->scale * 1.1) );
WRITE_BYTE( RANDOM_LONG(8,14) ); // framerate
MESSAGE_END();
pev->health--;
if ( pev->health > 0 )
pev->nextthink = gpGlobals->time + RANDOM_FLOAT(0.1, 0.2);
else
UTIL_Remove( this->edict() );
}
void CSpiral::Spawn( void ) void CSpiral::Spawn( void )
{ {
pev->movetype = MOVETYPE_NONE; pev->movetype = MOVETYPE_NONE;
@@ -1253,13 +1220,10 @@ void CSpiral::Think( void )
void SpawnExplosion( Vector center, float randomRange, float time, int magnitude, edict_t *owner ) void SpawnExplosion( Vector center, float randomRange, float time, int magnitude, edict_t *owner )
{ {
/* no need for this
KeyValueData kvd; KeyValueData kvd;
char buf[128]; char buf[128];
center.x += RANDOM_FLOAT( -randomRange, randomRange );
center.y += RANDOM_FLOAT( -randomRange, randomRange );
/*
CMBaseEntity *pExplosion = CreateClassPtr((CEnvExplosion *)NULL); // CMBaseEntity::Create( "env_explosion", center, g_vecZero, NULL ); CMBaseEntity *pExplosion = CreateClassPtr((CEnvExplosion *)NULL); // CMBaseEntity::Create( "env_explosion", center, g_vecZero, NULL );
sprintf( buf, "%3d", magnitude ); sprintf( buf, "%3d", magnitude );
kvd.szKeyName = "iMagnitude"; kvd.szKeyName = "iMagnitude";
@@ -1272,6 +1236,538 @@ void SpawnExplosion( Vector center, float randomRange, float time, int magnitude
pExplosion->pev->nextthink = gpGlobals->time + time; pExplosion->pev->nextthink = gpGlobals->time + time;
*/ */
center.x += RANDOM_FLOAT( -randomRange, randomRange );
center.y += RANDOM_FLOAT( -randomRange, randomRange );
// explode.h // explode.h
ExplosionCreate( center, g_vecZero, owner, magnitude, SF_ENVEXPLOSION_NODAMAGE, time ); ExplosionCreate( center, g_vecZero, owner, magnitude, SF_ENVEXPLOSION_NODAMAGE, time );
} }
/*
* The Sven Co-op's monster code was recreated from scratch.
* They do not contain their unique new attacks... YET. -Giegue
* */
//=========================================================
// Baby Gargantua
//=========================================================
const float BABYGARG_ATTACKDIST = 65.0;
#define BABYGARG_FLAME_LENGTH 180
const char *CMBabyGargantua::pBeamAttackSounds[] =
{
"babygarg/gar_flameoff1.wav",
"babygarg/gar_flameon1.wav",
"babygarg/gar_flamerun1.wav",
};
const char *CMBabyGargantua::pFootSounds[] =
{
"babygarg/gar_step1.wav",
"babygarg/gar_step2.wav",
};
const char *CMBabyGargantua::pIdleSounds[] =
{
"babygarg/gar_idle1.wav",
"babygarg/gar_idle2.wav",
"babygarg/gar_idle3.wav",
"babygarg/gar_idle4.wav",
"babygarg/gar_idle5.wav",
};
const char *CMBabyGargantua::pAttackSounds[] =
{
"babygarg/gar_attack1.wav",
"babygarg/gar_attack2.wav",
"babygarg/gar_attack3.wav",
};
const char *CMBabyGargantua::pAlertSounds[] =
{
"babygarg/gar_alert1.wav",
"babygarg/gar_alert2.wav",
"babygarg/gar_alert3.wav",
};
const char *CMBabyGargantua::pPainSounds[] =
{
"babygarg/gar_pain1.wav",
"babygarg/gar_pain2.wav",
"babygarg/gar_pain3.wav",
};
const char *CMBabyGargantua::pStompSounds[] =
{
"babygarg/gar_stomp1.wav",
};
const char *CMBabyGargantua::pBreatheSounds[] =
{
"babygarg/gar_breathe1.wav",
"babygarg/gar_breathe2.wav",
"babygarg/gar_breathe3.wav",
};
const char *CMBabyGargantua::pDieSounds[] =
{
"babygarg/gar_die1.wav",
"babygarg/gar_die2.wav",
};
//=========================================================
// Spawn
//=========================================================
void CMBabyGargantua::Spawn()
{
Precache( );
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/babygarg.mdl"));
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = !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 )
m_MonsterState = MONSTERSTATE_NONE;
MonsterInit();
// Sven Co-op uses a modified gargeye1.spr for the eye and stomp effects.
// To economize precache count, we are going to recycle the normal garg's sprites.
m_pEyeGlow = CMSprite::SpriteCreate( GARG_EYE_SPRITE_NAME, pev->origin, FALSE );
m_pEyeGlow->SetTransparency( kRenderGlow, 255, 255, 255, 0, kRenderFxNoDissipation );
m_pEyeGlow->SetAttachment( edict(), 1 );
EyeOff();
m_seeTime = gpGlobals->time + 5;
m_flameTime = gpGlobals->time + 2;
pev->classname = MAKE_STRING( "monster_babygarg" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Baby Gargantua" );
}
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CMBabyGargantua::Precache()
{
PRECACHE_MODEL("models/babygarg.mdl");
PRECACHE_MODEL( GARG_EYE_SPRITE_NAME );
PRECACHE_MODEL( GARG_BEAM_SPRITE_NAME );
PRECACHE_MODEL( GARG_BEAM_SPRITE2 );
gStompSprite = PRECACHE_MODELINDEX( GARG_STOMP_SPRITE_NAME );
gGargGibModel = PRECACHE_MODELINDEX( GARG_GIB_MODEL );
PRECACHE_SOUND( GARG_STOMP_BUZZ_SOUND );
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 )
{
ALERT( at_aiconsole, "CMBabyGargantua::TraceAttack\n");
if ( !IsAlive() )
{
CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
return;
}
if ( m_painSoundTime < gpGlobals->time )
{
EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM );
m_painSoundTime = gpGlobals->time + RANDOM_FLOAT( 2.5, 4 );
}
// Override Gargantua's specific damage. Baby Garg has no protection from those.
CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
}
int CMBabyGargantua::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
if ( IsAlive() )
{
// Always set
SetConditions( bits_COND_LIGHT_DAMAGE );
}
return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
}
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//=========================================================
void CMBabyGargantua::HandleAnimEvent(MonsterEvent_t *pEvent)
{
switch( pEvent->event )
{
case GARG_AE_SLASH_LEFT:
{
// HACKHACK!!!
edict_t *pHurt = BabyGargCheckTraceHullAttack( BABYGARG_ATTACKDIST + 10.0, gSkillData.babygargDmgSlash, DMG_SLASH );
if (pHurt)
{
if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) )
{
// Slightly lower numbers for babygarg (-20%)
pHurt->v.punchangle.x = -24; // pitch
pHurt->v.punchangle.y = -24; // yaw
pHurt->v.punchangle.z = 24; // roll
//UTIL_MakeVectors(pev->angles); // called by CheckTraceHullAttack
pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_right * 80;
}
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 50 + RANDOM_LONG(0,15) );
}
else // Play a random attack miss sound
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 50 + RANDOM_LONG(0,15) );
Vector forward;
UTIL_MakeVectorsPrivate( pev->angles, forward, NULL, NULL );
}
break;
case GARG_AE_RIGHT_FOOT:
case GARG_AE_LEFT_FOOT:
// babygarg does not shake the screen
EMIT_SOUND_DYN ( edict(), CHAN_BODY, pFootSounds[ RANDOM_LONG(0,ARRAYSIZE(pFootSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) );
break;
case GARG_AE_STOMP:
StompAttack();
m_seeTime = gpGlobals->time + 12;
break;
case GARG_AE_BREATHE:
EMIT_SOUND_DYN ( edict(), CHAN_VOICE, pBreatheSounds[ RANDOM_LONG(0,ARRAYSIZE(pBreatheSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) );
break;
default:
CMBaseMonster::HandleAnimEvent(pEvent);
break;
}
}
//=========================================================
// CheckMeleeAttack1
// Garg swipe attack
//=========================================================
BOOL CMBabyGargantua::CheckMeleeAttack1( float flDot, float flDist )
{
// ALERT(at_aiconsole, "CheckMelee(%f, %f)\n", flDot, flDist);
if (flDot >= 0.7)
{
if (flDist <= BABYGARG_ATTACKDIST)
return TRUE;
}
return FALSE;
}
//=========================================================
// CheckMeleeAttack2
// Flame thrower madness!
//=========================================================
BOOL CMBabyGargantua::CheckMeleeAttack2( float flDot, float flDist )
{
// ALERT(at_aiconsole, "CheckMelee(%f, %f)\n", flDot, flDist);
if ( gpGlobals->time > m_flameTime )
{
if (flDot >= 0.8 && flDist > BABYGARG_ATTACKDIST)
{
if ( flDist <= BABYGARG_FLAME_LENGTH )
return TRUE;
}
}
return FALSE;
}
//=========================================================
// CheckRangeAttack1
// Stomp attack
//=========================================================
BOOL CMBabyGargantua::CheckRangeAttack1( float flDot, float flDist )
{
if ( gpGlobals->time > m_seeTime )
{
if (flDot >= 0.7 && flDist > BABYGARG_ATTACKDIST)
{
return TRUE;
}
}
return FALSE;
}
void CMBabyGargantua::StartTask( Task_t *pTask )
{
switch ( pTask->iTask )
{
case TASK_FLAME_SWEEP:
FlameCreate();
m_flWaitFinished = gpGlobals->time + pTask->flData;
m_flameTime = gpGlobals->time + 6;
m_flameX = 0;
m_flameY = 0;
break;
case TASK_SOUND_ATTACK:
if ( RANDOM_LONG(0,100) < 30 )
EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM );
TaskComplete();
break;
case TASK_DIE:
// no death effect for babygarg, but give it a sound
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pDieSounds[ RANDOM_LONG(0,ARRAYSIZE(pDieSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM );
default:
CMBaseMonster::StartTask( pTask );
break;
}
}
//=========================================================
// RunTask
//=========================================================
void CMBabyGargantua::RunTask( Task_t *pTask )
{
switch ( pTask->iTask )
{
// babygarg does not explode upon death
case TASK_FLAME_SWEEP:
if ( gpGlobals->time > m_flWaitFinished )
{
FlameDestroy();
TaskComplete();
FlameControls( 0, 0 );
SetBoneController( 0, 0 );
SetBoneController( 1, 0 );
}
else
{
BOOL cancel = FALSE;
Vector angles = g_vecZero;
FlameUpdate();
CMBaseEntity *pEnemy = CMBaseEntity::Instance( m_hEnemy.Get() );
if ( pEnemy )
{
Vector org = pev->origin;
org.z += 64;
Vector dir = pEnemy->BodyTarget(org) - org;
angles = UTIL_VecToAngles( dir );
angles.x = -angles.x;
angles.y -= pev->angles.y;
if ( dir.Length() > 400 )
cancel = TRUE;
}
if ( fabs(angles.y) > 60 )
cancel = TRUE;
if ( cancel )
{
m_flWaitFinished -= 0.5;
m_flameTime -= 0.5;
}
// FlameControls( angles.x + 2 * sin(gpGlobals->time*8), angles.y + 28 * sin(gpGlobals->time*8.5) );
FlameControls( angles.x, angles.y );
}
break;
default:
CMBaseMonster::RunTask( pTask );
break;
}
}
void CMBabyGargantua::StompAttack( void )
{
TraceResult trace;
UTIL_MakeVectors( pev->angles );
Vector vecStart = pev->origin + Vector(0,0,60) + 35 * gpGlobals->v_forward;
Vector vecAim = ShootAtEnemy( vecStart );
Vector vecEnd = (vecAim * 1024) + vecStart;
UTIL_TraceLine( vecStart, vecEnd, ignore_monsters, edict(), &trace );
CStomp::StompCreate( vecStart, trace.vecEndPos, 0, gSkillData.babygargDmgStomp );
UTIL_ScreenShake( pev->origin, 9.6, 80.0, 1.8, 800 ); // -20% "power" to the babygarg's stomp
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pStompSounds[ RANDOM_LONG(0,ARRAYSIZE(pStompSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) );
UTIL_TraceLine( pev->origin, pev->origin - Vector(0,0,20), ignore_monsters, edict(), &trace );
if ( trace.flFraction < 1.0 )
UTIL_DecalTrace( &trace, DECAL_GARGSTOMP1 );
}
void CMBabyGargantua::FlameCreate( void )
{
int i;
Vector posGun, angleGun;
TraceResult trace;
UTIL_MakeVectors( pev->angles );
for ( i = 0; i < 4; i++ )
{
if ( i < 2 )
m_pFlame[i] = CMBeam::BeamCreate( GARG_BEAM_SPRITE_NAME, 120 );
else
m_pFlame[i] = CMBeam::BeamCreate( GARG_BEAM_SPRITE2, 70 );
if ( m_pFlame[i] )
{
int attach = i%2;
// attachment is 0 based in GetAttachment
GetAttachment( attach+1, posGun, angleGun );
Vector vecEnd = (gpGlobals->v_forward * BABYGARG_FLAME_LENGTH) + posGun;
UTIL_TraceLine( posGun, vecEnd, dont_ignore_monsters, edict(), &trace );
m_pFlame[i]->PointEntInit( trace.vecEndPos, entindex() );
if ( i < 2 )
m_pFlame[i]->SetColor( 255, 130, 90 );
else
m_pFlame[i]->SetColor( 0, 120, 255 );
m_pFlame[i]->SetBrightness( 190 );
m_pFlame[i]->SetFlags( BEAM_FSHADEIN );
m_pFlame[i]->SetScrollRate( 20 );
// attachment is 1 based in SetEndAttachment
m_pFlame[i]->SetEndAttachment( attach + 2 );
}
}
EMIT_SOUND_DYN ( edict(), CHAN_BODY, pBeamAttackSounds[ 1 ], 1.0, ATTN_NORM, 0, PITCH_NORM );
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pBeamAttackSounds[ 2 ], 1.0, ATTN_NORM, 0, PITCH_NORM );
}
void CMBabyGargantua::FlameUpdate( void )
{
int i;
static float offset[2] = { 60, -60 };
TraceResult trace;
Vector vecStart, angleGun;
BOOL streaks = FALSE;
for ( i = 0; i < 2; i++ )
{
if ( m_pFlame[i] )
{
Vector vecAim = pev->angles;
vecAim.x += m_flameX;
vecAim.y += m_flameY;
UTIL_MakeVectors( vecAim );
GetAttachment( i+1, vecStart, angleGun );
Vector vecEnd = vecStart + (gpGlobals->v_forward * BABYGARG_FLAME_LENGTH); // - offset[i] * gpGlobals->v_right;
UTIL_TraceLine( vecStart, vecEnd, dont_ignore_monsters, edict(), &trace );
m_pFlame[i]->SetStartPos( trace.vecEndPos );
m_pFlame[i+2]->SetStartPos( (vecStart * 0.6) + (trace.vecEndPos * 0.4) );
if ( trace.flFraction != 1.0 && gpGlobals->time > m_streakTime )
{
StreakSplash( trace.vecEndPos, trace.vecPlaneNormal, 6, 20, 50, 400 );
streaks = TRUE;
UTIL_DecalTrace( &trace, DECAL_SMALLSCORCH1 + RANDOM_LONG(0,2) );
}
// RadiusDamage( trace.vecEndPos, pev, pev, gSkillData.babygargDmgFire, Classify(), DMG_BURN );
FlameDamage( vecStart, trace.vecEndPos, pev, pev, gSkillData.babygargDmgFire, Classify(), DMG_BURN );
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
WRITE_BYTE( TE_ELIGHT );
WRITE_SHORT( entindex( ) + 0x1000 * (i + 2) ); // entity, attachment
WRITE_COORD( vecStart.x ); // origin
WRITE_COORD( vecStart.y );
WRITE_COORD( vecStart.z );
WRITE_COORD( RANDOM_FLOAT( 32, 48 ) ); // radius
WRITE_BYTE( 255 ); // R
WRITE_BYTE( 255 ); // G
WRITE_BYTE( 255 ); // B
WRITE_BYTE( 2 ); // life * 10
WRITE_COORD( 0 ); // decay
MESSAGE_END();
}
}
if ( streaks )
m_streakTime = gpGlobals->time;
}
void CMBabyGargantua::FlameDestroy( void )
{
int i;
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pBeamAttackSounds[ 0 ], 1.0, ATTN_NORM, 0, PITCH_NORM ); // sound must stop.
for ( i = 0; i < 4; i++ )
{
if ( m_pFlame[i] )
{
UTIL_Remove( m_pFlame[i]->edict() );
m_pFlame[i] = NULL;
}
}
}
//=========================================================
// CheckTraceHullAttack - expects a length to trace, amount
// of damage to do, and damage type. Returns a pointer to
// the damaged entity in case the monster wishes to do
// other stuff to the victim (punchangle, etc)
// Used for many contact-range melee attacks. Bites, claws, etc.
// Overridden for Gargantua because his swing starts lower as
// a percentage of his height (otherwise he swings over the
// players head)
// Also overriden for Baby Gargantua to prevent players from
// dodging the swing attacks by crouching.
//=========================================================
edict_t *CMBabyGargantua::BabyGargCheckTraceHullAttack(float flDist, int iDamage, int iDmgType)
{
TraceResult tr;
UTIL_MakeVectors( pev->angles );
Vector vecStart = pev->origin;
vecStart.z += 32;
Vector vecEnd = vecStart + (gpGlobals->v_forward * flDist) - (gpGlobals->v_up * flDist * 0.3);
UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr );
if ( tr.pHit )
{
if ( iDamage > 0 )
{
if ( UTIL_IsPlayer( tr.pHit ) )
UTIL_TakeDamage( tr.pHit, pev, pev, iDamage, iDmgType );
else if ( tr.pHit->v.euser4 != NULL )
{
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit));
pMonster->TakeDamage( pev, pev, iDamage, iDmgType );
}
else
UTIL_TakeDamageExternal( tr.pHit, pev, pev, iDamage, iDmgType );
}
return tr.pHit;
}
return NULL;
}

View File

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

873
src/dlls/gonome.cpp Normal file
View File

@@ -0,0 +1,873 @@
// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository!
/***
*
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* Use, distribution, and modification of this source code and/or resulting
* object code is restricted to non-commercial enhancements to products from
* Valve LLC. All other use, distribution, or modification is prohibited
* without written permission from Valve LLC.
*
****/
//=========================================================
// Gonome.cpp
//=========================================================
#include "extdll.h"
#include "util.h"
#include "cmbase.h"
#include "cmbasemonster.h"
#include "monsters.h"
#include "schedule.h"
#include "animation.h"
#include "decals.h"
#include "nodes.h"
#define GONOME_MELEE_ATTACK_RADIUS 70
enum
{
TASK_GONOME_GET_PATH_TO_ENEMY_CORPSE = LAST_COMMON_TASK + 1
};
//=========================================================
// Monster's Anim Events Go Here
//=========================================================
#define GONOME_AE_SLASH_RIGHT ( 1 )
#define GONOME_AE_SLASH_LEFT ( 2 )
#define GONOME_AE_SPIT ( 3 )
#define GONOME_AE_THROW ( 4 )
#define GONOME_AE_BITE1 ( 19 )
#define GONOME_AE_BITE2 ( 20 )
#define GONOME_AE_BITE3 ( 21 )
#define GONOME_AE_BITE4 ( 22 )
#define GONOME_SCRIPT_EVENT_SOUND ( 1011 )
void CGonomeGuts :: Spawn( void )
{
pev->movetype = MOVETYPE_FLY;
pev->classname = MAKE_STRING( "gonomeguts" );
pev->solid = SOLID_BBOX;
pev->rendermode = kRenderTransAlpha;
pev->renderamt = 255;
SET_MODEL( ENT( pev ), "sprites/bigspit.spr" );
pev->frame = 0;
pev->scale = 0.5;
pev->rendercolor.x = 255;
UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) );
m_maxFrame = (float)MODEL_FRAMES( pev->modelindex ) - 1;
}
void CGonomeGuts :: Animate( void )
{
pev->nextthink = gpGlobals->time + 0.1;
if ( pev->frame++ )
{
if ( pev->frame > m_maxFrame )
{
pev->frame = 0;
}
}
}
edict_t *CGonomeGuts :: Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity )
{
CGonomeGuts *pSpit = CreateClassPtr( (CGonomeGuts *)NULL );
if (pSpit == NULL)
return NULL;
pSpit->Spawn();
UTIL_SetOrigin( pSpit->pev, vecStart );
pSpit->pev->velocity = vecVelocity;
pSpit->pev->owner = ENT(pevOwner);
pSpit->SetThink ( &CGonomeGuts::Animate );
pSpit->pev->nextthink = gpGlobals->time + 0.1;
pSpit->SetTouch ( &CGonomeGuts::GutsTouch );
return pSpit->edict();
}
void CGonomeGuts :: GutsTouch( edict_t *pOther )
{
TraceResult tr;
int iPitch;
// splat sound
iPitch = RANDOM_FLOAT( 90, 110 );
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "bullchicken/bc_acid1.wav", 1, ATTN_NORM, 0, iPitch );
switch( RANDOM_LONG( 0, 1 ) )
{
case 0:
EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "bullchicken/bc_spithit1.wav", 1, ATTN_NORM, 0, iPitch );
break;
case 1:
EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "bullchicken/bc_spithit2.wav", 1, ATTN_NORM, 0, iPitch );
break;
}
if( !pOther->v.takedamage )
{
// make a splat on the wall
UTIL_TraceLine( pev->origin, pev->origin + pev->velocity * 10, dont_ignore_monsters, ENT( pev ), &tr );
UTIL_BloodDecalTrace( &tr, BLOOD_COLOR_RED );
UTIL_BloodDrips( tr.vecEndPos, UTIL_RandomBloodVector(), BLOOD_COLOR_RED, 35 );
}
else
{
if (UTIL_IsPlayer(pOther))
UTIL_TakeDamage( pOther, pev, VARS(pev->owner), gSkillData.gonomeDmgGuts, DMG_GENERIC );
else if (pOther->v.euser4 != NULL)
{
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
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 );
pev->nextthink = gpGlobals->time;
}
const char* CMGonome::pPainSounds[] = {
"gonome/gonome_pain1.wav",
"gonome/gonome_pain2.wav",
"gonome/gonome_pain3.wav",
"gonome/gonome_pain4.wav"
};
const char* CMGonome::pIdleSounds[] = {
"gonome/gonome_idle1.wav",
"gonome/gonome_idle2.wav",
"gonome/gonome_idle3.wav"
};
const char* CMGonome::pDeathSounds[] = {
"gonome/gonome_death2.wav",
"gonome/gonome_death3.wav",
"gonome/gonome_death4.wav"
};
const char* CMGonome::pAttackHitSounds[] =
{
"zombie/claw_strike1.wav",
"zombie/claw_strike2.wav",
"zombie/claw_strike3.wav",
};
const char* CMGonome::pAttackMissSounds[] =
{
"zombie/claw_miss1.wav",
"zombie/claw_miss2.wav",
};
void CMGonome::Killed(entvars_t *pevAttacker, int iGib)
{
ClearGuts();
UnlockPlayer();
CMBaseMonster::Killed(pevAttacker, iGib);
}
void CMGonome::UnlockPlayer()
{
if (m_fPlayerLocked)
{
edict_t *player = 0;
if (m_lockedPlayer != 0 && UTIL_IsPlayer(m_lockedPlayer))
player = m_lockedPlayer;
else // if ehandle is empty for some reason just unlock the first player
player = UTIL_FindEntityByClassname(0, "player");
if (player)
player->v.flags &= ~FL_FROZEN;
m_lockedPlayer = 0;
m_fPlayerLocked = FALSE;
}
}
CGonomeGuts* CMGonome::GetGonomeGuts(entvars_t *pevOwner, const Vector &pos)
{
if (m_pGonomeGuts)
return m_pGonomeGuts;
edict_t *pEdict = CGonomeGuts::Shoot( pevOwner, g_vecZero, g_vecZero );
CGonomeGuts *pGuts = GetClassPtr((CGonomeGuts*)VARS(pEdict));
pGuts->Spawn();
UTIL_SetOrigin( pGuts->pev, pos );
m_pGonomeGuts = pGuts;
return m_pGonomeGuts;
}
void CMGonome::ClearGuts()
{
if (m_pGonomeGuts)
{
UTIL_Remove( m_pGonomeGuts->edict() );
m_pGonomeGuts = 0;
}
}
void CMGonome::PainSound( void )
{
int pitch = 95 + RANDOM_LONG( 0, 9 );
if( RANDOM_LONG( 0, 5 ) < 2 )
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), 1.0, ATTN_NORM, 0, pitch );
}
void CMGonome::DeathSound( void )
{
int pitch = 95 + RANDOM_LONG( 0, 9 );
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), 1.0, ATTN_NORM, 0, pitch );
}
void CMGonome::IdleSound( void )
{
int pitch = 95 + RANDOM_LONG( 0, 9 );
// Play a random idle sound
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), 1.0, ATTN_NORM, 0, pitch );
}
void CMGonome::AlertSound( void )
{
const int iPitch = RANDOM_LONG(0, 9) + 95;
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), 1, ATTN_NORM, 0, iPitch);
}
void CMGonome::SetActivity( Activity NewActivity )
{
Activity OldActivity = m_Activity;
int iSequence = ACTIVITY_NOT_AVAILABLE;
if (NewActivity != ACT_RANGE_ATTACK1)
{
ClearGuts();
}
if (NewActivity == ACT_MELEE_ATTACK1 && m_hEnemy != 0)
{
// special melee animations
if ((pev->origin - m_hEnemy->v.origin).Length2D() >= 48 )
{
m_meleeAttack2 = false;
iSequence = LookupSequence("attack1");
}
else
{
m_meleeAttack2 = true;
iSequence = LookupSequence("attack2");
}
}
else
{
UnlockPlayer();
if (NewActivity == ACT_RUN && m_hEnemy != 0)
{
// special run animations
if ((pev->origin - m_hEnemy->v.origin).Length2D() <= 512 )
{
iSequence = LookupSequence("runshort");
}
else
{
iSequence = LookupSequence("runlong");
}
}
else
{
iSequence = LookupActivity(NewActivity);
}
}
m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present
// In case someone calls this with something other than the ideal activity
m_IdealActivity = m_Activity;
// Set to the desired anim, or default anim if the desired is not present
if( iSequence > ACTIVITY_NOT_AVAILABLE )
{
if( pev->sequence != iSequence || !m_fSequenceLoops )
{
// don't reset frame between walk and run
if( !( OldActivity == ACT_WALK || OldActivity == ACT_RUN ) || !( NewActivity == ACT_WALK || NewActivity == ACT_RUN ) )
pev->frame = 0;
}
pev->sequence = iSequence; // Set to the reset anim (if it's there)
ResetSequenceInfo();
SetYawSpeed();
}
else
{
// Not available try to get default anim
ALERT( at_aiconsole, "%s has no sequence for act:%d\n", STRING( pev->classname ), NewActivity );
pev->sequence = 0; // Set to the reset anim (if it's there)
}
}
//=========================================================
// Classify - indicates this monster's place in the
// relationship table.
//=========================================================
int CMGonome::Classify(void)
{
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_ALIEN_MONSTER;
}
//=========================================================
// TakeDamage - overridden for gonome so we can keep track
// of how much time has passed since it was last injured
//=========================================================
int CMGonome::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType)
{
// HACK HACK -- until we fix this.
if( IsAlive() )
PainSound();
return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
}
//=========================================================
// CheckRangeAttack1
//=========================================================
BOOL CMGonome::CheckRangeAttack1(float flDot, float flDist)
{
if (flDist < 256)
return FALSE;
if (IsMoving() && flDist >= 512)
{
// squid will far too far behind if he stops running to spit at this distance from the enemy.
return FALSE;
}
if (flDist > 64 && flDist <= 784 && flDot >= 0.5 && gpGlobals->time >= m_flNextThrowTime)
{
if (m_hEnemy != 0)
{
if (fabs(pev->origin.z - m_hEnemy->v.origin.z) > 256)
{
// don't try to spit at someone up really high or down really low.
return FALSE;
}
}
if (IsMoving())
{
// don't spit again for a long time, resume chasing enemy.
m_flNextThrowTime = gpGlobals->time + 5;
}
else
{
// not moving, so spit again pretty soon.
m_flNextThrowTime = gpGlobals->time + 0.5;
}
return TRUE;
}
return FALSE;
}
//=========================================================
// CheckMeleeAttack2 - both gonome's melee attacks are ACT_MELEE_ATTACK1
//=========================================================
BOOL CMGonome::CheckMeleeAttack2(float flDot, float flDist)
{
return FALSE;
}
//=========================================================
// SetYawSpeed - allows each sequence to have a different
// turn rate associated with it.
//=========================================================
void CMGonome::SetYawSpeed( void )
{
pev->yaw_speed = 120;
}
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//=========================================================
void CMGonome::HandleAnimEvent(MonsterEvent_t *pEvent)
{
switch (pEvent->event)
{
case GONOME_SCRIPT_EVENT_SOUND:
if (m_Activity != ACT_MELEE_ATTACK1)
EMIT_SOUND(ENT(pev), CHAN_BODY, pEvent->options, 1, ATTN_NORM);
break;
case GONOME_AE_SPIT:
{
Vector vecArmPos, vecArmAng;
GetAttachment(0, vecArmPos, vecArmAng);
if (GetGonomeGuts(pev, vecArmPos))
{
m_pGonomeGuts->pev->skin = entindex();
m_pGonomeGuts->pev->body = 1;
m_pGonomeGuts->pev->aiment = ENT(pev);
m_pGonomeGuts->pev->movetype = MOVETYPE_FOLLOW;
}
UTIL_BloodDrips( vecArmPos, UTIL_RandomBloodVector(), BLOOD_COLOR_RED, 35 );
}
break;
case GONOME_AE_THROW:
{
UTIL_MakeVectors(pev->angles);
Vector vecArmPos, vecArmAng;
GetAttachment(0, vecArmPos, vecArmAng);
if (GetGonomeGuts(pev, vecArmPos))
{
Vector vecSpitDir;
Vector vecEnemyPosition;
if (m_hEnemy != 0)
vecEnemyPosition = (m_hEnemy->v.origin + m_hEnemy->v.view_ofs);
else
vecEnemyPosition = m_vecEnemyLKP;
vecSpitDir = (vecEnemyPosition - vecArmPos).Normalize();
vecSpitDir.x += RANDOM_FLOAT(-0.05, 0.05);
vecSpitDir.y += RANDOM_FLOAT(-0.05, 0.05);
vecSpitDir.z += RANDOM_FLOAT(-0.05, 0);
m_pGonomeGuts->pev->body = 0;
m_pGonomeGuts->pev->skin = 0;
m_pGonomeGuts->pev->owner = ENT( pev );
m_pGonomeGuts->pev->aiment = 0;
m_pGonomeGuts->pev->movetype = MOVETYPE_FLY;
m_pGonomeGuts->pev->velocity = vecSpitDir * 900;
m_pGonomeGuts->SetThink( &CGonomeGuts::Animate );
m_pGonomeGuts->pev->nextthink = gpGlobals->time + 0.1;
UTIL_SetOrigin(m_pGonomeGuts->pev, vecArmPos);
m_pGonomeGuts = 0;
}
UTIL_BloodDrips( vecArmPos, UTIL_RandomBloodVector(), BLOOD_COLOR_RED, 35 );
}
break;
case GONOME_AE_SLASH_LEFT:
{
edict_t *pHurt = CheckTraceHullAttack(GONOME_MELEE_ATTACK_RADIUS, gSkillData.gonomeDmgOneSlash, DMG_SLASH);
if (pHurt)
{
if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT))
{
pHurt->v.punchangle.z = 9;
pHurt->v.punchangle.x = 5;
pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * 25;
}
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackHitSounds), 1, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5));
}
else
{
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackMissSounds), 1, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5));
}
}
break;
case GONOME_AE_SLASH_RIGHT:
{
edict_t *pHurt = CheckTraceHullAttack(GONOME_MELEE_ATTACK_RADIUS, gSkillData.gonomeDmgOneSlash, DMG_SLASH);
if (pHurt)
{
if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT))
{
pHurt->v.punchangle.z = -9;
pHurt->v.punchangle.x = 5;
pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * -25;
}
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackHitSounds), 1, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5));
}
else
{
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackMissSounds), 1, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5));
}
}
break;
case GONOME_AE_BITE1:
case GONOME_AE_BITE2:
case GONOME_AE_BITE3:
case GONOME_AE_BITE4:
{
int iPitch;
edict_t *pHurt = CheckTraceHullAttack(GONOME_MELEE_ATTACK_RADIUS, gSkillData.gonomeDmgOneBite, DMG_SLASH);
if (pHurt)
{
// croonchy bite sound
iPitch = RANDOM_FLOAT(90, 110);
switch (RANDOM_LONG(0, 1))
{
case 0:
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "bullchicken/bc_bite2.wav", 1, ATTN_NORM, 0, iPitch);
break;
case 1:
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "bullchicken/bc_bite3.wav", 1, ATTN_NORM, 0, iPitch);
break;
}
if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT))
{
if (pEvent->event == GONOME_AE_BITE4)
{
pHurt->v.punchangle.x = 15;
pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_forward * 75;
}
else
{
pHurt->v.punchangle.x = 9;
pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_forward * 25;
}
}
// lock player
if (pEvent->event == GONOME_AE_BITE4)
{
UnlockPlayer();
}
else if (UTIL_IsPlayer( pHurt ) && UTIL_IsAlive( pHurt ))
{
if (!m_fPlayerLocked)
{
edict_t *player = pHurt;
player->v.flags |= FL_FROZEN;
m_lockedPlayer = player;
m_fPlayerLocked = TRUE;
}
}
}
}
break;
default:
CMBaseMonster::HandleAnimEvent(pEvent);
}
}
#define GONOME_FLINCH_DELAY 2
int CMGonome::IgnoreConditions( void )
{
int iIgnore = CMBaseMonster::IgnoreConditions();
if( m_Activity == ACT_MELEE_ATTACK1 )
{
if( m_flNextFlinch >= gpGlobals->time )
iIgnore |= ( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE );
}
if( ( m_Activity == ACT_SMALL_FLINCH ) || ( m_Activity == ACT_BIG_FLINCH ) )
{
if( m_flNextFlinch < gpGlobals->time )
m_flNextFlinch = gpGlobals->time + GONOME_FLINCH_DELAY;
}
return iIgnore;
}
//=========================================================
// Spawn
//=========================================================
void CMGonome::Spawn()
{
Precache();
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/gonome.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = !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 )
m_MonsterState = MONSTERSTATE_NONE;
m_flNextThrowTime = gpGlobals->time;
MonsterInit();
pev->classname = MAKE_STRING( "monster_gonome" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Gonome" );
}
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CMGonome::Precache()
{
PRECACHE_MODEL("models/gonome.mdl");
PRECACHE_MODEL("sprites/bigspit.spr");// spit projectile.
PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event
PRECACHE_SOUND("gonome/gonome_eat.wav");
PRECACHE_SOUND("gonome/gonome_jumpattack.wav");
PRECACHE_SOUND("gonome/gonome_melee1.wav");
PRECACHE_SOUND("gonome/gonome_melee2.wav");
PRECACHE_SOUND_ARRAY(pIdleSounds);
PRECACHE_SOUND_ARRAY(pPainSounds);
PRECACHE_SOUND_ARRAY(pDeathSounds);
PRECACHE_SOUND("gonome/gonome_run.wav");
PRECACHE_SOUND("bullchicken/bc_acid1.wav");
PRECACHE_SOUND("bullchicken/bc_bite2.wav");
PRECACHE_SOUND("bullchicken/bc_bite3.wav");
PRECACHE_SOUND("bullchicken/bc_spithit1.wav");
PRECACHE_SOUND("bullchicken/bc_spithit2.wav");
}
//=========================================================
// GetSchedule
//=========================================================
Schedule_t *CMGonome::GetSchedule( void )
{
switch( m_MonsterState )
{
case MONSTERSTATE_COMBAT:
{
// dead enemy
if( HasConditions( bits_COND_ENEMY_DEAD ) )
{
// call base class, all code to handle dead enemies is centralized there.
return CMBaseMonster::GetSchedule();
}
if( HasConditions( bits_COND_NEW_ENEMY ) )
{
return GetScheduleOfType( SCHED_WAKE_ANGRY );
}
if( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) )
{
return GetScheduleOfType( SCHED_RANGE_ATTACK1 );
}
if( HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) )
{
return GetScheduleOfType( SCHED_MELEE_ATTACK1 );
}
if( HasConditions( bits_COND_CAN_MELEE_ATTACK2 ) )
{
return GetScheduleOfType( SCHED_MELEE_ATTACK2 );
}
return GetScheduleOfType( SCHED_CHASE_ENEMY );
break;
}
default:
break;
}
return CMBaseMonster::GetSchedule();
}
// primary range attack
Task_t tlGonomeRangeAttack1[] =
{
{ TASK_STOP_MOVING, 0 },
{ TASK_FACE_IDEAL, (float)0 },
{ TASK_RANGE_ATTACK1, (float)0 },
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
};
Schedule_t slGonomeRangeAttack1[] =
{
{
tlGonomeRangeAttack1,
ARRAYSIZE( tlGonomeRangeAttack1 ),
bits_COND_NEW_ENEMY |
bits_COND_ENEMY_DEAD |
bits_COND_HEAVY_DAMAGE |
bits_COND_ENEMY_OCCLUDED |
bits_COND_NO_AMMO_LOADED,
0,
"Gonome Range Attack1"
},
};
// Chase enemy schedule
Task_t tlGonomeChaseEnemy1[] =
{
{ TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 },// !!!OEM - this will stop nasty squid oscillation.
{ TASK_GET_PATH_TO_ENEMY, (float)0 },
{ TASK_RUN_PATH, (float)0 },
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
};
Schedule_t slGonomeChaseEnemy[] =
{
{
tlGonomeChaseEnemy1,
ARRAYSIZE( tlGonomeChaseEnemy1 ),
bits_COND_NEW_ENEMY |
bits_COND_ENEMY_DEAD |
bits_COND_SMELL_FOOD |
bits_COND_CAN_RANGE_ATTACK1 |
bits_COND_CAN_MELEE_ATTACK1 |
bits_COND_CAN_MELEE_ATTACK2 |
bits_COND_TASK_FAILED,
0,
"Gonome Chase Enemy"
},
};
// victory dance (eating body)
Task_t tlGonomeVictoryDance[] =
{
{ TASK_STOP_MOVING, (float)0 },
{ TASK_WAIT, (float)0.1 },
{ TASK_GONOME_GET_PATH_TO_ENEMY_CORPSE, (float)0 },
{ TASK_WALK_PATH, (float)0 },
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
{ TASK_FACE_ENEMY, (float)0 },
{ TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE },
{ TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE },
{ TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }
};
Schedule_t slGonomeVictoryDance[] =
{
{
tlGonomeVictoryDance,
ARRAYSIZE( tlGonomeVictoryDance ),
bits_COND_NEW_ENEMY |
bits_COND_LIGHT_DAMAGE |
bits_COND_HEAVY_DAMAGE,
0,
"GonomeVictoryDance"
},
};
DEFINE_CUSTOM_SCHEDULES( CMGonome )
{
slGonomeRangeAttack1,
slGonomeChaseEnemy,
slGonomeVictoryDance,
};
IMPLEMENT_CUSTOM_SCHEDULES( CMGonome, CMBaseMonster )
Schedule_t* CMGonome::GetScheduleOfType(int Type)
{
switch ( Type )
{
case SCHED_RANGE_ATTACK1:
return &slGonomeRangeAttack1[0];
break;
case SCHED_CHASE_ENEMY:
return &slGonomeChaseEnemy[0];
break;
case SCHED_VICTORY_DANCE:
return &slGonomeVictoryDance[0];
break;
default:
break;
}
return CMBaseMonster::GetScheduleOfType(Type);
}
void CMGonome::RunTask(Task_t *pTask)
{
// HACK to stop Gonome from playing attack sound twice
if (pTask->iTask == TASK_MELEE_ATTACK1)
{
if (!m_playedAttackSound)
{
const char* sample = NULL;
if (m_meleeAttack2)
{
sample = "gonome/gonome_melee2.wav";
}
else
{
sample = "gonome/gonome_melee1.wav";
}
EMIT_SOUND(ENT(pev), CHAN_BODY, sample, 1, ATTN_NORM);
m_playedAttackSound = true;
}
}
else
{
m_playedAttackSound = false;
}
CMBaseMonster::RunTask(pTask);
}
//=========================================================
// Start task - selects the correct activity and performs
// any necessary calculations to start the next task on the
// schedule.
//=========================================================
void CMGonome::StartTask(Task_t *pTask)
{
m_iTaskStatus = TASKSTATUS_RUNNING;
switch (pTask->iTask)
{
case TASK_GONOME_GET_PATH_TO_ENEMY_CORPSE:
{
UTIL_MakeVectors( pev->angles );
if( BuildRoute( m_vecEnemyLKP - gpGlobals->v_forward * 40, bits_MF_TO_LOCATION, NULL ) )
{
TaskComplete();
}
else
{
ALERT( at_aiconsole, "GonomeGetPathToEnemyCorpse failed!!\n" );
TaskFail();
}
}
break;
default:
CMBaseMonster::StartTask(pTask);
break;
}
}

View File

@@ -88,6 +88,11 @@ int CMHAssassin :: ISoundMask ( void)
//========================================================= //=========================================================
int CMHAssassin :: Classify ( void ) int CMHAssassin :: Classify ( void )
{ {
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_HUMAN_MILITARY; return CLASS_HUMAN_MILITARY;
} }
@@ -210,12 +215,12 @@ void CMHAssassin :: Spawn()
{ {
Precache( ); Precache( );
SET_MODEL(ENT(pev), "models/hassassin.mdl"); SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/hassassin.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX; pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP; pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_RED; m_bloodColor = !m_bloodColor ? BLOOD_COLOR_RED : m_bloodColor;
pev->effects = 0; pev->effects = 0;
pev->health = gSkillData.hassassinHealth; pev->health = gSkillData.hassassinHealth;
m_flFieldOfView = VIEW_FIELD_WIDE; // indicates the width of this monster's forward view cone ( as a dotproduct result ) m_flFieldOfView = VIEW_FIELD_WIDE; // indicates the width of this monster's forward view cone ( as a dotproduct result )
@@ -230,6 +235,13 @@ void CMHAssassin :: Spawn()
pev->rendermode = kRenderTransTexture; pev->rendermode = kRenderTransTexture;
MonsterInit(); MonsterInit();
pev->classname = MAKE_STRING( "monster_human_assassin" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Female Assassin" );
}
} }
//========================================================= //=========================================================
@@ -244,7 +256,7 @@ void CMHAssassin :: Precache()
PRECACHE_SOUND("debris/beamstart1.wav"); 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

@@ -119,6 +119,11 @@ const char *CMHeadCrab::pBiteSounds[] =
//========================================================= //=========================================================
int CMHeadCrab :: Classify ( void ) int CMHeadCrab :: Classify ( void )
{ {
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_ALIEN_PREY; return CLASS_ALIEN_PREY;
} }
@@ -222,7 +227,7 @@ void CMHeadCrab :: HandleAnimEvent( MonsterEvent_t *pEvent )
int iSound = RANDOM_LONG(0,2); int iSound = RANDOM_LONG(0,2);
if ( iSound != 0 ) if ( iSound != 0 )
EMIT_SOUND_DYN( edict(), CHAN_VOICE, pAttackSounds[iSound], GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); EMIT_SOUND_DYN( edict(), CHAN_VOICE, pAttackSounds[iSound], GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
pev->velocity = vecJumpDir; pev->velocity = vecJumpDir;
m_flNextAttack = gpGlobals->time + 2; m_flNextAttack = gpGlobals->time + 2;
@@ -242,12 +247,12 @@ void CMHeadCrab :: Spawn()
{ {
Precache( ); Precache( );
SET_MODEL(ENT(pev), "models/headcrab.mdl"); SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/headcrab.mdl"));
UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24)); UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24));
pev->solid = SOLID_SLIDEBOX; pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP; pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN; m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->effects = 0; pev->effects = 0;
pev->health = gSkillData.headcrabHealth; pev->health = gSkillData.headcrabHealth;
pev->view_ofs = Vector ( 0, 0, 20 );// position of the eyes relative to monster's origin. pev->view_ofs = Vector ( 0, 0, 20 );// position of the eyes relative to monster's origin.
@@ -256,6 +261,13 @@ void CMHeadCrab :: Spawn()
m_MonsterState = MONSTERSTATE_NONE; m_MonsterState = MONSTERSTATE_NONE;
MonsterInit(); MonsterInit();
pev->classname = MAKE_STRING( "monster_headcrab" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Head Crab" );
}
} }
//========================================================= //=========================================================
@@ -313,7 +325,7 @@ void CMHeadCrab :: LeapTouch ( edict_t *pOther )
// Don't hit if back on ground // Don't hit if back on ground
if ( !FBitSet( pev->flags, FL_ONGROUND ) ) if ( !FBitSet( pev->flags, FL_ONGROUND ) )
{ {
EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBiteSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBiteSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
if (UTIL_IsPlayer(pOther)) if (UTIL_IsPlayer(pOther))
UTIL_TakeDamage( pOther, pev, pev, GetDamageAmount(), DMG_SLASH ); UTIL_TakeDamage( pOther, pev, pev, GetDamageAmount(), DMG_SLASH );
@@ -347,7 +359,7 @@ void CMHeadCrab :: StartTask ( Task_t *pTask )
{ {
case TASK_RANGE_ATTACK1: case TASK_RANGE_ATTACK1:
{ {
EMIT_SOUND_DYN( edict(), CHAN_WEAPON, pAttackSounds[0], GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); EMIT_SOUND_DYN( edict(), CHAN_WEAPON, pAttackSounds[0], GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
m_IdealActivity = ACT_RANGE_ATTACK1; m_IdealActivity = ACT_RANGE_ATTACK1;
SetTouch ( &CMHeadCrab::LeapTouch ); SetTouch ( &CMHeadCrab::LeapTouch );
break; break;
@@ -403,7 +415,7 @@ int CMHeadCrab :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, f
#define CRAB_ATTN_IDLE (float)1.5 #define CRAB_ATTN_IDLE (float)1.5
void CMHeadCrab :: IdleSound ( void ) void CMHeadCrab :: IdleSound ( void )
{ {
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
} }
//========================================================= //=========================================================
@@ -411,7 +423,7 @@ void CMHeadCrab :: IdleSound ( void )
//========================================================= //=========================================================
void CMHeadCrab :: AlertSound ( void ) void CMHeadCrab :: AlertSound ( void )
{ {
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
} }
//========================================================= //=========================================================
@@ -419,7 +431,7 @@ void CMHeadCrab :: AlertSound ( void )
//========================================================= //=========================================================
void CMHeadCrab :: PainSound ( void ) void CMHeadCrab :: PainSound ( void )
{ {
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
} }
//========================================================= //=========================================================
@@ -427,7 +439,7 @@ void CMHeadCrab :: PainSound ( void )
//========================================================= //=========================================================
void CMHeadCrab :: DeathSound ( void ) void CMHeadCrab :: DeathSound ( void )
{ {
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() ); EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
} }
Schedule_t* CMHeadCrab :: GetScheduleOfType ( int Type ) Schedule_t* CMHeadCrab :: GetScheduleOfType ( int Type )
@@ -449,7 +461,7 @@ Schedule_t* CMHeadCrab :: GetScheduleOfType ( int Type )
void CMBabyCrab :: Spawn( void ) void CMBabyCrab :: Spawn( void )
{ {
CMHeadCrab::Spawn(); CMHeadCrab::Spawn();
SET_MODEL(ENT(pev), "models/baby_headcrab.mdl"); SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/baby_headcrab.mdl"));
pev->rendermode = kRenderTransTexture; pev->rendermode = kRenderTransTexture;
pev->renderamt = 192; pev->renderamt = 192;
UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24)); UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24));

View File

@@ -163,7 +163,7 @@ void CMHGrunt :: SpeakSentence( void )
if (FOkToSpeak()) 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(); JustSpoke();
} }
} }
@@ -174,12 +174,15 @@ void CMHGrunt :: SpeakSentence( void )
//========================================================= //=========================================================
int CMHGrunt::IRelationship ( CMBaseEntity *pTarget ) int CMHGrunt::IRelationship ( CMBaseEntity *pTarget )
{ {
if (( strcmp(STRING(pTarget->pev->model), "models/agrunt.mdl") == 0 ) || // on single player, forcing R_NM makes sense.
// on multiplayer, a custom classification will cause misbehaviour.
/*
if (( strcmp(STRING(pTarget->pev->model), "models/agrunt.mdl") == 0 ) ||
( strcmp(STRING(pTarget->pev->model), "models/garg.mdl") == 0 )) ( strcmp(STRING(pTarget->pev->model), "models/garg.mdl") == 0 ))
{ {
return R_NM; return R_NM;
} }
*/
return CMBaseMonster::IRelationship( pTarget ); return CMBaseMonster::IRelationship( pTarget );
} }
@@ -543,21 +546,22 @@ void CMHGrunt :: IdleSound( void )
{ {
if (FOkToSpeak() && (g_fGruntQuestion || RANDOM_LONG(0,1))) if (FOkToSpeak() && (g_fGruntQuestion || RANDOM_LONG(0,1)))
{ {
// there has to be a better way than spamming ternary operators... -Giegue
if (!g_fGruntQuestion) if (!g_fGruntQuestion)
{ {
// ask question or make statement // ask question or make statement
switch (RANDOM_LONG(0,2)) switch (RANDOM_LONG(0,2))
{ {
case 0: // check in 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; g_fGruntQuestion = 1;
break; break;
case 1: // question 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; g_fGruntQuestion = 2;
break; break;
case 2: // statement 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; break;
} }
} }
@@ -566,10 +570,10 @@ void CMHGrunt :: IdleSound( void )
switch (g_fGruntQuestion) switch (g_fGruntQuestion)
{ {
case 1: // check in 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; break;
case 2: // question 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; break;
} }
g_fGruntQuestion = 0; g_fGruntQuestion = 0;
@@ -596,6 +600,11 @@ void CMHGrunt :: CheckAmmo ( void )
//========================================================= //=========================================================
int CMHGrunt :: Classify ( void ) int CMHGrunt :: Classify ( void )
{ {
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_HUMAN_MILITARY; return CLASS_HUMAN_MILITARY;
} }
@@ -790,8 +799,10 @@ void CMHGrunt :: HandleAnimEvent( MonsterEvent_t *pEvent )
else if (pHurt->v.euser4 != NULL) else if (pHurt->v.euser4 != NULL)
{ {
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt)); CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt));
pMonster->TakeDamage( pev, pev, gSkillData.hgruntDmgKick, DMG_CLUB ); pMonster->TakeDamage(pev, pev, gSkillData.hgruntDmgKick, DMG_CLUB);
} }
else
UTIL_TakeDamageExternal(pHurt, pev, pev, gSkillData.hgruntDmgKick, DMG_CLUB);
} }
} }
break; break;
@@ -800,8 +811,8 @@ void CMHGrunt :: HandleAnimEvent( MonsterEvent_t *pEvent )
{ {
if ( FOkToSpeak() ) 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(); JustSpoke();
} }
} }
@@ -819,12 +830,12 @@ void CMHGrunt :: Spawn()
{ {
Precache( ); Precache( );
SET_MODEL(ENT(pev), "models/hgrunt.mdl"); SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/hgrunt.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX; pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP; pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_RED; m_bloodColor = !m_bloodColor ? BLOOD_COLOR_RED : m_bloodColor;
pev->effects = 0; pev->effects = 0;
pev->health = gSkillData.hgruntHealth; pev->health = gSkillData.hgruntHealth;
m_flFieldOfView = VIEW_FIELD_FULL; // indicates the width of this monster's forward view cone ( as a dotproduct result ) m_flFieldOfView = VIEW_FIELD_FULL; // indicates the width of this monster's forward view cone ( as a dotproduct result )
@@ -886,6 +897,13 @@ void CMHGrunt :: Spawn()
CMTalkMonster::g_talkWaitTime = 0; CMTalkMonster::g_talkWaitTime = 0;
MonsterInit(); MonsterInit();
pev->classname = MAKE_STRING( "monster_human_grunt" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Human Grunt" );
}
} }
//========================================================= //=========================================================
@@ -922,8 +940,8 @@ void CMHGrunt :: Precache()
else else
m_voicePitch = 100; m_voicePitch = 100;
m_iBrassShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell m_iBrassShell = PRECACHE_MODELINDEX("models/shell.mdl");// brass shell
m_iShotgunShell = PRECACHE_MODEL ("models/shotgunshell.mdl"); m_iShotgunShell = PRECACHE_MODELINDEX("models/shotgunshell.mdl");
} }
//========================================================= //=========================================================
@@ -1658,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 ) DEFINE_CUSTOM_SCHEDULES( CMHGrunt )
{ {
@@ -1682,6 +1731,7 @@ DEFINE_CUSTOM_SCHEDULES( CMHGrunt )
slGruntRepel, slGruntRepel,
slGruntRepelAttack, slGruntRepelAttack,
slGruntRepelLand, slGruntRepelLand,
slGruntChaseEnemyFailed,
}; };
IMPLEMENT_CUSTOM_SCHEDULES( CMHGrunt, CMBaseMonster ); IMPLEMENT_CUSTOM_SCHEDULES( CMHGrunt, CMBaseMonster );
@@ -1837,6 +1887,8 @@ Schedule_t *CMHGrunt :: GetSchedule( void )
// new enemy // new enemy
if ( HasConditions(bits_COND_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 //!!!KELLY - the leader of a squad of grunts has just seen the player or a
@@ -1852,14 +1904,14 @@ Schedule_t *CMHGrunt :: GetSchedule( void )
if ((m_hEnemy != NULL) && UTIL_IsPlayer(m_hEnemy)) if ((m_hEnemy != NULL) && UTIL_IsPlayer(m_hEnemy))
// player // player
SENTENCEG_PlayRndSz( ENT(pev), "HG_ALERT", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); SENTENCEG_PlayRndSz( ENT(pev), "HG_ALERT", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch);
/*jlb
else if ((m_hEnemy != NULL) && else if ((m_hEnemy != NULL) &&
(m_hEnemy->Classify() != CLASS_PLAYER_ALLY) && (m_hEnemy->Classify() != CLASS_PLAYER_ALLY) &&
(m_hEnemy->Classify() != CLASS_HUMAN_PASSIVE) && (m_hEnemy->Classify() != CLASS_HUMAN_PASSIVE) &&
(m_hEnemy->Classify() != CLASS_MACHINE)) (m_hEnemy->Classify() != CLASS_MACHINE))
// monster // monster
SENTENCEG_PlayRndSz( ENT(pev), "HG_MONST", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); SENTENCEG_PlayRndSz( ENT(pev), "HG_MONST", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch);
jlb*/
JustSpoke(); JustSpoke();
} }
@@ -1873,6 +1925,7 @@ jlb*/
} }
} }
} }
*/
} }
// no ammo // no ammo
else if ( HasConditions ( bits_COND_NO_AMMO_LOADED ) ) else if ( HasConditions ( bits_COND_NO_AMMO_LOADED ) )
@@ -1898,9 +1951,9 @@ jlb*/
//!!!KELLY - this grunt was hit and is going to run to cover. //!!!KELLY - this grunt was hit and is going to run to cover.
if (FOkToSpeak()) // && RANDOM_LONG(0,1)) 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; m_iSentence = HGRUNT_SENT_COVER;
//JustSpoke(); JustSpoke();
} }
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY );
} }
@@ -1924,6 +1977,10 @@ jlb*/
// can shoot // can shoot
else if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) 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 ) ) if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) )
{ {
// throw a grenade if can and no engage slots are available // throw a grenade if can and no engage slots are available
@@ -1931,24 +1988,34 @@ jlb*/
} }
else else
{ {
return GetScheduleOfType( SCHED_RANGE_ATTACK1 );
// hide! // hide!
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); //return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY );
} }
} }
// can't see enemy // can't see enemy
else if ( HasConditions( bits_COND_ENEMY_OCCLUDED ) ) 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 ) ) 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 //!!!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()) 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(); JustSpoke();
} }
return GetScheduleOfType( SCHED_RANGE_ATTACK2 ); return GetScheduleOfType( SCHED_RANGE_ATTACK2 );
} }
else else
{
return GetScheduleOfType( SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE );
}
/*
else
{ {
//!!!KELLY - grunt is going to stay put for a couple seconds to see if //!!!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 // the enemy wanders back out into the open, or approaches the
@@ -1960,6 +2027,7 @@ jlb*/
} }
return GetScheduleOfType( SCHED_STANDOFF ); return GetScheduleOfType( SCHED_STANDOFF );
} }
*/
} }
if ( HasConditions( bits_COND_SEE_ENEMY ) && !HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) if ( HasConditions( bits_COND_SEE_ENEMY ) && !HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) )
@@ -2094,6 +2162,11 @@ Schedule_t* CMHGrunt :: GetScheduleOfType ( int Type )
{ {
return &slGruntRepelLand[ 0 ]; return &slGruntRepelLand[ 0 ];
} }
case SCHED_CHASE_ENEMY_FAILED:
{
// add missing schedule from squadmonster.cpp
return &slGruntChaseEnemyFailed[ 0 ];
}
default: default:
{ {
return CMBaseMonster :: GetScheduleOfType ( Type ); return CMBaseMonster :: GetScheduleOfType ( Type );

View File

@@ -49,7 +49,7 @@ void CMHornet :: Spawn( void )
pev->movetype = MOVETYPE_FLY; pev->movetype = MOVETYPE_FLY;
pev->solid = SOLID_BBOX; pev->solid = SOLID_BBOX;
pev->takedamage = DAMAGE_YES; pev->takedamage = DAMAGE_YES;
pev->flags |= FL_MONSTER; pev->flags |= FL_MONSTER; // I have a bad feeling about this
pev->health = 1;// weak! pev->health = 1;// weak!
// hornets don't live as long in multiplayer // hornets don't live as long in multiplayer
@@ -83,6 +83,8 @@ void CMHornet :: Spawn( void )
pev->nextthink = gpGlobals->time + 0.1; pev->nextthink = gpGlobals->time + 0.1;
ResetSequenceInfo( ); ResetSequenceInfo( );
pev->classname = MAKE_STRING( "hornet" );
} }
@@ -102,8 +104,8 @@ void CMHornet :: Precache()
PRECACHE_SOUND( "hornet/ag_hornethit2.wav" ); PRECACHE_SOUND( "hornet/ag_hornethit2.wav" );
PRECACHE_SOUND( "hornet/ag_hornethit3.wav" ); PRECACHE_SOUND( "hornet/ag_hornethit3.wav" );
iHornetPuff = PRECACHE_MODEL( "sprites/muz1.spr" ); iHornetPuff = PRECACHE_MODELINDEX( "sprites/muz1.spr" );
iHornetTrail = PRECACHE_MODEL("sprites/laserbeam.spr"); iHornetTrail = PRECACHE_MODELINDEX("sprites/laserbeam.spr");
} }
//========================================================= //=========================================================
@@ -124,13 +126,14 @@ int CMHornet::IRelationship ( CMBaseEntity *pTarget )
//========================================================= //=========================================================
int CMHornet::Classify ( void ) int CMHornet::Classify ( void )
{ {
// Ensure classify is consistent with the owner, in the event
if ( pev->owner && pev->owner->v.flags & FL_CLIENT) // it's classification was overriden.
if (UTIL_IsValidEntity(pev->owner))
{ {
return CLASS_PLAYER_BIOWEAPON; CMBaseMonster *pOwner = GetClassPtr((CMBaseMonster *)VARS(pev->owner));
return pOwner->Classify();
} }
return CLASS_ALIEN_BIOWEAPON;
return CLASS_ALIEN_BIOWEAPON;
} }
//========================================================= //=========================================================
@@ -314,11 +317,10 @@ void CMHornet :: TrackTouch ( edict_t *pOther )
} }
// is this NOT a player and IS a monster? // is this NOT a player and IS a monster?
if (!UTIL_IsPlayer(pOther) && (pOther->v.euser4 != NULL)) if (!UTIL_IsPlayer(pOther) && (pOther->v.flags & FL_MONSTER))
{ {
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
if ( pMonster != NULL && IRelationship( pMonster ) <= R_NO || IRelationshipByClass( pOther->v.iuser4 ) <= R_NO )
if ( IRelationship( pMonster ) <= R_NO )
{ {
// hit something we don't want to hurt, so turn around. // hit something we don't want to hurt, so turn around.
@@ -361,6 +363,8 @@ void CMHornet::DieTouch ( edict_t *pOther )
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
pMonster->TakeDamage( pev, VARS( pev->owner ), pev->dmg, DMG_BULLET ); 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 pev->modelindex = 0;// so will disappear for the 0.1 secs we wait until NEXTTHINK gets rid

View File

@@ -78,6 +78,11 @@ enum
//========================================================= //=========================================================
int CMHoundeye :: Classify ( void ) int CMHoundeye :: Classify ( void )
{ {
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_ALIEN_MONSTER; return CLASS_ALIEN_MONSTER;
} }
@@ -262,12 +267,12 @@ void CMHoundeye :: Spawn()
{ {
Precache( ); Precache( );
SET_MODEL(ENT(pev), "models/houndeye.mdl"); SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/houndeye.mdl"));
UTIL_SetSize(pev, Vector ( -16, -16, 0 ), Vector ( 16, 16, 36 ) ); UTIL_SetSize(pev, Vector ( -16, -16, 0 ), Vector ( 16, 16, 36 ) );
pev->solid = SOLID_SLIDEBOX; pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP; pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_YELLOW; m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->effects = 0; pev->effects = 0;
pev->health = gSkillData.houndeyeHealth; 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? pev->yaw_speed = 5;//!!! should we put this in the monster's changeanim function since turn rates may vary with state/anim?
@@ -277,6 +282,13 @@ void CMHoundeye :: Spawn()
m_fDontBlink = FALSE; m_fDontBlink = FALSE;
MonsterInit(); MonsterInit();
pev->classname = MAKE_STRING( "monster_houndeye" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Houndeye" );
}
} }
//========================================================= //=========================================================
@@ -314,7 +326,7 @@ void CMHoundeye :: Precache()
PRECACHE_SOUND("houndeye/he_blast2.wav"); PRECACHE_SOUND("houndeye/he_blast2.wav");
PRECACHE_SOUND("houndeye/he_blast3.wav"); PRECACHE_SOUND("houndeye/he_blast3.wav");
m_iSpriteTexture = PRECACHE_MODEL( "sprites/shockwave.spr" ); m_iSpriteTexture = PRECACHE_MODELINDEX( "sprites/shockwave.spr" );
} }
//========================================================= //=========================================================
@@ -512,7 +524,8 @@ void CMHoundeye :: SonicAttack ( void )
{ {
if ( pEntity->v.takedamage != DAMAGE_NO ) 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 don't hurt other houndeyes with their attack
// houndeyes do FULL damage if the ent in question is visible. Half damage otherwise. // houndeyes do FULL damage if the ent in question is visible. Half damage otherwise.
@@ -553,6 +566,8 @@ void CMHoundeye :: SonicAttack ( void )
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
pMonster->TakeDamage( pev, pev, flAdjustedDamage, DMG_SONIC | DMG_ALWAYSGIB ); pMonster->TakeDamage( pev, pev, flAdjustedDamage, DMG_SONIC | DMG_ALWAYSGIB );
} }
else
UTIL_TakeDamageExternal( pEntity, pev, pev, flAdjustedDamage, DMG_SONIC | DMG_ALWAYSGIB );
} }
} }
} }

813
src/dlls/hwgrunt.cpp Normal file
View File

@@ -0,0 +1,813 @@
/***
*
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* This source code contains proprietary and confidential information of
* Valve LLC and its suppliers. Access to this code is restricted to
* persons who have executed a written SDK license with Valve. Any access,
* use or distribution of this code by or to any unlicensed person is illegal.
*
****/
//=========================================================
// Heavy Weapons Grunt
//=========================================================
#include "extdll.h"
#include "plane.h"
#include "util.h"
#include "cmbase.h"
#include "cmbasemonster.h"
#include "monsters.h"
#include "schedule.h"
#include "animation.h"
#include "weapons.h"
#include "cmtalkmonster.h"
#include "effects.h"
#include "customentity.h"
//=========================================================
// monster-specific DEFINE's
//=========================================================
// Weapon flags
#define HWGRUNT_MINIGUN 0
#define GUN_GROUP 1
// Gun values
#define GUN_MINIGUN 0
//=========================================================
// monster-specific schedule types
//=========================================================
enum
{
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_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.
//=========================================================
int CMHWGrunt::Classify(void)
{
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_HUMAN_MILITARY;
}
//=========================================================
// CheckRangeAttack1 - HWGrunt doesn't care about melee
//=========================================================
BOOL CMHWGrunt :: CheckRangeAttack1 ( float flDot, float flDist )
{
if ( !HasConditions( bits_COND_ENEMY_OCCLUDED ) && flDist <= 2048 && flDot >= 0.5 )
{
TraceResult tr;
Vector vecSrc = GetGunPosition();
// verify that a bullet fired from the gun will hit the enemy before the world.
UTIL_TraceLine( vecSrc, UTIL_BodyTarget(m_hEnemy, vecSrc), ignore_monsters, ignore_glass, ENT(pev), &tr);
if ( tr.flFraction == 1.0 )
{
return TRUE;
}
}
return FALSE;
}
//=========================================================
// CheckMeleeAttack1 - HWGrunt does not kick
//=========================================================
BOOL CMHWGrunt :: CheckMeleeAttack1 ( float flDot, float flDist )
{
return FALSE;
}
//=========================================================
// CheckRangeAttack2 - HWGrunt has no grenades
//=========================================================
BOOL CMHWGrunt :: CheckRangeAttack2 ( float flDot, float flDist )
{
return FALSE;
}
//=========================================================
// Shoot
//=========================================================
void CMHWGrunt::Minigun(void)
{
if (m_hEnemy == 0)
{
return;
}
Vector vecShootOrigin = GetGunPosition();
Vector vecShootDir = ShootAtEnemy(vecShootOrigin);
FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_10DEGREES, 2048, BULLET_MONSTER_12MM); // shoot +-5 degrees
pev->effects |= EF_MUZZLEFLASH;
// Minigunners have infinite ammo
//m_cAmmoLoaded--;// take away a bullet!
Vector angDir = UTIL_VecToAngles(vecShootDir);
SetBlending(0, angDir.x);
}
//=========================================================
// TraceAttack - hwgrunts do not wear helmets
//=========================================================
void CMHWGrunt :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
{
CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
}
//=========================================================
// TakeDamage - overridden for hwgrunts.
// They are meant to be aggresive, never take cover.
//=========================================================
int CMHWGrunt :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
return CMBaseMonster :: TakeDamage ( pevInflictor, pevAttacker, flDamage, bitsDamageType );
}
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//=========================================================
void CMHWGrunt::HandleAnimEvent(MonsterEvent_t *pEvent)
{
switch (pEvent->event)
{
case HWGRUNT_AE_DEATH:
break; // don't get rid of gun
case HWGRUNT_AE_MINIGUN:
{
// Sven Co-op uses a modified hassault/hw_gun4.wav for it's fire sound
Minigun();
// We don't want looping WAVs. Pick a different sound and change pitch on it
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "hassault/hw_shoot1.wav", 1, ATTN_NORM, 0, PITCH_NORM + RANDOM_LONG(-5,5));
}
break;
default:
CMHGrunt::HandleAnimEvent(pEvent);
break;
}
}
//=========================================================
// Spawn
//=========================================================
void CMHWGrunt::Spawn()
{
Precache();
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/hwgrunt.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = !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_iSentence = -1;
m_fStanding = TRUE;
//m_afCapability = bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
m_afCapability = bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
//m_fEnemyEluded = FALSE;
m_fFirstEncounter = FALSE;// false because hwgrunt does not send signals of any kind
m_HackedGunPos = Vector(0, 0, 55);
// Don't setup pev->weapons, always minigun if not specified
/*
if (FBitSet(pev->weapons, HWGRUNT_MINIGUN))
{
SetBodygroup(GUN_GROUP, GUN_MINIGUN);
m_cClipSize = 1;
}
*/
m_cAmmoLoaded = 99;
m_cClipSize = 99;
CMTalkMonster::g_talkWaitTime = 0;
MonsterInit();
pev->classname = MAKE_STRING( "monster_hwgrunt" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Heavy Weapons Grunt" );
}
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CMHWGrunt::Precache()
{
PRECACHE_MODEL("models/hwgrunt.mdl");
PRECACHE_SOUND("hassault/hw_shoot1.wav");
PRECACHE_SOUND("hassault/hw_spinup.wav");
PRECACHE_SOUND("hassault/hw_spindown.wav");
// get voice pitch
m_voicePitch = 95 + RANDOM_LONG(0, 3); // slighly lower than normal grunt
CMHGrunt hgrunt;
hgrunt.Precache();
}
//=========================================================
// AI Schedules Specific to this monster
//=========================================================
//=========================================================
// Fail
//=========================================================
Task_t tlHWGruntFail[] =
{
{ TASK_STOP_MOVING, 0 },
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
{ TASK_WAIT, (float)2 },
{ TASK_WAIT_PVS, (float)0 },
};
Schedule_t slHWGruntFail[] =
{
{
tlHWGruntFail,
ARRAYSIZE ( tlHWGruntFail ),
bits_COND_CAN_RANGE_ATTACK1,
0,
"HWGrunt Fail"
},
};
//=========================================================
// Combat Fail
//=========================================================
Task_t tlHWGruntCombatFail[] =
{
{ TASK_STOP_MOVING, 0 },
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
{ TASK_WAIT_FACE_ENEMY, (float)2 },
{ TASK_WAIT_PVS, (float)0 },
};
Schedule_t slHWGruntCombatFail[] =
{
{
tlHWGruntCombatFail,
ARRAYSIZE ( tlHWGruntCombatFail ),
bits_COND_CAN_RANGE_ATTACK1,
0,
"HWGrunt Combat Fail"
},
};
//=========================================================
// Not really victory dance
//=========================================================
Task_t tlHWGruntVictoryDance[] =
{
{ TASK_STOP_MOVING, (float)0 },
{ TASK_FACE_ENEMY, (float)0 },
{ TASK_WAIT, (float)1.5 },
{ TASK_GET_PATH_TO_ENEMY_CORPSE, (float)0 },
{ TASK_WALK_PATH, (float)0 },
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
{ TASK_FACE_ENEMY, (float)0 },
// { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE },
};
Schedule_t slHWGruntVictoryDance[] =
{
{
tlHWGruntVictoryDance,
ARRAYSIZE ( tlHWGruntVictoryDance ),
bits_COND_NEW_ENEMY |
bits_COND_LIGHT_DAMAGE |
bits_COND_HEAVY_DAMAGE,
0,
"HWGrunt Victory Dance"
},
};
//=========================================================
// Establish line of fire - move to a position that allows
// the grunt to attack.
//=========================================================
Task_t tlHWGruntEstablishLineOfFire[] =
{
{ TASK_SET_FAIL_SCHEDULE, (float)SCHED_HWGRUNT_ELOF_FAIL },
{ TASK_GET_PATH_TO_ENEMY, (float)0 },
// { TASK_GRUNT_SPEAK_SENTENCE,(float)0 },
{ TASK_RUN_PATH, (float)0 },
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
};
Schedule_t slHWGruntEstablishLineOfFire[] =
{
{
tlHWGruntEstablishLineOfFire,
ARRAYSIZE ( tlHWGruntEstablishLineOfFire ),
bits_COND_NEW_ENEMY |
bits_COND_ENEMY_DEAD |
bits_COND_CAN_RANGE_ATTACK1 |
bits_COND_HEAR_SOUND,
0,
"HWGrunt Establish Line Of Fire"
},
};
//=========================================================
// wait in cover - we don't allow danger or the ability to
// attack to break a grunt's run to cover schedule, but when
// a grunt is in cover, we do want them to attack if they can.
//=========================================================
Task_t tlHWGruntWaitInCover[] =
{
{ TASK_STOP_MOVING, (float)0 },
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
{ TASK_WAIT_FACE_ENEMY, (float)1 },
};
Schedule_t slHWGruntWaitInCover[] =
{
{
tlHWGruntWaitInCover,
ARRAYSIZE ( tlHWGruntWaitInCover ),
bits_COND_NEW_ENEMY |
bits_COND_HEAR_SOUND |
bits_COND_CAN_RANGE_ATTACK1,
0,
"HWGrunt Wait In Cover"
},
};
//=========================================================
// run to cover.
//=========================================================
Task_t tlHWGruntTakeCover[] =
{
{ TASK_STOP_MOVING, (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 slHWGruntTakeCover[] =
{
{
tlHWGruntTakeCover,
ARRAYSIZE ( tlHWGruntTakeCover ),
0,
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
//=========================================================
Task_t tlHWGruntRepel[] =
{
{ TASK_STOP_MOVING, (float)0 },
{ TASK_FACE_IDEAL, (float)0 },
{ TASK_PLAY_SEQUENCE, (float)ACT_GLIDE },
};
Schedule_t slHWGruntRepel[] =
{
{
tlHWGruntRepel,
ARRAYSIZE ( tlHWGruntRepel ),
bits_COND_SEE_ENEMY |
bits_COND_NEW_ENEMY |
bits_COND_LIGHT_DAMAGE |
bits_COND_HEAVY_DAMAGE |
bits_COND_HEAR_SOUND,
0,
"HWGrunt Repel"
},
};
//=========================================================
// repel land
//=========================================================
Task_t tlHWGruntRepelLand[] =
{
{ TASK_STOP_MOVING, (float)0 },
{ TASK_PLAY_SEQUENCE, (float)ACT_LAND },
{ TASK_GET_PATH_TO_LASTPOSITION, (float)0 },
{ TASK_RUN_PATH, (float)0 },
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
{ TASK_CLEAR_LASTPOSITION, (float)0 },
};
Schedule_t slHWGruntRepelLand[] =
{
{
tlHWGruntRepelLand,
ARRAYSIZE ( tlHWGruntRepelLand ),
bits_COND_SEE_ENEMY |
bits_COND_NEW_ENEMY |
bits_COND_LIGHT_DAMAGE |
bits_COND_HEAVY_DAMAGE |
bits_COND_HEAR_SOUND,
0,
"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"
},
};
DEFINE_CUSTOM_SCHEDULES( CMHWGrunt )
{
slHWGruntFail,
slHWGruntCombatFail,
slHWGruntVictoryDance,
slHWGruntEstablishLineOfFire,
slHWGruntWaitInCover,
slHWGruntTakeCover,
slHWGruntMinigunSpinUp,
slHWGruntMinigunAttack,
slHWGruntRepel,
slHWGruntRepelLand,
slHWGruntChaseEnemyFailed,
};
IMPLEMENT_CUSTOM_SCHEDULES( CMHWGrunt, CMBaseMonster );
//=========================================================
// SetActivity
//=========================================================
void CMHWGrunt :: SetActivity ( Activity NewActivity )
{
int iSequence = ACTIVITY_NOT_AVAILABLE;
void *pmodel = GET_MODEL_PTR( ENT(pev) );
switch ( NewActivity )
{
case ACT_RANGE_ATTACK1:
iSequence = LookupSequence( "attack" );
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 )
{
if ( pev->sequence != iSequence || !m_fSequenceLoops )
{
pev->frame = 0;
}
pev->sequence = iSequence; // Set to the reset anim (if it's there)
ResetSequenceInfo( );
SetYawSpeed();
}
else
{
// Not available try to get default anim
ALERT ( at_console, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity );
pev->sequence = 0; // Set to the reset anim (if it's there)
}
}
//=========================================================
// Get Schedule!
//=========================================================
Schedule_t *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 )
{
if (pev->flags & FL_ONGROUND)
{
// just landed
pev->movetype = MOVETYPE_STEP;
return GetScheduleOfType ( SCHED_HWGRUNT_REPEL_LAND );
}
else
{
// can not attack while holding a minigun in rapel
return GetScheduleOfType ( SCHED_HWGRUNT_REPEL );
}
}
switch ( m_MonsterState )
{
case MONSTERSTATE_COMBAT:
{
// 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();
}
// 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_GRUNT_SUPPRESS );
}
else
{
return GetScheduleOfType ( SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE );
}
}
}
*/
}
// damaged just a little
else if ( HasConditions( bits_COND_LIGHT_DAMAGE ) )
{
// 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 ) )
{
// 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 ) )
{
return GetScheduleOfType( SCHED_HWGRUNT_ESTABLISH_LINE_OF_FIRE );
}
}
}
// no special cases here, call the base class
return CMBaseMonster :: GetSchedule();
}
//=========================================================
//=========================================================
Schedule_t* CMHWGrunt :: GetScheduleOfType ( int Type )
{
switch ( Type )
{
case SCHED_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:
{
// 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:
{
return &slHWGruntEstablishLineOfFire[ 0 ];
}
break;
case SCHED_RANGE_ATTACK1:
{
// minigun should spin up first
if ( !HasMemory( bits_MEMORY_HWGRUNT_SPINUP ) )
{
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:
{
return &slHWGruntWaitInCover[ 0 ];
}
case SCHED_VICTORY_DANCE:
{
return &slHWGruntVictoryDance[ 0 ];
}
case SCHED_FAIL:
{
if ( m_hEnemy != NULL )
{
// has an enemy, so pick a different default fail schedule most likely to help recover.
return &slHWGruntCombatFail[ 0 ];
}
return &slHWGruntFail[ 0 ];
}
case SCHED_HWGRUNT_REPEL:
{
if (pev->velocity.z > -128)
pev->velocity.z -= 32;
return &slHWGruntRepel[ 0 ];
}
case SCHED_HWGRUNT_REPEL_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

@@ -67,6 +67,11 @@ const char *CMISlave::pDeathSounds[] =
//========================================================= //=========================================================
int CMISlave :: Classify ( void ) int CMISlave :: Classify ( void )
{ {
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_ALIEN_MILITARY; return CLASS_ALIEN_MILITARY;
} }
@@ -411,12 +416,12 @@ void CMISlave :: Spawn()
{ {
Precache( ); Precache( );
SET_MODEL(ENT(pev), "models/islave.mdl"); SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/islave.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX; pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP; pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN; m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->effects = 0; pev->effects = 0;
pev->health = gSkillData.slaveHealth; pev->health = gSkillData.slaveHealth;
pev->view_ofs = Vector ( 0, 0, 64 );// position of the eyes relative to monster's origin. pev->view_ofs = Vector ( 0, 0, 64 );// position of the eyes relative to monster's origin.
@@ -429,10 +434,16 @@ void CMISlave :: Spawn()
for (int i = 0; i < ISLAVE_MAX_BEAMS; i++) for (int i = 0; i < ISLAVE_MAX_BEAMS; i++)
m_pBeam[i] = NULL; m_pBeam[i] = NULL;
m_iBravery = 0; m_iBravery = 0;
m_flNextAttack = 0.0f; m_flNextAttack = 0.0f;
MonsterInit(); MonsterInit();
pev->classname = MAKE_STRING( "monster_alien_slave" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Alien Slave" );
}
} }
//========================================================= //=========================================================
@@ -440,8 +451,6 @@ void CMISlave :: Spawn()
//========================================================= //=========================================================
void CMISlave :: Precache() void CMISlave :: Precache()
{ {
int i;
PRECACHE_MODEL("models/islave.mdl"); PRECACHE_MODEL("models/islave.mdl");
PRECACHE_MODEL("sprites/lgtning.spr"); PRECACHE_MODEL("sprites/lgtning.spr");
PRECACHE_SOUND("debris/zap1.wav"); PRECACHE_SOUND("debris/zap1.wav");
@@ -452,17 +461,10 @@ void CMISlave :: Precache()
PRECACHE_SOUND("headcrab/hc_headbite.wav"); PRECACHE_SOUND("headcrab/hc_headbite.wav");
PRECACHE_SOUND("weapons/cbar_miss1.wav"); PRECACHE_SOUND("weapons/cbar_miss1.wav");
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) PRECACHE_SOUND_ARRAY(pAttackHitSounds);
PRECACHE_SOUND((char *)pAttackHitSounds[i]); PRECACHE_SOUND_ARRAY(pAttackMissSounds);
PRECACHE_SOUND_ARRAY(pPainSounds);
for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) PRECACHE_SOUND_ARRAY(pDeathSounds);
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]);
} }
@@ -726,6 +728,8 @@ void CMISlave :: ZapBeam( int side )
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
pMonster->TraceAttack( pev, gSkillData.slaveDmgZap, vecAim, &tr, DMG_SHOCK ); 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 ) ); UTIL_EmitAmbientSound( ENT(pev), tr.vecEndPos, "weapons/electro4.wav", 0.5, ATTN_NORM, 0, RANDOM_LONG( 140, 160 ) );

442
src/dlls/massn.cpp Normal file
View File

@@ -0,0 +1,442 @@
// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository!
/***
*
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* This source code contains proprietary and confidential information of
* Valve LLC and its suppliers. Access to this code is restricted to
* persons who have executed a written SDK license with Valve. Any access,
* use or distribution of this code by or to any unlicensed person is illegal.
*
****/
//=========================================================
// Black Ops - Male Assassin
//=========================================================
#include "extdll.h"
#include "plane.h"
#include "util.h"
#include "cmbase.h"
#include "cmbasemonster.h"
#include "monsters.h"
#include "schedule.h"
#include "animation.h"
#include "weapons.h"
#include "cmtalkmonster.h"
#include "effects.h"
#include "customentity.h"
extern cvar_t *monster_default_maxrange;
//=========================================================
// monster-specific DEFINE's
//=========================================================
#define MASSN_CLIP_SIZE 36 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x!
// Weapon flags
#define MASSN_9MMAR (1 << 0)
#define MASSN_HANDGRENADE (1 << 1)
#define MASSN_GRENADELAUNCHER (1 << 2)
#define MASSN_SNIPERRIFLE (1 << 3)
// Body groups.
#define HEAD_GROUP 1
#define GUN_GROUP 2
// Head values
#define HEAD_WHITE 0
#define HEAD_BLACK 1
#define HEAD_GOGGLES 2
// Gun values
#define GUN_MP5 0
#define GUN_SNIPERRIFLE 1
#define GUN_NONE 2
//=========================================================
// Monster's Anim Events Go Here
//=========================================================
#define MASSN_AE_KICK ( 3 )
#define MASSN_AE_BURST1 ( 4 )
#define MASSN_AE_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.
//=========================================================
// Override a few behaviours to make this grunt silent
//=========================================================
BOOL CMMassn::FOkToSpeak(void)
{
return FALSE;
}
void CMMassn::IdleSound(void)
{
}
void CMMassn::PainSound(void)
{
}
void CMMassn::DeathSound(void)
{
}
//=========================================================
// Classify - indicates this monster's place in the
// relationship table.
//=========================================================
int CMMassn::Classify(void)
{
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_HUMAN_MILITARY;
}
//=========================================================
// Shoot
//=========================================================
void CMMassn::Sniperrifle(void)
{
if (m_hEnemy == 0)
{
return;
}
Vector vecShootOrigin = GetGunPosition();
Vector vecShootDir = ShootAtEnemy(vecShootOrigin);
UTIL_MakeVectors(pev->angles);
Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40, 90) + gpGlobals->v_up * RANDOM_FLOAT(75, 200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40);
EjectBrass(vecShootOrigin - vecShootDir * 24, vecShellVelocity, pev->angles.y, m_iBrassShell, TE_BOUNCE_SHELL);
FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_1DEGREES, 2048, BULLET_MONSTER_762, 0); // shoot +-7.5 degrees
pev->effects |= EF_MUZZLEFLASH;
m_cAmmoLoaded--;// take away a bullet!
Vector angDir = UTIL_VecToAngles(vecShootDir);
SetBlending(0, angDir.x);
}
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//=========================================================
void CMMassn::HandleAnimEvent(MonsterEvent_t *pEvent)
{
Vector vecShootDir;
Vector vecShootOrigin;
switch (pEvent->event)
{
case MASSN_AE_DROP_GUN:
{
Vector vecGunPos;
Vector vecGunAngles;
GetAttachment(0, vecGunPos, vecGunAngles);
// switch to body group with no gun.
SetBodygroup(GUN_GROUP, GUN_NONE);
}
break;
case MASSN_AE_BURST1:
{
if (FBitSet(pev->weapons, MASSN_9MMAR))
{
Shoot();
// the first round of the three round burst plays the sound and puts a sound in the world sound list.
if (RANDOM_LONG(0, 1))
{
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "hgrunt/gr_mgun1.wav", 1, ATTN_NORM);
}
else
{
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "hgrunt/gr_mgun2.wav", 1, ATTN_NORM);
}
}
else if (FBitSet(pev->weapons, MASSN_SNIPERRIFLE))
{
Sniperrifle();
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/sniper_fire.wav", 1, ATTN_NORM);
}
}
break;
case MASSN_AE_BURST2:
case MASSN_AE_BURST3:
Shoot();
break;
case MASSN_AE_KICK:
{
edict_t *pHurt = Kick();
if (pHurt)
{
// SOUND HERE!
UTIL_MakeVectors(pev->angles);
pHurt->v.punchangle.x = 15;
pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50;
if (UTIL_IsPlayer(pHurt))
UTIL_TakeDamage( pHurt, pev, pev, gSkillData.massnDmgKick, DMG_CLUB );
else if (pHurt->v.euser4 != NULL)
{
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt));
pMonster->TakeDamage( pev, pev, gSkillData.massnDmgKick, DMG_CLUB );
}
else
UTIL_TakeDamageExternal( pHurt, pev, pev, gSkillData.massnDmgKick, DMG_CLUB );
}
}
break;
case MASSN_AE_CAUGHT_ENEMY:
break;
default:
CMHGrunt::HandleAnimEvent(pEvent);
break;
}
}
//=========================================================
// Spawn
//=========================================================
void CMMassn::Spawn()
{
Precache();
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/massn.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = !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 )
m_MonsterState = MONSTERSTATE_NONE;
m_flNextGrenadeCheck = gpGlobals->time + 1;
m_flNextPainTime = gpGlobals->time;
m_iSentence = -1;
//m_afCapability = bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
m_afCapability = bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
//m_fEnemyEluded = FALSE;
m_fFirstEncounter = TRUE;// this is true when the grunt spawns, because he hasn't encountered an enemy yet.
m_HackedGunPos = Vector(0, 0, 55);
if (pev->weapons == 0)
{
// weapons not specified, randomize
switch ( RANDOM_LONG( 0, 2 ) )
{
case 0:
pev->weapons = MASSN_9MMAR | MASSN_HANDGRENADE;
break;
case 1:
pev->weapons = MASSN_9MMAR | MASSN_GRENADELAUNCHER;
break;
case 2:
pev->weapons = MASSN_SNIPERRIFLE;
break;
}
}
if (FBitSet(pev->weapons, MASSN_SNIPERRIFLE))
{
SetBodygroup(GUN_GROUP, GUN_SNIPERRIFLE);
m_cClipSize = 5;
// if no attack range set, set 2x default
if (!m_flDistLook)
m_flDistLook = monster_default_maxrange->value * 2;
}
else
{
m_cClipSize = MASSN_CLIP_SIZE;
}
m_cAmmoLoaded = m_cClipSize;
if (RANDOM_LONG(0, 99) < 80)
pev->skin = 0; // light skin
else
pev->skin = 1; // dark skin
CMTalkMonster::g_talkWaitTime = 0;
MonsterInit();
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 )
{
// default name
m_szMonsterName = MAKE_STRING( "Male Assassin" );
}
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CMMassn::Precache()
{
PRECACHE_MODEL("models/massn.mdl");
PRECACHE_SOUND("hgrunt/gr_mgun1.wav");
PRECACHE_SOUND("hgrunt/gr_mgun2.wav");
PRECACHE_SOUND("hgrunt/gr_reload1.wav");
PRECACHE_SOUND("weapons/glauncher.wav");
PRECACHE_SOUND("weapons/sniper_bolt1.wav");
PRECACHE_SOUND("weapons/sniper_fire.wav");
PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event
// get voice pitch
if (RANDOM_LONG(0, 1))
m_voicePitch = 109 + RANDOM_LONG(0, 7);
else
m_voicePitch = 100;
m_iBrassShell = PRECACHE_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

@@ -40,23 +40,24 @@
#include "sdk_util.h" // UTIL_LogPrintf, etc #include "sdk_util.h" // UTIL_LogPrintf, etc
// Must provide at least one of these.. // Must provide at least one of these..
static META_FUNCTIONS gMetaFunctionTable = { static META_FUNCTIONS gMetaFunctionTable =
NULL, // pfnGetEntityAPI HL SDK; called before game DLL {
NULL, // pfnGetEntityAPI_Post META; called after game DLL NULL, // pfnGetEntityAPI HL SDK; called before game DLL
GetEntityAPI2, // pfnGetEntityAPI2 HL SDK2; called before game DLL NULL, // pfnGetEntityAPI_Post META; called after game DLL
GetEntityAPI2_Post, // pfnGetEntityAPI2_Post META; called after game DLL GetEntityAPI2, // pfnGetEntityAPI2 HL SDK2; called before game DLL
NULL, // pfnGetNewDLLFunctions HL SDK2; called before game DLL GetEntityAPI2_Post, // pfnGetEntityAPI2_Post META; called after game DLL
NULL, // pfnGetNewDLLFunctions_Post META; called after game DLL NULL, // pfnGetNewDLLFunctions HL SDK2; called before game DLL
NULL, // pfnGetEngineFunctions META; called before HL engine NULL, // pfnGetNewDLLFunctions_Post META; called after game DLL
NULL, // pfnGetEngineFunctions_Post META; called after HL engine NULL, // pfnGetEngineFunctions META; called before HL engine
GetEngineFunctions_Post, // pfnGetEngineFunctions_Post META; called after HL engine
}; };
// Description of plugin // Description of plugin
plugin_info_t Plugin_info = { plugin_info_t Plugin_info = {
META_INTERFACE_VERSION, // interface version META_INTERFACE_VERSION, // interface version
"MonsterMod", // name "MonsterMod", // name
"1.0", // version "4.0", // version
"17/03/2020", // date in DD/MM/YYYY format "14/07/2023", // date in DD/MM/YYYY format
"botman, Rick90, Giegue", // original authors + recreation by... "botman, Rick90, Giegue", // original authors + recreation by...
"https://github.com/JulianR0/monstermod-redo", // url "https://github.com/JulianR0/monstermod-redo", // url
"MONSTER", // logtag "MONSTER", // logtag
@@ -77,10 +78,25 @@ meta_globals_t *gpMetaGlobals; // metamod globals
gamedll_funcs_t *gpGamedllFuncs; // gameDLL function tables gamedll_funcs_t *gpGamedllFuncs; // gameDLL function tables
mutil_funcs_t *gpMetaUtilFuncs; // metamod utility functions mutil_funcs_t *gpMetaUtilFuncs; // metamod utility functions
// CVars
cvar_t init_dllapi_log = {"monster_log", "0", FCVAR_EXTDLL, 0, NULL}; cvar_t init_dllapi_log = {"monster_log", "0", FCVAR_EXTDLL, 0, NULL};
cvar_t *dllapi_log = NULL; cvar_t *dllapi_log = NULL;
cvar_t init_monster_spawn = {"monster_spawn", "1", FCVAR_EXTDLL, 0, NULL}; cvar_t init_monster_spawn = {"monster_spawn", "1", FCVAR_EXTDLL, 0, NULL};
cvar_t *monster_spawn = NULL; cvar_t *monster_spawn = NULL;
cvar_t init_monster_show_deaths = {"monster_show_deaths", "1", FCVAR_EXTDLL, 0, NULL};
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: // Metamod requesting info about this plugin:
@@ -103,37 +119,58 @@ C_DLLEXPORT int Meta_Query(char *ifvers, plugin_info_t **pPlugInfo,
// pFunctionTable (requested) table of function tables this plugin catches // pFunctionTable (requested) table of function tables this plugin catches
// pMGlobals (given) global vars from metamod // pMGlobals (given) global vars from metamod
// pGamedllFuncs (given) copy of function tables from game dll // pGamedllFuncs (given) copy of function tables from game dll
C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs)
meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs)
{ {
if(now); // to satisfy gcc -Wunused if(now); // to satisfy gcc -Wunused
if(!pMGlobals) { if(!pMGlobals)
LOG_ERROR(PLID, "Meta_Attach called with null pMGlobals"); {
return(FALSE); LOG_ERROR(PLID, "Meta_Attach called with null pMGlobals");
} return(FALSE);
gpMetaGlobals=pMGlobals; }
if(!pFunctionTable) { gpMetaGlobals=pMGlobals;
LOG_ERROR(PLID, "Meta_Attach called with null pFunctionTable"); if(!pFunctionTable)
return(FALSE); {
} LOG_ERROR(PLID, "Meta_Attach called with null pFunctionTable");
memcpy(pFunctionTable, &gMetaFunctionTable, sizeof(META_FUNCTIONS)); return(FALSE);
gpGamedllFuncs=pGamedllFuncs; }
memcpy(pFunctionTable, &gMetaFunctionTable, sizeof(META_FUNCTIONS));
gpGamedllFuncs=pGamedllFuncs;
LOG_MESSAGE(PLID, "%s %s, %s", VNAME, VVERSION, VDATE); LOG_MESSAGE(PLID, "%s %s, %s", VNAME, VVERSION, VDATE);
LOG_MESSAGE(PLID, "by %s", VAUTHOR); LOG_MESSAGE(PLID, "by %s", VAUTHOR);
LOG_MESSAGE(PLID, "%s", VURL); LOG_MESSAGE(PLID, "%s", VURL);
LOG_MESSAGE(PLID, "compiled: %s CDT", COMPILE_TIME); LOG_MESSAGE(PLID, "compiled: %s CDT", COMPILE_TIME);
LOG_CONSOLE(PLID, "[%s] %s v%s, %s", VLOGTAG, VNAME, VVERSION, VDATE); LOG_CONSOLE(PLID, "[%s] %s v%s, %s", VLOGTAG, VNAME, VVERSION, VDATE);
LOG_CONSOLE(PLID, "[%s] by %s", VLOGTAG, VAUTHOR); LOG_CONSOLE(PLID, "[%s] by %s", VLOGTAG, VAUTHOR);
CVAR_REGISTER(&init_dllapi_log); CVAR_REGISTER(&init_dllapi_log);
dllapi_log = CVAR_GET_POINTER("monster_log"); dllapi_log = CVAR_GET_POINTER("monster_log");
CVAR_REGISTER(&init_monster_spawn); CVAR_REGISTER(&init_monster_spawn);
monster_spawn = CVAR_GET_POINTER("monster_spawn"); monster_spawn = CVAR_GET_POINTER("monster_spawn");
return(TRUE); CVAR_REGISTER(&init_monster_show_deaths);
monster_show_deaths = CVAR_GET_POINTER("monster_show_deaths");
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 "meta_api.h"
#include "monster_plugin.h" #include "monster_plugin.h"
#include "ripent.h"
#include "globalreplace.h"
extern cvar_t *dllapi_log; 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 monster_type_t monster_types[];
extern int monster_spawn_count; extern int monster_spawn_count;
@@ -62,6 +68,84 @@ bool get_input(FILE *fp, char *input)
return FALSE; // no input found 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) void scan_monster_cfg(FILE *fp)
{ {
// Let's make a full rework of this. -Giegue // 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 >.> // Now that I think about it this looks slow and bad code >.>
// A match is found. What is this? // A match is found. What is this?
if (strncmp(monster_types[mIndex].name, "monster", 7) == 0) if (strncmp(monster_types[mIndex].name, "monster_", 8) == 0)
{ {
// It's a monster, add it to the list // It's a monster, add it to the list
if (monster_spawn_count == MAX_MONSTERS) if (monster_spawn_count == MAX_MONSTERS)
@@ -110,6 +194,37 @@ void scan_monster_cfg(FILE *fp)
monster = TRUE; 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) else if (strcmp(monster_types[mIndex].name, "info_node") == 0)
{ {
// Normal node // Normal node
@@ -142,7 +257,7 @@ void scan_monster_cfg(FILE *fp)
} }
if (monster_types[mIndex].name[0] == 0) if (monster_types[mIndex].name[0] == 0)
{ {
LOG_MESSAGE(PLID, "ERROR: unknown classname: %s", input); // print conflictive line LOG_MESSAGE(PLID, "ERROR: unknown classname: %s", data[kvd_index-1].value); // print conflictive line
LOG_MESSAGE(PLID, "ERROR: nothing will spawn here!"); LOG_MESSAGE(PLID, "ERROR: nothing will spawn here!");
badent = TRUE; badent = TRUE;
} }
@@ -150,8 +265,8 @@ void scan_monster_cfg(FILE *fp)
else else
{ {
// What are you doing?! // What are you doing?!
LOG_MESSAGE(PLID, "ERROR: BAD ENTITY STRUCTURE! Last line was %s", input); // print conflictive line LOG_MESSAGE(PLID, "ERROR: BAD ENTITY STRUCTURE! Last line was %s", data[kvd_index-1].key); // print conflictive line
LOG_MESSAGE(PLID, "ERROR: nothing will spawn here!"); LOG_MESSAGE(PLID, "ERROR: classname MUST be the last entry of the entity!" );
badent = TRUE; badent = TRUE;
} }
@@ -192,23 +307,6 @@ void scan_monster_cfg(FILE *fp)
node_spawnpoint[node_spawn_count].origin[2] = z; node_spawnpoint[node_spawn_count].origin[2] = z;
} }
} }
else if (strcmp(data[i].key, "delay") == 0)
{
// ToDo: Remove this keyvalue.
// Monsters spawned directly should not respawn.
if (monster)
{
if (sscanf(data[i].value, "%f", &x) != 1)
{
LOG_MESSAGE(PLID, "ERROR: invalid delay: %s", input); // print conflictive line
// default to 30 seconds
LOG_MESSAGE(PLID, "ERROR: entity respawn frequency will be set to 30 seconds");
x = 30;
}
monster_spawnpoint[monster_spawn_count].delay = x;
}
}
else if (strcmp(data[i].key, "angles") == 0) else if (strcmp(data[i].key, "angles") == 0)
{ {
if (monster) if (monster)
@@ -241,6 +339,94 @@ void scan_monster_cfg(FILE *fp)
monster_spawnpoint[monster_spawn_count].spawnflags = x; monster_spawnpoint[monster_spawn_count].spawnflags = x;
} }
} }
else if (strcmp(data[i].key, "model") == 0)
{
if (monster)
{
// only applicable for normal monsters
if (strcmp(data[kvd_index-1].value, "monstermaker") != 0 && 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 else
{ {
// We do not know this keyvalue, but an specific entity might use it. // We do not know this keyvalue, but an specific entity might use it.
@@ -255,8 +441,7 @@ void scan_monster_cfg(FILE *fp)
if (monster) if (monster)
{ {
// Init monster // Spawn right away
monster_spawnpoint[monster_spawn_count].respawn_time = gpGlobals->time + 0.1; // spawn (nearly) right away
monster_spawnpoint[monster_spawn_count].need_to_respawn = TRUE; monster_spawnpoint[monster_spawn_count].need_to_respawn = TRUE;
monster_spawn_count++; monster_spawn_count++;
} }
@@ -282,7 +467,7 @@ void scan_monster_cfg(FILE *fp)
// Bruteforce to remove quotes // Bruteforce to remove quotes
char parse[66] = {0}; char parse[66] = {0};
int skip = 0; int skip = 0;
for (int i = 0; i < strlen(input); i++) for (unsigned i = 0; i < strlen(input); i++)
{ {
if (input[i] == '"') if (input[i] == '"')
{ {
@@ -317,52 +502,563 @@ void scan_monster_cfg(FILE *fp)
} }
} }
void process_monster_cfg(void) 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 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; FILE *fp = NULL;
bool status = FALSE; // no error
monster_spawn_count = 0; monster_spawn_count = 0;
node_spawn_count = 0;
// find the directory name of the currently running MOD... // find the directory name of the currently running MOD...
(*g_engfuncs.pfnGetGameDir)(game_dir); (*g_engfuncs.pfnGetGameDir)(game_dir);
strcpy(filename, game_dir); // build route...
#ifdef __linux__ strcpy(CFGfilename, game_dir);
strcat(filename, "/maps/"); strcat(CFGfilename, "/maps/");
#else strcat(CFGfilename, STRING(gpGlobals->mapname));
strcat(filename, "\\maps\\"); strcpy(BSPfilename, CFGfilename);
#endif strcpy(EXTfilename, CFGfilename);
strcat(filename, STRING(gpGlobals->mapname));
strcat(filename, "_monster.cfg");
// check if the map specific filename exists... strcat(BSPfilename, ".bsp");
if (access(filename, 0) == 0) 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)
{ {
if (dllapi_log->value) // read from bsp? (mode 0 or 2)
if (monster_entity_config->value != 1)
{ {
//META_CONS("[MONSTER] Processing config file=%s", filename); LoadBSPFile(BSPfilename);
LOG_MESSAGE(PLID, "Processing config file=%s", filename); ParseEntities();
scan_monster_bsp();
} }
if ((fp = fopen(filename, "r")) == NULL) // read from cfg? (mode 1 or 2)
if (monster_entity_config->value > 0)
{ {
//META_CONS("[MONSTER] ERROR: Could not open \"%s\"!", filename); // check if the map specific filename exists...
LOG_MESSAGE(PLID, "ERROR: Could not open \"%s\" file!", filename); if (access(CFGfilename, 0) == 0)
{
if (dllapi_log->value)
{
//META_CONS("[MONSTER] Processing config file=%s", filename);
LOG_MESSAGE(PLID, "Processing config file '%s'", CFGfilename);
}
return; if ((fp = fopen(CFGfilename, "r")) == NULL)
{
//META_CONS("[MONSTER] ERROR: Could not open \"%s\"!", filename);
LOG_MESSAGE(PLID, "ERROR: Could not open \"%s\" file!", CFGfilename);
return TRUE; // error
}
scan_monster_cfg(fp);
fclose(fp);
}
} }
scan_monster_cfg(fp);
fclose(fp);
} }
return; /* 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) bool scan_monster_precache_cfg(FILE *fp)
{ {

View File

@@ -1,378 +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"
# Begin Custom Build - Copying to DLL folder
TargetPath=.\Release\monster_mm.dll
TargetName=monster_mm
InputPath=.\Release\monster_mm.dll
SOURCE="$(InputPath)"
"$(TargetName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
copy $(TargetPath) D:\Half-Life\valve\dlls
copy $(TargetPath) D:\Half-Life\tfc\dlls
copy $(TargetPath) D:\Half-Life\cstrike\dlls
copy $(TargetPath) D:\Half-Life\dmc\dlls
copy $(TargetPath) D:\Half-Life\dod\dlls
copy $(TargetPath) D:\Half-Life\firearms\dlls
copy $(TargetPath) D:\Half-Life\frontline\dlls
# End Custom Build
!ELSEIF "$(CFG)" == "monster_mm - Win32 Debug"
# 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
# Begin Custom Build - Copying to DLL folder
TargetPath=.\Debug\monster_mm.dll
TargetName=monster_mm
InputPath=.\Debug\monster_mm.dll
SOURCE="$(InputPath)"
"$(TargetName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
copy $(TargetPath) D:\Half-Life\valve\dlls
copy $(TargetPath) D:\Half-Life\tfc\dlls
copy $(TargetPath) D:\Half-Life\cstrike\dlls
copy $(TargetPath) D:\Half-Life\dmc\dlls
copy $(TargetPath) D:\Half-Life\dod\dlls
copy $(TargetPath) D:\Half-Life\firearms\dlls
copy $(TargetPath) D:\Half-Life\frontline\dlls
copy $(TargetPath) D:\Half-Life\gearbox\dlls
# End Custom Build
!ENDIF
# 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=.\flyingmonster.cpp
# End Source File
# Begin Source File
SOURCE=.\ggrenade.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=.\islave.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=.\scientist.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=.\squeakgrenade.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=.\util.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=.\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=.\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=.\monster_plugin.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=.\schedule.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

@@ -5,11 +5,11 @@
#ifndef MONSTER_PLUGIN_H #ifndef MONSTER_PLUGIN_H
#define MONSTER_PLUGIN_H #define MONSTER_PLUGIN_H
typedef struct pKVD typedef struct
{ {
char key[33]; char key[33];
char value[33]; char value[481];
}; } pKVD;
#define MAX_KEYVALUES 32 #define MAX_KEYVALUES 32
@@ -27,26 +27,23 @@ typedef struct
int monster_index; int monster_index;
edict_t *monster_pent; edict_t *monster_pent;
bool killed; bool killed;
int respawn_index;
CMBaseMonster *pMonster; CMBaseMonster *pMonster;
} monster_t; } monster_t;
#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]; extern monster_t monsters[MAX_MONSTER_ENTS];
typedef struct { typedef struct {
Vector origin; Vector origin;
Vector angles; Vector angles;
float delay;
unsigned char monster; unsigned char monster;
int spawnflags; int spawnflags;
pKVD *keyvalue; pKVD *keyvalue;
float respawn_time;
bool need_to_respawn; bool need_to_respawn;
} monster_spawnpoint_t; } monster_spawnpoint_t;
#define MAX_MONSTERS 100 #define MAX_MONSTERS 200
extern monster_spawnpoint_t monster_spawnpoint[MAX_MONSTERS]; 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 // 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 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. // Eat - makes a monster full for a little while.
@@ -120,7 +123,7 @@ void CMBaseMonster :: Look ( int iDistance )
// DON'T let visibility information from last frame sit around! // DON'T let visibility information from last frame sit around!
ClearConditions(bits_COND_SEE_HATE | bits_COND_SEE_DISLIKE | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR | bits_COND_SEE_NEMESIS | bits_COND_SEE_CLIENT); ClearConditions(bits_COND_SEE_HATE | bits_COND_SEE_DISLIKE | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR | bits_COND_SEE_NEMESIS | bits_COND_SEE_CLIENT);
m_edictList_count = 0; m_edictList_count = 0;
edict_t *pSightEnt = NULL;// the current visible entity that we're dealing with edict_t *pSightEnt = NULL;// the current visible entity that we're dealing with
@@ -137,23 +140,20 @@ void CMBaseMonster :: Look ( int iDistance )
{ {
pSightEnt = pList[i]; pSightEnt = pList[i];
// !!!temporarily only considering other monsters and clients, don't see prisoners // !!!temporarily only considering other monsters and clients, don't see prisoners
if ( pSightEnt != this->edict() && if ( pSightEnt != this->edict() && !FBitSet( pSightEnt->v.spawnflags, SF_MONSTER_PRISONER ) && pSightEnt->v.health > 0 )
!FBitSet( pSightEnt->v.spawnflags, SF_MONSTER_PRISONER ) &&
pSightEnt->v.health > 0 )
{ {
// is this a player AND are they alive? // is this a player AND are they alive?
if (UTIL_IsPlayer(pSightEnt) && UTIL_IsAlive(pSightEnt)) if (UTIL_IsPlayer(pSightEnt) && UTIL_IsAlive(pSightEnt))
{ {
// the looker will want to consider this entity // the looker will want to consider this entity
// don't check anything else about an entity that can't be seen. // don't check anything else about an entity that can't be seen.
if ( UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) && if ( UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) && !FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) )
!FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) )
{ {
m_edictList[m_edictList_count] = pSightEnt; m_edictList[m_edictList_count] = pSightEnt;
m_edictList_count++; m_edictList_count++;
// if we see a client, remember that (mostly for scripted AI) // if we see a client, remember that (mostly for scripted AI)
iSighted |= bits_COND_SEE_CLIENT; iSighted |= bits_COND_SEE_CLIENT;
// is this monster NOT a scientist? // is this monster NOT a scientist?
if (strcmp(STRING(pev->model), "models/scientist.mdl") != 0) if (strcmp(STRING(pev->model), "models/scientist.mdl") != 0)
@@ -166,19 +166,19 @@ void CMBaseMonster :: Look ( int iDistance )
iSighted |= bits_COND_SEE_ENEMY; iSighted |= bits_COND_SEE_ENEMY;
} }
} }
} }
} }
else if (pSightEnt->v.euser4 != NULL) else if (pSightEnt->v.euser4 != NULL)
{ {
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pSightEnt)); /* MonsterMod monster looking at another MonsterMod monster */
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pSightEnt));
// the looker will want to consider this entity // the looker will want to consider this entity
// don't check anything else about an entity that can't be seen, or an entity that you don't care about. // don't check anything else about an entity that can't be seen, or an entity that you don't care about.
if ( IRelationship( pMonster ) != R_NO && UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) && if ( IRelationship( pMonster ) != R_NO && UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) && !FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) )
!FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) )
{ {
m_edictList[m_edictList_count] = pSightEnt; m_edictList[m_edictList_count] = pSightEnt;
m_edictList_count++; m_edictList_count++;
if ( ENT(pMonster->pev) == m_hEnemy ) if ( ENT(pMonster->pev) == m_hEnemy )
{ {
@@ -210,6 +210,47 @@ void CMBaseMonster :: Look ( int iDistance )
} }
} }
} }
else
{
/* MonsterMod monster looking at a NON-MonsterMod monster */
// the looker will want to consider this entity
// don't check anything else about an entity that can't be seen, or an entity that you don't care about.
if ( IRelationship( pSightEnt->v.iuser4 ) != R_NO && UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) && !FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) )
{
m_edictList[m_edictList_count] = pSightEnt;
m_edictList_count++;
if ( pSightEnt == m_hEnemy )
{
// we know this ent is visible, so if it also happens to be our enemy, store that now.
iSighted |= bits_COND_SEE_ENEMY;
}
// don't add the Enemy's relationship to the conditions. We only want to worry about conditions when
// we see monsters other than the Enemy.
switch ( IRelationship ( pSightEnt->v.iuser4 ) )
{
case R_NM:
iSighted |= bits_COND_SEE_NEMESIS;
break;
case R_HT:
iSighted |= bits_COND_SEE_HATE;
break;
case R_DL:
iSighted |= bits_COND_SEE_DISLIKE;
break;
case R_FR:
iSighted |= bits_COND_SEE_FEAR;
break;
case R_AL:
break;
default:
ALERT ( at_aiconsole, "%s can't assess %s\n", STRING(pev->classname), STRING(pSightEnt->v.classname ) );
break;
}
}
}
} }
} }
} }
@@ -1514,7 +1555,7 @@ void CMBaseMonster :: Move ( float flInterval )
} }
else else
{ {
//jlb TaskFail(); TaskFail();
ALERT( at_aiconsole, "%s Failed to move (%d)!\n", STRING(pev->classname), HasMemory( bits_MEMORY_MOVE_FAILED ) ); 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 ); //ALERT( at_aiconsole, "%f, %f, %f\n", pev->origin.z, (pev->origin + (vecDir * flCheckDist)).z, m_Route[m_iRouteIndex].vecLocation.z );
} }
@@ -1623,8 +1664,9 @@ void CMBaseMonster :: MonsterInit ( void )
for (int i=0; i < MAX_OLD_ENEMIES; i++) for (int i=0; i < MAX_OLD_ENEMIES; i++)
m_hOldEnemy[ i ] = NULL; m_hOldEnemy[ i ] = NULL;
m_flDistTooFar = 1024.0; if (!m_flDistLook)
m_flDistLook = 2048.0; m_flDistLook = monster_default_maxrange->value;
m_flDistTooFar = m_flDistLook / 2; // always 50%
// set eye position // set eye position
SetEyePosition(); SetEyePosition();
@@ -1743,6 +1785,9 @@ void CMBaseMonster :: StartMonster ( void )
SetActivity( ACT_IDLE ); SetActivity( ACT_IDLE );
ChangeSchedule( GetScheduleOfType( SCHED_WAIT_TRIGGER ) ); ChangeSchedule( GetScheduleOfType( SCHED_WAIT_TRIGGER ) );
} }
// Notify normal game engine of monster classify
pev->iuser4 = Classify();
} }
@@ -1785,25 +1830,35 @@ int CMBaseMonster::TaskIsRunning( void )
//========================================================= //=========================================================
int CMBaseMonster::IRelationship ( CMBaseEntity *pTarget ) int CMBaseMonster::IRelationship ( CMBaseEntity *pTarget )
{ {
static int iEnemy[14][14] = return IRelationshipByClass( pTarget->Classify() );
{ // NONE MACH PLYR HPASS HMIL AMIL APASS AMONST APREY APRED INSECT PLRALY PBWPN ABWPN }
/*NONE*/ { R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO, R_NO, R_NO }, int CMBaseMonster::IRelationship ( int iTargetClass )
/*MACHINE*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_DL, R_DL }, {
/*PLAYER*/ { R_NO ,R_DL ,R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_DL, R_DL }, return IRelationshipByClass( iTargetClass );
/*HUMANPASSIVE*/{ R_NO ,R_NO ,R_AL ,R_AL ,R_HT ,R_FR ,R_NO ,R_HT ,R_DL ,R_FR ,R_NO ,R_AL, R_NO, R_NO }, }
/*HUMANMILITAR*/{ R_NO ,R_NO ,R_HT ,R_DL ,R_NO ,R_HT ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_HT, R_NO, R_NO }, int CMBaseMonster::IRelationshipByClass ( int iClass )
/*ALIENMILITAR*/{ R_NO ,R_DL ,R_HT ,R_DL ,R_HT ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO }, {
/*ALIENPASSIVE*/{ R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO, R_NO, R_NO }, static int iEnemy[16][16] =
/*ALIENMONSTER*/{ R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO }, { // NONE MACH PLYR HPASS HMIL AMIL APASS AMONST APREY APRED INSECT PLRALY PBWPN ABWPN RXPIT RXSHK
/*ALIENPREY */{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_NO ,R_FR ,R_NO ,R_DL, R_NO, R_NO }, /*NONE*/ { R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO, R_NO, R_NO, R_NO, R_NO },
/*ALIENPREDATO*/{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_HT ,R_DL ,R_NO ,R_DL, R_NO, R_NO }, /*MACHINE*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_DL, R_DL, R_DL, R_DL },
/*INSECT*/ { R_FR ,R_FR ,R_FR ,R_FR ,R_FR ,R_NO ,R_FR ,R_FR ,R_FR ,R_FR ,R_NO ,R_FR, R_NO, R_NO }, /*PLAYER*/ { R_NO ,R_DL ,R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_DL, R_DL, R_DL, R_DL },
/*PLAYERALLY*/ { R_NO ,R_DL ,R_AL ,R_AL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_NO, R_NO }, /*HUMANPASSIVE*/{ R_NO ,R_FR ,R_AL ,R_AL ,R_HT ,R_DL ,R_DL ,R_HT ,R_DL ,R_DL ,R_NO ,R_AL, R_NO, R_NO, R_FR, R_FR },
/*PBIOWEAPON*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_NO, R_DL }, /*HUMANMILITAR*/{ R_NO ,R_NO ,R_DL ,R_DL ,R_AL ,R_HT ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_NO, R_NO, R_HT, R_HT },
/*ABIOWEAPON*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_AL ,R_NO ,R_DL ,R_DL ,R_NO ,R_NO ,R_DL, R_DL, R_NO } /*ALIENMILITAR*/{ R_NO ,R_DL ,R_DL ,R_DL ,R_HT ,R_AL ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO, R_DL, R_HT },
/*ALIENPASSIVE*/{ R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO, R_NO, R_NO, R_NO, R_NO },
/*ALIENMONSTER*/{ R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO, R_NO, R_NO },
/*ALIENPREY */{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_NO ,R_FR ,R_NO ,R_DL, R_NO, R_NO, R_FR, R_NO },
/*ALIENPREDATO*/{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_HT ,R_NO ,R_NO ,R_DL, R_NO, R_NO, R_DL, R_DL },
/*INSECT*/ { R_NO ,R_FR ,R_FR ,R_FR ,R_FR ,R_NO ,R_FR ,R_FR ,R_FR ,R_FR ,R_NO ,R_FR, R_NO, R_NO, R_NO, R_NO },
/*PLAYERALLY*/ { R_NO ,R_DL ,R_AL ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_NO, R_NO, R_DL, R_DL },
/*PBIOWEAPON*/ { R_NO ,R_NO ,R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_NO, R_DL, R_DL, R_DL },
/*ABIOWEAPON*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_AL ,R_NO ,R_DL ,R_DL ,R_NO ,R_NO ,R_DL, R_DL, R_NO, R_DL, R_DL },
/*RXPITDRONE*/ { R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL ,R_DL ,R_NO ,R_DL, R_NO, R_NO, R_AL, R_AL },
/*RXSHOCKTRP*/ { R_NO ,R_DL ,R_HT ,R_DL ,R_HT ,R_HT ,R_DL ,R_NO ,R_NO ,R_DL ,R_NO ,R_DL, R_NO, R_NO, R_AL, R_AL }
}; };
return iEnemy[ Classify() ][ pTarget->Classify() ]; return iEnemy[ Classify() ][ iClass ];
} }
//========================================================= //=========================================================
@@ -2017,38 +2072,52 @@ edict_t *CMBaseMonster :: BestVisibleEnemy ( void )
int iNearest; int iNearest;
int iDist; int iDist;
int iBestRelationship; int iBestRelationship;
edict_t *pReturn; edict_t *pReturn;
edict_t *pEnt; edict_t *pEnt;
int edictList_index = 0; int edictList_index = 0;
iNearest = 8192;// so first visible entity will become the closest. iNearest = 8192;// so first visible entity will become the closest.
iBestRelationship = R_NO; iBestRelationship = R_NO;
pReturn = NULL; pReturn = NULL;
while (edictList_index < m_edictList_count) while (edictList_index < m_edictList_count)
{ {
pEnt = m_edictList[edictList_index]; pEnt = m_edictList[edictList_index];
if ( UTIL_IsPlayer(pEnt) ) if ( UTIL_IsPlayer(pEnt) )
{ {
// it's a player... // it's a player...
iDist = ( pEnt->v.origin - pev->origin ).Length(); if ( UTIL_IsAlive(pEnt) )
if ( iDist <= iNearest )
{ {
iNearest = iDist; // repeat2
iBestRelationship = R_NM; // player is always nemsis if ( IRelationshipByClass( CLASS_PLAYER ) > iBestRelationship )
pReturn = pEnt; {
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 = IRelationshipByClass( CLASS_PLAYER );
pReturn = pEnt;
}
}
} }
} }
else if (pEnt->v.euser4 != NULL) else if (pEnt->v.euser4 != NULL)
{ {
// it's a monstermod monster...
CMBaseMonster *pNextEnt = GetClassPtr((CMBaseMonster *)VARS(pEnt)); CMBaseMonster *pNextEnt = GetClassPtr((CMBaseMonster *)VARS(pEnt));
if ( pNextEnt->IsAlive() ) if ( pNextEnt->IsAlive() )
{ {
if ( IRelationship( pNextEnt) > iBestRelationship ) if ( IRelationship( pNextEnt ) > iBestRelationship )
{ {
// this entity is disliked MORE than the entity that we // this entity is disliked MORE than the entity that we
// currently think is the best visible enemy. No need to do // currently think is the best visible enemy. No need to do
@@ -2057,7 +2126,7 @@ edict_t *CMBaseMonster :: BestVisibleEnemy ( void )
iNearest = ( pNextEnt->pev->origin - pev->origin ).Length(); iNearest = ( pNextEnt->pev->origin - pev->origin ).Length();
pReturn = pEnt; pReturn = pEnt;
} }
else if ( IRelationship( pNextEnt) == iBestRelationship ) else if ( IRelationship( pNextEnt ) == iBestRelationship )
{ {
// this entity is disliked just as much as the entity that // this entity is disliked just as much as the entity that
// we currently think is the best visible enemy, so we only // we currently think is the best visible enemy, so we only
@@ -2072,9 +2141,34 @@ edict_t *CMBaseMonster :: BestVisibleEnemy ( void )
} }
} }
} }
} }
else
{
// it's a normal game entity...
if ( UTIL_IsAlive(pEnt) )
{
//repeat3
if ( IRelationship( pEnt->v.iuser4 ) > iBestRelationship )
{
iBestRelationship = IRelationship( pEnt->v.iuser4 );
iNearest = ( pEnt->v.origin - pev->origin ).Length();
pReturn = pEnt;
}
else if ( IRelationship( pEnt->v.iuser4 ) == iBestRelationship )
{
iDist = ( pEnt->v.origin - pev->origin ).Length();
edictList_index++; if ( iDist <= iNearest )
{
iNearest = iDist;
iBestRelationship = IRelationship( pEnt->v.iuser4 );
pReturn = pEnt;
}
}
}
}
edictList_index++;
} }
return pReturn; return pReturn;
@@ -2144,7 +2238,22 @@ float CMBaseMonster::ChangeYaw ( int yawSpeed )
ideal = pev->ideal_yaw; ideal = pev->ideal_yaw;
if (current != ideal) 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; move = ideal - current;
if (ideal > current) if (ideal > current)
@@ -2504,11 +2613,51 @@ void CMBaseMonster :: KeyValue( KeyValueData *pkvd )
m_iszTriggerTarget = ALLOC_STRING( pkvd->szValue ); m_iszTriggerTarget = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE; pkvd->fHandled = TRUE;
} }
else if (FStrEq(pkvd->szKeyName, "TriggerCondition") ) else if (FStrEq(pkvd->szKeyName, "TriggerCondition"))
{ {
m_iTriggerCondition = atoi( pkvd->szValue ); m_iTriggerCondition = atoi( pkvd->szValue );
pkvd->fHandled = TRUE; pkvd->fHandled = TRUE;
} }
else if (FStrEq(pkvd->szKeyName, "displayname"))
{
m_szMonsterName = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "classify"))
{
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 else
{ {
CMBaseToggle::KeyValue( pkvd ); CMBaseToggle::KeyValue( pkvd );
@@ -2872,8 +3021,13 @@ BOOL CMBaseMonster :: GetEnemy ( void )
if (HasConditions(bits_COND_SEE_CLIENT) && (m_hEnemy == NULL)) if (HasConditions(bits_COND_SEE_CLIENT) && (m_hEnemy == NULL))
{ {
m_hEnemy = BestVisibleEnemy(); m_hEnemy = BestVisibleEnemy();
m_hTargetEnt = m_hEnemy;
m_vecEnemyLKP = m_hEnemy->v.origin; // 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 // remember old enemies

View File

@@ -150,9 +150,10 @@ public:
void LimitVelocity( void ); void LimitVelocity( void );
virtual int ObjectCaps( void ) { return (CMBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; } virtual int ObjectCaps( void ) { return (CMBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; }
static void SpawnHeadGib( entvars_t *pevVictim ); static void SpawnHeadGib( entvars_t *pevVictim );
static void SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human ); static void SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human );
static void SpawnStickyGibs( entvars_t *pevVictim, Vector vecOrigin, int cGibs ); static void SpawnRandomGibs( entvars_t *pevVictim, int cGibs, const char *pGibModel, int human );
static void SpawnStickyGibs( entvars_t *pevVictim, Vector vecOrigin, int cGibs );
int m_bloodColor; int m_bloodColor;
int m_cBloodDecals; int m_cBloodDecals;

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

@@ -25,12 +25,12 @@
#include "animation.h" #include "animation.h"
#include "doors.h" #include "doors.h"
//#if !defined ( _WIN32 ) #if !defined ( _WIN32 )
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> // mkdir #include <unistd.h> // mkdir
//#endif #endif
#define HULL_STEP_SIZE 16// how far the test hull moves on each step #define HULL_STEP_SIZE 16// how far the test hull moves on each step
#define NODE_HEIGHT 8 // how high to lift nodes off the ground after we drop them all (make stair/ramp mapping easier) #define NODE_HEIGHT 8 // how high to lift nodes off the ground after we drop them all (make stair/ramp mapping easier)
@@ -38,7 +38,7 @@
// to help eliminate node clutter by level designers, this is used to cap how many other nodes // to help eliminate node clutter by level designers, this is used to cap how many other nodes
// any given node is allowed to 'see' in the first stage of graph creation "LinkVisibleNodes()". // any given node is allowed to 'see' in the first stage of graph creation "LinkVisibleNodes()".
#define MAX_NODE_INITIAL_LINKS 128 #define MAX_NODE_INITIAL_LINKS 128
#define MAX_NODES 1024 //#define MAX_NODES 1024 // already defined in monster_plugin.h
Vector VecBModelOrigin( entvars_t* pevBModel ); Vector VecBModelOrigin( entvars_t* pevBModel );
@@ -212,6 +212,10 @@ entvars_t* CGraph :: LinkEntForLink ( CLink *pLink, CNode *pNode )
//========================================================= //=========================================================
int CGraph :: HandleLinkEnt ( int iNode, entvars_t *pevLinkEnt, int afCapMask, NODEQUERY queryType ) 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; edict_t *pentWorld;
CMBaseEntity *pDoor; CMBaseEntity *pDoor;
TraceResult tr; TraceResult tr;
@@ -282,6 +286,7 @@ int CGraph :: HandleLinkEnt ( int iNode, entvars_t *pevLinkEnt, int afCapMask, N
} }
return FALSE; return FALSE;
#endif
} }
#if 0 #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, " 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; pLinkPool [ cTotalLinks ].m_iDestNode = j;
@@ -1485,8 +1490,6 @@ void CTestHull :: Spawn( entvars_t *pevMasterNode )
//========================================================= //=========================================================
void CTestHull::DropDelay ( void ) void CTestHull::DropDelay ( void )
{ {
// Do NOT uncomment or you'll get a "Tried to create a message with a bogus message type ( 0 )" crash!
// Left here only because it's on the original HLSDK, and for comedy purposes. -Giegue
//UTIL_CenterPrintAll( "Node Graph out of Date. Rebuilding..." ); //UTIL_CenterPrintAll( "Node Graph out of Date. Rebuilding..." );
UTIL_SetOrigin ( VARS(pev), WorldGraph.m_pNodes[ 0 ].m_vecOrigin ); UTIL_SetOrigin ( VARS(pev), WorldGraph.m_pNodes[ 0 ].m_vecOrigin );
@@ -1891,17 +1894,17 @@ void CTestHull :: BuildNodeGraph( void )
switch ( hull ) switch ( hull )
{ {
case NODE_SMALL_HULL: // if this hull can't fit, nothing can, so drop the connection 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); 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 fSkipRemainingHulls = TRUE;// don't bother checking larger hulls
break; break;
case NODE_HUMAN_HULL: 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); pTempPool[ pSrcNode->m_iFirstLink + j ].m_afLinkInfo &= ~(bits_LINK_HUMAN_HULL | bits_LINK_LARGE_HULL);
fSkipRemainingHulls = TRUE;// don't bother checking larger hulls fSkipRemainingHulls = TRUE;// don't bother checking larger hulls
break; break;
case NODE_LARGE_HULL: 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; pTempPool[ pSrcNode->m_iFirstLink + j ].m_afLinkInfo &= ~bits_LINK_LARGE_HULL;
break; break;
} }
@@ -3439,7 +3442,7 @@ void CGraph :: TestRoutingTables( void )
#endif #endif
ALERT(at_aiconsole, "Routing is inconsistent!!!\n"); ALERT(at_aiconsole, "Routing is inconsistent!!!\n");
ALERT(at_aiconsole, "(%d to %d |%d/%d)1:", iFrom, iTo, iHull, iCap); 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]); ALERT(at_aiconsole, "%d ", pMyPath[i]);
} }

329
src/dlls/otis.cpp Normal file
View File

@@ -0,0 +1,329 @@
// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository!
/***
*
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* This source code contains proprietary and confidential information of
* Valve LLC and its suppliers. Access to this code is restricted to
* persons who have executed a written SDK license with Valve. Any access,
* use or distribution of this code by or to any unlicensed person is illegal.
*
****/
//=========================================================
// monster template
//=========================================================
// UNDONE: Holster weapon?
#include "extdll.h"
#include "util.h"
#include "cmbase.h"
#include "cmbasemonster.h"
#include "monsters.h"
#include "schedule.h"
#include "defaultai.h"
#include "weapons.h"
#define NUM_OTIS_HEADS 2 // heads available for otis model
#define GUN_GROUP 1
#define HEAD_GROUP 2
#define HEAD_HAIR 0
#define HEAD_BALD 1
#define GUN_NONE 0
#define GUN_EAGLE 1
#define GUN_DONUT 2
//=========================================================
// Monster's Anim Events Go Here
//=========================================================
// first flag is Otis dying for scripted sequences?
#define OTIS_AE_DRAW ( 2 )
#define OTIS_AE_SHOOT ( 3 )
#define OTIS_AE_HOLSTER ( 4 )
#define OTIS_BODY_GUNHOLSTERED 0
#define OTIS_BODY_GUNDRAWN 1
#define OTIS_BODY_DONUT 2
//=========================================================
// ALertSound - otis says "Freeze!"
//=========================================================
void CMOtis::AlertSound(void)
{
if (m_hEnemy != 0)
{
if (FOkToSpeak())
{
PlaySentence("OT_ATTACK", RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE);
}
}
}
//=========================================================
// BarneyFirePistol - shoots one round from the pistol at
// the enemy otis is facing.
//=========================================================
void CMOtis::BarneyFirePistol(void)
{
Vector vecShootOrigin;
UTIL_MakeVectors(pev->angles);
vecShootOrigin = pev->origin + Vector(0, 0, 55);
Vector vecShootDir = ShootAtEnemy(vecShootOrigin);
Vector angDir = UTIL_VecToAngles(vecShootDir);
SetBlending(0, angDir.x);
pev->effects = EF_MUZZLEFLASH;
FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024, BULLET_MONSTER_357);
int pitchShift = RANDOM_LONG(0, 20);
// Only shift about half the time
if (pitchShift > 10)
pitchShift = 0;
else
pitchShift -= 5;
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "weapons/desert_eagle_fire.wav", 1, ATTN_NORM, 0, 100 + pitchShift);
// UNDONE: Reload?
m_cAmmoLoaded--;// take away a bullet!
}
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//
// Returns number of events handled, 0 if none.
//=========================================================
void CMOtis::HandleAnimEvent(MonsterEvent_t *pEvent)
{
switch (pEvent->event)
{
case OTIS_AE_SHOOT:
BarneyFirePistol();
break;
case OTIS_AE_DRAW:
// otis' bodygroup switches here so he can pull gun from holster
// pev->body = OTIS_BODY_GUNDRAWN;
SetBodygroup( GUN_GROUP, GUN_EAGLE );
m_fGunDrawn = TRUE;
break;
case OTIS_AE_HOLSTER:
// change bodygroup to replace gun in holster
// pev->body = OTIS_BODY_GUNHOLSTERED;
SetBodygroup( GUN_GROUP, GUN_NONE );
m_fGunDrawn = FALSE;
break;
default:
CMBarney::HandleAnimEvent(pEvent);
}
}
//=========================================================
// Spawn
//=========================================================
void CMOtis::Spawn()
{
Precache();
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/otis.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = !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
m_MonsterState = MONSTERSTATE_NONE;
pev->body = 0; // gun in holster
m_fGunDrawn = FALSE;
m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
// Make sure hands are white.
pev->skin = 0;
// Select a random head.
if (head == -1)
{
SetBodygroup(HEAD_GROUP, RANDOM_LONG(0, NUM_OTIS_HEADS - 1));
}
else
{
SetBodygroup(HEAD_GROUP, head);
}
if (bodystate == -1)
{
SetBodygroup(GUN_GROUP, RANDOM_LONG(OTIS_BODY_GUNHOLSTERED, OTIS_BODY_GUNDRAWN)); // don't random donut
}
else
{
SetBodygroup(GUN_GROUP, bodystate);
}
MonsterInit();
pev->classname = MAKE_STRING( "monster_otis" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Otis" );
}
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CMOtis::Precache()
{
PRECACHE_MODEL("models/otis.mdl");
PRECACHE_SOUND("weapons/desert_eagle_fire.wav");
PRECACHE_SOUND("barney/ba_pain1.wav");
PRECACHE_SOUND("barney/ba_pain2.wav");
PRECACHE_SOUND("barney/ba_pain3.wav");
PRECACHE_SOUND("barney/ba_die1.wav");
PRECACHE_SOUND("barney/ba_die2.wav");
PRECACHE_SOUND("barney/ba_die3.wav");
// every new otis must call this, otherwise
// when a level is loaded, nobody will talk (time is reset to 0)
TalkInit();
CMTalkMonster::Precache();
}
// Init talk data
void CMOtis::TalkInit()
{
CMTalkMonster::TalkInit();
// scientists speach group names (group names are in sentences.txt)
m_szGrp[TLK_ANSWER] = "OT_ANSWER";
m_szGrp[TLK_QUESTION] = "OT_QUESTION";
m_szGrp[TLK_IDLE] = "OT_IDLE";
m_szGrp[TLK_STARE] = "OT_STARE";
m_szGrp[TLK_USE] = "OT_OK";
m_szGrp[TLK_UNUSE] = "OT_WAIT";
m_szGrp[TLK_STOP] = "OT_STOP";
m_szGrp[TLK_NOSHOOT] = "OT_SCARED";
m_szGrp[TLK_HELLO] = "OT_HELLO";
m_szGrp[TLK_PLHURT1] = "!OT_CUREA";
m_szGrp[TLK_PLHURT2] = "!OT_CUREB";
m_szGrp[TLK_PLHURT3] = "!OT_CUREC";
m_szGrp[TLK_PHELLO] = NULL;
m_szGrp[TLK_PIDLE] = NULL;
m_szGrp[TLK_PQUESTION] = NULL;
m_szGrp[TLK_SMELL] = "OT_SMELL";
m_szGrp[TLK_WOUND] = "OT_WOUND";
m_szGrp[TLK_MORTAL] = "OT_MORTAL";
// get voice for head - just one otis voice for now
m_voicePitch = 100;
}
int CMOtis::TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType)
{
// make sure friends talk about it if player hurts talkmonsters...
int ret = CMTalkMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType);
if (!IsAlive() || pev->deadflag == DEAD_DYING)
return ret;
if (m_MonsterState != MONSTERSTATE_PRONE && (pevAttacker->flags & FL_CLIENT))
{
// This is a heurstic to determine if the player intended to harm me
// If I have an enemy, we can't establish intent (may just be crossfire)
if ( ( m_hEnemy != NULL ) && UTIL_IsPlayer(m_hEnemy) )
{
Remember( bits_MEMORY_PROVOKED );
}
}
return ret;
}
void CMOtis::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
{
switch (ptr->iHitgroup)
{
case HITGROUP_CHEST:
case HITGROUP_STOMACH:
if (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_BLAST))
{
flDamage = flDamage / 2;
}
break;
case 10: // Otis wears no helmet, so do not prevent taking headshot damage.
// always a head shot
ptr->iHitgroup = HITGROUP_HEAD;
break;
default:
break;
}
CMTalkMonster::TraceAttack(pevAttacker, flDamage, vecDir, ptr, bitsDamageType);
}
void CMOtis::Killed(entvars_t *pevAttacker, int iGib)
{
if (GetBodygroup(GUN_GROUP) != OTIS_BODY_GUNHOLSTERED)
{
// drop the gun!
SetBodygroup(GUN_GROUP, OTIS_BODY_GUNHOLSTERED);
}
CMTalkMonster::Killed(pevAttacker, iGib);
}
//=========================================================
// AI Schedules Specific to this monster
//=========================================================
//=========================================================
// GetSchedule - Decides which type of schedule best suits
// the monster's current state and conditions. Then calls
// monster's member function to get a pointer to a schedule
// of the proper type.
//=========================================================
Schedule_t *CMOtis::GetSchedule(void)
{
if (HasConditions(bits_COND_ENEMY_DEAD) && FOkToSpeak())
{
PlaySentence("OT_KILL", 4, VOL_NORM, ATTN_NORM);
}
return CMBarney::GetSchedule();
}
void CMOtis::KeyValue(KeyValueData *pkvd)
{
if (FStrEq(pkvd->szKeyName, "head"))
{
head = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else
CMBarney::KeyValue(pkvd);
}

1072
src/dlls/pitdrone.cpp Normal file

File diff suppressed because it is too large Load Diff

423
src/dlls/rgrunt.cpp Normal file
View File

@@ -0,0 +1,423 @@
/***
*
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* This source code contains proprietary and confidential information of
* Valve LLC and its suppliers. Access to this code is restricted to
* persons who have executed a written SDK license with Valve. Any access,
* use or distribution of this code by or to any unlicensed person is illegal.
*
****/
//=========================================================
// Robo Grunt
//=========================================================
#include "extdll.h"
#include "plane.h"
#include "util.h"
#include "cmbase.h"
#include "cmbasemonster.h"
#include "monsters.h"
#include "schedule.h"
#include "animation.h"
#include "weapons.h"
#include "cmtalkmonster.h"
#include "effects.h"
#include "explode.h"
#include "customentity.h"
//=========================================================
// monster-specific DEFINE's
//=========================================================
#define RGRUNT_CLIP_SIZE 36 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x!
// Weapon flags
#define RGRUNT_9MMAR (1 << 0)
#define RGRUNT_HANDGRENADE (1 << 1)
#define RGRUNT_GRENADELAUNCHER (1 << 2)
#define RGRUNT_SHOTGUN (1 << 3)
// Body groups
#define GUN_GROUP 2
// Gun values
#define GUN_MP5 0
#define GUN_SHOTGUN 1
#define GUN_NONE 2
// How many sparks to emit when low on health
#define RGRUNT_MAX_SPARKS 5
//=========================================================
// This sound is muted for Robo Grunts
//=========================================================
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
//=========================================================
void CMRGrunt::DeathSound(void)
{
switch ( RANDOM_LONG(0,1) )
{
case 0:
EMIT_SOUND( ENT(pev), CHAN_VOICE, "turret/tu_die.wav", 1, ATTN_IDLE );
break;
case 1:
EMIT_SOUND( ENT(pev), CHAN_VOICE, "turret/tu_die2.wav", 1, ATTN_IDLE );
break;
}
// Use this spot to activate the explosion
int duration = RANDOM_LONG( 3, 9 );
/* Smoke effect */
// variable smoke balls
// 1.7X normal size
// 0 radial distribution
// instant start
SmokeCreate( pev->origin, duration * 7, 17, 0, 0 );
// "Gib"
pev->nextthink = gpGlobals->time + duration;
SetThink( &CMRGrunt::StartGib );
}
//=========================================================
// Classify - indicates this monster's place in the
// relationship table.
//=========================================================
int CMRGrunt::Classify(void)
{
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_HUMAN_MILITARY;
}
//=========================================================
// Killed - Explode a few seconds after death
//=========================================================
void CMRGrunt::Killed(entvars_t *pevAttacker, int iGib)
{
// Turn off electricity
if ( m_flActiveDischarge != 0 )
{
pev->renderfx = kRenderFxNone;
m_flActiveDischarge = 0;
}
// Disallow this monster to fade away, need to keep it around for the explosion
pev->owner = 0;
pev->spawnflags &= ~SF_MONSTER_FADECORPSE;
pev->solid = SOLID_NOT; // stop interacting with the world
CMBaseMonster::Killed(pevAttacker, iGib);
}
void CMRGrunt::StartGib(void)
{
// derp
GibMonster();
}
//=========================================================
// GibMonster - Boom!
//=========================================================
void CMRGrunt::GibMonster()
{
// Don't call this more times than needed
if ( pev->iuser1 != 0 )
return;
pev->iuser1 = 1;
Vector vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5;
// Explosion
ExplosionCreate( vecSpot, g_vecZero, ENT(pev), 128, 0, 0 );
// Wreckage
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot );
WRITE_BYTE( TE_BREAKMODEL );
// position
WRITE_COORD( vecSpot.x );
WRITE_COORD( vecSpot.y );
WRITE_COORD( vecSpot.z );
// size
WRITE_COORD( 96 );
WRITE_COORD( 96 );
WRITE_COORD( 16 );
// velocity
WRITE_COORD( 0 );
WRITE_COORD( 0 );
WRITE_COORD( 30 );
// randomization
WRITE_BYTE( 15 );
// Model
WRITE_SHORT( m_iBodyGibs ); //model id#
// # of shards
WRITE_BYTE( 35 );
// duration
WRITE_BYTE( 100 );// 5.0 seconds
// flags
WRITE_BYTE( BREAK_METAL );
MESSAGE_END();
SetThink( &CMBaseEntity::SUB_Remove );
pev->nextthink = gpGlobals->time;
}
//=========================================================
// RunAI - Robo Grunt emits sparks when its low on health.
//=========================================================
void CMRGrunt::RunAI(void)
{
CMBaseMonster::RunAI();
if ( pev->health <= ( pev->max_health / 10 ) ) // below 10% health
{
// Spark ON
if ( gpGlobals->time > m_flNextSpark )
{
// Code looks familiar? It's CBaseButton::DoSpark
for ( int spark_num = 0; spark_num < RGRUNT_MAX_SPARKS; spark_num++ )
{
Vector tmp = pev->origin + (pev->mins + pev->maxs) * 0.5; // grab center
tmp.x += RANDOM_FLOAT( -( pev->size.x / 2 ), pev->size.x / 2); // then randomize
tmp.y += RANDOM_FLOAT( -( pev->size.y / 2 ), pev->size.y / 2);
tmp.z += RANDOM_FLOAT( -( pev->size.z / 2 ), pev->size.z / 2);
UTIL_Sparks( tmp );
}
switch ( (int)(RANDOM_FLOAT(0,1) * 6) )
{
case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark1.wav", 0.6, ATTN_NORM); break;
case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark2.wav", 0.6, ATTN_NORM); break;
case 2: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark3.wav", 0.6, ATTN_NORM); break;
case 3: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark4.wav", 0.6, ATTN_NORM); break;
case 4: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark5.wav", 0.6, ATTN_NORM); break;
case 5: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark6.wav", 0.6, ATTN_NORM); break;
}
m_flNextSpark = gpGlobals->time + 0.5;
}
// Glow/Hurt ON
if ( gpGlobals->time > m_flNextDischarge )
{
// Turn on the electric glow
pev->renderfx = kRenderFxGlowShell;
pev->rendercolor = Vector( 100, 150, 250 ); // r, g, b
EMIT_SOUND(ENT(pev), CHAN_BODY, "debris/beamstart14.wav", 0.8, ATTN_NORM);
// Sustain the electricity for this long
m_flActiveDischarge = gpGlobals->time + RANDOM_FLOAT( 0.3, 0.6 );
// Discharge again in...
m_flNextDischarge = gpGlobals->time + RANDOM_FLOAT( 0.9, 2.7 );
}
// Glow/Hurt OFF
if ( gpGlobals->time > m_flActiveDischarge )
{
// Turn off electricity
pev->renderfx = kRenderFxNone;
m_flActiveDischarge = 0;
}
}
}
//=========================================================
// SparkTouch - Hurt players who come too close to it
//=========================================================
void CMRGrunt::SparkTouch( edict_t *pOther )
{
// No electricity, no harm
if ( m_flActiveDischarge == 0 )
return;
// Only affect players
if ( UTIL_IsPlayer( pOther ) )
{
// Because of Touch(), players are going to be hurt every server frame.
// Don't be bullshit like Sven Co-op, set the damage REALLY LOW.
UTIL_TakeDamage( pOther, pev, pev, 1, DMG_SHOCK );
}
}
//=========================================================
// TraceAttack - Override for robo grunt
// Emit ricochet sparks if getting hurt from bullets
//=========================================================
void CMRGrunt::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
{
// Absorb damage and emit ricochet if bullets or melee attacks are used
if ( bitsDamageType & ( DMG_BULLET | DMG_SLASH | DMG_CLUB ) )
{
if ( RANDOM_LONG( 0, 100 ) < 20 )
{
UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT( 0.5, 1.5 ) );
// EMIT_SOUND_DYN( ENT(pev), CHAN_BODY, pRicSounds[ RANDOM_LONG(0,ARRAYSIZE(pRicSounds)-1) ], 1.0, ATTN_NORM, 0, PITCH_NORM );
}
flDamage *= (1.00f - gSkillData.rgruntArmor); // cut damage
}
// Lower protection against explosions
else if ( bitsDamageType & DMG_BLAST )
flDamage *= (1.00f - (gSkillData.rgruntArmor / 2.00f)); // 50% less protection
// No protection at all against other types of damages
CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
}
//=========================================================
// TakeDamage - Robo Grunts should not take cover as soon
// as they take damage.
//=========================================================
int CMRGrunt::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
}
//=========================================================
// Spawn
//=========================================================
void CMRGrunt::Spawn()
{
Precache();
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/rgrunt.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = DONT_BLEED;
pev->effects = 0;
pev->health = gSkillData.rgruntHealth;
pev->max_health = pev->health; // to determine when sparks should be emitted
m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
m_MonsterState = MONSTERSTATE_NONE;
m_flNextGrenadeCheck = gpGlobals->time + 1;
m_flNextPainTime = gpGlobals->time;
m_flNextSpark = gpGlobals->time; // when to emit sparks again
m_flNextDischarge = gpGlobals->time; // when electric shell should activate
m_flActiveDischarge = 0; // how long to sustain the electricity
m_iSentence = -1;
//m_afCapability = bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
m_afCapability = bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
//m_fEnemyEluded = FALSE;
m_fFirstEncounter = TRUE;// this is true when the grunt spawns, because he hasn't encountered an enemy yet.
m_HackedGunPos = Vector(0, 0, 55);
if (pev->weapons == 0)
{
// weapons not specified, randomize
switch(RANDOM_LONG(0, 2))
{
case 0:
pev->weapons = RGRUNT_9MMAR | RGRUNT_HANDGRENADE;
break;
case 1:
pev->weapons = RGRUNT_SHOTGUN;
break;
case 2:
pev->weapons = RGRUNT_9MMAR | RGRUNT_GRENADELAUNCHER;
break;
}
}
if (FBitSet(pev->weapons, RGRUNT_SHOTGUN))
{
SetBodygroup(GUN_GROUP, GUN_SHOTGUN);
m_cClipSize = 8;
}
else
{
m_cClipSize = RGRUNT_CLIP_SIZE;
}
m_cAmmoLoaded = m_cClipSize;
CMTalkMonster::g_talkWaitTime = 0;
MonsterInit();
SetTouch( &CMRGrunt::SparkTouch );
pev->classname = MAKE_STRING( "monster_robogrunt" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Robo Grunt" );
}
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CMRGrunt::Precache()
{
PRECACHE_MODEL("models/rgrunt.mdl");
m_iBodyGibs = PRECACHE_MODELINDEX( "models/metalplategibs_green.mdl" );
PRECACHE_SOUND("hgrunt/gr_mgun1.wav");
PRECACHE_SOUND("hgrunt/gr_mgun2.wav");
PRECACHE_SOUND("turret/tu_die.wav");
PRECACHE_SOUND("turret/tu_die2.wav");
PRECACHE_SOUND("buttons/spark1.wav");
PRECACHE_SOUND("buttons/spark2.wav");
PRECACHE_SOUND("buttons/spark3.wav");
PRECACHE_SOUND("buttons/spark4.wav");
PRECACHE_SOUND("buttons/spark5.wav");
PRECACHE_SOUND("buttons/spark6.wav");
PRECACHE_SOUND("debris/beamstart14.wav");
PRECACHE_SOUND("hgrunt/gr_reload1.wav");
PRECACHE_SOUND("weapons/glauncher.wav");
PRECACHE_SOUND("weapons/sbarrel1.wav");
PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event
// get voice pitch
m_voicePitch = 115; // always the same
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

@@ -517,6 +517,11 @@ void CMScientist :: RunTask( Task_t *pTask )
//========================================================= //=========================================================
int CMScientist :: Classify ( void ) int CMScientist :: Classify ( void )
{ {
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_HUMAN_PASSIVE; return CLASS_HUMAN_PASSIVE;
} }
@@ -591,12 +596,12 @@ void CMScientist :: Spawn( void )
// when a level is loaded, nobody will talk (time is reset to 0) // when a level is loaded, nobody will talk (time is reset to 0)
TalkInit(); TalkInit();
SET_MODEL(ENT(pev), "models/scientist.mdl"); SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/scientist.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX; pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP; pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_RED; m_bloodColor = !m_bloodColor ? BLOOD_COLOR_RED : m_bloodColor;
pev->health = gSkillData.scientistHealth; pev->health = gSkillData.scientistHealth;
pev->view_ofs = Vector ( 0, 0, 50 );// position of the eyes relative to monster's origin. 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 m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so scientists will notice player and say hello
@@ -616,6 +621,13 @@ void CMScientist :: Spawn( void )
pev->skin = 1; pev->skin = 1;
MonsterInit(); MonsterInit();
pev->classname = MAKE_STRING( "monster_scientist" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Scientist" );
}
} }
//========================================================= //=========================================================

235
src/dlls/shock.cpp Normal file
View File

@@ -0,0 +1,235 @@
// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository!
/***
*
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* This source code contains proprietary and confidential information of
* Valve LLC and its suppliers. Access to this code is restricted to
* persons who have executed a written SDK license with Valve. Any access,
* use or distribution of this code by or to any unlicensed person is illegal.
*
****/
//=========================================================
// shock - projectile shot from shockrifles.
//=========================================================
#include "extdll.h"
#include "util.h"
#include "cmbase.h"
#include "cmbasemonster.h"
#include "monsters.h"
#include "schedule.h"
#include "effects.h"
#include "decals.h"
#include "weapons.h"
#include "customentity.h"
#include "shock.h"
void CMShock::Spawn()
{
Precache();
pev->movetype = MOVETYPE_FLY;
pev->solid = SOLID_BBOX;
pev->classname = MAKE_STRING("shock_beam");
SET_MODEL(ENT(pev), "models/shock_effect.mdl");
UTIL_SetOrigin(pev, pev->origin);
pev->dmg = gSkillData.monDmgShockroach;
UTIL_SetSize(pev, Vector(-4, -4, -4), Vector(4, 4, 4));
CreateEffects();
SetThink( &CMShock::FlyThink );
pev->nextthink = gpGlobals->time;
}
void CMShock::Precache()
{
PRECACHE_MODEL("sprites/flare3.spr");
PRECACHE_MODEL("sprites/lgtning.spr");
PRECACHE_MODEL("models/shock_effect.mdl");
PRECACHE_SOUND("weapons/shock_impact.wav");
}
void CMShock::FlyThink()
{
if (pev->waterlevel == 3)
{
entvars_t *pevOwner = VARS(pev->owner);
EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/shock_impact.wav", VOL_NORM, ATTN_NORM);
RadiusDamage(pev->origin, pev, pevOwner ? pevOwner : pev, pev->dmg * 3, 144, CLASS_NONE, DMG_SHOCK | DMG_ALWAYSGIB );
ClearEffects();
SetThink( &CMBaseEntity::SUB_Remove );
pev->nextthink = gpGlobals->time;
}
else
{
pev->nextthink = gpGlobals->time + 0.05;
}
}
edict_t *CMShock::Shoot(entvars_t *pevOwner, const Vector angles, const Vector vecStart, const Vector vecVelocity)
{
CMShock *pShock = CreateClassPtr((CMShock *)NULL);
if (pShock == NULL)
return NULL;
UTIL_SetOrigin(pShock->pev, vecStart);
pShock->Spawn();
pShock->pev->velocity = vecVelocity;
pShock->pev->owner = ENT(pevOwner);
pShock->pev->angles = angles;
pShock->pev->nextthink = gpGlobals->time;
return pShock->edict();
}
void CMShock::Touch(edict_t *pOther)
{
// Do not collide with the owner.
if (pOther == pev->owner)
return;
TraceResult tr = UTIL_GetGlobalTrace( );
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
WRITE_BYTE(TE_DLIGHT);
WRITE_COORD(pev->origin.x); // X
WRITE_COORD(pev->origin.y); // Y
WRITE_COORD(pev->origin.z); // Z
WRITE_BYTE( 8 ); // radius * 0.1
WRITE_BYTE( 0 ); // r
WRITE_BYTE( 255 ); // g
WRITE_BYTE( 255 ); // b
WRITE_BYTE( 10 ); // time * 10
WRITE_BYTE( 10 ); // decay * 0.1
MESSAGE_END( );
ClearEffects();
if (!pOther->v.takedamage)
{
// make a splat on the wall
const int baseDecal = DECAL_SCORCH1;
UTIL_DecalTrace(&tr, baseDecal + RANDOM_LONG(0, 1));
int iContents = UTIL_PointContents(pev->origin);
// Create sparks
if (iContents != CONTENTS_WATER)
{
UTIL_Sparks(tr.vecEndPos);
}
}
else
{
int damageType = DMG_SHOCK;
ClearMultiDamage();
entvars_t *pevOwner = VARS(pev->owner);
entvars_t *pevAttacker = pevOwner ? pevOwner : pev;
if ( UTIL_IsPlayer( pOther ) )
UTIL_TraceAttack( pOther, pevAttacker, pev->dmg, pev->velocity.Normalize(), &tr, damageType );
else if ( pOther->v.euser4 != NULL )
{
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
pMonster->TraceAttack( pevAttacker, pev->dmg, pev->velocity.Normalize(), &tr, damageType );
}
else
UTIL_TraceAttack( pOther, pevAttacker, pev->dmg, pev->velocity.Normalize(), &tr, damageType );
ApplyMultiDamage(pev, pevAttacker);
}
// splat sound
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/shock_impact.wav", VOL_NORM, ATTN_NORM);
pev->modelindex = 0;
pev->solid = SOLID_NOT;
SetThink( &CMBaseEntity::SUB_Remove );
pev->nextthink = gpGlobals->time + 0.01; // let the sound play
}
void CMShock::CreateEffects()
{
m_pSprite = CMSprite::SpriteCreate( "sprites/flare3.spr", pev->origin, FALSE );
m_pSprite->SetAttachment( edict(), 0 );
m_pSprite->pev->scale = 0.35;
m_pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 170, kRenderFxNoDissipation );
//m_pSprite->pev->spawnflags |= SF_SPRITE_TEMPORARY;
//m_pSprite->pev->flags |= FL_SKIPLOCALHOST;
m_pBeam = CMBeam::BeamCreate( "sprites/lgtning.spr", 30 );
if (m_pBeam)
{
m_pBeam->EntsInit( entindex(), entindex() );
m_pBeam->SetStartAttachment( 1 );
m_pBeam->SetEndAttachment( 2 );
m_pBeam->SetBrightness( 180 );
m_pBeam->SetScrollRate( 10 );
m_pBeam->SetNoise( 0 );
m_pBeam->SetFlags( BEAM_FSHADEOUT );
m_pBeam->SetColor( 0, 255, 255 );
//m_pBeam->pev->spawnflags = SF_BEAM_TEMPORARY;
m_pBeam->RelinkBeam();
}
else
{
ALERT(at_console, "Could not create shockbeam beam!\n");
}
m_pNoise = CMBeam::BeamCreate( "sprites/lgtning.spr", 30 );
if (m_pNoise)
{
m_pNoise->EntsInit( entindex(), entindex() );
m_pNoise->SetStartAttachment( 1 );
m_pNoise->SetEndAttachment( 2 );
m_pNoise->SetBrightness( 180 );
m_pNoise->SetScrollRate( 30 );
m_pNoise->SetNoise( 30 );
m_pNoise->SetFlags( BEAM_FSHADEOUT );
m_pNoise->SetColor( 255, 255, 173 );
//m_pNoise->pev->spawnflags = SF_BEAM_TEMPORARY;
m_pNoise->RelinkBeam();
}
else
{
ALERT(at_console, "Could not create shockbeam noise!\n");
}
}
void CMShock::ClearEffects()
{
if (m_pBeam)
{
UTIL_Remove( m_pBeam->edict() );
m_pBeam = NULL;
}
if (m_pNoise)
{
UTIL_Remove( m_pNoise->edict() );
m_pNoise = NULL;
}
if (m_pSprite)
{
UTIL_Remove( m_pSprite->edict() );
m_pSprite = NULL;
}
}
void CMShock::UpdateOnRemove()
{
CMBaseAnimating::UpdateOnRemove();
ClearEffects();
}

25
src/dlls/shock.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef SHOCKBEAM_H
#define SHOCKBEAM_H
//=========================================================
// Shockrifle projectile
//=========================================================
class CMShock : public CMBaseAnimating
{
public:
void Spawn(void);
void Precache(void);
static edict_t *Shoot(entvars_t *pevOwner, const Vector angles, const Vector vecStart, const Vector vecVelocity);
void Touch(edict_t *pOther);
void EXPORT FlyThink();
void CreateEffects();
void ClearEffects();
void UpdateOnRemove();
CMBeam *m_pBeam;
CMBeam *m_pNoise;
CMSprite *m_pSprite;
};
#endif

246
src/dlls/shockroach.cpp Normal file
View File

@@ -0,0 +1,246 @@
// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository!
/***
*
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* This source code contains proprietary and confidential information of
* Valve LLC and its suppliers. Access to this code is restricted to
* persons who have executed a written SDK license with Valve. Any access,
* use or distribution of this code by or to any unlicensed person is illegal.
*
****/
//=========================================================
// shockroach.cpp
//=========================================================
#include "extdll.h"
#include "util.h"
#include "cmbase.h"
#include "cmbasemonster.h"
#include "monsters.h"
#include "schedule.h"
#include "weapons.h"
#define SR_AE_JUMPATTACK ( 2 )
const char *CMShockRoach::pIdleSounds[] =
{
"shockroach/shock_idle1.wav",
"shockroach/shock_idle2.wav",
"shockroach/shock_idle3.wav",
};
const char *CMShockRoach::pAlertSounds[] =
{
"shockroach/shock_angry.wav",
};
const char *CMShockRoach::pPainSounds[] =
{
"shockroach/shock_flinch.wav",
};
const char *CMShockRoach::pAttackSounds[] =
{
"shockroach/shock_jump1.wav",
"shockroach/shock_jump2.wav",
};
const char *CMShockRoach::pDeathSounds[] =
{
"shockroach/shock_die.wav",
};
const char *CMShockRoach::pBiteSounds[] =
{
"shockroach/shock_bite.wav",
};
//=========================================================
// Spawn
//=========================================================
void CMShockRoach::Spawn()
{
Precache();
SET_MODEL(ENT(pev), "models/w_shock_rifle.mdl");
UTIL_SetOrigin(pev, pev->origin);
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_FLY;
m_bloodColor = BLOOD_COLOR_YELLOW;
pev->effects = 0;
pev->health = gSkillData.roachHealth;
pev->view_ofs = Vector(0, 0, 20);// position of the eyes relative to monster's origin.
pev->yaw_speed = 5;//!!! should we put this in the monster's changeanim function since turn rates may vary with state/anim?
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
m_MonsterState = MONSTERSTATE_NONE;
m_fRoachSolid = 0;
m_flBirthTime = gpGlobals->time;
MonsterInit();
pev->classname = MAKE_STRING( "monster_shockroach" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Shock Roach" );
}
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CMShockRoach::Precache()
{
PRECACHE_SOUND_ARRAY(pIdleSounds);
PRECACHE_SOUND_ARRAY(pAlertSounds);
PRECACHE_SOUND_ARRAY(pPainSounds);
PRECACHE_SOUND_ARRAY(pAttackSounds);
PRECACHE_SOUND_ARRAY(pDeathSounds);
PRECACHE_SOUND_ARRAY(pBiteSounds);
PRECACHE_SOUND("shockroach/shock_walk.wav");
PRECACHE_MODEL("models/w_shock_rifle.mdl");
}
//=========================================================
// LeapTouch - this is the headcrab's touch function when it
// is in the air
//=========================================================
void CMShockRoach::LeapTouch(edict_t *pOther)
{
if (!pOther->v.takedamage)
{
return;
}
// Don't hit if back on ground
if (!FBitSet(pev->flags, FL_ONGROUND))
{
EMIT_SOUND_DYN(edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBiteSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
if (UTIL_IsPlayer(pOther))
UTIL_TakeDamage( pOther, pev, pev, GetDamageAmount(), DMG_SLASH );
else if (pOther->v.euser4 != NULL)
{
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
pMonster->TakeDamage( pev, pev, GetDamageAmount(), DMG_SLASH );
}
else
UTIL_TakeDamageExternal( pOther, pev, pev, GetDamageAmount(), DMG_SLASH );
}
SetTouch(NULL);
}
//=========================================================
// PrescheduleThink
//=========================================================
void CMShockRoach::MonsterThink(void)
{
float lifeTime = (gpGlobals->time - m_flBirthTime);
if (lifeTime >= 0.2)
{
pev->movetype = MOVETYPE_STEP;
}
if (!m_fRoachSolid && lifeTime >= 2.0 ) {
m_fRoachSolid = TRUE;
UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 4));
}
if (lifeTime >= gSkillData.roachLifespan)
{
pev->health = -1;
Killed(pev, 0);
//return; // it still needs to think
}
CMHeadCrab::MonsterThink();
}
//=========================================================
// IdleSound
//=========================================================
void CMShockRoach::IdleSound(void)
{
EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
}
//=========================================================
// AlertSound
//=========================================================
void CMShockRoach::AlertSound(void)
{
EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
}
//=========================================================
// AlertSound
//=========================================================
void CMShockRoach::PainSound(void)
{
EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
}
//=========================================================
// DeathSound
//=========================================================
void CMShockRoach::DeathSound(void)
{
EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
}
void CMShockRoach::StartTask(Task_t *pTask)
{
m_iTaskStatus = TASKSTATUS_RUNNING;
switch (pTask->iTask)
{
case TASK_RANGE_ATTACK1:
{
m_IdealActivity = ACT_RANGE_ATTACK1;
SetTouch(&CMShockRoach::LeapTouch);
break;
}
default:
CMHeadCrab::StartTask(pTask);
}
}
int CMShockRoach::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
if ( gpGlobals->time - m_flBirthTime < 2.0 )
flDamage = 0.0;
// Skip headcrab's TakeDamage to avoid unwanted immunity to acid.
return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
}
//=========================================================
// 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_VOICE, RANDOM_SOUND_ARRAY(pAttackSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch());
}

View File

@@ -72,11 +72,47 @@ skill_cfg_t skill_cfg[] = {
{"sk_turret_health", &gSkillData.turretHealth}, {"sk_turret_health", &gSkillData.turretHealth},
{"sk_miniturret_health", &gSkillData.miniturretHealth}, {"sk_miniturret_health", &gSkillData.miniturretHealth},
{"sk_sentry_health", &gSkillData.sentryHealth}, {"sk_sentry_health", &gSkillData.sentryHealth},
{"sk_gonome_health", &gSkillData.gonomeHealth},
{"sk_gonome_dmg_guts", &gSkillData.gonomeDmgGuts},
{"sk_gonome_dmg_one_slash", &gSkillData.gonomeDmgOneSlash},
{"sk_gonome_dmg_one_bite", &gSkillData.gonomeDmgOneBite},
{"sk_massassin_health", &gSkillData.massnHealth},
{"sk_massassin_kick", &gSkillData.massnDmgKick},
{"sk_otis_health", &gSkillData.otisHealth},
{"sk_pitdrone_health", &gSkillData.pitdroneHealth},
{"sk_pitdrone_dmg_spit", &gSkillData.pitdroneDmgSpit},
{"sk_pitdrone_dmg_whip", &gSkillData.pitdroneDmgWhip},
{"sk_pitdrone_dmg_bite", &gSkillData.pitdroneDmgBite},
{"sk_shockroach_health", &gSkillData.roachHealth},
{"sk_shockroach_lifespan", &gSkillData.roachLifespan},
{"sk_shocktrooper_health", &gSkillData.strooperHealth},
{"sk_shocktrooper_kick", &gSkillData.strooperDmgKick},
{"sk_shocktrooper_maxcharge", &gSkillData.strooperMaxCharge},
{"sk_shocktrooper_rchgspeed", &gSkillData.strooperRchgSpeed},
{"sk_voltigore_health", &gSkillData.voltigoreHealth},
{"sk_voltigore_dmg_beam", &gSkillData.voltigoreDmgBeam},
{"sk_voltigore_dmg_punch", &gSkillData.voltigoreDmgPunch},
{"sk_babyvoltigore_health", &gSkillData.babyVoltigoreHealth},
{"sk_babyvoltigore_dmg_punch", &gSkillData.babyVoltigoreDmgPunch},
{"sk_babygarg_health", &gSkillData.babygargHealth},
{"sk_babygarg_dmg_slash", &gSkillData.babygargDmgSlash},
{"sk_babygarg_dmg_fire", &gSkillData.babygargDmgFire},
{"sk_babygarg_dmg_stomp", &gSkillData.babygargDmgStomp},
{"sk_hwgrunt_health", &gSkillData.hwgruntHealth},
{"sk_rgrunt_health", &gSkillData.rgruntHealth},
{"sk_rgrunt_armor", &gSkillData.rgruntArmor},
{"sk_stukabat_health", &gSkillData.stukabatHealth},
{"sk_stukabat_dmg_bite", &gSkillData.stukabatDmgBite},
{"sk_stukabat_speed", &gSkillData.stukabatSpeed},
{"sk_12mm_bullet", &gSkillData.monDmg9MM}, {"sk_12mm_bullet", &gSkillData.monDmg9MM},
{"sk_9mmAR_bullet", &gSkillData.monDmgMP5}, {"sk_9mmAR_bullet", &gSkillData.monDmgMP5},
{"sk_9mm_bullet", &gSkillData.monDmg12MM}, {"sk_9mm_bullet", &gSkillData.monDmg12MM},
{"sk_9mmAR_grenade", &gSkillData.monDmgM203Grenade}, {"sk_9mmAR_grenade", &gSkillData.monDmgM203Grenade},
{"sk_762_bullet", &gSkillData.monDmg762},
{"sk_357_bullet", &gSkillData.monDmg357},
{"sk_hornet_dmg", &gSkillData.monDmgHornet}, {"sk_hornet_dmg", &gSkillData.monDmgHornet},
{"sk_shock_dmg", &gSkillData.monDmgShockroach},
{"sk_spore_dmg", &gSkillData.monDmgSpore},
{"", NULL} {"", NULL}
}; };
@@ -123,9 +159,9 @@ void scan_monster_skill(FILE *fp)
void monster_skill_init(void) void monster_skill_init(void)
{ {
char game_dir[256]; char game_dir[256];
char filename[256]; char filename[256];
FILE *fp = NULL; FILE *fp = NULL;
// Alien Grunt // Alien Grunt
gSkillData.agruntHealth = 90.0f; gSkillData.agruntHealth = 90.0f;
@@ -212,15 +248,78 @@ void monster_skill_init(void)
// Sentry // Sentry
gSkillData.sentryHealth = 40.0f; gSkillData.sentryHealth = 40.0f;
// Gonome
gSkillData.gonomeHealth = 85.0f;
gSkillData.gonomeDmgGuts = 10.0f;
gSkillData.gonomeDmgOneSlash = 20.0f;
gSkillData.gonomeDmgOneBite = 14.0f;
// Male Assassin
gSkillData.massnHealth = 50.0f;
gSkillData.massnDmgKick = 25.0f;
// Otis
gSkillData.otisHealth = 35.0f;
// Pit Drone
gSkillData.pitdroneHealth = 40.0f;
gSkillData.pitdroneDmgSpit = 10.0f;
gSkillData.pitdroneDmgWhip = 35.0f;
gSkillData.pitdroneDmgBite = 25.0f;
// Shock Roach
gSkillData.roachHealth = 10.0f;
gSkillData.roachLifespan = 10.0f;
// Shock Trooper
gSkillData.strooperHealth = 50.0f;
gSkillData.strooperDmgKick = 10.0f;
gSkillData.strooperMaxCharge = 8.0f;
gSkillData.strooperRchgSpeed = 1.0f;
// Voltigore
gSkillData.voltigoreHealth = 320.0f;
gSkillData.voltigoreDmgBeam = 50.0f;
gSkillData.voltigoreDmgPunch = 40.0f;
// Baby Voltigore
gSkillData.babyVoltigoreHealth = 60.0f;
gSkillData.babyVoltigoreDmgPunch = 15.0f;
// Baby Gargantua
gSkillData.babygargHealth = 640.0f;
gSkillData.babygargDmgSlash = 24.0f;
gSkillData.babygargDmgFire = 4.0f;
gSkillData.babygargDmgStomp = 80.0f;
// Heavy Weapons Grunt
gSkillData.hwgruntHealth = 60.0f;
// Robo Grunt
gSkillData.rgruntHealth = 50.0f;
gSkillData.rgruntArmor = 0.75f;
// Stukabat
gSkillData.stukabatHealth = 80.0f;
gSkillData.stukabatDmgBite = 12.0f;
gSkillData.stukabatSpeed = 400.0f;
// MONSTER WEAPONS // MONSTER WEAPONS
gSkillData.monDmg9MM = 5.0f; gSkillData.monDmg9MM = 5.0f;
gSkillData.monDmgMP5 = 4.0f; gSkillData.monDmgMP5 = 4.0f;
gSkillData.monDmg12MM = 10.0f; gSkillData.monDmg12MM = 10.0f;
gSkillData.monDmgM203Grenade = 100.0f; gSkillData.monDmgM203Grenade = 100.0f;
gSkillData.monDmg762 = 100.0f;
gSkillData.monDmg357 = 40.0f;
// HORNET // HORNET
gSkillData.monDmgHornet = 5.0f; gSkillData.monDmgHornet = 5.0f;
// SHOCK ROACH
gSkillData.monDmgShockroach = 15.0f;
// SPORE GRENADE
gSkillData.monDmgSpore = 50.0f;
// find the directory name of the currently running MOD... // find the directory name of the currently running MOD...
(*g_engfuncs.pfnGetGameDir)(game_dir); (*g_engfuncs.pfnGetGameDir)(game_dir);

View File

@@ -22,7 +22,7 @@
struct skilldata_t struct skilldata_t
{ {
// Monster Health & Damage // Monster Health & Damage
float agruntHealth; float agruntHealth;
float agruntDmgPunch; float agruntDmgPunch;
float apacheHealth; float apacheHealth;
@@ -86,13 +86,65 @@ struct skilldata_t
float miniturretHealth; float miniturretHealth;
float sentryHealth; float sentryHealth;
//OP4 monsters
float gonomeHealth;
float gonomeDmgGuts;
float gonomeDmgOneSlash;
float gonomeDmgOneBite;
float massnHealth;
float massnDmgKick;
float otisHealth;
float pitdroneHealth;
float pitdroneDmgSpit;
float pitdroneDmgWhip;
float pitdroneDmgBite;
float roachHealth;
float roachLifespan;
float strooperHealth;
float strooperDmgKick;
float strooperMaxCharge;
float strooperRchgSpeed;
float voltigoreHealth;
float voltigoreDmgBeam;
float voltigoreDmgPunch;
float babyVoltigoreHealth;
float babyVoltigoreDmgPunch;
//SC monsters
float babygargHealth;
float babygargDmgSlash;
float babygargDmgFire;
float babygargDmgStomp;
float hwgruntHealth;
float rgruntHealth;
float rgruntArmor;
float stukabatHealth;
float stukabatDmgBite;
float stukabatSpeed;
// weapons shared by monsters // weapons shared by monsters
float monDmg9MM; float monDmg9MM;
float monDmgMP5; float monDmgMP5;
float monDmg12MM; float monDmg12MM;
float monDmgM203Grenade; float monDmgM203Grenade;
float monDmg762;
float monDmg357;
float monDmgHornet; float monDmgHornet;
float monDmgShockroach;
float monDmgSpore;
}; };
extern DLL_GLOBAL skilldata_t gSkillData; extern DLL_GLOBAL skilldata_t gSkillData;

View File

@@ -540,19 +540,18 @@ int SENTENCEG_Lookup(const char *sample, char *sentencenum)
return -1; return -1;
} }
void EMIT_SOUND_DYN(edict_t *entity, int channel, const char *sample, float volume, float attenuation, void EMIT_SOUND_DYN(edict_t *entity, int channel, const char *sample, float volume, float attenuation, int flags, int pitch)
int flags, int pitch)
{ {
if (sample && *sample == '!') if (sample && *sample == '!')
{ {
char name[32]; char name[32];
if (SENTENCEG_Lookup(sample, name) >= 0) if (SENTENCEG_Lookup(sample, name) >= 0)
EMIT_SOUND_DYN2(entity, channel, name, volume, attenuation, flags, pitch); EMIT_SOUND_DYN2(entity, channel, name, volume, attenuation, flags, pitch);
else else
ALERT( at_aiconsole, "Unable to find %s in sentences.txt\n", sample ); ALERT( at_aiconsole, "Unable to find %s in sentences.txt\n", sample );
} }
else 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 // play a specific sentence over the HEV suit speaker - just pass player entity, and !sentencename

353
src/dlls/sporegrenade.cpp Normal file
View File

@@ -0,0 +1,353 @@
// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository!
/***
*
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* Use, distribution, and modification of this source code and/or resulting
* object code is restricted to non-commercial enhancements to products from
* Valve LLC. All other use, distribution, or modification is prohibited
* without written permission from Valve LLC.
*
****/
#include "extdll.h"
#include "util.h"
#include "cmbase.h"
#include "cmbasemonster.h"
#include "monsters.h"
#include "weapons.h"
#include "decals.h"
#include "explode.h"
int gSporeExplode, gSporeExplodeC;
void CMSporeGrenade::Precache()
{
PRECACHE_MODEL("models/spore.mdl");
PRECACHE_MODEL("sprites/glow02.spr");
g_sModelIndexTinySpit = PRECACHE_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;
// Pull out of the wall a bit
if (pTrace->flFraction != 1.0)
{
pev->origin = pTrace->vecEndPos + (pTrace->vecPlaneNormal * (pev->dmg - 24) * 0.6);
}
Vector vecSpraySpot = pTrace->vecEndPos;
float flSpraySpeed = RANDOM_LONG(10, 15);
// If the trace is pointing up, then place
// spawn position a few units higher.
if (pTrace->vecPlaneNormal.z > 0)
{
vecSpraySpot = vecSpraySpot + (pTrace->vecPlaneNormal * 8);
flSpraySpeed *= 2; // Double the speed to make them fly higher
// in the air.
}
// Spawn small particles at the explosion origin.
SpawnExplosionParticles(
vecSpraySpot, // position
pTrace->vecPlaneNormal, // direction
g_sModelIndexTinySpit, // modelindex
RANDOM_LONG(40, 50), // count
flSpraySpeed, // speed
RANDOM_FLOAT(600, 640)); // noise
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin );
WRITE_BYTE( TE_SPRITE );
WRITE_COORD( pev->origin.x );
WRITE_COORD( pev->origin.y );
WRITE_COORD( pev->origin.z );
WRITE_SHORT( RANDOM_LONG( 0, 1 ) ? gSporeExplode : gSporeExplodeC );
WRITE_BYTE( 25 ); // scale * 10
WRITE_BYTE( 155 ); // framerate
MESSAGE_END();
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
WRITE_BYTE(TE_DLIGHT);
WRITE_COORD( pev->origin.x ); // X
WRITE_COORD( pev->origin.y ); // Y
WRITE_COORD( pev->origin.z ); // Z
WRITE_BYTE( 12 ); // radius * 0.1
WRITE_BYTE( 0 ); // r
WRITE_BYTE( 180 ); // g
WRITE_BYTE( 0 ); // b
WRITE_BYTE( 20 ); // time * 10
WRITE_BYTE( 20 ); // decay * 0.1
MESSAGE_END( );
// Play explode sound.
EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/splauncher_impact.wav", 1, ATTN_NORM);
entvars_t *pevOwner;
if (pev->owner)
pevOwner = VARS(pev->owner);
else
pevOwner = NULL;
pev->owner = NULL; // can't traceline attack owner if this is set
RadiusDamage(pev, pevOwner, pev->dmg, CLASS_NONE, DMG_BLAST);
// Place a decal on the surface that was hit.
UTIL_DecalTrace(pTrace, DECAL_SPIT1 + RANDOM_LONG(0, 1));
UpdateOnRemove();
UTIL_Remove( this->edict() );
}
void CMSporeGrenade::Detonate()
{
TraceResult tr;
Vector vecSpot = pev->origin + Vector(0, 0, 8);
UTIL_TraceLine(vecSpot, vecSpot + Vector(0, 0, -40), ignore_monsters, ENT(pev), &tr);
Explode(&tr);
}
void CMSporeGrenade::BounceSound()
{
if (m_hOwner == NULL)
pev->owner = NULL;
EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/splauncher_bounce.wav", 0.25, ATTN_NORM);
}
void CMSporeGrenade::TumbleThink()
{
if (!IsInWorld())
{
UpdateOnRemove();
UTIL_Remove( this->edict() );
return;
}
pev->nextthink = gpGlobals->time + 0.1;
if (pev->dmgtime <= gpGlobals->time)
{
SetThink(&CMSporeGrenade::Detonate);
}
// Spawn particles.
SpawnTrailParticles(
pev->origin, // position
-pev->velocity.Normalize(), // dir
g_sModelIndexTinySpit, // modelindex
RANDOM_LONG( 2, 4 ), // count
RANDOM_FLOAT(10, 15), // speed
RANDOM_FLOAT(2, 3) * 100); // noise ( client will divide by 100 )
}
//
// Contact grenade, explode when it touches something
//
void CMSporeGrenade::ExplodeTouch(edict_t *pOther)
{
TraceResult tr;
Vector vecSpot;// trace starts here!
pev->enemy = pOther;
vecSpot = pev->origin - pev->velocity.Normalize() * 32;
UTIL_TraceLine(vecSpot, vecSpot + pev->velocity.Normalize() * 64, ignore_monsters, ENT(pev), &tr);
Explode(&tr);
}
void CMSporeGrenade::DangerSoundThink()
{
if (!IsInWorld())
{
UpdateOnRemove();
UTIL_Remove( this->edict() );
return;
}
pev->nextthink = gpGlobals->time + 0.2;
// Spawn particles.
SpawnTrailParticles(
pev->origin, // position
-pev->velocity.Normalize(), // dir
g_sModelIndexTinySpit, // modelindex
RANDOM_LONG( 5, 10), // count
RANDOM_FLOAT(10, 15), // speed
RANDOM_FLOAT(2, 3) * 100); // noise ( client will divide by 100 )
}
void CMSporeGrenade::BounceTouch(edict_t *pOther)
{
if ( !pOther->v.takedamage )
{
if (!(pev->flags & FL_ONGROUND)) {
if (pev->dmg_save < gpGlobals->time) {
BounceSound();
pev->dmg_save = gpGlobals->time + 0.1;
}
} else {
pev->velocity = pev->velocity * 0.9;
}
if (pev->flags & FL_SWIM)
{
pev->velocity = pev->velocity * 0.5;
}
}
else
{
TraceResult tr = UTIL_GetGlobalTrace();
Explode(&tr);
}
}
void CMSporeGrenade::Spawn()
{
Precache();
pev->classname = MAKE_STRING("spore");
pev->movetype = MOVETYPE_BOUNCE;
pev->solid = SOLID_BBOX;
SET_MODEL(ENT(pev), "models/spore.mdl");
UTIL_SetSize(pev, Vector(0, 0, 0), Vector(0, 0, 0));
//pev->gravity = 0.5;
pev->dmg = gSkillData.monDmgSpore;
m_pSporeGlow = CMSprite::SpriteCreate("sprites/glow02.spr", pev->origin, FALSE);
if (m_pSporeGlow)
{
m_pSporeGlow->SetTransparency(kRenderGlow, 150, 158, 19, 155, kRenderFxNoDissipation);
m_pSporeGlow->SetAttachment(edict(), 0);
m_pSporeGlow->SetScale(.75f);
}
}
CMSporeGrenade* CMSporeGrenade::ShootTimed(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, bool ai)
{
CMSporeGrenade *pGrenade = CreateClassPtr((CMSporeGrenade *)NULL);
if (pGrenade == NULL)
return NULL;
UTIL_SetOrigin(pGrenade->pev, vecStart);
pGrenade->Spawn();
pGrenade->pev->velocity = vecVelocity;
pGrenade->pev->angles = UTIL_VecToAngles(pGrenade->pev->velocity);
pGrenade->pev->owner = ENT(pevOwner);
pGrenade->m_hOwner = ENT(pevOwner);
pGrenade->SetTouch(&CMSporeGrenade::BounceTouch); // Bounce if touched
float lifetime = 2.0;
if (ai) {
lifetime = 4.0;
pGrenade->pev->gravity = 0.5;
pGrenade->pev->friction = 0.9;
}
pGrenade->pev->dmgtime = gpGlobals->time + lifetime;
pGrenade->SetThink(&CMSporeGrenade::TumbleThink);
pGrenade->pev->nextthink = gpGlobals->time + 0.1;
if (lifetime < 0.1)
{
pGrenade->pev->nextthink = gpGlobals->time;
pGrenade->pev->velocity = Vector(0, 0, 0);
}
return pGrenade;
}
CMSporeGrenade *CMSporeGrenade::ShootContact(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity)
{
CMSporeGrenade *pGrenade = CreateClassPtr((CMSporeGrenade *)NULL);
if (pGrenade == NULL)
return NULL;
UTIL_SetOrigin(pGrenade->pev, vecStart);
pGrenade->Spawn();
pGrenade->pev->movetype = MOVETYPE_FLY;
pGrenade->pev->velocity = vecVelocity;
pGrenade->pev->angles = UTIL_VecToAngles(pGrenade->pev->velocity);
pGrenade->pev->owner = ENT(pevOwner);
pGrenade->m_hOwner = ENT(pevOwner);
// make monsters afraid of it while in the air
pGrenade->SetThink(&CMSporeGrenade::DangerSoundThink);
pGrenade->pev->nextthink = gpGlobals->time;
// Explode on contact
pGrenade->SetTouch(&CMSporeGrenade::ExplodeTouch);
pGrenade->pev->gravity = 0.5;
pGrenade->pev->friction = 0.7;
return pGrenade;
}
void CMSporeGrenade::SpawnTrailParticles(const Vector& origin, const Vector& direction, int modelindex, int count, float speed, float noise)
{
MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, origin);
WRITE_BYTE(TE_SPRITE_SPRAY);
WRITE_COORD(origin.x); // pos
WRITE_COORD(origin.y);
WRITE_COORD(origin.z);
WRITE_COORD(direction.x); // dir
WRITE_COORD(direction.y);
WRITE_COORD(direction.z);
WRITE_SHORT(modelindex); // model
WRITE_BYTE(count); // count
WRITE_BYTE(speed); // speed
WRITE_BYTE(noise); // noise ( client will divide by 100 )
MESSAGE_END();
}
void CMSporeGrenade::SpawnExplosionParticles(const Vector& origin, const Vector& direction, int modelindex, int count, float speed, float noise)
{
MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, origin);
WRITE_BYTE(TE_SPRITE_SPRAY);
WRITE_COORD(origin.x); // pos
WRITE_COORD(origin.y);
WRITE_COORD(origin.z);
WRITE_COORD(direction.x); // dir
WRITE_COORD(direction.y);
WRITE_COORD(direction.z);
WRITE_SHORT(modelindex); // model
WRITE_BYTE(count); // count
WRITE_BYTE(speed); // speed
WRITE_BYTE(noise); // noise ( client will divide by 100 )
MESSAGE_END();
}
void CMSporeGrenade::UpdateOnRemove()
{
CMBaseMonster::UpdateOnRemove();
if (m_pSporeGlow)
{
UTIL_Remove(m_pSporeGlow->edict());
m_pSporeGlow = NULL;
}
}

View File

@@ -47,6 +47,12 @@ float CMSqueakGrenade::m_flNextBounceSoundTime = 0;
int CMSqueakGrenade :: Classify ( void ) int CMSqueakGrenade :: Classify ( void )
{ {
// E
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_ALIEN_MONSTER; return CLASS_ALIEN_MONSTER;
} }
@@ -84,6 +90,13 @@ void CMSqueakGrenade :: Spawn( void )
ResetSequenceInfo( ); ResetSequenceInfo( );
m_hEnemy = NULL; m_hEnemy = NULL;
pev->classname = MAKE_STRING( "monster_snark" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// hi :3
m_szMonsterName = MAKE_STRING( "Snark" );
}
} }
void CMSqueakGrenade::Precache( void ) void CMSqueakGrenade::Precache( void )
@@ -282,13 +295,15 @@ void CMSqueakGrenade::SuperBounceTouch( edict_t *pOther )
// ALERT( at_console, "hit enemy\n"); // ALERT( at_console, "hit enemy\n");
ClearMultiDamage( ); ClearMultiDamage( );
if (UTIL_IsPlayer(pOther)) if (UTIL_IsPlayer(pOther))
UTIL_TraceAttack(pOther, pev, gSkillData.snarkDmgBite, gpGlobals->v_forward, &tr, DMG_SLASH ); UTIL_TraceAttack(pOther, pev, gSkillData.snarkDmgBite, gpGlobals->v_forward, &tr, DMG_SLASH );
else if (pOther->v.euser4 != NULL) else if (pOther->v.euser4 != NULL)
{ {
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther));
pMonster->TraceAttack(pev, gSkillData.snarkDmgBite, gpGlobals->v_forward, &tr, DMG_SLASH ); 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 ); ApplyMultiDamage( pev, pev );

898
src/dlls/strooper.cpp Normal file
View File

@@ -0,0 +1,898 @@
// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository!
/***
*
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* This source code contains proprietary and confidential information of
* Valve LLC and its suppliers. Access to this code is restricted to
* persons who have executed a written SDK license with Valve. Any access,
* use or distribution of this code by or to any unlicensed person is illegal.
*
****/
//=========================================================
// shocktrooper
//=========================================================
#include "extdll.h"
#include "plane.h"
#include "util.h"
#include "cmbase.h"
#include "cmbasemonster.h"
#include "monsters.h"
#include "schedule.h"
#include "animation.h"
#include "weapons.h"
#include "cmtalkmonster.h"
#include "effects.h"
#include "customentity.h"
#include "shock.h"
int g_fStrooperQuestion; // true if an idle grunt asked a question. Cleared when someone answers.
extern Schedule_t slGruntTakeCover[];
extern Schedule_t slGruntGrenadeCover[];
//=========================================================
// monster-specific DEFINE's
//=========================================================
#define STROOPER_CLIP_SIZE 10 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x!
#define STROOPER_VOL 0.35 // volume of grunt sounds
#define STROOPER_ATTN ATTN_NORM // attenutation of grunt sentences
#define STROOPER_LIMP_HEALTH 20
#define STROOPER_DMG_HEADSHOT ( DMG_BULLET | DMG_CLUB ) // damage types that can kill a grunt with a single headshot.
#define STROOPER_NUM_HEADS 2 // how many grunt heads are there?
#define STROOPER_MINIMUM_HEADSHOT_DAMAGE 15 // must do at least this much damage in one shot to head to score a headshot kill
#define STROOPER_SENTENCE_VOLUME (float)0.35 // volume of grunt sentences
#define STROOPER_MUZZLEFLASH "sprites/muzzle_shock.spr"
#define STROOPER_SHOCKRIFLE (1 << 0)
#define STROOPER_HANDGRENADE (1 << 1)
#define GUN_GROUP 1
#define GUN_SHOCKRIFLE 0
#define GUN_NONE 1
//=========================================================
// Monster's Anim Events Go Here
//=========================================================
#define STROOPER_AE_RELOAD ( 2 )
#define STROOPER_AE_KICK ( 3 )
#define STROOPER_AE_BURST1 ( 4 )
#define STROOPER_AE_BURST2 ( 5 )
#define STROOPER_AE_BURST3 ( 6 )
#define STROOPER_AE_GREN_TOSS ( 7 )
#define STROOPER_AE_GREN_LAUNCH ( 8 )
#define STROOPER_AE_GREN_DROP ( 9 )
#define STROOPER_AE_CAUGHT_ENEMY ( 10 ) // shocktrooper established sight with an enemy (player only) that had previously eluded the squad.
#define STROOPER_AE_DROP_GUN ( 11 ) // shocktrooper (probably dead) is dropping his shockrifle.
//=========================================================
// monster-specific schedule types
//=========================================================
enum
{
SCHED_STROOPER_SUPPRESS = LAST_COMMON_SCHEDULE + 1,
SCHED_STROOPER_ESTABLISH_LINE_OF_FIRE,// move to a location to set up an attack against the enemy. (usually when a friendly is in the way).
SCHED_STROOPER_COVER_AND_RELOAD,
SCHED_STROOPER_SWEEP,
SCHED_STROOPER_FOUND_ENEMY,
SCHED_STROOPER_REPEL,
SCHED_STROOPER_REPEL_ATTACK,
SCHED_STROOPER_REPEL_LAND,
SCHED_STROOPER_WAIT_FACE_ENEMY,
SCHED_STROOPER_TAKECOVER_FAILED,// special schedule type that forces analysis of conditions and picks the best possible schedule to recover from this type of failure.
SCHED_STROOPER_ELOF_FAIL,
};
//=========================================================
// monster-specific tasks
//=========================================================
enum
{
TASK_STROOPER_FACE_TOSS_DIR = LAST_COMMON_TASK + 1,
TASK_STROOPER_SPEAK_SENTENCE,
TASK_STROOPER_CHECK_FIRE,
};
int iStrooperMuzzleFlash;
const char *CMStrooper::pGruntSentences[] =
{
"ST_GREN", // grenade scared grunt
"ST_ALERT", // sees player
"ST_MONST", // sees monster
"ST_COVER", // running to cover
"ST_THROW", // about to throw grenade
"ST_CHARGE", // running out to get the enemy
"ST_TAUNT", // say rude things
};
typedef enum
{
STROOPER_SENT_NONE = -1,
STROOPER_SENT_GREN = 0,
STROOPER_SENT_ALERT,
STROOPER_SENT_MONSTER,
STROOPER_SENT_COVER,
STROOPER_SENT_THROW,
STROOPER_SENT_CHARGE,
STROOPER_SENT_TAUNT,
} STROOPER_SENTENCE_TYPES;
void CMStrooper::SpeakSentence()
{
if( m_iSentence == STROOPER_SENT_NONE )
{
// no sentence cued up.
return;
}
if( FOkToSpeak() )
{
SENTENCEG_PlayRndSz( ENT( pev ), pGruntSentences[m_iSentence], STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch );
JustSpoke();
}
}
#define STROOPER_GIB_COUNT 8
//=========================================================
// GibMonster - make gun fly through the air.
//=========================================================
void CMStrooper::GibMonster()
{
if (GetBodygroup(GUN_GROUP) != GUN_NONE)
{
DropShockRoach(true);
}
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "common/bodysplat.wav", 1, ATTN_NORM );
if( CVAR_GET_FLOAT( "violence_agibs" ) != 0 ) // Should never get here, but someone might call it directly
{
CMGib::SpawnRandomGibs( pev, STROOPER_GIB_COUNT, "models/strooper_gibs.mdl", 0 ); // Throw alien gibs
}
SetThink( &CMBaseEntity::SUB_Remove );
pev->nextthink = gpGlobals->time;
}
void CMStrooper::IdleSound()
{
if (FOkToSpeak() && (g_fStrooperQuestion || RANDOM_LONG(0, 1)))
{
if (!g_fStrooperQuestion)
{
// ask question or make statement
switch (RANDOM_LONG(0, 2))
{
case 0: // check in
SENTENCEG_PlayRndSz(ENT(pev), "ST_CHECK", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
g_fStrooperQuestion = 1;
break;
case 1: // question
SENTENCEG_PlayRndSz(ENT(pev), "ST_QUEST", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
g_fStrooperQuestion = 2;
break;
case 2: // statement
SENTENCEG_PlayRndSz(ENT(pev), "ST_IDLE", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
break;
}
}
else
{
switch (g_fStrooperQuestion)
{
case 1: // check in
SENTENCEG_PlayRndSz(ENT(pev), "ST_CLEAR", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
break;
case 2: // question
SENTENCEG_PlayRndSz(ENT(pev), "ST_ANSWER", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch);
break;
}
g_fStrooperQuestion = 0;
}
JustSpoke();
}
}
//=========================================================
// Classify - indicates this monster's place in the
// relationship table.
//=========================================================
int CMStrooper::Classify()
{
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_RACEX_SHOCK;
}
BOOL CMStrooper::CheckRangeAttack1(float flDot, float flDist)
{
return (m_cAmmoLoaded >= 1) && CMHGrunt::CheckRangeAttack1(flDot, flDist);
}
BOOL CMStrooper::CheckRangeAttack2( float flDot, float flDist )
{
if( !FBitSet( pev->weapons, STROOPER_HANDGRENADE ) )
{
return FALSE;
}
return CMHGrunt::CheckRangeAttack2(flDot, flDist);
}
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//=========================================================
void CMStrooper::HandleAnimEvent(MonsterEvent_t *pEvent)
{
switch (pEvent->event)
{
case STROOPER_AE_DROP_GUN:
{
if (GetBodygroup(GUN_GROUP) != GUN_NONE)
{
DropShockRoach(false);
}
}
break;
case STROOPER_AE_RELOAD:
m_cAmmoLoaded = m_cClipSize;
ClearConditions(bits_COND_NO_AMMO_LOADED);
break;
case STROOPER_AE_GREN_TOSS:
{
UTIL_MakeVectors(pev->angles);
// CGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 34 + Vector (0, 0, 32), m_vecTossVelocity, 3.5 );
CMSporeGrenade::ShootTimed(pev, pev->origin + Vector(0,0,98), m_vecTossVelocity, 3.5);
m_fThrowGrenade = FALSE;
m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown.
// !!!LATER - when in a group, only try to throw grenade if ordered.
}
break;
case STROOPER_AE_GREN_LAUNCH:
case STROOPER_AE_GREN_DROP:
break;
case STROOPER_AE_BURST1:
{
if (m_hEnemy)
{
Vector vecGunPos;
Vector vecGunAngles;
GetAttachment(0, vecGunPos, vecGunAngles);
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecGunPos );
WRITE_BYTE( TE_SPRITE );
WRITE_COORD( vecGunPos.x ); // pos
WRITE_COORD( vecGunPos.y );
WRITE_COORD( vecGunPos.z );
WRITE_SHORT( iStrooperMuzzleFlash ); // model
WRITE_BYTE( 4 ); // size * 10
WRITE_BYTE( 128 ); // brightness
MESSAGE_END();
UTIL_MakeVectors(pev->angles);
Vector vecShootOrigin = vecGunPos + gpGlobals->v_forward * 32;
Vector vecShootDir = ShootAtEnemy( vecShootOrigin );
vecGunAngles = UTIL_VecToAngles( vecShootDir );
vecGunAngles.z += RANDOM_FLOAT( -0.05, 0 );
Vector vecVelocity = vecShootDir * 2000;
edict_t *pShock = CMShock::Shoot( pev, vecGunAngles, vecShootOrigin, vecVelocity );
if (pShock != NULL)
{
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;
case STROOPER_AE_KICK:
{
EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "zombie/claw_miss2.wav", 1.0, ATTN_NORM, 0, PITCH_NORM + RANDOM_LONG( -5, 5 ) );
edict_t *pHurt = Kick();
if (pHurt)
{
// SOUND HERE!
UTIL_MakeVectors(pev->angles);
pHurt->v.punchangle.x = 15;
pHurt->v.punchangle.z = (m_fRightClaw) ? -10 : 10;
pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50;
if ( UTIL_IsPlayer( pHurt ) )
UTIL_TakeDamage( pHurt, pev, pev, gSkillData.strooperDmgKick, DMG_CLUB );
else if ( pHurt->v.euser4 != NULL )
{
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt));
pMonster->TakeDamage( pev, pev, gSkillData.strooperDmgKick, DMG_CLUB );
}
else
UTIL_TakeDamageExternal( pHurt, pev, pev, gSkillData.strooperDmgKick, DMG_CLUB );
}
m_fRightClaw = !m_fRightClaw;
}
break;
case STROOPER_AE_CAUGHT_ENEMY:
{
if (FOkToSpeak())
{
SENTENCEG_PlayRndSz(ENT(pev), "ST_ALERT", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch);
JustSpoke();
}
}
default:
CMHGrunt::HandleAnimEvent(pEvent);
break;
}
}
//=========================================================
// Spawn
//=========================================================
void CMStrooper::Spawn()
{
Precache();
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/strooper.mdl"));
UTIL_SetSize( pev, Vector(-24, -24, 0), Vector(24, 24, 72) );
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = !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 )
m_MonsterState = MONSTERSTATE_NONE;
m_flNextGrenadeCheck = gpGlobals->time + 1;
m_flNextPainTime = gpGlobals->time;
m_iSentence = STROOPER_SENT_NONE;
m_afCapability = bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
//m_fEnemyEluded = FALSE;
m_fFirstEncounter = TRUE;// this is true when the grunt spawns, because he hasn't encountered an enemy yet.
m_HackedGunPos = Vector(0, 0, 55);
if (pev->weapons == 0)
{
// initialize to original values
pev->weapons = STROOPER_SHOCKRIFLE | STROOPER_HANDGRENADE;
}
m_cClipSize = gSkillData.strooperMaxCharge;
m_cAmmoLoaded = m_cClipSize;
m_fRightClaw = FALSE;
CMTalkMonster::g_talkWaitTime = 0;
m_rechargeTime = gpGlobals->time + gSkillData.strooperRchgSpeed;
m_blinkTime = gpGlobals->time + RANDOM_FLOAT(3.0f, 7.0f);
MonsterInit();
pev->classname = MAKE_STRING( "monster_shocktrooper" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Shock Trooper" );
}
}
void CMStrooper::MonsterThink()
{
if (m_cAmmoLoaded < m_cClipSize)
{
if (m_rechargeTime < gpGlobals->time)
{
m_cAmmoLoaded++;
m_rechargeTime = gpGlobals->time + gSkillData.strooperRchgSpeed;
}
}
if (m_blinkTime <= gpGlobals->time && pev->skin == 0) {
pev->skin = 1;
m_blinkTime = gpGlobals->time + RANDOM_FLOAT(3.0f, 7.0f);
m_eyeChangeTime = gpGlobals->time + 0.1;
}
if (pev->skin != 0) {
if (m_eyeChangeTime <= gpGlobals->time) {
m_eyeChangeTime = gpGlobals->time + 0.1;
pev->skin++;
if (pev->skin > 3) {
pev->skin = 0;
}
}
}
CMHGrunt::MonsterThink();
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CMStrooper::Precache()
{
PRECACHE_MODEL("models/strooper.mdl");
PRECACHE_MODEL("models/strooper_gibs.mdl");
iStrooperMuzzleFlash = PRECACHE_MODELINDEX(STROOPER_MUZZLEFLASH);
PRECACHE_SOUND("shocktrooper/shock_trooper_attack.wav");
PRECACHE_SOUND("shocktrooper/shock_trooper_die1.wav");
PRECACHE_SOUND("shocktrooper/shock_trooper_die2.wav");
PRECACHE_SOUND("shocktrooper/shock_trooper_die3.wav");
PRECACHE_SOUND("shocktrooper/shock_trooper_die4.wav");
PRECACHE_SOUND("shocktrooper/shock_trooper_pain1.wav");
PRECACHE_SOUND("shocktrooper/shock_trooper_pain2.wav");
PRECACHE_SOUND("shocktrooper/shock_trooper_pain3.wav");
PRECACHE_SOUND("shocktrooper/shock_trooper_pain4.wav");
PRECACHE_SOUND("shocktrooper/shock_trooper_pain5.wav");
PRECACHE_SOUND("weapons/shock_fire.wav");
PRECACHE_SOUND("weapons/shock_impact.wav");
PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event
// shock_beam
CMShock shock;
shock.Precache();
// spore
CMSporeGrenade spore;
spore.Precache();
// shockroach
CMShockRoach shockroach;
shockroach.Precache();
// get voice pitch
if (RANDOM_LONG(0, 1))
m_voicePitch = 109 + RANDOM_LONG(0, 7);
else
m_voicePitch = 100;
m_iBrassShell = PRECACHE_MODELINDEX("models/shell.mdl");// brass shell
}
//=========================================================
// PainSound
//=========================================================
void CMStrooper::PainSound()
{
if (gpGlobals->time > m_flNextPainTime)
{
#if 0
if (RANDOM_LONG(0, 99) < 5)
{
// pain sentences are rare
if (FOkToSpeak())
{
SENTENCEG_PlayRndSz(ENT(pev), "HG_PAIN", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, PITCH_NORM);
JustSpoke();
return;
}
}
#endif
switch (RANDOM_LONG(0, 4))
{
case 0:
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain1.wav", 1, ATTN_NORM);
break;
case 1:
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain2.wav", 1, ATTN_NORM);
break;
case 2:
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain3.wav", 1, ATTN_NORM);
break;
case 3:
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain4.wav", 1, ATTN_NORM);
break;
case 4:
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain5.wav", 1, ATTN_NORM);
break;
}
m_flNextPainTime = gpGlobals->time + 1;
}
}
//=========================================================
// DeathSound
//=========================================================
void CMStrooper::DeathSound()
{
switch (RANDOM_LONG(0, 3))
{
case 0:
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die1.wav", 1, ATTN_IDLE);
break;
case 1:
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die2.wav", 1, ATTN_IDLE);
break;
case 2:
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die3.wav", 1, ATTN_IDLE);
break;
case 3:
EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die4.wav", 1, ATTN_IDLE);
break;
}
}
//=========================================================
// TraceAttack - reimplemented in shock trooper because they never have helmets
//=========================================================
void CMStrooper::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
{
CMBaseMonster::TraceAttack(pevAttacker, flDamage, vecDir, ptr, bitsDamageType);
}
void CMStrooper::DropShockRoach(bool gibbed)
{
Vector vecGunPos;
Vector vecGunAngles;
GetAttachment(0, vecGunPos, vecGunAngles);
SetBodygroup(GUN_GROUP, GUN_NONE);
Vector vecDropAngles;
// Remove any pitch.
vecDropAngles.x = 0;
vecDropAngles.y = vecGunAngles.y;
vecDropAngles.z = 0;
Vector vecPos = pev->origin;
if (gibbed)
vecPos.z += 32;
else
vecPos.z += 48;
// now spawn a shockroach.
//CBaseEntity* roach = CBaseEntity::Create( "monster_shockroach", vecPos, vecDropAngles );
CMShockRoach *roach = CreateClassPtr((CMShockRoach *)NULL);
if (roach != NULL)
{
UTIL_SetOrigin(roach->pev, vecPos);
roach->pev->angles = vecDropAngles;
roach->Spawn();
if (ShouldFadeOnDeath())
roach->pev->spawnflags |= SF_MONSTER_FADECORPSE;
if (gibbed)
{
roach->pev->velocity = Vector(RANDOM_FLOAT(-100.0f, 100.0f), RANDOM_FLOAT(-100.0f, 100.0f), RANDOM_FLOAT(200.0f, 300.0f));
roach->pev->avelocity = Vector(0, RANDOM_FLOAT(200.0f, 300.0f), 0);
}
else
{
roach->pev->velocity = Vector(RANDOM_FLOAT(-20.0f, 20.0f) , RANDOM_FLOAT(-20.0f, 20.0f), RANDOM_FLOAT(20.0f, 30.0f));
roach->pev->avelocity = Vector(0, RANDOM_FLOAT(20.0f, 40.0f), 0);
}
}
}
//=========================================================
// SetActivity
//=========================================================
void CMStrooper::SetActivity(Activity NewActivity)
{
int iSequence = ACTIVITY_NOT_AVAILABLE;
void *pmodel = GET_MODEL_PTR(ENT(pev));
switch (NewActivity)
{
case ACT_RANGE_ATTACK1:
// shocktrooper is either shooting standing or shooting crouched
if (m_fStanding)
{
// get aimable sequence
iSequence = LookupSequence("standing_mp5");
}
else
{
// get crouching shoot
iSequence = LookupSequence("crouching_mp5");
}
break;
case ACT_RANGE_ATTACK2:
// shocktrooper is going to throw a grenade.
// get toss anim
iSequence = LookupSequence("throwgrenade");
break;
case ACT_RUN:
if (pev->health <= STROOPER_LIMP_HEALTH)
{
// limp!
iSequence = LookupActivity(ACT_RUN_HURT);
}
else
{
iSequence = LookupActivity(NewActivity);
}
break;
case ACT_WALK:
if (pev->health <= STROOPER_LIMP_HEALTH)
{
// limp!
iSequence = LookupActivity(ACT_WALK_HURT);
}
else
{
iSequence = LookupActivity(NewActivity);
}
break;
case ACT_IDLE:
if (m_MonsterState == MONSTERSTATE_COMBAT)
{
NewActivity = ACT_IDLE_ANGRY;
}
iSequence = LookupActivity(NewActivity);
break;
default:
iSequence = LookupActivity(NewActivity);
break;
}
m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present
// Set to the desired anim, or default anim if the desired is not present
if (iSequence > ACTIVITY_NOT_AVAILABLE)
{
if (pev->sequence != iSequence || !m_fSequenceLoops)
{
pev->frame = 0;
}
pev->sequence = iSequence; // Set to the reset anim (if it's there)
ResetSequenceInfo();
SetYawSpeed();
}
else
{
// Not available try to get default anim
ALERT(at_console, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity);
pev->sequence = 0; // Set to the reset anim (if it's there)
}
}
//=========================================================
// Get Schedule!
//=========================================================
Schedule_t *CMStrooper::GetSchedule(void)
{
// clear old sentence
m_iSentence = STROOPER_SENT_NONE;
// flying? If PRONE, barnacle has me. IF not, it's assumed I am rapelling.
if (pev->movetype == MOVETYPE_FLY && m_MonsterState != MONSTERSTATE_PRONE)
{
if (pev->flags & FL_ONGROUND)
{
// just landed
pev->movetype = MOVETYPE_STEP;
return GetScheduleOfType(SCHED_STROOPER_REPEL_LAND);
}
else
{
// repel down a rope,
if (m_MonsterState == MONSTERSTATE_COMBAT)
return GetScheduleOfType(SCHED_STROOPER_REPEL_ATTACK);
else
return GetScheduleOfType(SCHED_STROOPER_REPEL);
}
}
switch (m_MonsterState)
{
case MONSTERSTATE_COMBAT:
{
// dead enemy
if (HasConditions(bits_COND_ENEMY_DEAD))
{
// call base class, all code to handle dead enemies is centralized there.
return CMBaseMonster::GetSchedule();
}
// new enemy
if (HasConditions(bits_COND_NEW_ENEMY))
{
// 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
// or a monster. He's going to immediately start
// firing, though. If you'd like, we can make an alternate "first sight"
// schedule where the leader plays a handsign anim
// that gives us enough time to hear a short sentence or spoken command
// before he starts pluggin away.
if (FOkToSpeak())// && RANDOM_LONG(0,1))
{
if ((m_hEnemy != 0) && UTIL_IsPlayer( m_hEnemy ))
// player
SENTENCEG_PlayRndSz(ENT(pev), "ST_ALERT", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch);
else if ((m_hEnemy != 0) &&
(m_hEnemy->Classify() != CLASS_PLAYER_ALLY) &&
(m_hEnemy->Classify() != CLASS_HUMAN_PASSIVE) &&
(m_hEnemy->Classify() != CLASS_MACHINE))
// monster
SENTENCEG_PlayRndSz(ENT(pev), "ST_MONST", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch);
JustSpoke();
}
if (HasConditions(bits_COND_CAN_RANGE_ATTACK1))
{
return GetScheduleOfType(SCHED_STROOPER_SUPPRESS);
}
else
{
return GetScheduleOfType(SCHED_STROOPER_ESTABLISH_LINE_OF_FIRE);
}
*/
}
// no ammo
else if (HasConditions(bits_COND_NO_AMMO_LOADED))
{
//!!!KELLY - this individual just realized he's out of bullet ammo.
// He's going to try to find cover to run to and reload, but rarely, if
// none is available, he'll drop and reload in the open here.
return GetScheduleOfType(SCHED_STROOPER_COVER_AND_RELOAD);
}
// damaged just a little
else if (HasConditions(bits_COND_LIGHT_DAMAGE))
{
// if hurt:
// 90% chance of taking cover
// 10% chance of flinch.
int iPercent = RANDOM_LONG(0, 99);
if (iPercent <= 90 && m_hEnemy != 0)
{
// only try to take cover if we actually have an enemy!
//!!!KELLY - this grunt was hit and is going to run to cover.
if (FOkToSpeak()) // && RANDOM_LONG(0,1))
{
SENTENCEG_PlayRndSz( ENT(pev), "ST_COVER", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch);
m_iSentence = STROOPER_SENT_COVER;
JustSpoke();
}
return GetScheduleOfType(SCHED_TAKE_COVER_FROM_ENEMY);
}
else
{
return GetScheduleOfType(SCHED_SMALL_FLINCH);
}
}
// can kick
else if (HasConditions(bits_COND_CAN_MELEE_ATTACK1))
{
return GetScheduleOfType(SCHED_MELEE_ATTACK1);
}
// can shoot
if (HasConditions(bits_COND_CAN_RANGE_ATTACK1))
{
if (HasConditions(bits_COND_CAN_RANGE_ATTACK2))
{
// throw a grenade if can and no engage slots are available
return GetScheduleOfType(SCHED_RANGE_ATTACK2);
}
else
{
return GetScheduleOfType(SCHED_RANGE_ATTACK1);
// hide!
//return GetScheduleOfType(SCHED_TAKE_COVER_FROM_ENEMY);
}
}
// can't see enemy
else if (HasConditions(bits_COND_ENEMY_OCCLUDED))
{
if (HasConditions(bits_COND_CAN_RANGE_ATTACK2))
{
//!!!KELLY - this grunt is about to throw or fire a grenade at the player. Great place for "fire in the hole" "frag out" etc
if (FOkToSpeak())
{
SENTENCEG_PlayRndSz(ENT(pev), "ST_THROW", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch);
JustSpoke();
}
return GetScheduleOfType(SCHED_RANGE_ATTACK2);
}
else
{
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
// grunt's covered position. Good place for a taunt, I guess?
if (FOkToSpeak() && RANDOM_LONG(0, 1))
{
SENTENCEG_PlayRndSz(ENT(pev), "ST_TAUNT", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch);
JustSpoke();
}
return GetScheduleOfType(SCHED_STANDOFF);
}
*/
}
if (HasConditions(bits_COND_SEE_ENEMY) && !HasConditions(bits_COND_CAN_RANGE_ATTACK1))
{
return GetScheduleOfType(SCHED_STROOPER_ESTABLISH_LINE_OF_FIRE);
}
}
}
// no special cases here, call the base class
return CMBaseMonster::GetSchedule();
}
//=========================================================
//=========================================================
Schedule_t* CMStrooper::GetScheduleOfType(int Type)
{
switch (Type)
{
case SCHED_TAKE_COVER_FROM_ENEMY:
{
if (RANDOM_LONG(0, 1))
{
return &slGruntTakeCover[0];
}
else
{
return &slGruntGrenadeCover[0];
}
}
break;
default:
{
return CMHGrunt::GetScheduleOfType(Type);
}
break;
}
}

291
src/dlls/stukabat.cpp Normal file
View File

@@ -0,0 +1,291 @@
/***
*
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* This source code contains proprietary and confidential information of
* Valve LLC and its suppliers. Access to this code is restricted to
* persons who have executed a written SDK license with Valve. Any access,
* use or distribution of this code by or to any unlicensed person is illegal.
*
****/
//=========================================================
// Stukabat - Xen Birb
//=========================================================
#include "extdll.h"
#include "util.h"
#include "cmbase.h"
#include "cmflyingmonster.h"
#include "monsters.h"
#include "schedule.h"
#include "animation.h"
//=========================================================
// Monster's Anim Events Go Here
//=========================================================
#define STUKABAT_AE_BITE 1
#define STUKABAT_AE_FLAP 8
//=========================================================
// Classify - indicates this monster's place in the
// relationship table.
//=========================================================
int CMStukabat :: Classify ( void )
{
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_ALIEN_PREDATOR;
}
//=========================================================
// SetYawSpeed - allows each sequence to have a different
// turn rate associated with it.
//=========================================================
void CMStukabat :: SetYawSpeed ( void )
{
int ys;
switch ( m_Activity )
{
case ACT_HOVER:
default:
ys = 90;
}
pev->yaw_speed = ys;
}
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//=========================================================
void CMStukabat :: HandleAnimEvent( MonsterEvent_t *pEvent )
{
switch( pEvent->event )
{
case STUKABAT_AE_BITE:
{
edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.stukabatDmgBite, DMG_SLASH|DMG_POISON );
if ( pHurt )
{
// Play bite sound
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "headcrab/hc_headbite.wav", 1.0, ATTN_NORM, 0, GetBitePitch() );
}
}
break;
case STUKABAT_AE_FLAP:
{
m_flightSpeed = gSkillData.stukabatSpeed; // set our own speed
}
break;
default:
CMFlyingMonster::HandleAnimEvent( pEvent );
break;
}
}
//=========================================================
// Spawn
//=========================================================
void CMStukabat :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/stukabat.mdl"));
UTIL_SetSize( pev, Vector( -12, -12, 0 ), Vector( 12, 12, 24 ) );
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_FLY;
pev->flags |= FL_FLY;
m_bloodColor = !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 = REPLACER::FindSoundReplacement( "stukabat/stukabat_flap1.wav" );
MonsterInit();
pev->classname = MAKE_STRING( "monster_stukabat" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Stukabat" );
}
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CMStukabat :: Precache()
{
PRECACHE_MODEL("models/stukabat.mdl");
PRECACHE_SOUND("stukabat/stukabat_flap1.wav" ); // flying sound
PRECACHE_SOUND("headcrab/hc_headbite.wav"); // bite sound
}
//=========================================================
// AI Schedules Specific to this monster
//=========================================================
// Chase enemy
Task_t tlStukabatChaseEnemy[] =
{
{ 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"
},
};
// 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, (float)0 },
{ TASK_SET_ACTIVITY, (float)ACT_HOVER },
{ TASK_WAIT, (float)2 },
{ TASK_WAIT_PVS, (float)0 },
};
Schedule_t slStukabatFail[] =
{
{
tlStukabatFail,
ARRAYSIZE ( tlStukabatFail ),
bits_COND_CAN_ATTACK,
0,
"Stukabat Fail"
},
};
DEFINE_CUSTOM_SCHEDULES( CMStukabat )
{
slStukabatChaseEnemy,
slStukabatChaseEnemyFailed,
slStukabatFail,
};
IMPLEMENT_CUSTOM_SCHEDULES( CMStukabat, CMFlyingMonster );
//=========================================================
// SetActivity
//=========================================================
void CMStukabat :: SetActivity ( Activity NewActivity )
{
int iSequence = ACTIVITY_NOT_AVAILABLE;
void *pmodel = GET_MODEL_PTR( ENT(pev) );
switch ( NewActivity )
{
case ACT_IDLE:
return; // refuse
case ACT_HOVER:
case ACT_FLY:
default:
iSequence = LookupActivity ( NewActivity );
break;
}
m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present
// Set to the desired anim, or default anim if the desired is not present
if ( iSequence > ACTIVITY_NOT_AVAILABLE )
{
if ( pev->sequence != iSequence || !m_fSequenceLoops )
{
pev->frame = 0;
}
pev->sequence = iSequence; // Set to the reset anim (if it's there)
ResetSequenceInfo( );
SetYawSpeed();
}
else
{
// Not available try to get default anim
ALERT ( at_console, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity );
pev->sequence = 0; // Set to the reset anim (if it's there)
}
}
//=========================================================
// GetScheduleOfType
//=========================================================
Schedule_t* CMStukabat :: GetScheduleOfType ( int Type )
{
switch ( Type )
{
case SCHED_CHASE_ENEMY:
return slStukabatChaseEnemy;
case SCHED_CHASE_ENEMY_FAILED:
return slStukabatChaseEnemyFailed;
case SCHED_FAIL:
return slStukabatFail;
}
return CMBaseMonster :: GetScheduleOfType( Type );
}
//=========================================================
// CheckRangeAttack1 - Poisonous Bite
//=========================================================
BOOL CMStukabat :: CheckRangeAttack1 ( float flDot, float flDist )
{
if ( flDot > 0.7 && flDist <= 64 )
{
return TRUE;
}
return FALSE;
}

View File

@@ -145,12 +145,25 @@ void FireTargets( const char *targetName, edict_t *pActivator, edict_t *pCaller,
if (FNullEnt(pentTarget)) if (FNullEnt(pentTarget))
break; break;
// MonsterMod entity
CMBaseEntity *pTarget = CMBaseEntity::Instance( pentTarget ); 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 ); ALERT( at_aiconsole, "Found: %s, firing (%s)\n", STRING(pTarget->pev->classname), targetName );
pTarget->Use( pActivator, pCaller, useType, value ); 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 );
}
}
} }
} }

54
src/dlls/turret.cpp Executable file → Normal file
View File

@@ -92,6 +92,9 @@ void CMBaseTurret::Spawn()
m_iAutoStart = TRUE; m_iAutoStart = TRUE;
} }
if (!m_flDistLook)
m_flDistLook = TURRET_RANGE;
ResetSequenceInfo( ); ResetSequenceInfo( );
SetBoneController( 0, 0 ); SetBoneController( 0, 0 );
SetBoneController( 1, 0 ); SetBoneController( 1, 0 );
@@ -121,7 +124,7 @@ void CMBaseTurret::Precache( )
void CMTurret::Spawn() void CMTurret::Spawn()
{ {
Precache( ); Precache( );
SET_MODEL(ENT(pev), "models/turret.mdl"); SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/turret.mdl"));
pev->health = gSkillData.turretHealth; pev->health = gSkillData.turretHealth;
m_HackedGunPos = Vector( 0, 0, 12.75 ); m_HackedGunPos = Vector( 0, 0, 12.75 );
m_flMaxSpin = TURRET_MAXSPIN; m_flMaxSpin = TURRET_MAXSPIN;
@@ -142,6 +145,13 @@ void CMTurret::Spawn()
m_eyeBrightness = 0; m_eyeBrightness = 0;
pev->nextthink = gpGlobals->time + 0.3; pev->nextthink = gpGlobals->time + 0.3;
pev->classname = MAKE_STRING( "monster_turret" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Turret" );
}
} }
void CMTurret::Precache() void CMTurret::Precache()
@@ -154,7 +164,7 @@ void CMTurret::Precache()
void CMMiniTurret::Spawn() void CMMiniTurret::Spawn()
{ {
Precache( ); Precache( );
SET_MODEL(ENT(pev), "models/miniturret.mdl"); SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/miniturret.mdl"));
pev->health = gSkillData.miniturretHealth; pev->health = gSkillData.miniturretHealth;
m_HackedGunPos = Vector( 0, 0, 12.75 ); m_HackedGunPos = Vector( 0, 0, 12.75 );
m_flMaxSpin = 0; m_flMaxSpin = 0;
@@ -168,6 +178,13 @@ void CMMiniTurret::Spawn()
SetThink(&CMMiniTurret::Initialize); SetThink(&CMMiniTurret::Initialize);
pev->nextthink = gpGlobals->time + 0.3; pev->nextthink = gpGlobals->time + 0.3;
pev->classname = MAKE_STRING( "monster_miniturret" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Mini-Turret" );
}
} }
@@ -334,7 +351,7 @@ void CMBaseTurret::ActiveThink(void)
Vector vec = UTIL_VecToAngles(vecMidEnemy - vecMid); Vector vec = UTIL_VecToAngles(vecMidEnemy - vecMid);
// Current enemy is not visible. // Current enemy is not visible.
if (!fEnemyVisible || (flDistToEnemy > TURRET_RANGE)) if (!fEnemyVisible || (flDistToEnemy > m_flDistLook))
{ {
if (!m_flLastSight) if (!m_flLastSight)
m_flLastSight = gpGlobals->time + 0.5; m_flLastSight = gpGlobals->time + 0.5;
@@ -445,7 +462,7 @@ void CMBaseTurret::ActiveThink(void)
void CMTurret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy) 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); EMIT_SOUND(ENT(pev), CHAN_WEAPON, "turret/tu_fire1.wav", 1, 0.6);
pev->effects = pev->effects | EF_MUZZLEFLASH; pev->effects = pev->effects | EF_MUZZLEFLASH;
} }
@@ -453,7 +470,7 @@ void CMTurret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy)
void CMMiniTurret::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)) switch(RANDOM_LONG(0,2))
{ {
@@ -707,7 +724,7 @@ void CMBaseTurret::SearchThink(void)
// Acquire Target // Acquire Target
if (m_hEnemy == NULL) if (m_hEnemy == NULL)
{ {
Look(TURRET_RANGE); Look(m_flDistLook);
m_hEnemy = BestVisibleEnemy(); m_hEnemy = BestVisibleEnemy();
} }
@@ -765,7 +782,7 @@ void CMBaseTurret::AutoSearchThink(void)
if (m_hEnemy == NULL) if (m_hEnemy == NULL)
{ {
Look( TURRET_RANGE ); Look( m_flDistLook );
m_hEnemy = BestVisibleEnemy(); m_hEnemy = BestVisibleEnemy();
} }
@@ -787,6 +804,7 @@ void CMBaseTurret :: TurretDeath( void )
if (pev->deadflag != DEAD_DEAD) if (pev->deadflag != DEAD_DEAD)
{ {
pev->deadflag = DEAD_DEAD; pev->deadflag = DEAD_DEAD;
FCheckAITrigger(); // trigger death condition
float flRndSound = RANDOM_FLOAT ( 0 , 1 ); float flRndSound = RANDOM_FLOAT ( 0 , 1 );
@@ -978,7 +996,15 @@ int CMBaseTurret::MoveTurret(void)
int CMBaseTurret::Classify ( void ) int CMBaseTurret::Classify ( void )
{ {
if (m_iOn || m_iAutoStart) if (m_iOn || m_iAutoStart)
return CLASS_MACHINE; {
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_MACHINE;
}
return CLASS_NONE; return CLASS_NONE;
} }
@@ -992,7 +1018,7 @@ void CMSentry::Precache()
void CMSentry::Spawn() void CMSentry::Spawn()
{ {
Precache( ); Precache( );
SET_MODEL(ENT(pev), "models/sentry.mdl"); SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/sentry.mdl"));
pev->health = gSkillData.sentryHealth; pev->health = gSkillData.sentryHealth;
m_HackedGunPos = Vector( 0, 0, 48 ); m_HackedGunPos = Vector( 0, 0, 48 );
pev->view_ofs.z = 48; pev->view_ofs.z = 48;
@@ -1008,11 +1034,18 @@ void CMSentry::Spawn()
SetTouch(&CMSentry::SentryTouch); SetTouch(&CMSentry::SentryTouch);
SetThink(&CMSentry::Initialize); SetThink(&CMSentry::Initialize);
pev->nextthink = gpGlobals->time + 0.3; pev->nextthink = gpGlobals->time + 0.3;
pev->classname = MAKE_STRING( "monster_sentry" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Sentry Turret" );
}
} }
void CMSentry::Shoot(Vector &vecSrc, Vector &vecDirToEnemy) 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)) switch(RANDOM_LONG(0,2))
{ {
@@ -1076,6 +1109,7 @@ void CMSentry::SentryDeath( void )
if (pev->deadflag != DEAD_DEAD) if (pev->deadflag != DEAD_DEAD)
{ {
pev->deadflag = DEAD_DEAD; pev->deadflag = DEAD_DEAD;
FCheckAITrigger(); // trigger death condition
float flRndSound = RANDOM_FLOAT ( 0 , 1 ); float flRndSound = RANDOM_FLOAT ( 0 , 1 );

View File

@@ -39,7 +39,7 @@ typedef struct {
} gamedll_funcs_t; } gamedll_funcs_t;
extern gamedll_funcs_t *gpGamedllFuncs; extern gamedll_funcs_t *gpGamedllFuncs;
extern void check_player_dead( edict_t *pPlayer );
// Print to console. // Print to console.
void META_CONS(char *fmt, ...) { 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 #ifdef DEBUG
edict_t *DBG_EntOfVars( const entvars_t *pev ) edict_t *DBG_EntOfVars( const entvars_t *pev )
{ {
@@ -757,7 +871,7 @@ int gmsgSayText = 0;
void UTIL_ClientPrintAll( int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 ) void UTIL_ClientPrintAll( int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 )
{ {
if (gmsgTextMsg) if (gmsgTextMsg == 0)
gmsgTextMsg = REG_USER_MSG( "TextMsg", -1 ); gmsgTextMsg = REG_USER_MSG( "TextMsg", -1 );
MESSAGE_BEGIN( MSG_ALL, gmsgTextMsg ); MESSAGE_BEGIN( MSG_ALL, gmsgTextMsg );
@@ -778,7 +892,7 @@ void UTIL_ClientPrintAll( int msg_dest, const char *msg_name, const char *param1
void ClientPrint( entvars_t *client, int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 ) void ClientPrint( entvars_t *client, int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 )
{ {
if (gmsgTextMsg) if (gmsgTextMsg == 0)
gmsgTextMsg = REG_USER_MSG( "TextMsg", -1 ); gmsgTextMsg = REG_USER_MSG( "TextMsg", -1 );
MESSAGE_BEGIN( MSG_ONE, gmsgTextMsg, NULL, client ); MESSAGE_BEGIN( MSG_ONE, gmsgTextMsg, NULL, client );
@@ -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 ) Vector VecBModelOrigin( entvars_t* pevBModel )
{ {
return pevBModel->absmin + ( pevBModel->size * 0.5 ); return pevBModel->absmin + ( pevBModel->size * 0.5 );
@@ -1520,15 +1683,13 @@ Vector VecBModelOrigin( entvars_t* pevBModel )
bool UTIL_IsAlive(entvars_t *pev) bool UTIL_IsAlive(entvars_t *pev)
{ {
return ((pev->deadflag == DEAD_NO) && (pev->health > 0) && return ((pev->deadflag == DEAD_NO) && (pev->health > 0));
((pev->flags & FL_NOTARGET) == 0) && (pev->takedamage != 0));
} }
bool UTIL_IsAlive(edict_t *pEdict) bool UTIL_IsAlive(edict_t *pEdict)
{ {
return ((pEdict->v.deadflag == DEAD_NO) && (pEdict->v.health > 0) && return ((pEdict->v.deadflag == DEAD_NO) && (pEdict->v.health > 0));
((pEdict->v.flags & FL_NOTARGET) == 0) && (pEdict->v.takedamage != 0));
} }
@@ -1540,10 +1701,10 @@ bool UTIL_IsPlayer(edict_t *pEdict)
Vector UTIL_BodyTarget(edict_t *pEdict, Vector posSrc) Vector UTIL_BodyTarget(edict_t *pEdict, Vector posSrc)
{ {
if (pEdict->v.flags & FL_CLIENT) if (pEdict->v.flags & FL_CLIENT)
return pEdict->v.origin + (pEdict->v.view_ofs * RANDOM_FLOAT(0.5, 1.1)); return pEdict->v.origin + (pEdict->v.view_ofs * RANDOM_FLOAT(0.5, 1.1));
else else
return (pEdict->v.origin + ((pEdict->v.mins + pEdict->v.maxs) * 0.5)); return (pEdict->v.origin + ((pEdict->v.mins + pEdict->v.maxs) * 0.5));
} }
//========================================================= //=========================================================
@@ -1677,8 +1838,8 @@ int UTIL_TakeDamage( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAtt
flBonus = ARMOR_BONUS; flBonus = ARMOR_BONUS;
flRatio = ARMOR_RATIO; flRatio = ARMOR_RATIO;
if (!pEdict->v.takedamage) if (!pEdict->v.takedamage || (pEdict->v.flags & FL_GODMODE))
return 0; return 0; // refuse the damage
if ( ( bitsDamageType & DMG_BLAST ) ) if ( ( bitsDamageType & DMG_BLAST ) )
{ {
@@ -1755,10 +1916,18 @@ int UTIL_TakeDamage( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAtt
// do the damage // do the damage
pEdict->v.health -= flTake; pEdict->v.health -= flTake;
// store entity that hurt this player
pEdict->v.dmg_inflictor = ENT(pevAttacker);
if ( pEdict->v.health <= 0 ) if ( pEdict->v.health <= 0 )
{ {
pEdict->v.health = 1; // can't suicide if already dead! pEdict->v.health = 1; // can't suicide if already dead!
gpGamedllFuncs->dllapi_table->pfnClientKill(pEdict); 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 )
pevAttacker->frags += 1.0;
} }
// tell director about it // tell director about it
@@ -1868,7 +2037,11 @@ void UTIL_TraceBleed( edict_t *pEdict, float flDamage, Vector vecDir, TraceResul
if ( Bloodtr.flFraction != 1.0 ) if ( Bloodtr.flFraction != 1.0 )
{ {
UTIL_BloodDecalTrace( &Bloodtr, BLOOD_COLOR_RED ); int bloodColor = pEdict->v.iuser3;
if ( !bloodColor )
bloodColor = BLOOD_COLOR_RED;
UTIL_BloodDecalTrace( &Bloodtr, bloodColor );
} }
} }
} }
@@ -1879,9 +2052,13 @@ void UTIL_TraceAttack( edict_t *pEdict, entvars_t *pevAttacker, float flDamage,
if ( pEdict->v.takedamage ) if ( pEdict->v.takedamage )
{ {
int bloodColor = pEdict->v.iuser3;
if ( !bloodColor )
bloodColor = BLOOD_COLOR_RED;
AddMultiDamage( pevAttacker, pEdict, flDamage, bitsDamageType ); AddMultiDamage( pevAttacker, pEdict, flDamage, bitsDamageType );
SpawnBlood(ptr->vecEndPos, BLOOD_COLOR_RED, flDamage);// a little surface blood. SpawnBlood(ptr->vecEndPos, bloodColor, flDamage);// a little surface blood.
UTIL_TraceBleed( pEdict, flDamage, vecDir, ptr, bitsDamageType ); UTIL_TraceBleed( pEdict, flDamage, vecDir, ptr, bitsDamageType );
} }
@@ -1961,3 +2138,14 @@ bool UTIL_IsBSPModel( edict_t *pent )
{ {
return (pent->v.solid == SOLID_BSP || pent->v.movetype == MOVETYPE_PUSHSTEP); return (pent->v.solid == SOLID_BSP || pent->v.movetype == MOVETYPE_PUSHSTEP);
} }
void UTIL_TakeDamageExternal( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
// Tell AMXX to call TakeDamage for us if it can.
if (CVAR_GET_FLOAT("_glb_takedamage"))
{
char extCmd[64];
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 DONT_BLEED -1
#define BLOOD_COLOR_RED (BYTE)247 #define BLOOD_COLOR_RED (BYTE)247
#define BLOOD_COLOR_YELLOW (BYTE)195 #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 typedef enum
{ {
@@ -400,6 +405,9 @@ extern DLL_GLOBAL const Vector g_vecZero;
#define SVC_ROOMTYPE 37 #define SVC_ROOMTYPE 37
#define SVC_HLTV 50 #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 // prxoy director stuff
#define DRC_EVENT 3 // informs the dircetor about ann important game event #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 #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 // 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 // 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). // 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, void EMIT_SOUND_DYN(edict_t *entity, int channel, const char *sample, float volume, float attenuation, int flags, int pitch);
int flags, int pitch);
inline void EMIT_SOUND(edict_t *entity, int channel, const char *sample, float volume, float attenuation) 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); void EMIT_GROUPNAME_SUIT(edict_t *entity, const char *groupname);
#define PRECACHE_SOUND_ARRAY( a ) \ #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 ) \ #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) ); EMIT_SOUND_DYN ( ENT(pev), chan , array [ RANDOM_LONG(0,ARRAYSIZE( array )-1) ], 1.0, ATTN_NORM, 0, RANDOM_LONG(95,105) );
@@ -538,3 +556,4 @@ Vector UTIL_Center(edict_t *pEdict);
edict_t *UTIL_GetNextTarget( edict_t *pEntity ); edict_t *UTIL_GetNextTarget( edict_t *pEntity );
edict_t *UTIL_FindNearestPlayer(edict_t *pEdict, float m_flFieldOfView); edict_t *UTIL_FindNearestPlayer(edict_t *pEdict, float m_flFieldOfView);
bool UTIL_IsBSPModel( edict_t *pent ); bool UTIL_IsBSPModel( edict_t *pent );
void UTIL_TakeDamageExternal( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );

1336
src/dlls/voltigore.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -79,6 +79,8 @@ void ApplyMultiDamage(entvars_t *pevInflictor, entvars_t *pevAttacker )
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(gMultiDamage.pEntity)); CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(gMultiDamage.pEntity));
pMonster->TakeDamage(pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type ); pMonster->TakeDamage(pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type );
} }
else
UTIL_TakeDamageExternal(gMultiDamage.pEntity, pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type );
} }
@@ -136,6 +138,19 @@ void DecalGunshot( TraceResult *pTrace, int iBulletType )
switch( iBulletType ) switch( iBulletType )
{ {
case BULLET_PLAYER_CROWBAR:
{
// wall decal
UTIL_DecalTrace( pTrace, DamageDecal( pEntity, DMG_CLUB ) );
break;
}
default:
{
// smoke and decal
UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) );
break;
}
/* why the duplicate case?
case BULLET_PLAYER_9MM: case BULLET_PLAYER_9MM:
case BULLET_MONSTER_9MM: case BULLET_MONSTER_9MM:
case BULLET_PLAYER_MP5: case BULLET_PLAYER_MP5:
@@ -154,6 +169,7 @@ void DecalGunshot( TraceResult *pTrace, int iBulletType )
// wall decal // wall decal
UTIL_DecalTrace( pTrace, DamageDecal( pEntity, DMG_CLUB ) ); UTIL_DecalTrace( pTrace, DamageDecal( pEntity, DMG_CLUB ) );
break; break;
*/
} }
} }
} }

View File

@@ -47,8 +47,37 @@ public:
virtual void Killed( entvars_t *pevAttacker, int iGib ); 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. 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
class CMSporeGrenade : public CMBaseMonster
{
public:
void Precache(void);
void Spawn(void);
static CMSporeGrenade *ShootTimed(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, bool ai);
static CMSporeGrenade *ShootContact(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity);
void Explode(TraceResult *pTrace);
void EXPORT BounceTouch(edict_t *pOther);
void EXPORT ExplodeTouch(edict_t *pOther);
void EXPORT DangerSoundThink(void);
void EXPORT Detonate(void);
void EXPORT TumbleThink(void);
void BounceSound(void);
void DangerSound();
static void SpawnTrailParticles(const Vector& origin, const Vector& direction, int modelindex, int count, float speed, float noise);
static void SpawnExplosionParticles(const Vector& origin, const Vector& direction, int modelindex, int count, float speed, float noise);
void UpdateOnRemove();
CMSprite* m_pSporeGlow;
EHANDLE m_hOwner;
};
// constant items // constant items
#define ITEM_HEALTHKIT 1 #define ITEM_HEALTHKIT 1
@@ -177,6 +206,8 @@ typedef enum
BULLET_MONSTER_9MM, BULLET_MONSTER_9MM,
BULLET_MONSTER_MP5, BULLET_MONSTER_MP5,
BULLET_MONSTER_12MM, BULLET_MONSTER_12MM,
BULLET_MONSTER_762,
BULLET_MONSTER_357,
} Bullet; } Bullet;

View File

@@ -82,6 +82,11 @@ const char *CMZombie::pPainSounds[] =
//========================================================= //=========================================================
int CMZombie :: Classify ( void ) int CMZombie :: Classify ( void )
{ {
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_ALIEN_MONSTER; return CLASS_ALIEN_MONSTER;
} }
@@ -106,16 +111,6 @@ void CMZombie :: SetYawSpeed ( void )
int CMZombie :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) 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. // HACK HACK -- until we fix this.
if ( IsAlive() ) if ( IsAlive() )
PainSound(); PainSound();
@@ -241,12 +236,12 @@ void CMZombie :: Spawn()
{ {
Precache( ); Precache( );
SET_MODEL(ENT(pev), "models/zombie.mdl"); SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/zombie.mdl"));
UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX ); UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX );
pev->solid = SOLID_SLIDEBOX; pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP; pev->movetype = MOVETYPE_STEP;
m_bloodColor = BLOOD_COLOR_GREEN; m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->health = gSkillData.zombieHealth; pev->health = gSkillData.zombieHealth;
pev->view_ofs = VEC_VIEW;// position of the eyes relative to monster's origin. 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 ) m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
@@ -254,6 +249,13 @@ void CMZombie :: Spawn()
m_afCapability = bits_CAP_DOORS_GROUP; m_afCapability = bits_CAP_DOORS_GROUP;
MonsterInit(); MonsterInit();
pev->classname = MAKE_STRING( "monster_zombie" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Zombie" );
}
} }
//========================================================= //=========================================================
@@ -261,27 +263,14 @@ void CMZombie :: Spawn()
//========================================================= //=========================================================
void CMZombie :: Precache() void CMZombie :: Precache()
{ {
int i;
PRECACHE_MODEL("models/zombie.mdl"); PRECACHE_MODEL("models/zombie.mdl");
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) PRECACHE_SOUND_ARRAY(pAttackHitSounds);
PRECACHE_SOUND((char *)pAttackHitSounds[i]); PRECACHE_SOUND_ARRAY(pAttackMissSounds);
PRECACHE_SOUND_ARRAY(pAttackSounds);
for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) PRECACHE_SOUND_ARRAY(pIdleSounds);
PRECACHE_SOUND((char *)pAttackMissSounds[i]); PRECACHE_SOUND_ARRAY(pAlertSounds);
PRECACHE_SOUND_ARRAY(pPainSounds);
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]);
} }
//========================================================= //=========================================================

View File

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

View File

@@ -74,14 +74,17 @@
#endif //defined WIN32 #endif //defined WIN32
#endif #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. // On x86 va_list is just a pointer.
#define va_copy(dst,src) ((dst)=(src)) #define va_copy(dst,src) ((dst)=(src))
#else #else
// Some systems that do not supply va_copy have __va_copy instead, since #if (linux)
// that was the name used in the draft proposal. // Some systems that do not supply va_copy have __va_copy instead, since
#if !defined(__GNUC__) || __GNUC__ < 3 // that was the name used in the draft proposal.
#define va_copy __va_copy #if !defined(__GNUC__) || __GNUC__ < 3
#define va_copy __va_copy
#endif
#endif #endif
#endif #endif

View File

@@ -37,7 +37,13 @@
#ifndef ENGINE_API_H #ifndef ENGINE_API_H
#define ENGINE_API_H #define ENGINE_API_H
#include <stdint.h> // why? // No proper C99 support in old MVSC6, use custom stdint.h
#ifdef _WIN32
#include "stdint_c99.h"
#else
#include <stdint.h>
#endif
#include "comp_dep.h" #include "comp_dep.h"
// Plugin's GetEngineFunctions, called by metamod. // Plugin's GetEngineFunctions, called by metamod.

View File

@@ -80,7 +80,9 @@
// DLL. // DLL.
#undef DLLEXPORT #undef DLLEXPORT
#ifdef _WIN32 #ifdef _WIN32
#define DLLEXPORT __declspec(dllexport) __attribute__ ((externally_visible)) // __attribute__ ((externally_visible)) is not recognized by VC++6
// or, rather, is GCC-only. do we need it for windows builds? -Giegue
#define DLLEXPORT __declspec(dllexport)
// WINAPI should be provided in the windows compiler headers. // WINAPI should be provided in the windows compiler headers.
// It's usually defined to something like "__stdcall". // It's usually defined to something like "__stdcall".
#elif defined(linux) #elif defined(linux)

218
src/metamod/stdint_c99.h Normal file
View File

@@ -0,0 +1,218 @@
/**
* 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.
*
* This code is distributed in the hope that it will be useful but
* WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
* DISCLAIMED. This includes but is not limited to warranties of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* Date: 2010-04-02
*/
#ifndef _MSC_VER
#error This header should only be used with Microsoft compilers
#endif
#ifndef _STDINT_H
#define _STDINT_H
#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
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 __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;
typedef short int_least16_t;
typedef unsigned short uint_least16_t;
typedef int int_least32_t;
typedef unsigned uint_least32_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 __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 __int64 intmax_t;
typedef unsigned __int64 uintmax_t;
/* 7.18.2 Limits of specified-width integer types */
/* 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 (-9223372036854775807LL - 1)
#define INT8_MAX 127
#define INT16_MAX 32767
#define INT32_MAX 2147483647
#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 */
#ifdef _WIN64
#define INTPTR_MIN INT64_MIN
#define INTPTR_MAX INT64_MAX
#define UINTPTR_MAX UINT64_MAX
#else
#define INTPTR_MIN INT32_MIN
#define INTPTR_MAX INT32_MAX
#define UINTPTR_MAX UINT32_MAX
#endif
/* 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 */
#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
#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 0U
#define WCHAR_MAX 0xffffU
#endif
/*
* 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 */
/* 7.18.4.1 Macros for minimum-width integer constants
Accoding to Douglas Gwyn <gwyn@arl.mil>:
"This spec was changed in ISO/IEC 9899:1999 TC1; in ISO/IEC
9899:1999 as initially published, the expansion was required
to be an integer constant of precisely matching type, which
is impossible to accomplish for the shorter types on most
platforms, because C99 provides no standard way to designate
an integer constant with width less than that of type int.
TC1 changed this to require just an integer constant
*expression* with *promoted* type."
The trick used here is from Clive D W Feather.
*/
#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) val##i64
#define UINTMAX_C(val) val##ui64
#endif