//vscript by stardustspy //do not use without my credit PLEASE!!!!!!!!! /////////////////////////////////////// //////////////// SETUP //////////////// /////////////////////////////////////// ::CONST <- getconsttable() ::ROOT <- getroottable() if (!("ConstantNamingConvention" in ROOT)) { foreach(a, b in Constants) foreach(k, v in b) { CONST[k] <- v != null ? v : 0 ROOT[k] <- v != null ? v : 0 } } IncludeScript("robot_va_oxidize", null) IncludeScript("hhh_death", null) IncludeScript("mvm_perks", null) local _root = getroottable() local util_obj_resource = Entities.FindByClassname(null, "tf_objective_resource") local nav_area_table = {} local weapons_ents_cleanup = [] local delete_thinks = [] NavMesh.GetAllAreas(nav_area_table) ::TEAM_RED <- 2 ::TEAM_BLU <- 3 ::TF_NAV_SPAWN_ROOM_RED <- 2 ::TF_NAV_SPAWN_ROOM_BLUE <- 4 ::TF_BOT_FAKE_CLIENT <- 1337 ::MAX_COUNT_WEAPON_EQUPPED <- 8 ::SOLID_NONE <- 0 ::CONTENTS_SOLID <- 1 ::CONTENTS_WINDOW <- 2 ::CONTENTS_GRATE <- 8 ::CONTENTS_MOVEABLE <- 16384 ::CONTENTS_PLAYERCLIP <- 65536 ::MASK_NPCWORLDSTATIC <- 131083 ::MASK_PLAYERSOLID <- 33636363 ::MASK_NPCSOLID <- 33701899 ::MASK_SOLID <- 33570827 ::MASK_SHOT <- 1174421507 ::MASK_SHOT_HULL <- 100679691 ::IN_ATTACK2 <- 2048 ::PI <- 3.14159265359 ::ALTFIRE <- Constants.FButtons.IN_ATTACK2 ::gamerules <- Entities.FindByClassname(null, "tf_gamerules") ::player_manager <- Entities.FindByClassname(null, "tf_player_manager") ::WORLD_SPAWN <- Entities.FindByClassname(null, "worldspawn") ::MISSION_NAME <- NetProps.GetPropString(util_obj_resource, "m_iszMvMPopfileName") ::MAX_COUNT_PLAYERS <- MaxClients().tointeger() ::DMG_CRITICAL <- 1048576 ::TF_DMG_CUSTOM_HEADSHOT <- 1 ::TF_DMG_CUSTOM_BACKSTAB <- 2 const STRING_NETPROP_ITEMDEF = "m_AttributeManager.m_Item.m_iItemDefinitionIndex" const SLOT_COUNT = 7 const EFL_USER = 1048576 // EFL_IS_BEING_LIFTED_BY_BARNACLE const EFL_USER2 = 1073741824 //EFL_NO_PHYSCANNON_INTERACTION const SINGLE_TICK = 0.015 const DMG_NO_BULLET_FALLOFF = 2097152 //fold ::ROOT <- getroottable(); if (!("ConstantNamingConvention" in ROOT)) // make sure folding is only done once { foreach (a,b in Constants) foreach (k,v in b) ROOT[k] <- v != null ? v : 0; } PrecacheModel("models/props_2fort/groundlight002.mdl") PrecacheModel("models/props_hydro/barrel_crate_half.mdl") PrecacheModel("models/props_oxidize_vscript/box_cloak.mdl") // by trigger hurt PrecacheSound("npc/scanner/cbot_energyexplosion1.wav") PrecacheSound("weapons/rescue_ranger_teleport_receive_02.wav") PrecacheSound("portal2/amb_container_alarm_01.wav") PrecacheSound("portal2/alarm_sweep_lp_lg_01.wav") PrecacheSound("npc/strider/fire.wav") PrecacheSound("weapons/cow_mangler_idle.wav") Convars.SetValue("tf_bot_flag_escort_max_count", 2); Convars.SetValue("tf_bot_flag_escort_range", 100); //eyeboss_team_blue /////////////////////////////////////// ///////////// UTILITY FUNCS /////////// /////////////////////////////////////// function FindOtherTeleportSpot(ent, spot_table) { // Define the size of the entity's bounding box with a slight increase local box_min_size = ent.GetBoundingMins() * 1.5 local box_max_size = ent.GetBoundingMaxs() * 1.5 // Repeat until a valid spot is found local is_valid_spot = false while (!is_valid_spot) { is_valid_spot = true // Assume the spot is valid initially // Get the bounding box of the current entity local ent_origin = ent.GetOrigin() local new_min = ent_origin + box_min_size local new_max = ent_origin + box_max_size // Loop through all other entities with the same name to check for overlap for (local other_ent = null; other_ent = Entities.FindByClassname(other_ent, "prop_dynamic");) { if (other_ent.GetName() == ent.GetName() && other_ent != ent) { // Get the bounding box of the other entity local other_min = other_ent.GetOrigin() + box_min_size local other_max = other_ent.GetOrigin() + box_max_size // Check if bounding boxes overlap (AABB collision check) if (new_min.x <= other_max.x && new_max.x >= other_min.x && new_min.y <= other_max.y && new_max.y >= other_min.y && new_min.z <= other_max.z && new_max.z >= other_min.z) { // Overlap detected, find a new spot for the entity is_valid_spot = false local new_nav = spot_table[RandomInt(0, spot_table.len() - 1)] local new_spot = new_nav.FindRandomSpot() // Set the new origin of the entity with a small offset to avoid ground collision ent.SetOrigin(new_spot) // Break out of the loop and recheck all entities break } } } } } //credit to lite function UnstuckEntity(entity) { ::MASK_PLAYERSOLID <- 33636363 local origin = entity.GetOrigin(); local trace = { start = origin, end = origin, hullmin = entity.GetBoundingMins(), hullmax = entity.GetBoundingMaxs(), mask = MASK_PLAYERSOLID, ignore = entity } TraceHull(trace); if ("startsolid" in trace) { local dirs = [Vector(1, 0, 0), Vector(-1, 0, 0), Vector(0, 1, 0), Vector(0, -1, 0), Vector(0, 0, 1), Vector(0, 0, -1)]; for (local i = 16; i <= 96; i += 16) { foreach (dir in dirs) { trace.start = origin + dir * i; trace.end = trace.start; delete trace.startsolid; TraceHull(trace); if (!("startsolid" in trace)) { entity.SetAbsOrigin(trace.end); return true; } } } return false; } return true; } //unique funcs function GetAveragePlayerPosition() { local total_position = Vector(0, 0, 0) // To store the sum of all players' positions local player_count = 0 // To count the number of alive players // Iterate through all the players for (local i = 1; i <= MAX_COUNT_PLAYERS; i++) { local player = PlayerInstanceFromIndex(i) if (player != null && player.IsAlive()) // Check if the player exists and is alive { local player_position = player.GetOrigin() // Get the player's position total_position = total_position + player_position // Add the player's position to the total player_count++ } } // If there are no alive players, return a default position if (player_count == 0) { return Vector(0, 0, 0) } // Calculate the average position by dividing the total by the number of players // VScript cannot divide interger by vector? Idk moment. This works. local average_position_x = total_position.x / player_count local average_position_y = total_position.y / player_count local average_position_z = total_position.z / player_count local final_position = Vector(average_position_x, average_position_y, average_position_z) //printl(final_position) //DebugDrawCircle(final_position, Vector(43, 123, 5), 0, 100, true, 10) return final_position // Return the average position vector } //popext funcs function PrintTable(table) { if (table == null) return; this.DoPrintTable(table, 0) } function DoPrintTable(table, indent) { local line = "" for (local i = 0; i < indent; i++) { line += " " } line += typeof table == "array" ? "[" : "{"; ClientPrint(null, 2, line) indent += 2 foreach(k, v in table) { line = "" for (local i = 0; i < indent; i++) { line += " " } line += k.tostring() line += " = " if (typeof v == "table" || typeof v == "array") { ClientPrint(null, 2, line) this.DoPrintTable(v, indent) } else { try { line += v.tostring() } catch (e) { line += typeof v } ClientPrint(null, 2, line) } } indent -= 2 line = "" for (local i = 0; i < indent; i++) { line += " " } line += typeof table == "array" ? "]" : "}"; ClientPrint(null, 2, line) } function RemovePlayerWearables(player) { for (local wearable = player.FirstMoveChild(); wearable != null; wearable = wearable.NextMovePeer()) { if (wearable.GetClassname() == "tf_wearable") { //use entfirebyhandle because using Destroy() breaks next iteration //will remove passive weapons such as backpacks and boots, but tbh this is fine since //we will just regenerate anyway EntFireByHandle(wearable, "Kill", "", -1, null, null) } } return } function StripWeapon(player, slot) { if (slot == -1) slot = player.GetActiveWeapon().GetSlot() for (local i = 0; i < SLOT_COUNT; i++) { local weapon = this.GetItemInSlot(player, i); if (weapon == null || weapon.GetSlot() != slot) continue; weapon.Kill(); break; } } function GiveWeapon(player, className, itemID) { if (typeof itemID == "string" && className == "tf_wearable") { CTFBot.GenerateAndWearItem.call(player, itemID) return } local weapon = Entities.CreateByClassname(className) NetProps.SetPropInt(weapon, STRING_NETPROP_ITEMDEF, itemID) NetProps.SetPropBool(weapon, "m_AttributeManager.m_Item.m_bInitialized", true) NetProps.SetPropBool(weapon, "m_bValidatedAttachedEntity", true) weapon.SetTeam(player.GetTeam()) weapon.DispatchSpawn() // Store the created weapon in the weapons cleanup table weapons_ents_cleanup.append(weapon) // remove existing weapon in same slot for (local i = 0; i < SLOT_COUNT; i++) { local heldWeapon = NetProps.GetPropEntityArray(player, "m_hMyWeapons", i) if (heldWeapon == null || heldWeapon.GetSlot() != weapon.GetSlot()) continue heldWeapon.Destroy() NetProps.SetPropEntityArray(player, "m_hMyWeapons", null, i) break } player.Weapon_Equip(weapon) player.Weapon_Switch(weapon) // fixes bug where if a player is firing/reloading, the player has to switch weapons again to equip the weapon EntFireByHandle(gamerules, "RunScriptCode", "activator.Weapon_Switch(caller)", 0.1, player, weapon) return weapon } function FindEntAndDealDamage(damage_origin) { for (local enemy; enemy = Entities.FindInSphere(enemy, damage_origin, 65);) { if (enemy == null) continue local damagable_ents = { "player" : 1 "obj_sentrygun" : 1 "obj_dispenser" : 1 "obj_teleporter" : 1 } if (enemy.GetClassname() in damagable_ents && enemy.GetTeam() == TEAM_RED) { enemy.TakeDamageEx(null, self, null, Vector(0, 0, 0), enemy.GetOrigin(), 95, 1024) } } } /////////////////////////////////////// ///////// R.A.S.T. FUNCTIONALITY ////// /////////////////////////////////////// // TODO // Ask CTriggerHurt if material_modify_control affects all ents and if so, how get around this // function SpawnGiantPowerup(powerup_count) { EmitSoundEx({ sound_name = "weapons/rescue_ranger_teleport_receive_02.wav"}); ::PowerupThink <- function() { local origin = self.GetOrigin() local soundlevel = (40 + (20 * log10(6000 / 36.0))).tointeger() //make it rotate if (iAngles_spin <= 360) { self.SetAngles(0, iAngles_spin++, 0) } else { self.SetAngles(0, 0, 0) iAngles_spin = 0 } //when we spawn, we must rise until we find the worldspawn just above us local nav_decloak_check = NavMesh.FindNavAreaAlongRay(origin, origin + Vector(0, 0, -1600), null) local nav_sit_here = NavMesh.FindNavAreaAlongRay(origin, origin + Vector(0, 0, -25), null) local trace_find_drop_area = { start = origin + Vector(0, 0, 20), end = origin + Vector(0, 0, 150), // Trace up hullmin = Vector(-10, -10, -10), // Bounding box min hullmax = Vector(10, 10, 10), // Bounding box max mask = (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_GRATE), ignore = self // Ignore the current entity itself } TraceHull(trace_find_drop_area) //if we find something, set our spawning if (trace_find_drop_area.hit) { bIsSpawning = true cloak.AcceptInput("StartFloatLerp" "1.0 0.0 2 0", null, null) } else { if (bIsSpawning == false) { self.SetOrigin(self.GetOrigin() + Vector(0, 0, 10)) } } // DebugDrawLine(origin, origin + Vector(0, 0, -800), 34, 114, 12, true, 0.1) // DebugDrawLine(origin, origin + Vector(0, 0, -15), 211, 42, 63, true, 0.1) iNoAnimateTimer++ if (iAnimatingStage == 0) { self.AcceptInput("SetAnimation" "deploy", null, null) iAnimatingStage = 1 } else if (iAnimatingStage == 1) { self.AcceptInput("SetAnimation" "deploy_idle", null, null) } else if (iAnimatingStage == 2) { self.AcceptInput("SetAnimation" "retract", null, null) iAnimatingStage = 3 } else if (iAnimatingStage == 3) { self.AcceptInput("SetAnimation" "retract_idle", null, null) iAnimatingStage = 4 bIsGrounded = true } if (bIsSpawning == true && nav_sit_here == null) { self.SetOrigin(self.GetOrigin() + Vector(0, 0, falling_value * -1)) } if (nav_sit_here != null && iNoAnimateTimer > 25) { iAnimatingStage = 3 } if (bIsGrounded == false) return -1 function PickGiantWeaponFunc(player) { player.ValidateScriptScope() local scope = player.GetScriptScope() local giant_type = 1 local string_caps = ["", "Scout", "Sniper", "Soldier", "Demoman", "Medic", "Heavy", "Pyro", "Spy", "Engineer", "Civilian"] local class_string = string_caps[player.GetPlayerClass()]; // Get the player's class // Define Giant Loadout Options local Giant_Loadout_Options = { //BALANCING PHILOSOPHY "Scout" : { "Giant Scout" : { "ChiefType" : 0, "tf_weapon_scattergun" : { "WepID" : 200, "Slot" : 0 "damage bonus" : 2, "fire rate bonus" : 0.5, "faster reload rate" : 0.5, "critboost on kill" : 5 "provide on active" : 1 "disable weapon switch" : 1 } }, "Armor Sandman" : { "ChiefType" : 1, "tf_weapon_bat_wood" : { "WepID" : 44, "Slot" : 2 "always crit" : 1 "dmg penalty vs players" : 5, "damage bonus hidden" : 0.2 "effect bar recharge rate increased" : 0.05, "max health additive penalty" : 0, "increased jump height" : 2, "cancel falling damage" : 1, //"damage bonus hidden" : 0.1556, "max health additive bonus" : 1400 "provide on active" : 1 "disable weapon switch" : 1 } }, }, "Soldier" : { "Giant Spammer Soldier" : { //spam "ChiefType" : 0, "tf_weapon_rocketlauncher" : { "WepID" : 205, "Slot" : 0 "mod max primary clip override" : -1, "fire rate bonus" : 0.5, "mod no reload DISPLAY ONLY" : 1, "provide on active" : 1 "disable weapon switch" : 1 } }, "Giant Burst Soldier" : { //burst "ChiefType" : 1, "tf_weapon_rocketlauncher" : { "WepID" : 730, "Slot" : 0 "faster reload rate" : 0.6, "fire rate bonus" : 0.1, "clip size upgrade atomic" : 5, "Projectile speed decreased" : 0.65, "provide on active" : 1 "disable weapon switch" : 1 "can overload" : 0 "projectile spread angle penalty" : 0 "Blast radius decreased" : 1 "reload time increased hidden" : 0.8 // cuz slower reload "fire rate bonus HIDDEN" : 1 } }, }, "Pyro" : { "Giant Dragon Pyro" : { //burst* "ChiefType" : 0, "tf_weapon_rocketlauncher_fireball" : { "WepID" : 1178, "Slot" : 0 "damage bonus" : 1.5, "mult_item_meter_charge_rate" : 0.6, "provide on active" : 1 "hidden primary max ammo bonus" : 4 "disable weapon switch" : 1 } }, "Giant Pyro" : { //Sustained fire "ChiefType" : 1, "tf_weapon_flamethrower" : { "WepID" : 208, "Slot" : 0 "heal on hit for rapidfire" : 3, "speed_boost_on_hit" : 3, "bleeding duration" : 3, "flame_drag": 4.25 "lunchbox adds minicrits" : 2 "projectile range increased" : 1.5 "damage bonus" : 2 "airblast_give_teammate_speed_boost" : 1, "provide on active" : 1 "disable weapon switch" : 1 } }, }, "Demoman" : { "Giant Burst Demo" : { //Burst "ChiefType" : 0, "tf_weapon_grenadelauncher" : { "WepID" : 206, "Slot" : 0 "provide on active" : 1 "disable weapon switch" : 1 "faster reload rate" : 0.65 "fire rate bonus" : 0.1 "clip size upgrade atomic" : 7.0 "projectile spread angle penalty" : 5 "Projectile speed increased" : 1.1 "auto fires full clip" : 1 } }, "Giant Spammer Demo" : { //Sustained fire "ChiefType" : 1, "tf_weapon_grenadelauncher" : { "WepID" : 1151, "provide on active" : 1 "disable weapon switch" : 1 "Slot" : 0 "mod max primary clip override" : -1, "fire rate bonus" : 0.75, "mod no reload DISPLAY ONLY" : 1, "provide on active" : 1 "disable weapon switch" : 1 } }, // "Giant Nuker Demo" : // { // "ChiefType" : 1, // "tf_weapon_cannon" : // { // "WepID" : 996, // "provide on active" : 1 // "disable weapon switch" : 1 // "grenade launcher mortar mode" : 0 // "mod max primary clip override" : -1, // "mod no reload DISPLAY ONLY" : 1, // "fire rate penalty" : 3 // "Projectile speed decreased" : 0.8 // "damage bonus" : 7 // "fuse bonus" : 0.2 // "Projectile speed increased HIDDEN" : 1.4 // "damage causes airblast" : 1 // "blast radius increased" : 1.2 // //"use large smoke explosion" : 1 // } // }, }, "Heavy" : { "Giant Deflector Heavy" : { //Sustained fire "ChiefType" : 0, "tf_weapon_minigun" : { "WepID" : 850, "Slot" : 0 "damage bonus" : 1.5 "attack projectiles" : 2 "provide on active" : 1 "disable weapon switch" : 1 } } "Giant Shotgun Heavy" : { //One-Shot/High damage "ChiefType" : 1, "TF_WEAPON_SHOTGUN_HWG" : { "WepID" : 199, "Slot" : 1 "fire rate penalty" : 2.5 "bullets per shot bonus" : 10 "damage penalty" : 0.5 "faster reload rate" : 0.1 "move speed bonus" : 1.2 "provide on active" : 1 "disable weapon switch" : 1 } } } "Engineer" : { //Support "Giant Revengineer" : { "ChiefType" : 0, "tf_weapon_sentry_revenge" : { "WepID" : 141, "Slot" : 0 "fire rate bonus" : 0.5 "cannot pick up buildings" : 1 "engy sentry damage bonus" : 2.5 "faster reload rate" : 0.5 "damage bonus bullet vs sentry target" : 2 "weapon spread bonus" : 0.5 "fire rate bonus" : 0.25 "provide on active" : 1 "disable weapon switch" : 1 } } //DPS "Giant Lasengineer" : { "ChiefType" : 1, "tf_weapon_pistol" : { "WepID" : 30666, "Slot" : 1 "provide on active" : 1 "fire rate bonus" : 0.2 "weapon spread bonus" : 0 "disable weapon switch" : 1 "max health additive bonus" : 1500 "accuracy scales damage" : 2 "revolver use hit locations" : 1 } } } "Medic" : { "Giant Quick-Uber Medic" : { "ChiefType" : 0, "tf_weapon_medigun" : { "WepID" : 211, "Slot" : 1 "provide on active" : 1 "disable weapon switch" : 1 "uber duration bonus" : -3 "ubercharge rate bonus" : 5 } } "Giant Kritz Medic" : { "ChiefType" : 1, "tf_weapon_medigun" : { "WepID" : 35, "Slot" : 1 "provide on active" : 1 "disable weapon switch" : 1 "uber duration bonus" : 999 } } } "Sniper" : { "Giant Huntsman" : { "ChiefType" : 0, "tf_weapon_compound_bow" : { "WepID" : 56, "Slot" : 0 "provide on active" : 1 "disable weapon switch" : 1 "card: damage bonus" : 3 "faster reload rate" : 0.5 "fire rate bonus" : 0.5 } } "Giant Carbine Sniper" : { "ChiefType" : 1, "tf_weapon_charged_smg" : { "WepID" : 751, "Slot" : 1 "provide on active" : 1 "disable weapon switch" : 1 "damage bonus" : 1.5 "fire rate bonus" : 0.35 "clip size bonus" : 3.75 "weapon spread bonus" : 0.25 "maxammo secondary increased" : 2 "minicrits become crits" : 1 "minicrit_boost_charge_rate" : 0.25 // "mod rage on hit penalty" : -50 // "generate rage on damage" : 3 } } } "Spy" : { "Giant Spy" : { "ChiefType" : 0, "tf_weapon_knife" : { "WepID" : 194, "Slot" : 2 "provide on active" : 1 "disable weapon switch" : 1 "armor piercing" : 999 } } "Giant Revolver Spy" : { "ChiefType" : 1, "tf_weapon_revolver" : { "WepID" : 210, "Slot" : 1 "provide on active" : 1 "disable weapon switch" : 1 "damage bonus" : 2 "fire rate bonus" : 0.5 "faster reload rate" : 0.5 "clip size bonus" : 2 } } } }; local upgrades_to_remove = { "damage bonus" : 1, "fire rate bonus" : 1, "faster reload rate" : 1, "clip size upgrade atomic" : 0, "maxammo primary increased" : 1, "uber duration bonus" : 0, // Add more upgrades as needed, using the attribute names from mvm_upgrades.txt } local attribs_do_not_remove = {}; // This will store attributes as key-value pairs foreach (giant_class, loadout in Giant_Loadout_Options) { if (giant_class == class_string) { printl("We are playing as: " + class_string); local num_giants = loadout.len(); giant_type = RandomInt(0, num_giants - 1); printl("Randomly selected giant type: " + giant_type); foreach (chief_title, chief_data in loadout) { if (chief_data["ChiefType"] == giant_type) { printl("ChiefType matches giant_type: " + giant_type + " for " + chief_title); foreach (weapon_class, weapon_data in chief_data) { if (typeof weapon_data == "table" && "WepID" in weapon_data && "Slot" in weapon_data) { local className = weapon_class; local itemID = weapon_data["WepID"]; local wep_slot = weapon_data["Slot"]; local weapon = GiveWeapon(player, className, itemID); if (itemID == 141 || itemID == 751) { SendGlobalGameEvent("post_inventory_application", { userid = NetProps.GetPropIntArray(player_manager, "m_iUserID", player.entindex()) }); } if (itemID == 194) { local watch = GiveWeapon(player, "tf_weapon_invis", 212); printl(watch) watch.AddAttribute("cloak consume rate decreased", -999, -1) watch.AddAttribute("mult cloak rate", -4, -1) SendGlobalGameEvent("post_inventory_application", { userid = NetProps.GetPropIntArray(player_manager, "m_iUserID", player.entindex()) }); } NetProps.SetPropFloat(weapon, "m_flChargeLevel", 1); foreach (attrib_name, attrib_value in weapon_data) { if (attrib_name != "WepID" && attrib_name != "always crit") { // Store applied attributes in attribs_do_not_remove as key-value pair if (attrib_name in upgrades_to_remove) { attribs_do_not_remove[attrib_name] <- attrib_value } weapon.AddAttribute(attrib_name, attrib_value, -1); weapon.SetClip1(weapon.GetMaxClip1()); printl("Applied attribute: " + attrib_name + " with value: " + attrib_value); } else if (attrib_name == "always crit") { player.AddCondEx(56, -1, null); // Apply crit condition } } player.Weapon_Switch(wep_slot); local wep_active = player.GetActiveWeapon(); foreach (attr, value in upgrades_to_remove) { local get_attr = wep_active.GetAttribute(attr, value); if (get_attr == value || attr in attribs_do_not_remove) { printl(attr+" is not present or in keep box, ignore"); continue; } else { printl(attr+" is present, check it"); if (attr != value) { printl(attr+" should not be here!"); wep_active.RemoveAttribute(attr) } } } printl("Gave player weapon: " + className + " with ID: " + itemID); } } } } } } player.SetHealth(player.GetMaxHealth()); } function BecomeGiant(ent) { //NetProps.SetPropBool(ent, "m_bUseBossHealthBar", true) ::NoSpamThink <- function() { if (self.InCond(4)) { self.AddCustomAttribute("SET BONUS: move speed set bonus", 2.5, -1) } else { self.RemoveCustomAttribute("SET BONUS: move speed set bonus") } //only nerf for player soldier if (self.GetPlayerClass() != 3 || self.IsBotOfType(TF_BOT_FAKE_CLIENT) || NetProps.GetPropInt(self, "m_lifeState") != 0) { return -1 } local wep = self.GetActiveWeapon() if (wep != null && wep.Clip1() == 0) { self.AddCustomAttribute("reload time increased", 3, -1) } else { self.RemoveCustomAttribute("reload time increased") } } ::EngineerSentryDeployerThink <- function() { if (self.GetPlayerClass() != 9 || self.IsBotOfType(TF_BOT_FAKE_CLIENT) || NetProps.GetPropInt(self, "m_lifeState") != 0 || self.GetActiveWeapon().GetClassname() != "tf_weapon_sentry_revenge") { return -1 } local buttons = NetProps.GetPropInt(self, "m_nButtons") local buttons_changed = buttons_last ^ buttons local buttons_pressed = buttons_changed & buttons local buttons_released = buttons_changed & (~buttons) local cur_server_time = Time() function AnglesToVector(angles) { local pitch = angles.x * Pi / 180.0 local yaw = angles.y * Pi / 180.0 local x = cos(pitch) * cos(yaw) local y = cos(pitch) * sin(yaw) local z = sin(pitch) return Vector(x, y, z) } // printl("sentry cooldown: "+deploy_sentry_time) // printl("time: "+cur_server_time) cooldown_time = cur_server_time - deploy_sentry_time local remaining_time = 5 - cooldown_time // ensure timer counts down, not up if (remaining_time >= 1) { ClientPrint(self, 4, "NEXT SENTRY READY IN: " + floor(remaining_time)) } else { ClientPrint(self, 4, "DEPLOY SENTRY WITH RIGHT CLICK") } if (buttons_pressed & ALTFIRE && deploy_sentry_time + 4 < cur_server_time) { //printl("Player pressed left-click") deploy_sentry_time = cur_server_time local grenade = SpawnEntityFromTable("tf_projectile_stun_ball", { targetname = "tool_box_sentry" }) local velocity = AnglesToVector(self.EyeAngles()) grenade.SetOrigin(self.EyePosition()) grenade.SetOwner(self) grenade.SetTeam(grenade.GetTeam() + 2) grenade.SetModelSimple("models/weapons/c_models/c_toolbox/c_toolbox.mdl") grenade.SetPhysVelocity(Vector(velocity.x * 500, velocity.y * 500, velocity.z * -500)) } for (local toolbox; toolbox = Entities.FindByClassname(toolbox, "tf_projectile_stun_ball");) { if (toolbox.GetOwner() == self && NetProps.GetPropBool(toolbox, "m_bTouched")) { local toolbox_origin = toolbox.GetOrigin() local building_classnames = { "obj_sentrygun" : 1, "obj_dispenser": 1, "obj_teleporter" : 1 } toolbox.SetSolid(0) // ignore trace local sentry = SpawnEntityFromTable("obj_sentrygun", { targetname = "sentry", defaultupgrade = 2, angles = self.GetAbsAngles(), origin = toolbox_origin }) local sentry_origin = sentry.GetOrigin() local trace_sentry_can_deploy = { start = sentry_origin + Vector(0, 0, 20), end = sentry_origin + Vector(0, 0, 20), hullmin = sentry.GetBoundingMins() + Vector(-20, -20, -20), // Bounding box min hullmax = sentry.GetBoundingMaxs() + Vector(20, 20, 20), // Bounding box max mask = MASK_SOLID, ignore = sentry // ignore the sentry that was just created } TraceHull(trace_sentry_can_deploy) if (trace_sentry_can_deploy.hit && "enthit" in trace_sentry_can_deploy) { if (trace_sentry_can_deploy.enthit.GetClassname() in building_classnames) { local destroyed_sentry = trace_sentry_can_deploy.enthit // Get the kills and assists from the destroyed sentry before killing it local sentry_kills = NetProps.GetPropInt(destroyed_sentry, "m_iKills") local sentry_assists = NetProps.GetPropInt(destroyed_sentry, "m_iAssists") destroyed_sentry.Kill() // Add the kills and assists to the crits local local crits = NetProps.GetPropInt(self, "m_Shared.m_iRevengeCrits") NetProps.SetPropInt(self, "m_Shared.m_iRevengeCrits", crits + sentry_kills + sentry_assists) } } extra_sentry_list.append(sentry) // Add the newly created sentry to the list // Check if the sentry list exceeds 3 entries if (extra_sentry_list.len() > 3) { // Get the oldest sentry (first entry in the list) local oldest_sentry = extra_sentry_list[0] // Destroy the oldest sentry if (oldest_sentry) { oldest_sentry.Kill() // Remove the oldest sentry from the list extra_sentry_list.remove(0) } } // Set up the newly created sentry sentry.SetSkin(sentry.GetSkin() + 2) sentry.SetTeam(sentry.GetTeam() + 2) sentry.AcceptInput("SetBuilder", "activator", self, self) sentry.SetOwner(self) // Destroy the toolbox entity toolbox.Kill() } } // Clean up the extra sentry list for (local i = extra_sentry_list.len() - 1; i >= 0; i--) { local sentry = extra_sentry_list[i] if (!sentry || !sentry.IsValid()) { extra_sentry_list.remove(i) // Remove destroyed sentry from the list } } // Failsafe if the sentry trace fails to find a sentry beign thrown out foreach (gun in extra_sentry_list) { local gun_origin = gun.GetOrigin() for (local no_sentry_proj; no_sentry_proj = Entities.FindByClassnameWithin(no_sentry_proj, "tf_projectile_stun_ball", gun_origin, 70);) { if (no_sentry_proj != null && no_sentry_proj.GetOwner() == self) { no_sentry_proj.Kill() } } } buttons_last = buttons return -1 } ::RobotVOThink <- function() { // error happens if newer taunts are used // just prevent taunting if (self.IsTaunting()) { ClientPrint(self, 3, "Your stiff, robotic form prevents you from Taunting!") self.CancelTaunt() } function StopAndPlayMVMSound(player, soundscript, delay) { player.ValidateScriptScope() local scope = player.GetScriptScope() scope.sound <- soundscript EntFireByHandle(player, "RunScriptCode", "activator.StopSound(sound);", delay, player, null) local sound = scope.sound local dotindex = sound.find(".") if (dotindex == null) return scope.mvmsound <- sound.slice(0, dotindex+1) + "MVM_" + sound.slice(dotindex+1) EntFireByHandle(player, "RunScriptCode", "activator.EmitSound(mvmsound);", delay + SINGLE_TICK, player, null) } for (local ent; ent = Entities.FindByClassname(ent, "instanced_scripted_scene"); ) { if (ent.IsEFlagSet(EFL_USER)) continue ent.AddEFlags(EFL_USER) local owner = NetProps.GetPropEntity(ent, "m_hOwner") if (owner != null && !owner.IsBotOfType(TF_BOT_TYPE)) { local vcdpath = NetProps.GetPropString(ent, "m_szInstanceFilename"); if (!vcdpath || vcdpath == "") return -1 local dotindex = vcdpath.find(".") local slashindex = null; for (local i = dotindex; i >= 0; --i) { if (vcdpath[i] == '/' || vcdpath[i] == '\\') { slashindex = i break } } owner.ValidateScriptScope() local scope = owner.GetScriptScope() scope.soundtable <- VCD_SOUNDSCRIPT_MAP[owner.GetPlayerClass()] scope.vcdname <- vcdpath.slice(slashindex+1, dotindex) if (scope.vcdname in scope.soundtable) { local soundscript = scope.soundtable[scope.vcdname]; if (typeof soundscript == "string") StopAndPlayMVMSound(owner, soundscript, 0); else if (typeof soundscript == "array") foreach (sound in soundscript) StopAndPlayMVMSound(owner, sound[1], sound[0]); } } } } ent.ValidateScriptScope() local ent_scope = ent.GetScriptScope() ent_scope.buttons_last <- 0 ent_scope.deploy_sentry_time <- 0 ent_scope.cooldown_time <- 0 ent_scope.extra_sentry_list <- [] ent_scope.ThinkTable_Player.NoSpamThink <- NoSpamThink ent_scope.ThinkTable_Player.RobotVOThink <- RobotVOThink ent_scope.ThinkTable_Player.EngineerSentryDeployerThink <- EngineerSentryDeployerThink ent.SetIsMiniBoss(true) ent.SetModelScale(1.75, -1) ent.AddCustomAttribute("healing received penalty", 0.5, -1) //cannot remove attribs, override resistences with this //todo: revert body upgrades ent.AddCustomAttribute("dmg taken from fire reduced", 1, -1) ent.AddCustomAttribute("dmg taken from crit reduced", 1, -1) ent.AddCustomAttribute("dmg taken from blast reduced", 1, -1) ent.AddCustomAttribute("dmg taken from bullets reduced", 1, -1) local not_giants = { "medic" : 1 "sniper" : 1 "engineer" : 1 "spy" : 1 } local get_class_string = ["", "scout", "sniper", "soldier", "demo", "medic", "heavy", "pyro", "spy", "engineer", "civilian"] local get_cur_class = get_class_string[ent.GetPlayerClass()] local model_path_giant = format("models/bots/%s_boss/bot_%s_boss.mdl", get_cur_class, get_cur_class) local model_path_common = format("models/bots/%s/bot_%s.mdl", get_cur_class, get_cur_class) // Precache the model to ensure it loads correctly if (!(get_cur_class in not_giants)) { PrecacheModel(model_path_giant) ent.SetCustomModelWithClassAnimations(model_path_giant) } else { PrecacheModel(model_path_common) ent.SetCustomModelWithClassAnimations(model_path_common) } local giant_classes = { "Scout" : { "max health additive bonus" : 1600 "damage force reduction" : 0.7 "airblast vulnerability multiplier" : 0.7 "crit mod disabled" : 0 "cancel falling damage" : 1, "override footstep sound set" : 5 "voice pitch scale" : 0.7 "ammo regen" : 1 } "Soldier" : { "max health additive bonus" : 4000 "damage force reduction" : 0.4 "airblast vulnerability multiplier" : 0.4 "move speed penalty" : 0.5 "no_duck" : 1 "ammo regen" : 1 "voice pitch scale" : 0.7 "no_jump" : 1 "cancel falling damage" : 1, "crit mod disabled" : 0 "override footstep sound set" : 3 } "Pyro" : { "max health additive bonus" : 3000 "damage force reduction" : 0.6 "airblast vulnerability multiplier" : 0.6 "move speed penalty" : 0.5 "no_jump" : 1 "voice pitch scale" : 0.7 "crit mod disabled" : 0 "ammo regen" : 1 "cancel falling damage" : 1, "override footstep sound set" : 6 //"no_duck" : 1 } "Demoman" : { "max health additive bonus" : 3000 "damage force reduction" : 0.5 "airblast vulnerability multiplier" : 0.5 "move speed penalty" : 0.5 "no_duck" : 1 "ammo regen" : 1 //"no_jump" : 1 "voice pitch scale" : 0.7 "crit mod disabled" : 0 "override footstep sound set" : 4 "cancel falling damage" : 1, } "Heavy" : { "max health additive bonus" : 5000 "damage force reduction" : 0.2 "airblast vulnerability multiplier" : 0.2 "move speed penalty" : 0.5 "no_duck" : 1 "voice pitch scale" : 0.7 "no_jump" : 1 "ammo regen" : 1 "crit mod disabled" : 0 "cancel falling damage" : 1, "override footstep sound set" : 2 } "Engineer" : { "max health additive bonus" : 2500 "damage force reduction" : 0.5 "airblast vulnerability multiplier" : 0.5 "move speed penalty" : 0.5 "voice pitch scale" : 0.7 "ammo regen" : 1 // "no_duck" : 1 // "no_jump" : 1 "cancel falling damage" : 1, "head scale" : 0.7 "crit mod disabled" : 0 "override footstep sound set" : 7 } "Medic" : { "max health additive bonus" : 4500 "damage force reduction" : 0.5 "airblast vulnerability multiplier" : 0.5 "move speed penalty" : 0.5 "no_duck" : 1 "voice pitch scale" : 0.7 "ammo regen" : 1 "no_jump" : 1 "crit mod disabled" : 0 "cancel falling damage" : 1, "head scale" : 0.7 "heal rate bonus" : 200 "override footstep sound set" : 0 } "Sniper" : { "max health additive bonus" : 2500 "damage force reduction" : 0.5 "airblast vulnerability multiplier" : 0.5 "move speed penalty" : 0.5 //"no_duck" : 1 "voice pitch scale" : 0.7 "ammo regen" : 1 "cancel falling damage" : 1, "no_jump" : 1 "head scale" : 0.7 "crit mod disabled" : 0 "override footstep sound set" : 7 } "Spy" : { "max health additive bonus" : 2500 "damage force reduction" : 0.5 "cancel falling damage" : 1, "airblast vulnerability multiplier" : 0.5 "move speed penalty" : 0.5 "ammo regen" : 1 //has cloak move speed bonusm keep here "no_duck" : 1 "no_jump" : 1 "voice pitch scale" : 0.7 "head scale" : 0.7 "crit mod disabled" : 0 "override footstep sound set" : 0 // silent } } // local sound_range = (40 + (20 * log10(300 / 36.0))).tointeger(); // ent.EmitSound("items/powerup_pickup_team_revenge.wav") ent.Teleport(true, ent.GetOrigin() + Vector(0, 0, 25), true, ent.EyeAngles(), false, Vector(0, 0, 0)) ent.SetHealth(ent.GetMaxHealth()) foreach(player_class, attribs in giant_classes) { local get_class_string = ["", "Scout", "Sniper", "Soldier", "Demoman", "Medic", "Heavy", "Pyro", "Spy", "Engineer", "Civilian"] local get_cur_class = get_class_string[ent.GetPlayerClass()] local health = ent.GetMaxHealth() if (player_class == get_cur_class) { foreach (attrib_name, attrib_value in attribs) { if (attrib_name == "max health additive bonus") { //ensure that health bonus/health penalty weapons dont cause weird values ent.AddCustomAttribute(attrib_name, attrib_value - health, -1) } else { ent.AddCustomAttribute(attrib_name, attrib_value, -1) } } break } } UnstuckEntity(ent) PickGiantWeaponFunc(ent) } for(local player; player = Entities.FindByClassnameWithin(player, "player", origin, 125);) { //prevent giants from collecting //check if self is valid after calling .kill (fixes error) //check if the box is grounded (theory: on certain frames this check isnt run, allowing giants to bypass the check) if (player.IsMiniBoss() || self == null || !self.IsValid() || bIsGrounded == false) { break } BecomeGiant(player) RemovePlayerWearables(player) UnstuckEntity(player) EntFireByHandle(gamerules, "RunScriptCode", "UnstuckEntity(activator)", 2, player, player) self.Kill() } return -1 } local average_pos = GetAveragePlayerPosition() local box_spots = {} NavMesh.GetNavAreasInRadius(average_pos, 2500, box_spots) //in tank spawnroom: players cant get this local nav_no_use = {} NavMesh.GetNavAreasInRadius(Vector(-2595 3114 106), 460, nav_no_use) for (local a = 1; a <= powerup_count; a++) { local minareasize = 7500 local spots = [] //check for if nav is in tank spawn function IsNavInNoUseList(nav, nav_no_use) { foreach(no_use_id, no_use_nav in nav_no_use) { if (no_use_nav == nav) { return true // Nav is in the nav_no_use array } } return false // Nav is not in the nav_no_use array } // Gather all valid spots based on area size and excluding those in nav_no_use foreach(id, nav in box_spots) { if (nav.GetSizeX() * nav.GetSizeY() > minareasize && !nav.HasAttributeTF(TF_NAV_SPAWN_ROOM_RED) && !nav.HasAttributeTF(TF_NAV_SPAWN_ROOM_BLUE) && !IsNavInNoUseList(nav, nav_no_use)) // Exclude navs in nav_no_use { spots.append(nav) } } // Choose a random navigation spot from the spots array local nav = spots[RandomInt(0, spots.len() - 1)] local spot = nav.FindRandomSpot() local falling_value = RandomInt(2, 3) // Spawn the box entity at the chosen location local box = SpawnEntityFromTable("prop_dynamic", { model = "models/props_oxidize_vscript/box_cloak.mdl", origin = spot angles = Vector(0, 0, 0), skin = 1 targetname = "powerup_Prop" disableshadows = 1 }) local cloak = SpawnEntityFromTable("material_modify_control", { materialName = "models/props_oxidize_vscript/barrel_crate" materialVar = "$cloakfactor" targetname = "modify_box_"+a origin = Vector(0, 0, 0) }) // Unstuck the box if needed UnstuckEntity(box) EntFireByHandle(cloak, "SetParent", "!activator", -1, box, null) // wont work if not parented box.ResetSequence(box.LookupSequence("retract")) box.SetPlaybackRate(0.7) cloak.AcceptInput("StartFloatLerp" "1.0 1.0 0 0", null, null) // Attach necessary properties and functions to the box box.ValidateScriptScope() local box_scope = box.GetScriptScope() box_scope.iAngles_spin <- 0 box_scope.bIsGrounded <- false box_scope.bIsSpawning <- false box_scope.iAnimatingStage <- 0 box_scope.iNoAnimateTimer <- 0 box_scope.iNoAnimateTimer <- 0 box_scope.falling_value <- falling_value box_scope.GiveWeapon <- GiveWeapon box_scope.RemovePlayerWearables <- RemovePlayerWearables box_scope.FindOtherTeleportSpot <- FindOtherTeleportSpot box_scope.PrintTable <- PrintTable box_scope.DoPrintTable <- DoPrintTable box_scope.spots <- spots box_scope.cloak <- cloak box_scope.UnstuckEntity <- UnstuckEntity // Check for collisions and adjust the position if necessary box_scope.FindOtherTeleportSpot(box, spots) // Add a think function to the entity AddThinkToEnt(box, "PowerupThink") } } function SpawnStunMines(amp_count) { EmitSoundEx({ sound_name = "weapons/rescue_ranger_teleport_receive_02.wav"}); ::EMPMineThink <- function() { local origin = self.GetOrigin() local soundlevel = (40 + (20 * log10(6000 / 36.0))).tointeger() for(local player; player = Entities.FindByClassnameWithin(player, "player", origin, 40);) { if (player.GetTeam() == 2) { player.TakeDamageEx(null, self, null, Vector(0, 0, 0), origin, 26.34, 1048576) DispatchParticleEffect("drg_cow_explosioncore_charged_blue", origin, Vector(0, 0, 0)) ClientPrint(player, 4, "STUNNED!") EmitSoundEx ({ sound_name = "npc/scanner/cbot_energyexplosion1.wav", origin = self.GetOrigin(), sound_level = soundlevel }); EmitSoundEx ({ sound_name = "npc/scanner/cbot_energyexplosion1.wav", origin = self.GetOrigin(), sound_level = soundlevel }); player.AddCustomAttribute("move speed penalty", 0.65, 5) //player.AddCustomAttribute("fire rate penalty", 1.5, 5) // doesnt work? player.AddCustomAttribute("reload time increased", 1.5, 5) player.AddCustomAttribute("no_jump", 1, 5) player.AddCustomAttribute("no_duck", 1, 5) player.AddCustomAttribute("disable weapon switch", 1, 5) player.AddCondEx(50, 5, null) // head spark self.Kill() } } } for(local a = 1; a <= amp_count; a++) { local minareasize = 1750; local spots = [] local auto_detonate_time = RandomInt(60, 90) local arm_time = RandomInt(5, 10) //TF_NAV_SPAWN_ROOM_BLUE //TF_NAV_SPAWN_ROOM_RED foreach(id, nav in nav_area_table) { if (nav.GetSizeX() * nav.GetSizeY() > minareasize && !nav.HasAttributeTF(TF_NAV_SPAWN_ROOM_RED) && !nav.HasAttributeTF(TF_NAV_SPAWN_ROOM_BLUE)) { spots.append(nav) } } local nav = spots[RandomInt(0, spots.len() - 1)] local spot = nav.FindRandomSpot() //DebugDrawBox(spot, Vector(-50, -50, -50), Vector(50, 50, 50), 6, 66, 231, 0, 50) //printl("amp deploy") //models/props_2fort/groundlight002.mdl local amp = SpawnEntityFromTable("prop_dynamic", { model = "models/props_2fort/groundlight002.mdl" origin = spot + Vector(0, 0, -8) angles = Vector(0, 0, 0) targetname = "amp_prop" rendercolor = Vector(16, 107, 209) }) local amp_particle = SpawnEntityFromTable("info_particle_system", { effect_name = "sparks_powerline_blue", targetname = "amp_fx" origin = spot + Vector(0, 0, -5) angles = Vector(0, 0, 0) }) amp_particle.AcceptInput("SetParent", "!activator", amp, amp) //printl(amp_particle.GetOrigin()) local amp_scope = amp.GetScriptScope() EntFireByHandle(amp_particle, "Start", "", arm_time, null, null) // delay until active amp.ValidateScriptScope() AddThinkToEnt(amp, "EMPMineThink") EntFireByHandle(amp, "Kill", "", auto_detonate_time, null, null) EntFireByHandle(amp_particle, "Kill", "", auto_detonate_time, null, null) //EntFireByHandle(amp_particle, "SetParent", "!activator", 3, amp, amp) //printl(spot) } } function SpawnLaserBeams(beam_count) { EntFireByHandle(gamerules, "RunScriptCode", "EmitSoundEx({ sound_name = `portal2/alarm_sweep_lp_lg_01.wav`});", 0, null, null) EntFireByHandle(gamerules, "RunScriptCode", "EmitSoundEx({ sound_name = `portal2/alarm_sweep_lp_lg_01.wav`, flags = 4});", 4, null, null) local average_pos = GetAveragePlayerPosition() local laser_spots = {} NavMesh.GetNavAreasInRadius(average_pos, 1750, laser_spots) SendGlobalGameEvent("show_annotation", { text = "WARNING! ENERGY BEAMS INCOMING! KEEP MOVING!" lifetime = 5 worldPosX = average_pos.x worldPosY = average_pos.y worldPosZ = average_pos.z id = 4 play_sound = "misc/null.wav" show_distance = false show_effect = false follow_entindex = 0 }) for(local a = 1; a <= beam_count; a++) { local minareasize = 2000; local spots = [] local attack_time = RandomInt(12, 35) foreach(id, nav in laser_spots) { if (nav.GetSizeX() * nav.GetSizeY() > minareasize && !nav.HasAttributeTF(TF_NAV_SPAWN_ROOM_RED) && !nav.HasAttributeTF(TF_NAV_SPAWN_ROOM_BLUE)) { spots.append(nav) } } local nav = spots[RandomInt(0, spots.len() - 1)] local spot = nav.FindRandomSpot() local beam_particle = SpawnEntityFromTable("info_particle_system", { effect_name = "mannpower_imbalance_blue", targetname = "amp_fx" origin = spot + Vector(0, 0, -5) angles = Vector(0, 0, 0) }) EntFireByHandle(beam_particle, "Start", "", attack_time - 6, null, null) // delay until active EntFireByHandle(beam_particle, "Kill", "", attack_time, null, null) EntFireByHandle(gamerules, "RunScriptCode", "FindEntAndDealDamage(activator.GetOrigin())", attack_time, beam_particle, beam_particle) //beam incoming EntFireByHandle(gamerules, "RunScriptCode", "EmitSoundEx({ sound_name = `weapons/cow_mangler_idle.wav`, origin = activator.GetOrigin(), sound_level = (40 + (20 * log10(1200 / 36.0))).tointeger() });", attack_time - 6, beam_particle, beam_particle) EntFireByHandle(gamerules, "RunScriptCode", "EmitSoundEx({ sound_name = `weapons/cow_mangler_idle.wav`, origin = activator.GetOrigin(), sound_level = (40 + (20 * log10(1200 / 36.0))).tointeger() });", attack_time - 6, beam_particle, beam_particle) EntFireByHandle(gamerules, "RunScriptCode", "EmitSoundEx({ sound_name = `weapons/cow_mangler_idle.wav`, origin = activator.GetOrigin(), flags = 4, sound_level = (40 + (20 * log10(1200 / 36.0))).tointeger() });", attack_time, beam_particle, beam_particle) EntFireByHandle(gamerules, "RunScriptCode", "EmitSoundEx({ sound_name = `weapons/cow_mangler_idle.wav`, origin = activator.GetOrigin(), flags = 4, sound_level = (40 + (20 * log10(1200 / 36.0))).tointeger() });", attack_time, beam_particle, beam_particle) //impact EntFireByHandle(gamerules, "RunScriptCode", "EmitSoundEx({ sound_name = `npc/strider/fire.wav`, origin = activator.GetOrigin(), sound_level = (40 + (20 * log10(900 / 36.0))).tointeger() });", attack_time, beam_particle, beam_particle) EntFireByHandle(gamerules, "RunScriptCode", "EmitSoundEx({ sound_name = `npc/strider/fire.wav`, origin = activator.GetOrigin(), sound_level = (40 + (20 * log10(900 / 36.0))).tointeger() });", attack_time, beam_particle, beam_particle) EntFireByHandle(gamerules, "RunScriptCode", "DispatchParticleEffect(`mvm_soldier_shockwave`, activator.GetOrigin(), Vector(0, 0, 0))", attack_time, beam_particle, beam_particle) } } /////////////////////////////////////// ////////////// CALLBACKS ////////////// /////////////////////////////////////// ::HardwareHackingNamespace <- { PlayerCleanup = function() { for (local player; player = Entities.FindByClassname(player, "player");) { //remove all current scope and thinks player.ValidateScriptScope() local scope = player.GetScriptScope() if (player.GetTeam() == TEAM_RED) { player.SetCustomModelWithClassAnimations("") // reset player models to use human anims } foreach (think, v in scope) { if (scope == null) break delete scope[think] } //create new ones here scope.self <- player if (!("ThinkTable_Player" in scope)) { scope.ThinkTable_Player <- {} } else { //removing the think table to clear player thinks and then creating a new table is easier then keeping old //table and clearing thinks within existing tables delete scope.ThinkTable_Player scope.ThinkTable_Player <- {} } scope.Thinks_Player <- function() { foreach (name, func in scope.ThinkTable_Player) { func.call(scope); } return -1 } AddThinkToEnt(player, "Thinks_Player") } } Cleanup = function() { //clean up lingering/looping sounds for(local a = 1; a <= 99; a++) { EntFireByHandle(gamerules, "RunScriptCode", "EmitSoundEx({ sound_name = `weapons/cow_mangler_idle.wav`, flags = 4, sound_level = (40 + (20 * log10(1200 / 36.0))).tointeger() });", 0, null, null) } //destroy created weapons incase the wave ends with giants //also fixes a bug that causes weapons to float near players? foreach(wep in weapons_ents_cleanup) { if (wep == null || !wep.IsValid()) continue wep.Destroy() } // cleanup any persistent changes here // PlayerCleanup() // keep this at the end delete ::HardwareHackingNamespace } PlayAlarmSound = function() { EntFireByHandle(gamerules, "RunScriptCode", "EmitSoundEx({ sound_name = `portal2/amb_container_alarm_01.wav`});", 0, null, null) EntFireByHandle(gamerules, "RunScriptCode", "EmitSoundEx({ sound_name = `portal2/amb_container_alarm_01.wav`, flags = 4});", 5.4, null, null) } // mandatory events OnGameEvent_recalculate_holidays = function(_) { if (GetRoundState() == 3) { Cleanup() } } OnGameEvent_mvm_wave_complete = function(_) { Cleanup() } OnGameEvent_post_inventory_application = function(params) { local player = GetPlayerFromUserID(params.userid) player.ValidateScriptScope() local playerscope = player.GetScriptScope() } OnGameEvent_player_spawn = function(params) { local player = GetPlayerFromUserID(params.userid) player.ValidateScriptScope() local playerscope = player.GetScriptScope() // PlayerCleanup() } OnGameEvent_mvm_wave_failed = function(params) { local remove1 = Entities.FindByName(null, "escape_template") local remove2 = Entities.FindByName(null, "medic_fool") local remove3 = Entities.FindByName(null, "demonstration") local remove4 = Entities.FindByName(null, "demonstration_rotato") remove1.Kill() //remove2.Kill() // remove3.Kill() // remove4.Kill() } OnGameEvent_mvm_begin_wave = function(params) { PlayAlarmSound() } OnScriptHook_OnTakeDamage = function(params) { local victim = params.const_entity local weapon = params.weapon local attacker = params.attacker local entity = params.inflictor local damage = params.damage local dmgtype = params.damage_type local dmg_special = params.damage_stats local weapon_id = NetProps.GetPropInt(weapon, STRING_NETPROP_ITEMDEF) if (attacker.GetClassname() == "player" && attacker.IsMiniBoss() && weapon_id == 30666) { if (NetProps.GetPropInt(victim, "m_LastHitGroup") != HITGROUP_HEAD) { params.damage_type = params.damage_type &~ DMG_NO_BULLET_FALLOFF //printl("bodyshot") } else { //printl("headshot") params.damage_stats = TF_DMG_CUSTOM_HEADSHOT params.damage_type = params.damage_type | DMG_CRITICAL // &~ DMG_NO_BULLET_FALLOFF } } if (attacker.GetClassname() == "player" && attacker.IsMiniBoss() && weapon_id == 194) { if (params.damage_stats == TF_DMG_CUSTOM_BACKSTAB) { params.damage = params.damage + victim.GetMaxHealth() } } // if (attacker.GetClassname() == "player" && attacker.IsMiniBoss() && weapon_id == 56) // { // if (params.damage_stats == TF_DMG_CUSTOM_HEADSHOT) // { // printl("headshot") // entfir // } // else // { // printl("body") // } // } } }; __CollectGameEventCallbacks(HardwareHackingNamespace)