diff --git a/README.md b/README.md index b5d86b4..962d563 100644 --- a/README.md +++ b/README.md @@ -193,7 +193,7 @@ Current milestones are separated by "Tiers", which are as follows: - Update source code so it can compile AND run **ON WINDOWS**. **[DONE]** - Implement *-almost-* all Opposing Force monsters. **[DONE]** - Implement *-almost-* all default Sven Co-op monsters. **[DONE]** -- Make MonsterMod aware of normal game entities. *-For those who want to use the plugin in vanilla HL.-* +- Make MonsterMod aware of normal game entities. *-For those who want to use the plugin in vanilla HL.-* **[DONE]** - Custom model support. ### Tier 4 diff --git a/aux/README.md b/aux/README.md new file mode 100644 index 0000000..5df6f0e --- /dev/null +++ b/aux/README.md @@ -0,0 +1,7 @@ +## Auxiliary Tools + +This folder contains auxiliary tools used to enhance the compatibility and/or interaction of MonsterMod with other mods. + +As these are auxiliary, they aren't needed to enjoy MonsterMod. If you want an extra boost, feel free to install these. + +Once all milestones are completed, the possibility of removing the need for external plugins will be considered. Until then, this has to stay. diff --git a/aux/valve/README.md b/aux/valve/README.md new file mode 100644 index 0000000..0010cdf --- /dev/null +++ b/aux/valve/README.md @@ -0,0 +1,19 @@ +## Half-Life (valve) + +Auxiliary Tools to use MonsterMod in the original Half-Life. + +### AMX Mod X Plugins + +#### -A note about AMXX plugins- + +All plugins in this section require AMXX 1.9.0 or greater, they will not work on 1.8.2 or older. + +Compiled plugins are provided in the `bin` folder for your convenience. However, if you prefer to build the plugins yourself, the source code of all AMXX plugins are located in the `src` folder for compilation. + +#### Half-Life <--> MonsterMod Bridge Plugin + +MonsterMod monsters are hacked "func_wall"'s with simulated AI. Because of this, MonsterMod cannot interact with the Half-Life monsters. This issue also happens on the other side: The Half-Life monsters cannot interact with the MonsterMod monsters. + +In simple terms, this plugin acts as a "bridge" to help Half-Life and MonsterMod communicate with each other. With this plugin, HL and MM monsters can "see" and interact with each other, and will react accordingly. + +Without this plugin, HL and MM monsters will be "invisible" to each other. diff --git a/aux/valve/bin/hl_monsterbridge.amxx b/aux/valve/bin/hl_monsterbridge.amxx new file mode 100644 index 0000000..59a174c Binary files /dev/null and b/aux/valve/bin/hl_monsterbridge.amxx differ diff --git a/aux/valve/src/hl_monsterbridge.sma b/aux/valve/src/hl_monsterbridge.sma new file mode 100644 index 0000000..199c817 --- /dev/null +++ b/aux/valve/src/hl_monsterbridge.sma @@ -0,0 +1,188 @@ +#pragma semicolon 1 + +#include +#include +#include + +// (monster.h from HLSDK) monster to monster relationship types +const R_AL = -2; // (ALLY) pals. Good alternative to R_NO when applicable. +const R_FR = -1; // (FEAR) will run. +const R_NO = 0; // (NO RELATIONSHIP) disregard. +const R_DL = 1; // (DISLIKE) will attack. +const R_HT = 2; // (HATE) will attack this character instead of any visible DISLIKEd characters. +//const R_NM = 3; // (NEMESIS) a monster will ALWAYS attack its nemesis, no matter what. + +new Trie:g_HLDefaultNames; + +public plugin_init() +{ + register_plugin( "HL-MONSTER Bridge", "1.0", "Giegue" ); + + RegisterHam( Ham_IRelationship, "monster_alien_controller", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_alien_grunt", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_alien_slave", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_apache", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_babycrab", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_barnacle", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_barney", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_bigmomma", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_bullchicken", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_gargantua", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_headcrab", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_houndeye", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_human_assassin", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_human_grunt", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_ichthyosaur", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_miniturret", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_scientist", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_sentry", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_snark", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_turret", "mmIRelationship" ); + RegisterHam( Ham_IRelationship, "monster_zombie", "mmIRelationship" ); + + g_HLDefaultNames = TrieCreate(); + + // Better than a nest of if-else if i guess... + TrieSetString( g_HLDefaultNames, "monster_headcrab", "Head Crab" ); + TrieSetString( g_HLDefaultNames, "monster_babycrab", "Baby Head Crab" ); + TrieSetString( g_HLDefaultNames, "monster_bullchicken", "Bullsquid" ); + TrieSetString( g_HLDefaultNames, "monster_barnacle", "Barnacle" ); + TrieSetString( g_HLDefaultNames, "monster_bigmomma", "Big Momma" ); + TrieSetString( g_HLDefaultNames, "monster_houndeye", "Houndeye" ); + TrieSetString( g_HLDefaultNames, "monster_alien_slave", "Alien Slave" ); + TrieSetString( g_HLDefaultNames, "monster_alien_controller", "Alien Controller" ); + TrieSetString( g_HLDefaultNames, "monster_alien_grunt", "Alien Grunt" ); + TrieSetString( g_HLDefaultNames, "monster_zombie", "Zombie" ); + TrieSetString( g_HLDefaultNames, "monster_ichthyosaur", "Ichthyosaur" ); + TrieSetString( g_HLDefaultNames, "monster_human_grunt", "Human Grunt" ); + TrieSetString( g_HLDefaultNames, "monster_human_assassin", "Female Assassin" ); + TrieSetString( g_HLDefaultNames, "monster_barney", "Barney" ); + TrieSetString( g_HLDefaultNames, "monster_gman", "Goverment Man" ); + TrieSetString( g_HLDefaultNames, "monster_scientist", "Scientist" ); + TrieSetString( g_HLDefaultNames, "monster_sentry", "Sentry Turret" ); + TrieSetString( g_HLDefaultNames, "monster_snark", "Snark" ); + TrieSetString( g_HLDefaultNames, "monster_miniturret", "Mini-Turret" ); + TrieSetString( g_HLDefaultNames, "monster_turret", "Turret" ); + TrieSetString( g_HLDefaultNames, "monster_apache", "Apache" ); + TrieSetString( g_HLDefaultNames, "monster_osprey", "Osprey Helicopter" ); + TrieSetString( g_HLDefaultNames, "monster_gargantua", "Gargantua" ); + TrieSetString( g_HLDefaultNames, "monster_nihilanth", "Nihilanth" ); + TrieSetString( g_HLDefaultNames, "monster_tentacle"," Tentacle" ); + + set_task( 0.3, "hlScan", 0, "", 0, "b" ); + register_srvcmd( "monster_hurt_entity", "hlTakeDamage" ); +} +public plugin_end() +{ + TrieDestroy( g_HLDefaultNames ); // free the handle +} + +public mmIRelationship( entity, other ) +{ + new selfClassify, otherClassify; + + selfClassify = entity_get_int( entity, EV_INT_iuser4 ); + otherClassify = entity_get_int( other, EV_INT_iuser4 ); + + if ( entity_get_int( other, EV_INT_flags ) & FL_CLIENT ) + otherClassify = 2; + + // Get proper relationship + SetHamReturnInteger( IRelationshipByClass( selfClassify, otherClassify ) ); + return HAM_OVERRIDE; +} + +public hlScan() +{ + new entity, szClassname[ 33 ], szDisplayname[ 129 ], bool:found; + for ( entity = 0; entity < get_global_int( GL_maxEntities ); entity++ ) + { + // Nothing deleted me? + if ( is_valid_ent( entity ) ) + { + entity_get_string( entity, EV_SZ_classname, szClassname, charsmax( szClassname ) ); + if ( equal( szClassname, "monster_", 8 ) ) + { + // Classify not overriden? + if ( !entity_get_int( entity, EV_INT_iuser4 ) ) + { + // Set default + entity_set_int( entity, EV_INT_iuser4, ExecuteHam( Ham_Classify, entity ) ); + } + + // Blood color not overriden? + if ( !entity_get_int( entity, EV_INT_iuser3 ) ) + { + // Set default + entity_set_int( entity, EV_INT_iuser3, ExecuteHam( Ham_BloodColor, entity ) ); + } + + // No name set? + entity_get_string( entity, EV_SZ_netname, szDisplayname, charsmax( szDisplayname ) ); + if ( !strlen( szDisplayname ) ) + { + // Find a default name + found = TrieGetString( g_HLDefaultNames, szClassname, szDisplayname, charsmax( szDisplayname ) ); + + // Use this name + entity_set_string( entity, EV_SZ_netname, ( found ? szDisplayname : "Monster" ) ); + } + } + } + } +} + +stock IRelationshipByClass( classEntity, classOther ) +{ + if ( classEntity == -1 ) + classEntity = 0; // CLASS_FORCE_NONE + + new iEnemy[16][16] = + { // NONE MACH PLYR HPASS HMIL AMIL APASS AMONST APREY APRED INSECT PLRALY PBWPN ABWPN RXPIT RXSHK + /*NONE*/ { R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO, R_NO, R_NO, R_NO, R_NO }, + /*MACHINE*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_DL, R_DL, R_DL, R_DL }, + /*PLAYER*/ { R_NO ,R_DL ,R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_DL, R_DL, R_DL, R_DL }, + /*HUMANPASSIVE*/{ R_NO ,R_FR ,R_AL ,R_AL ,R_HT ,R_DL ,R_DL ,R_HT ,R_DL ,R_DL ,R_NO ,R_AL, R_NO, R_NO, R_FR, R_FR }, + /*HUMANMILITAR*/{ R_NO ,R_NO ,R_DL ,R_DL ,R_AL ,R_HT ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_NO, R_NO, R_HT, R_HT }, + /*ALIENMILITAR*/{ R_NO ,R_DL ,R_DL ,R_DL ,R_HT ,R_AL ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO, R_DL, R_HT }, + /*ALIENPASSIVE*/{ R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO, R_NO, R_NO, R_NO, R_NO }, + /*ALIENMONSTER*/{ R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO, R_NO, R_NO }, + /*ALIENPREY */{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_NO ,R_FR ,R_NO ,R_DL, R_NO, R_NO, R_FR, R_NO }, + /*ALIENPREDATO*/{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_HT ,R_NO ,R_NO ,R_DL, R_NO, R_NO, R_DL, R_DL }, + /*INSECT*/ { R_NO ,R_FR ,R_FR ,R_FR ,R_FR ,R_NO ,R_FR ,R_FR ,R_FR ,R_FR ,R_NO ,R_FR, R_NO, R_NO, R_NO, R_NO }, + /*PLAYERALLY*/ { R_NO ,R_DL ,R_AL ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_NO, R_NO, R_DL, R_DL }, + /*PBIOWEAPON*/ { R_NO ,R_NO ,R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_NO, R_DL, R_DL, R_DL }, + /*ABIOWEAPON*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_AL ,R_NO ,R_DL ,R_DL ,R_NO ,R_NO ,R_DL, R_DL, R_NO, R_DL, R_DL }, + /*RXPITDRONE*/ { R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL ,R_DL ,R_NO ,R_DL, R_NO, R_NO, R_AL, R_AL }, + /*RXSHOCKTRP*/ { R_NO ,R_DL ,R_HT ,R_DL ,R_HT ,R_HT ,R_DL ,R_NO ,R_NO ,R_DL ,R_NO ,R_DL, R_NO, R_NO, R_AL, R_AL } + }; + + return iEnemy[ classEntity ][ classOther ]; +} + +public hlTakeDamage() +{ + if ( read_argc() == 6 ) + { + new victim, inflictor, attacker; + new Float:damage; + new damageBits; + + victim = read_argv_int( 1 ); + inflictor = read_argv_int( 2 ); + attacker = read_argv_int( 3 ); + damage = read_argv_float( 4 ); + damageBits = read_argv_int( 5 ); + + // attacker and inflictor can be null, but never allow victim to be null + if ( !is_valid_ent( victim ) ) + return; + + if ( !is_valid_ent( inflictor ) ) + inflictor = 0; + if ( !is_valid_ent( attacker ) ) + attacker = 0; + + ExecuteHamB( Ham_TakeDamage, victim, inflictor, attacker, damage, damageBits ); + } +}