From 513cde62317cfd8b3db8451b798ee8a0c305ad30 Mon Sep 17 00:00:00 2001 From: Giegue Date: Thu, 23 Feb 2023 03:29:53 -0300 Subject: [PATCH] Add compatibility with Half-Life. --- README.md | 2 +- aux/README.md | 7 ++ aux/valve/README.md | 19 +++ aux/valve/bin/hl_monsterbridge.amxx | Bin 0 -> 4049 bytes aux/valve/src/hl_monsterbridge.sma | 188 ++++++++++++++++++++++++++++ 5 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 aux/README.md create mode 100644 aux/valve/README.md create mode 100644 aux/valve/bin/hl_monsterbridge.amxx create mode 100644 aux/valve/src/hl_monsterbridge.sma 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 0000000000000000000000000000000000000000..59a174c47e5cbe21a648e691db50611021fb9058 GIT binary patch literal 4049 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 DP8=bR literal 0 HcmV?d00001 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 ); + } +}