/* AMX Mod X * Natural Selection Module * * by the AMX Mod X Development Team * * This file is part of AMX Mod X. * * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * In addition, as a special exception, the author gives permission to * link the code of this program with the Half-Life Game Engine ("HL * Engine") and Modified Game Libraries ("MODs") developed by Valve, * L.L.C ("Valve"). You must obey the GNU General Public License in all * respects for all of the code used other than the HL Engine and MODs * from Valve. If you modify this file, you may extend this exception * to your version of the file, but you are not obligated to do so. If * you do not wish to do so, delete this exception statement from your * version. */ #include #include "../sdk/amxxmodule.h" #include "cbase.h" // TakeDamage #include "../ns.h" #include "../utilfunctions.h" #include "../NEW_Util.h" #include "../GameManager.h" #include "../SpawnManager.h" #include "../LocationManager.h" #include "../TitleManager.h" #include "../CPlayer.h" edict_t* avhgameplay=NULL; // drop-in replacement for user_kill static cell AMX_NATIVE_CALL ns_user_kill(AMX *amx, cell *params) { CreatePlayerPointer(amx,params[1]); // 2 is commander, never slay commander if (player->GetPev()->iuser3 == 2) { return 0; } if (MF_IsPlayerIngame(params[1]) && MF_IsPlayerAlive(params[1])) { REAL bef = player->GetPev()->frags; edict_t *pEntity = CREATE_NAMED_ENTITY(MAKE_STRING("trigger_hurt")); if (pEntity) { KeyValueData kvd; kvd.szClassName="trigger_hurt"; kvd.szKeyName="classname"; kvd.szValue="trigger_hurt"; kvd.fHandled=0; MDLL_KeyValue(pEntity,&kvd); kvd.szClassName="trigger_hurt"; kvd.szKeyName="dmg"; kvd.szValue="50000.0"; kvd.fHandled=0; MDLL_KeyValue(pEntity,&kvd); kvd.szClassName="trigger_hurt"; kvd.szKeyName="damagetype"; kvd.szValue="1"; kvd.fHandled=0; MDLL_KeyValue(pEntity,&kvd); kvd.szClassName="trigger_hurt"; kvd.szKeyName="origin"; kvd.szValue="8192 8192 8192"; kvd.fHandled=0; MDLL_KeyValue(pEntity,&kvd); MDLL_Spawn(pEntity); pEntity->v.classname=MAKE_STRING("slay"); MDLL_Touch(pEntity,player->GetEdict()); REMOVE_ENTITY(pEntity); } // If the optional parameter is 1, restore the frag count if (params[2]) { player->GetPev()->frags = bef; } return 1; } return 0; } // drop-in replacement for user_slap #define ANGLEVECTORS (*g_engfuncs.pfnAngleVectors) static cell AMX_NATIVE_CALL ns_user_slap(AMX *amx, cell *params) /* 2 param */ { CreatePlayerPointer(amx,params[1]); int power = abs((int)params[2]); // Check if commander, if player is comm then stop if (player->GetPev()->iuser3 == 2) { return 0; } if (MF_IsPlayerIngame(player->index()) && MF_IsPlayerAlive(player->index())) { if (player->GetPev()->health <= power) { float bef = player->GetPev()->frags; /*MDLL_ClientKill(pPlayer->pEdict);*/ edict_t *pEntity = CREATE_NAMED_ENTITY(MAKE_STRING("trigger_hurt")); if (pEntity) { KeyValueData kvd; kvd.szClassName="trigger_hurt"; kvd.szKeyName="classname"; kvd.szValue="trigger_hurt"; kvd.fHandled=0; MDLL_KeyValue(pEntity,&kvd); kvd.szClassName="trigger_hurt"; kvd.szKeyName="dmg"; kvd.szValue="20000.0"; kvd.fHandled=0; MDLL_KeyValue(pEntity,&kvd); kvd.szClassName="trigger_hurt"; kvd.szKeyName="damagetype"; kvd.szValue="1"; kvd.fHandled=0; MDLL_KeyValue(pEntity,&kvd); kvd.szClassName="trigger_hurt"; kvd.szKeyName="origin"; kvd.szValue="8192 8192 8192"; kvd.fHandled=0; MDLL_KeyValue(pEntity,&kvd); MDLL_Spawn(pEntity); pEntity->v.classname=MAKE_STRING("slap"); MDLL_Touch(pEntity,player->GetEdict()); REMOVE_ENTITY(pEntity); } player->GetPev()->frags = bef; } else { int numparam = *params/sizeof(cell); if (numparam<3 || params[3]) { player->GetPev()->velocity.x += RANDOM_LONG(-600,600); player->GetPev()->velocity.y += RANDOM_LONG(-180,180); player->GetPev()->velocity.z += RANDOM_LONG(100,200); } else { vec3_t v_forward, v_right; vec3_t vang = player->GetPev()->angles; float fang[3]; fang[0] = vang.x; fang[1] = vang.y; fang[2] = vang.z; ANGLEVECTORS( fang, v_forward, v_right, NULL ); player->GetPev()->velocity = player->GetPev()->velocity + v_forward * 220 + Vector(0,0,200); } player->GetPev()->punchangle.x = RANDOM_LONG(-10,10); player->GetPev()->punchangle.y = RANDOM_LONG(-10,10); player->GetPev()->health -= power; int armor = (int)player->GetPev()->armorvalue; armor -= power; if (armor < 0) { armor = 0; } player->GetPev()->armorvalue = armor; player->GetPev()->dmg_inflictor = player->GetEdict(); static const char *bit_sound[3] = { "weapons/cbar_hitbod1.wav", "weapons/cbar_hitbod2.wav", "weapons/cbar_hitbod3.wav" }; EMIT_SOUND_DYN2(player->GetEdict(), CHAN_VOICE, bit_sound[RANDOM_LONG(0,2)], 1.0, ATTN_NORM, 0, PITCH_NORM); } return 1; } return 0; } // native ns_get_locationname(Float:x, Float:y, name[], len, lookup=0); static cell AMX_NATIVE_CALL ns_get_locationname(AMX *amx, cell *params) { vec3_t location; location.x=amx_ctof2(params[1]); location.y=amx_ctof2(params[2]); if ((params[0] / sizeof(cell)) >= 5) { return MF_SetAmxString(amx,params[3],LocationMan.Lookup(location,params[5]),params[4]); } else { return MF_SetAmxString(amx,params[3],LocationMan.Lookup(location,0),params[4]); } } // ns_lookup_title(const Key[], Output[], Size) static cell AMX_NATIVE_CALL ns_lookup_title(AMX *amx, cell *params) { // FIX: some keys have upper case characters; to fix i store all keys as lower case String Input(MF_GetAmxString(amx,params[1],0,NULL)); Input.toLower(); const char *Output=TitleMan.Lookup(Input); if (Output==NULL) // not found { return -1; } return MF_SetAmxString(amx,params[2],Output,params[3]); }; // ns_round_in_progress() static cell AMX_NATIVE_CALL ns_round_in_progress(AMX *amx, cell *params) { return GameMan.RoundInProgress(); } // ns_get_spawn(team,number=0,Float:ret[3]) static cell AMX_NATIVE_CALL ns_get_spawn(AMX *amx, cell *params) { return SpawnMan.Lookup(amx,params); } // ns_get_mask(id,MASK_*) static cell AMX_NATIVE_CALL ns_get_mask(AMX *amx, cell *params) { CreateEdict(amx,params[1],-1); if (Entity->v.iuser4 & static_cast(params[2])) { return 1; } return 0; } // ns_set_mask(id,MASK_*,1 or 0) static cell AMX_NATIVE_CALL ns_set_mask(AMX *amx, cell *params) { CreateEdict(amx,params[1],-1); if (static_cast(params[3]) > 0) { if (Entity->v.iuser4 & static_cast(params[2])) { return 0; } Entity->v.iuser4 |= static_cast(params[2]); return 1; } if (Entity->v.iuser4 & static_cast(params[2])) { Entity->v.iuser4 &= ~static_cast(params[2]); return 1; } return 0; } // ns_popup(id (0 for all),"text",OnlyShowWithCLHelpOn=0) static cell AMX_NATIVE_CALL ns_popup(AMX *amx, cell *params) { GameMan.UpdateHudText2(); if (params[1]) { CreatePlayerPointer(amx, params[1]); if (!player->IsConnected()) { return 0; } MESSAGE_BEGIN(MSG_ONE,GameMan.GetHudText2(),NULL,player->GetEdict()); } else { MESSAGE_BEGIN(MSG_ALL,GameMan.GetHudText2()); } char msg[190]; strncpy(&msg[0],MF_GetAmxString(amx,params[2],0,NULL),188); WRITE_STRING(msg); WRITE_BYTE(params[3]); MESSAGE_END(); return 1; } // ns_is_combat() static cell AMX_NATIVE_CALL ns_is_combat(AMX *amx, cell *params) { return GameMan.IsCombat(); } /** * Pretty much a direct port of CheezyPeteza's unstick routine * from the old unstuck.amxx plugin. This is included here * to remove the engine requirement. * - * Return values: * 1 success * 0 no spot to move to * -1 invalid state (stunned/webbed) * -2 invalid class (commander/gorge) * -3 player is dead or a spectator * -4 player is invalid (unknown) * -5 player is invalid (disconnected) */ inline int GetPlayerHullSize(CPlayer *Player, int PlayerClass) { switch (PlayerClass) { case CLASS_SKULK: case CLASS_GORGE: case CLASS_LERK: return static_cast(head_hull); case CLASS_FADE: case CLASS_JETPACK: case CLASS_HEAVY: case CLASS_MARINE: if (Player->GetPev()->button & IN_DUCK || Player->GetPev()->flags & FL_DUCKING) { return static_cast(head_hull); } return static_cast(human_hull); case CLASS_ONOS: if (Player->GetPev()->button & IN_DUCK || Player->GetPev()->flags & FL_DUCKING) { return static_cast(human_hull); } return static_cast(large_hull); default: return -1; } return -1; } static cell AMX_NATIVE_CALL ns_unstick_player(AMX *amx, cell *params) { CreatePlayerPointer(amx,params[1]); if (!player->IsConnected()) { return -5; } if (player->GetPev()->iuser4 & (MASK_ENSNARED | MASK_PLAYER_STUNNED)) { return -1; } int PlayerClass=player->GetClass(); if (PlayerClass == CLASS_GESTATE || PlayerClass == CLASS_COMMANDER) { return -2; } if (PlayerClass == CLASS_DEAD || PlayerClass == CLASS_NOTEAM || PlayerClass == CLASS_UNKNOWN) { return -3; } int HullSize=GetPlayerHullSize(player, PlayerClass); if (HullSize==-1) { return -4; } Vector OriginalOrigin=player->GetPev()->origin; Vector NewOrigin; int Distance=params[2]; int Attempts; TraceResult Result; while (Distance < 1000) { Attempts=params[3]; while (Attempts--) { NewOrigin.x = RANDOM_FLOAT(OriginalOrigin.x - Distance,OriginalOrigin.x + Distance); NewOrigin.y = RANDOM_FLOAT(OriginalOrigin.y - Distance,OriginalOrigin.y + Distance); NewOrigin.z = RANDOM_FLOAT(OriginalOrigin.z - Distance,OriginalOrigin.z + Distance); // (const float *v1, const float *v2, int fNoMonsters, int hullNumber, edict_t *pentToSkip, TraceResult *ptr); TRACE_HULL(NewOrigin, NewOrigin, 0, HullSize, player->GetEdict(), &Result); if (Result.fInOpen && !Result.fAllSolid && !Result.fStartSolid) { SET_ORIGIN(player->GetEdict(),NewOrigin); return 1; } } Distance += params[2]; } return 0; // Couldn't be found } // Type: 131072 = DoT // ns_takedamage static cell AMX_NATIVE_CALL ns_takedamage(AMX *amx, cell *params) { // NASTY // Reinterprets pvPrivateData as CBaseEntity, then calls TakeDamage with the entvar of Inflictor, and Attacker, with the float value and damage type // The NS offset of TakeDamage hasn't changed from the HLSDK fortunately, so no offset digging is necessary return (reinterpret_cast(INDEXENT_NEW(params[1])->pvPrivateData))->TakeDamage(&(INDEXENT_NEW(params[2])->v),&(INDEXENT_NEW(params[3])->v),amx_ctof2(params[4]),static_cast(params[5])); } static cell AMX_NATIVE_CALL ns_get_gameplay(AMX* amx, cell* params) { if (avhgameplay == NULL) { avhgameplay = FIND_ENTITY_BY_CLASSNAME(NULL, "avhgameplay"); } if (avhgameplay == NULL || avhgameplay->pvPrivateData == NULL) // Still null? Get out of here { return NSGame_CantTell; } int ATeam /* i pity da foo */ = *reinterpret_cast(reinterpret_cast(avhgameplay->pvPrivateData) + MAKE_OFFSET(GAMEPLAY_TEAMA)); int BTeam = *reinterpret_cast(reinterpret_cast(avhgameplay->pvPrivateData) + MAKE_OFFSET(GAMEPLAY_TEAMB)); if (ATeam == 2 && // alien BTeam == 2) // alien { return NSGame_AlienVAlien; } if (ATeam == 1 && // marine BTeam == 1) // marine { return NSGame_MarineVMarine; } if (ATeam == 1 && // marine BTeam == 2) // alien { return NSGame_MarineVAlien; } return NSGame_Unknown; } #ifdef DEVELOPER_BUILD static cell AMX_NATIVE_CALL refmem(AMX *amx, cell *params) { return *(reinterpret_cast(params[1])); }; static cell AMX_NATIVE_CALL setmem(AMX *amx, cell *params) { int *ptr=reinterpret_cast(params[1]); *ptr=params[2]; return 1; }; #endif AMX_NATIVE_INFO general_natives[] = { { "user_kill", ns_user_kill }, // replacement natives { "user_slap", ns_user_slap }, // since ClientKill is changed in NS { "ns_get_locationname", ns_get_locationname }, { "ns_lookup_title", ns_lookup_title }, { "ns_round_in_progress", ns_round_in_progress }, { "ns_get_spawn", ns_get_spawn }, { "ns_get_mask", ns_get_mask }, { "ns_set_mask", ns_set_mask }, { "ns_is_combat", ns_is_combat }, { "ns_unstick_player", ns_unstick_player }, { "ns_popup", ns_popup }, { "ns_takedamage", ns_takedamage}, { "ns_get_gameplay", ns_get_gameplay }, #ifdef DEVELOPER_BUILD { "refmem", refmem }, { "setmem", setmem }, #endif { NULL, NULL } }; void AddNatives_General() { MF_AddNatives(general_natives); }