ClientPrint(null,2,"DEBUG: Script activated!\n"); // ClientPrint(null,2,SLOT_MELEE.tostring()); ////////////////////////////////////////////////////////////////////////// //CHANGES TO FUNCTION REFERENCES ////////////////////////////////////////////////////////////////////////// //Simplifying references to constants and NetProps. //Credit: VDC wiki, lite. foreach (k, v in NetProps.getclass()) if (k != "IsValid") getroottable()[k] <- NetProps[k].bindenv(NetProps); foreach (k, v in Entities.getclass()) if (k != "IsValid") getroottable()[k] <- Entities[k].bindenv(Entities); ::ROOT <- getroottable(); if (!("ConstantNamingConvention" in ROOT)) // make sure folding is only done once { foreach (a,b in Constants) foreach (k,v in b) if (v == null) ROOT[k] <- 0; else ROOT[k] <- v; } ////////////////////////////////////////////////////////////////////////// //CONSTANTS ////////////////////////////////////////////////////////////////////////// const OVERLAY_BUDDHA_MODE = 0x02000000; const WEAPONRESTRICT_MELEE = 1; const WEAPONRESTRICT_PRIMARY = 2; const WEAPONRESTRICT_SECONDARY = 4; const SLOT_PRIMARY = 0 const SLOT_SECONDARY = 1 const SLOT_MELEE = 2 const SLOT_UTILITY = 3 const SLOT_BUILDING = 4 const SLOT_PDA = 5 const SLOT_PDA2 = 6 const SLOT_COUNT = 7 const TF_BOT_TYPE = 1337 ::ALL_PLAYERS <- MaxClients().tointeger(); ::WORLDSPAWN <- FindByClassname(null, "worldspawn") for (local i = 1; i <= ALL_PLAYERS ; i++) { local player = PlayerInstanceFromIndex(i) AddThinkToEnt(player,null) } local classIndices_Internal = { [1] = "Scout", [3] = "Soldier", [7] = "Pyro", [4] = "Demoman", [6] = "Heavy", [9] = "Engineer", [5] = "Medic", [2] = "Sniper", [8] = "Spy", } local ParticleSpawner = Entities.CreateByClassname("trigger_particle"); ParticleSpawner.KeyValueFromInt("spawnflags", 64); //Spy's slots are weird: //0 = Revolver //1 = Knife //2 = Sapper //3 = Disguise Kit ("PDA_spy") //4 = Invis Watch ////////////////////////////////////////////////////// //Game Events (and ondamage hook) ////////////////////////////////////////////////////// function OnGameEvent_player_builtobject(params) { local ent = GetPlayerFromUserID(params.userid); printl(format("A building has been built! Owner: %d. Type: %d.",params.userid,params.object)) if (params.object == 1 && ent.IsBotOfType(1337) && !ent.HasBotTag("stripslot_1_2")) { local building = EntIndexToHScript(params.index) building.ValidateScriptScope() local scope = building.GetScriptScope() scope.telehp_text <- SpawnEntityFromTable("point_worldtext", { textsize = 40 message = 0 font = 1 orientation = 1 textspacingx = 1 textspacingy = 1 spawnflags = 0 origin = building.GetOrigin() + Vector(0, 0, 100) rendermode = 3 }) UpdateHealthThink <- function() { telehp_text.KeyValueFromString("message",self.GetHealth().tostring()) printl(format("Updating health to %d...",self.GetHealth())) } AddThinkToEnt(building, "UpdateHealthThink") } if (params.object == 1 && ent.HasBotTag("stripslot_1_2")) { // ent.AddWeaponRestriction(WEAPONRESTRICT_PRIMARY) // GetItemInSlot(ent, SLOT_SECONDARY).Kill() if (GetItemInSlot(ent, SLOT_MELEE) != null) GetItemInSlot(ent, SLOT_MELEE).Kill() ent.Weapon_Switch(GetItemInSlot(ent, SLOT_PRIMARY)) } if (params.object == 2 && ent.HasBotTag("stripslot_1_2")) { // ent.AddWeaponRestriction(WEAPONRESTRICT_PRIMARY) for (local hTele; hTele = Entities.FindByClassname(hTele,"obj_teleporter"); ) { printl("Teleporter found, checking if owner is the same...") printl("Owner: " + GetBuilder(hTele)) printl("Builder of sentry: " + ent) if (GetBuilder(hTele) == ent) { GetItemInSlot(ent, SLOT_MELEE).Kill() ent.Weapon_Switch(GetItemInSlot(ent, SLOT_PRIMARY)) } } } } function OnGameEvent_object_detonated(params) { local ent = GetPlayerFromUserID(params.userid); printl(format("A building has been detonated! Owner: %d. Type: %d.",params.userid,params.objecttype)) if (params.objecttype == 2 && ent.IsBotOfType(1337) && ent.HasBotTag("stripslot_1_2")) { ent.GenerateAndWearItem("TF_WEAPON_WRENCH") } } function OnGameEvent_object_destroyed(params) { printl(format("A building has been destroyed! Owner: %d. Type: %d.",params.userid,params.objecttype)) local ent = GetPlayerFromUserID(params.userid); if (params.objecttype == 2 && ent.IsBotOfType(1337) && ent.HasBotTag("stripslot_1_2")) { ent.GenerateAndWearItem("TF_WEAPON_WRENCH") } } function OnGameEvent_player_spawn(params) { printl("A player has spawned!"); local ent = GetPlayerFromUserID(params.userid); if (ent && ent.IsBotOfType(1337)) EntFireByHandle(ent, "CallScriptFunction", "BotCheck", -1, null, null); } function OnGameEvent_player_death(params) { local killer = GetPlayerFromUserID(params.userid) local player = GetPlayerFromUserID(params.userid) //Player == who died if (!player) return //Automatically remove think functions on death. AddThinkToEnt(player, null) printl("A death has occurred!"); local died = EntIndexToHScript(params.victim_entindex); local killer_handle = EntIndexToHScript(params.inflictor_entindex); if (IsPlayerABot(died)) { if (died.HasBotTag("trackdeath")) { NetProps.SetPropStringArray(hud, "m_iszMannVsMachineWaveClassNames", "scout", 0); } } } function OnScriptHook_OnTakeDamage(params) { local player = params.attacker; local victim = params.const_entity; if (player && ( !(player.IsPlayer()) )) { //ClientPrint(null,3,format("Invalid attacker: isn't a player!")) return } if (victim && ( !(victim.IsPlayer()) )) { //ClientPrint(null,3,format("Invalid victim: isn't a player!")) return } if (player.IsBotOfType(1337) && player.HasBotTag("arrow_grenade")) { params.damage_type = DMG_BULLET params.damage_custom = TF_DMG_CUSTOM_NONE ClientPrint(null,3,format("Damage: %d\nDamage type: %d\nCustom damage: %d",params.damage,params.damage_type,params.damage_custom)) } if (victim.IsBotOfType(1337) && victim.HasBotTag("boss")) { ClientPrint(null,3,format("Damage: %d\nBase damage: %d\nDamage bonus: %d\nMax damage: %d",params.damage,params.const_base_damage,params.damage_bonus,params.max_damage)) return } //ClientPrint(null,3,"Nothing special, damage dealt succesfully.") } // function OnScriptHook_OnTakeDamage(params) // { // local victim = params.const_entity; // local player = params.inflictor; // if (params.damage_custom == TF_DMG_CUSTOM_BACKSTAB) // { // local weapon = PopExtUtil.GetItemInSlot(player,SLOT_MELEE) // local weaponID = PopExtUtil.GetItemIndex(weapon) // local WEAPON_ID_YER = // { // [225] = 1, // [574] = 1 // } //Your Eternal Reward | Wanga Prick // ClientPrint(null,3,format("Backstab detected! Weapon ID: %d\n",weaponID)) // if (params.damage >= victim.GetHealth() && weaponID in WEAPON_ID_YER) // { // ClientPrint(null,3,"Backstab was lethal and with the YER!\n") // EntFire("!activator","RunScriptCode","self.AddCond(TF_COND_DISGUISING)",-1,player) // SetPropInt(player,"m_Shared.m_iDisguiseHealth",player.GetMaxHealth()) // SetPropInt(player,"m_Shared.m_nDisguiseTeam",victim.GetTeam()) // SetPropInt(player,"m_Shared.m_nDisguiseClass",victim.GetPlayerClass()) // SetPropEntity(player,"m_Shared.m_hDisguiseTarget",victim) // EntFire("!activator","RunScriptCode","self.AddCond(TF_COND_DISGUISED)",0.2,player) // } // } // } ////////////////////////////////////////////////////////////////////////// //SPAWNED ENTITIES ////////////////////////////////////////////////////////////////////////// local commandEnt = SpawnEntityFromTable("point_clientcommand", { targetname = "commander_action_slot" }) local igniter = SpawnEntityFromTable("trigger_ignite", { targetname = "burn_that_area" origin = Vector(-340, 960, -20) spawnflags = 1 filtername = "filter_bluteam" burn_duration = 10 damage_percent_per_second = 0 }) igniter.SetSolid(2) igniter.SetSize(Vector(-256, -256, -256),Vector(256, 256, 256)) ////////////////////////////////////////////////////////////////////////// //FUNCTIONS ////////////////////////////////////////////////////////////////////////// //Spawns a particle on an entity. //entity - handle of the entity to spawn the particle on. //name - name of the particle. //attach_name - Attachment to dispatch at, such as head, flag, eye_1, etc. //attach_type - Type of attachment. Variants: // 0 - Create at origin, don't follow // 1 - Create at origin, follow entity // 2 - Create at CUSTOM origin, don't follow // 3 - Create on attachment point, don't follow // 4 - Create on attachment point, follow entity // 5 - Create at world origin // 6 - Create at root bone, follow entity function SpawnParticle(entity, name, attach_name, attach_type) { NetProps.SetPropString(ParticleSpawner, "m_iszParticleName", name) NetProps.SetPropString(ParticleSpawner, "m_iszAttachmentName", attach_name) NetProps.SetPropInt(ParticleSpawner, "m_nAttachType", attach_type) ParticleSpawner.AcceptInput("StartTouch", "", entity, entity) } function GetBuilder(hEnt) { return GetPropEntity(hEnt,"m_hBuilder") } //Stolen from PopExtUtil function GetItemInSlot(player, slot) { local item for (local i = 0; i < SLOT_COUNT; i++) { local wep = GetPropEntityArray(player, "m_hMyWeapons", i) if ( wep == null || wep.GetSlot() != slot) continue item = wep break } return item } ::GiveHealthToPlayer <- function(player,amt) { player.SetHealth(player.GetHealth() + amt) SendGlobalGameEvent("player_healonhit", { amount = amt, entindex = player.entindex(), weapon_def_index = 65535 }); } function GetButtonsThink() { local button_forward = GetPropInt(self, "m_nButtons"); ClientPrint(null,3,"Button: " + button_forward.tostring()) return -1 } ::CreateMimic <- function() { local botscope = self.GetScriptScope() botscope.shooter <- SpawnEntityFromTable("tf_point_weapon_mimic", { targetname = "mimic_weapon" origin = self.GetOrigin() + Vector(0, 0, 25) angles = QAngle(90, 0, 0) spawnflags = 1 WeaponType = 3 Damage = 60 SplashRadius = 140 }); ClientPrint(null, 3, self.GetPlayerClass().tostring()); botscope.MimicDetonateThink <- function() { if (!self) return 3 if (!shooter) return 3 shooter.SetOwner(self) EntFireByHandle(shooter,"SetParent","!activator",-1,self,null) EntFireByHandle(shooter,"FireOnce",null,-1,null,null) EntFireByHandle(shooter,"DetonateStickies",null,1.5,null,null) return 3 } AddThinkToEnt(self,"MimicDetonateThink") } ::tfor <- Entities.FindByClassname(null,"tf_objective_resource") //Walks down the indices of all icons til it finds the one it should. //Granted, this is inefficient, but there are a max of 24 icons anyway. The difference should be negligible. ::FindIndexOfIcon <- function(name) { local i = 0 //Check: do I start at 0 or at 1? local two = "" local i2 = 0 for(i; i < 24; i+=1) //Check: and thus, do I end at 24, or 23? { if (i >= 12) two = "2.",i2 = 12; local curIconName = GetPropStringArray(tfor, "m_iszMannVsMachineWaveClassNames"+two, i-i2) ClientPrint(null,3,format("Checking icon: %s",curIconName)) if (curIconName == name) { ClientPrint(null,3,format("Found %s! It was at index %d!",name,i)) return i; } } ClientPrint(null,3,"\x07FF3F3FIncorrect icon!") return null } ::UnUberOnUberThink <- function() { if (self.InCond(5)) self.RemoveCond(5); if (NetProps.GetPropInt(self, "m_lifeState") != 0) { AddThinkToEnt(self, null); NetProps.SetPropString(self, "m_iszScriptThinkFunction", ""); } return 0.1; } ::SetChargeToZero <- function() { SetPropFloat(self,"m_Shared.m_flEnergyDrinkMeter",0.1) } function TankFlamerStart() { local tank = Entities.FindByName(null,"tank_flamethrower") if (!(tank)) return ClientPrint(null,3,"Tank found. Let's burn.") //Bot is defined thru spawn event. TankFlamerWeapon <- Entities.CreateByClassname("tf_weapon_flamethrower") SetPropInt(TankFlamerWeapon, "m_AttributeManager.m_Item.m_iItemDefinitionIndex", PopExtUtil.GetItemIndex(TankFlamerWeapon)) SetPropBool(TankFlamerWeapon, "m_AttributeManager.m_Item.m_bInitialized", true) Entities.DispatchSpawn(TankFlamerWeapon) TankFlamerWeapon.SetClip1(-1) EntFireByHandle(TankFlamerWeapon,"SetParent","!activator",-1,tank,null) } function TankFlamerThink() { //self = bot SetPropBool(self, "m_bLagCompensation", false); SetPropFloat(TankFlamerWeapon, "m_flNextPrimaryAttack", 0); SetPropEntity(TankFlamerWeapon, "m_hOwner", self); TankFlamerWeapon.PrimaryAttack(); SetPropBool(self, "m_bLagCompensation", true); return -1 } function TankFlamerBot() { local botscope = self.GetScriptScope() botscope.shooter <- SpawnEntityFromTable("tf_point_weapon_mimic", { targetname = "mimic_weapon" origin = self.GetOrigin() + Vector(0, 0, 25) angles = QAngle(90, 0, 0) spawnflags = 1 WeaponType = 3 Damage = 60 SplashRadius = 140 }); ClientPrint(null, 3, self.GetPlayerClass().tostring()); botscope.MimicDetonateThink <- function() { if (!self) return 3 if (!shooter) return 3 shooter.SetOwner(self) EntFireByHandle(shooter,"SetParent","!activator",-1,self,null) EntFireByHandle(shooter,"FireOnce",null,-1,null,null) EntFireByHandle(shooter,"DetonateStickies",null,1.5,null,null) return 3 } AddThinkToEnt(self,"MimicDetonateThink") } local boss_elder_can_die = false local boss_elder_animation_started = false const BOSS_ELDER_MAXHEALTH = 22000 const ANIM_PRIMARY_DEATH_01 = 93 //Checked with GetSequence() //TODO: create prop; set prop to invisible; set prop to visible with anim at transition; //set self to invisible during transition; properly time setting prop to invisible and self to visible post-transition //Could also force-set local angles? --> LOW prio, do main parts first, please. function BossElderPrinceSpawn() { //self IS indeed the bot spawned ::boss_elder <- self boss_elder_can_die = false boss_elder_animation_started = false //Set Buddha to yes (Picked up said BUDDHA-setting from ficool) NetProps.SetPropInt(self, "m_debugOverlays", GetPropInt(self, "m_debugOverlays") | OVERLAY_BUDDHA_MODE); AddThinkToEnt(self,"BossElderPrinceThink") } //Constantly executed. As such, keep state in mind. function BossElderPrinceThink() { //setsequence `primary_death_01`, wait about 1.5 secs? //spawn model: models/props_mvm/mvm_revive_tombstone_blu.mdl if (self.GetHealth() == 1 && !(boss_elder_animation_started) && !(boss_elder_can_die)) { boss_elder_animation_started = true if (self.GetSequence() != ANIM_PRIMARY_DEATH_01) { self.SetSequence(ANIM_PRIMARY_DEATH_01); //self.SetPoseParameter(pose_move_x, 1.0); // Set the move_x pose to max weight } } //if (self.GetHealth() >= BOSS_ELDER_MAXHEALTH * 0.5 && boss_elder_animation_started) //{} // disable once we're revived. } //Executed once health is back up to full after reviving. function BossElderPrinceRevive() { boss_elder_can_die = true //Keep for what functionality? SetPropInt(player, "m_debugOverlays", GetPropInt(player, "m_debugOverlays") & ~OVERLAY_BUDDHA_MODE); } function SpawnTestParticle() { SpawnEntityFromTable("info_particle_system", { targetname = "boss_particle" effect_name = "utaunt_arcane_yellow_parent" start_active = 1 origin = Vector(625,25,-190) angles = QAngle(0,0,0) }) } function TestKnockback() { local vecBotOrigin = Vector(625,25,-190) local vecPlayerOrigin = self.GetOrigin() local flDist = (vecPlayerOrigin - vecBotOrigin).Length() if (flDist > 400) return; printl("Difference value: "+flDist) printl("Speed multiplier: "+0.15*pow(1+1000/flDist,2)) local flDiffx = vecPlayerOrigin.x - vecBotOrigin.x local flDiffy = vecPlayerOrigin.y - vecBotOrigin.y local vecDiff = Vector(flDiffx, flDiffy, 0) printl("Difference vector:"+vecDiff) // vecDiff.Norm() // printl("Normalized:"+vecDiff) vecDiff = vecDiff * (0.15*pow(1+1000/flDist,2)) vecDiff.z = 330 printl("Final vector:"+vecDiff) self.SetAbsVelocity(vecDiff) } function UseCanteen() { local botscope = self.GetScriptScope() ClientPrint(null,3,"Canteen bot spawned!") botscope.canteen_popped <- false botscope.UseCanteenThink <- function() { ClientPrint(null,2,"Canteen bot thinking...") //If we already used our canteen or aren't in a state to use it, don't use your action slot! // if (self.GetHealth() > (self.GetMaxHealth() * 0.5) || canteen_popped) // { // SetPropBool(self, "m_bUsingActionSlot", false) // } //If we're below half health and we haven't used our canteen yet, use it! if (self.GetHealth() <= (self.GetMaxHealth() * 0.5) && canteen_popped == false) { ClientPrint(null,3,"\x0799CCFF" + GetPropString(self,"m_szNetname") + " \x07FBECCB: Canteen popped = "+canteen_popped.tostring()) EntFire("commandEnt","command","+use_action_slot_item;wait 100;-use_action_slot_item",0,self) canteen_popped = true } return 0.1 //A little slow, but that's only kind, is it not? } AddThinkToEnt(self, "UseCanteenThink") } function ResetBotMission() { local botscope = self.GetScriptScope() ClientPrint(null,3,"Bot spawned with a reset mission!") self.SetMission(0,1) //0 - NO_MISSION. This function is bugged and only works to set mission to 0. //(1: reset behavior TRUE) } function BotCheck() { if (self.HasBotTag("overclock")) AddThinkToEnt(self, "UnUberOnUberThink"); if (self.HasBotTag("weaponmimic")) CreateMimic() if (self.HasBotTag("bot_tank_flamer")) TankFlamerThink() if (self.HasBotTag("nochargeonspawn")) SetChargeToZero() if (self.HasBotTag("boss")) BossElderPrinceSpawn() if (self.HasBotTag("canteen")) UseCanteen() if (self.HasBotTag("reset_mission")) ResetBotMission() } function ForEveryBotWithTag(tag,func) { for (local i = 1; i <= ALL_PLAYERS ; i++) { local player = PlayerInstanceFromIndex(i) if (player == null) continue if (!player.IsBotOfType(TF_BOT_TYPE)) continue if (player == self) continue if (!player.IsAlive()) continue if (player.HasBotTag(tag)) func(player); } } function GetItemInSlot(player, slot) { local item for (local i = 0; i < 10; i++) { local wep = GetPropEntityArray(player, "m_hMyWeapons", i) if ( wep == null || wep.GetSlot() != slot) continue item = wep break } return item } ::DebugText <- function() { ClientPrint(null,2,"DEBUG: Script is reachable.\n"); } // local table = t; NetProps.GetTable(self,0,t); for (k,v in t) printl(k + v); //1 = datamaps. 0 = netprops (default). ::GetNetprops <- function(b_datamaps = 0) { ClientPrint(null,2,"Datamap value given: "+b_datamaps+"\n"); local newNetpropTable = {}; NetProps.GetTable(self, b_datamaps, newNetpropTable); foreach(idx,val in newNetpropTable) { if (val != null) ClientPrint(null,2,idx+": "+val+"\n"); } } ::CreateFakeLauncher <- function() { if (self.GetClassname() != "player") { error("Hey, you're not a player! No launcher for you!") return } local scope = self.GetScriptScope() scope.FakeLauncher <- CreateByClassname("tf_weapon_rocketlauncher") SetPropInt(scope.FakeLauncher, "m_AttributeManager.m_Item.m_iItemDefinitionIndex", 18) //18 is stock rocket launcher SetPropBool(scope.FakeLauncher, "m_AttributeManager.m_Item.m_bInitialized", true) SetPropBool(scope.FakeLauncher, "m_bValidatedAttachedEntity", true) // SetPropEntityArray(self, "m_hMyWeapons", scope.FakeLauncher, 40) //This supposedly makes it work. I think. scope.FakeLauncher.SetTeam(self.GetTeam()) DispatchSpawn(scope.FakeLauncher) } ::FireFakeLauncher <- function() { if (self.GetClassname() != "player") { error("Hey, you're not a player! No launcher for you!") return } local scope = self.GetScriptScope() if (!("FakeLauncher" in scope)) { error("You don't have a fake launcher to fire!") return } scope.FakeLauncher.SetClip1(10) SetPropFloat(scope.FakeLauncher, "m_flNextPrimaryAttack", 0) SetPropEntity(scope.FakeLauncher, "m_hOwner", self) scope.FakeLauncher.PrimaryAttack() } ::MoveFakeLauncher <- function(origin) { if (self.GetClassname() != "player") { error("Hey, you're not a player! No launcher for you!") return } local scope = self.GetScriptScope() if (!("FakeLauncher" in scope)) { error("You don't have a fake launcher to fire!") return } scope.FakeLauncher.SetOrigin(origin) } ::CreateWorldLauncher <- function() { if ("FakeLauncher" in getroottable()) { printl("FakeLauncher already exists, don't worry.") return; } } local path1 = null local path2 = null function SpawnCustomTankPaths(bot) { if (!bot.IsValid()) return; local PathMoveThink = function() { if (!self.IsAlive() && !self.HasRobotTag("tank_carrier")) { printl("Removing think...") SetPropString(self, "m_iszScriptThinkFunction", ""); AddThinkToEnt(self, "null") } if (path1 != null && path2 != null) { path1.SetAbsOrigin(bot.GetAttachmentOrigin(bot.LookupAttachment("flag"))) } path1 = SpawnEntityFromTable("path_track",{ targetname = "carrier_path_1", origin = bot.GetAttachmentOrigin(bot.LookupAttachment("flag")) target = "carrier_path_2" }) path2 = SpawnEntityFromTable("path_track",{ targetname = "carrier_path_2", origin = Vector(3810,-710,85) //Hatch coordinates. }) } AddThinkToEnt(bot,"PathMoveThink") } ::CreateFireball <- function() { // ForEveryBotWithTag("fireballer", function(player) // { ::FakeLauncher <- CreateByClassname("tf_weapon_rocketlauncher_fireball") SetPropInt(FakeLauncher, "m_AttributeManager.m_Item.m_iItemDefinitionIndex", 18) //18 is stock rocket launcher SetPropBool(FakeLauncher, "m_AttributeManager.m_Item.m_bInitialized", true) SetPropBool(FakeLauncher, "m_bValidatedAttachedEntity", true) FakeLauncher.SetTeam(3) //blue team DispatchSpawn(FakeLauncher) local fireball = SpawnEntityFromTable("tf_projectile_balloffire", { targetname = "fireballtest" teamnum = 3 origin = Vector(-160,42,0) }) // fireball.SetOwner(player) // SetPropEntity(fireball,"m_hOriginalLauncher",GetItemInSlot(player,SLOT_PRIMARY)) // SetPropEntity(fireball,"m_hLauncher",GetItemInSlot(player,SLOT_PRIMARY)) fireball.SetOwner(WORLDSPAWN) SetPropEntity(fireball,"m_hOriginalLauncher",FakeLauncher) SetPropEntity(fireball,"m_hLauncher",FakeLauncher) SetPropVector(fireball,"m_vecInitialVelocity",Vector(3000,0,0)) fireball.SetAbsVelocity(Vector(3000,0,0)) // }) } //Give a spell to caller. Type defaults to overheal. ::GiveSpell <- function(type = 2) { //2 = overheal local spellbook = GetItemInSlot(self, SLOT_PDA) if (spellbook == null) { ClientPrint(null,3,"You don't have a spellbook, dummy!") return } SetPropInt(spellbook, "m_iSelectedSpellIndex", type) SetPropInt(spellbook, "m_iSpellCharges", 1) } //Wrapper because I keep misspelling it ffs ::CompareNetProps <- function(b_compare = 1, b_datamaps = 0) { CompareNetprops(b_compare, b_datamaps) } ::CompareNetprops <- function(b_compare = 1, b_datamaps = 0) { if (b_compare) { ::preNetpropTable <- {}; NetProps.GetTable(self, b_datamaps, preNetpropTable); ClientPrint(null,2,"DEBUG: Stored data.\n"); } else { local postNetpropTable = {}; NetProps.GetTable(self, b_datamaps, postNetpropTable); ClientPrint(null,2,"DEBUG: Stored new data.\n"); foreach(idx,val in postNetpropTable) { if (val != preNetpropTable[idx]) { if (typeof val == "Vector") continue if (typeof val == "table") continue ClientPrint(null,2,idx+" changed: from "+val+" to "+preNetpropTable[idx]+"\n"); } } ClientPrint(null,2,"DEBUG: Compare finished.\n"); } } ::CompareNetpropsOfEnt <- function(h_entity, b_compare = 1, b_datamaps = 0) { if (h_entity.IsValid()) EntFireByHandle(h_entity, "RunScriptCode", format("CompareNetprops(%d, %d)",b_compare,b_datamaps), 0, null, null) } ::GetOutputs <- function(outputName) { local newOutputsTable = {}; ClientPrint(null,2,"DEBUG: Getting output table of " + self.GetName() + "...\n"); if (outputName == "OnCase") { ClientPrint(null,2,"Output is OnCase!\n"); for (local j = 1; j <= 16; j+=1) { if (j < 10) { for (local i = 0; i < EntityOutputs.GetNumElements(self, outputName + "0" + j.tostring()); i+=1) { ClientPrint(null,2, "Getting output " + outputName + "0" + j.tostring()) EntityOutputs.GetOutputTable(self, outputName + "0" + j.tostring(), newOutputsTable, i); foreach(idx,val in newOutputsTable) { if (idx != "times_to_fire") ClientPrint(null,2,idx+": "+val+"\n"); } } } else if (j >= 10) { for (local i = 0; i < EntityOutputs.GetNumElements(self, outputName + j.tostring()); i+=1) { ClientPrint(null,2, "Getting output " + outputName + j.tostring()) EntityOutputs.GetOutputTable(self, outputName + j.tostring(), newOutputsTable, i); foreach(idx,val in newOutputsTable) { if (idx != "times_to_fire") ClientPrint(null,2,idx+": "+val+"\n"); } } } } } else { ClientPrint(null,2, "Getting output " + outputName) for (local i = 0; i < EntityOutputs.GetNumElements(self, outputName); i+=1) { EntityOutputs.GetOutputTable(self, outputName, newOutputsTable, i); foreach(idx,val in newOutputsTable) { if (idx != "times_to_fire") ClientPrint(null,2,idx+": "+val+"\n"); } } } } //HEAVILY based on lite's GiveWeapon() function as used in rangescript.nut, and the "Detecting melee smacks" script from the VDC wiki. ::AlwaysAttack <- function() { local owner = self.GetOwner() //Preserve old charge meter and ammo count local charge = GetPropFloat(owner, "m_Shared.m_flItemChargeMeter"); local ammo = GetPropIntArray(owner, "m_iAmmo", 1); // set up stuff needed to ensure the weapon always fires SetPropIntArray(owner, "m_iAmmo", 99, 1); SetPropFloat(owner, "m_Shared.m_flItemChargeMeter", 100.0); SetPropBool(owner, "m_bLagCompensation", false); self.SetClip1(-1); SetPropFloat(self,"m_flNextPrimaryAttack",0); SetPropEntity(self, "m_hOwner", owner); local newOrigin = owner.EyePosition() newOrigin.z = newOrigin.z + 0 local newAngles = owner.EyeAngles() newAngles.z = newOrigin.z + 0 self.SetAbsOrigin(newOrigin) self.SetAbsAngles(newAngles) // SetPropInt(self,"m_iWeaponState",2) self.PrimaryAttack() // revert changes SetPropBool(owner, "m_bLagCompensation", true); SetPropIntArray(owner, "m_iAmmo", ammo, 1); SetPropFloat(owner, "m_Shared.m_flItemChargeMeter", charge); ClientPrint(null,3,"FIRE!") return -1; } ::CreateWeapon <- function(weaponName,itemID) { ClientPrint(null,3,format("Attempting to create %s!",weaponName)) local weapon = Entities.CreateByClassname(weaponName) //m_iItemDefinitionIndex --> likely related to ensuring the item id is correct and synced? //m_bInitialized --> ensures the weapon is considered "active"? //m_bValidatedAttachedEntity --> the attached entity is validated? SetPropInt(weapon, "m_AttributeManager.m_Item.m_iItemDefinitionIndex", itemID); SetPropBool(weapon, "m_AttributeManager.m_Item.m_bInitialized", true); SetPropBool(weapon, "m_bValidatedAttachedEntity", true); weapon.SetClip1(-1); weapon.SetTeam(self.GetTeam()); Entities.DispatchSpawn(weapon); weapon.SetOwner(self) self.Weapon_Equip(weapon) NetProps.SetPropEntityArray(self, "m_hMyWeapons", weapon, 4); AddThinkToEnt(weapon, "AlwaysAttack") } ::multitrace <- [ [-1,1], [-0.5,1], [0,1], [0.5,1], [1,1], [-1,0.5], [-0.5,0.5], [0,0.5], [0.5,0.5], [1,0.5], [-1,0], [-0.5,0], [0,0], [0.5,0], [1,0], [-1,-0.5], [-0.5,-0.5], [0,-0.5], [0.5,-0.5], [1,-0.5], [-1,-1], [-0.5,-1], [0,-1], [0.5,-1], [1,-1] ] ::LineOfSightFromLite <- function() { local eyepos = self.EyePosition() local eyeang = self.EyeAngles() local fwd = eyeang.Forward() local lft = eyeang.Left()*0.06 local up = eyeang.Up()*0.06 foreach(k in multitrace) { local trace = { start = eyepos end = (fwd + (lft*k[0]) + (up*k[1])) * (1<<30) ignore = self } TraceLineEx(trace) DebugDrawLine(trace.start,trace.pos,0,255,255,false,3) } } //engineer_nest, teleporter_exit, sentrygun function CreateEngineerHint(iNumber, sType, vecOrigin, angAngle) { SpawnEntityFromTable("bot_hint_"+sType, { targetname = "engineer_nest_"+iNumber teamnum = 3 //Blue team origin = vecOrigin angles = angAngle }) ClientPrint(null,3,"\x0700FFC3[DEBUG] \x07FBECCBCreated bot_hint_"+sType+" entity at "+vecOrigin.tostring()+"!") } //Attention focus doesn't work at all, so this kinda does nothing but spam text. ::PrintIfSeeEnemyThink <- function() { if (self.GetTeam() != 3) { ClientPrint(null,2,"No longer on blue, abandon ship.") NetProps.SetPropString(self, "m_iszScriptThinkFunction", ""); AddThinkToEnt(self, "null") } for (local i = 1; i <= ALL_PLAYERS ; i++) { local player = PlayerInstanceFromIndex(i) if (player == null) continue if (player.GetTeam() != 2) continue if (self.IsAttentionFocused()) ClientPrint(null,3,"Attention focused!") else ClientPrint(null,3,"Attention unfocused.") ClientPrint(null,2,"Checking for focus on "+GetPropString(player,"m_szNetname")) if (self.IsAttentionFocusedOn(player)) ClientPrint(null,2,"Attention focused on: "+GetPropString(player,"m_szNetname")) } return 0.5; } PrecacheModel("models/neo_fixed_bots/bots/soldier/bot_soldier.mdl") local botModel_Soldier = "models/neo_fixed_bots/bots/soldier/bot_soldier.mdl" CTFPlayer_ToggleBotModel <- function() { local playerModelName = this.GetModelName() ClientPrint(null,2,"Player model: "+playerModelName) if (classIndices_Internal[this.GetPlayerClass()] == "Soldier") { if (playerModelName == botModel_Soldier) { EntFire("!activator","SetCustomModelWithClassAnimations","models/player/soldier.mdl",0,activator = this) ClientPrint(null,2,"Resetting model.") } else { EntFire("!activator","SetCustomModelWithClassAnimations",botModel_Soldier,0,activator = this) ClientPrint(null,2,"Bot model set.") } } } CTFPlayer_SetForwardVelocity <- function(velocity) { local forwardAng = this.GetForwardVector(); SetPropEntity(this,"m_hGroundEntity",null); this.SetAbsVelocity(forwardAng.Scale(velocity)); ClientPrint(null,3,format("Forward angle: %s.\nAttempted to set velocity to %s.",forwardAng.tostring(),forwardAng.Scale(velocity).tostring())) } //Adds attribute to weapon in given slot with given value. //int slot: slot of the weapon. //string attribute: name of the attribute //float value: value of the attribute. Therefore,does not take strings. CTFPlayer_AddAttr <- function(slot, attribute, value) { local playerClass = classIndices_Internal[this.GetPlayerClass()] printl("Class: "+playerClass.tostring()) switch(slot) { case 0: //Primary slot = 0 case 1: //Secondary; Knife for Spy, swap to Sapper if (playerClass == "Spy") slot = 2 case 2: //Melee; Sapper for Spy, swap to Knife if (playerClass == "Spy") slot = 1 } printl("Slot: "+slot.tostring()) local weapon = GetPropEntityArray(this,"m_hMyWeapons",slot) weapon.AddAttribute(attribute, value, -1) } CTFPlayer_MakeSnowfall <- function() { local snowfall = SpawnEntityFromTable("func_precipitation", { targetname = "prec_particle" preciptype = 3 renderamt = 100 origin = Vector(-1325,-3470,35) }) snowfall.SetSolid(SOLID_BBOX) snowfall.SetSize(Vector(-1125,-930,-165), Vector(1125,930,165)) } CTFPlayer_MakeSnowstorm <- function() { local playerOrigin = this.GetOrigin() local playerAngles = this.GetAbsAngles() SpawnEntityFromTable("info_particle_system", { targetname = "boss_particle" effect_name = "env_snow_stormfront_001" start_active = 1 origin = playerOrigin angles = playerAngles }) } CTFPlayer_SpawnBossSpell <- function() { local eyeboss_proj = SpawnEntityFromTable("tf_projectile_spellspawnboss", { teamnum = 3 origin = this.GetOrigin() + Vector(0,0,(20 + 85 * 1.75)) angles = QAngle(90,0,0) }) SetPropEntity(eyeboss_proj,"m_hThrower",this) } // SetPropInt(PopExtUtil.GetItemInSlot(self, SLOT_PDA), `m_iSelectedSpellIndex`, 10) // SetPropInt(PopExtUtil.GetItemInSlot(self, SLOT_PDA), `m_iSpellCharges`, 10) // printl(GetPropEntity(self,`m_hOwnerEntity`)) // printl(GetPropEntity(self,`m_hOriginalLauncher`)) // printl(GetPropEntity(self,`m_hLauncher`)) // printl(GetPropEntity(self,`m_hThrower`)) ////////////////////////////////////////////////////////////////////////// //GAME EVENT FUNCTIONS ////////////////////////////////////////////////////////////////////////// //Type 1 = kritz // 2 = uber // 3 = tele to spawn // 4 = ammo refill // 5 = building upgrade function OnGameEvent_player_used_powerup_bottle(params) { foreach(idx,slot in params) print("Index = " + idx + ", with value: " + slot + "\n") } function OnGameEvent_player_healed(params) { foreach(idx,slot in params) print("Index = " + idx + ", with value: " + slot + "\n") local healer = GetPlayerFromUserID(params.healer) local patient = GetPlayerFromUserID(params.patient) if (classIndices_Internal[healer.GetPlayerClass()] == "Medic" && patient != healer) { ClientPrint(null,3,"Yeah that's a medic healing someone else.") params.amount = params.amount * 0.5 return params } } foreach ( key, value in this ) { if ( typeof( value ) == "function" && startswith( key, "CTFPlayer_" ) ) { local func_name = key.slice(10); CTFPlayer[ func_name ] <- value; CTFBot[ func_name ] <- value; delete this[ key ]; } } ////////////////////////////////////////////////////////////////////////// //DIRECT CHANGES ////////////////////////////////////////////////////////////////////////// Convars.SetValue("tf_forced_holiday", 9) __CollectGameEventCallbacks(this);