ClientPrint(null,2,"DEBUG: Script activated!\n"); // ClientPrint(null,2,SLOT_MELEE.tostring()); // How Thinks Are Allowed To Work(tm) // example written by Lobotomy // ::IThink <- function() // { // printl("I'm thinking from global") // } // local scope = bot.GetScriptScope() //bot = TFBot handle // scope.ITryToThink <- function() // { // printl("I'm thinking from my scope") // } // AddThinkToEnt(bot, "IThink") // AddThinkToEnt(bot, "ITryToThink") ////////////////////////////////////////////////////////////////////////// //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") local classIndices_Internal = { [1] = "Scout", [3] = "Soldier", [7] = "Pyro", [4] = "Demoman", [6] = "Heavy", [9] = "Engineer", [5] = "Medic", [2] = "Sniper", [8] = "Spy", } for (local i = 1; i <= ALL_PLAYERS ; i++) { local player = PlayerInstanceFromIndex(i) AddThinkToEnt(player,null) } ::TestSpace <- { SetXZAnglesToZero = function(hEnt) { hEnt.SetAbsAngles(QAngle(0,hEnt.GetAbsAngles().y,0)) printl("Reset angles to "+hEnt.GetAbsAngles()) } EnableBossThink = function(hBot) { hBot.GetScriptScope().bCola <- false hBot.GetScriptScope().bBonch <- false hBot.GetScriptScope().bMulti <- false hBot.GetScriptScope().bHaste <- false hBot.GetScriptScope().bShield <- false hBot.GetScriptScope().flDrinkTime <- 0 hBot.GetScriptScope().BossThink <- function() { local iHealth = fabs(self.GetHealth()) / fabs(self.GetMaxHealth()) local flTime = (abs(Time()*10)/fabs(10)) local szTime = flTime == floor(flTime) ? flTime.tostring()+".0" : flTime.tostring() ClientPrint(null,4,"Health perc: "+iHealth.tostring()+" at time: "+szTime) //95%: Crit-a-Cola, 85%: Bonch, 75%: Multi-Pop, 65%: Haste Fizz, 50%: Shield Shake if (iHealth < 0.95 && !(bCola)) { printl("Try to drink Crit-a-Cola!") bCola = true flDrinkTime = Time() + 3 ClientPrint(null,3,"Next drink time: "+flDrinkTime.tostring()) return -1 } else if (iHealth < 0.85 && !(bBonch) && flDrinkTime <= Time()) { printl("Try to drink Multi-Pop!") bBonch = true flDrinkTime = Time() + 3 ClientPrint(null,3,"Next drink time: "+flDrinkTime.tostring()) return -1 } else if (iHealth < 0.75 && !(bMulti) && flDrinkTime <= Time()) { printl("Try to drink Multi-Pop!") bMulti = true flDrinkTime = Time() + 3 ClientPrint(null,3,"Next drink time: "+flDrinkTime.tostring()) return -1 } else if (iHealth < 0.65 && !(bHaste) && flDrinkTime <= Time()) { printl("Try to drink Haste Fizz!") bHaste = true flDrinkTime = Time() + 3 ClientPrint(null,3,"Next drink time: "+flDrinkTime.tostring()) return -1 } else if (iHealth < 0.5 && !(bShield) && flDrinkTime <= Time()) { printl("Try to drink Shield Shake!") bShield = true flDrinkTime = Time() + 3 ClientPrint(null,3,"Next drink time: "+flDrinkTime.tostring()) return -1 } return -1 } AddThinkToEnt(hBot,"BossThink") } //Function taken from the example page, because the math is HARD. function VectorAngles(forward) { local yaw, pitch if (forward.y == 0.0 && forward.x == 0.0) { yaw = 0.0 if (forward.z > 0.0) pitch = 270.0 else pitch = 90.0 } else { yaw = (atan2(forward.y, forward.x) * 180.0 / Constants.Math.Pi) if (yaw < 0.0) yaw += 360.0 pitch = (atan2(-forward.z, forward.Length2D()) * 180.0 / Constants.Math.Pi) if (pitch < 0.0) pitch += 360.0 } return QAngle(pitch, yaw, 0.0) } function CTFPlayer_AlwaysAimAtBotWithTag(sTag) { printl("Attempting to set "+this+" to look at a bot!") local hBot = null 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.IsAlive()) continue if (player.HasBotTag(tag)) { hBot = player break } } if (hBot == null) { printl("[ERROR] Couldn't find a bot with tag "+sTag+"!") return } this.GetScriptScope().hTarget <- hBot this.GetScriptScope().AimThink <- function() { if ( !(hTarget.IsAlive()) || !(self.IsAlive()) ) { printl("Target or self no longer alive, removing think...") SetPropString(self, "m_iszScriptThinkFunction", "") AddThinkToEnt(self,null) } if (Time() % 2 == 0) printl("Setting aim!") local vecDir = hTarget.GetOrigin() - self.GetOrigin() vecDir.Norm() local angLook = TestSpace.VectorAngles(vecDir) self.SnapEyeAngles(angLook) return -1 } AddThinkToEnt(this,"AimThink") } } foreach ( key, value in TestSpace ) { if ( typeof( value ) == "function" && startswith( key, "CTFPlayer_" ) ) { local func_name = key.slice(10); CTFPlayer[ func_name ] <- value; CTFBot[ func_name ] <- value; delete TestSpace[ key ]; } } __CollectGameEventCallbacks(TestSpace); ::SeelSpace <- { Cleanup = function() { // cleanup any persistent changes here // keep this at the end if (SeelSpace) delete ::SeelSpace; } // mandatory events OnGameEvent_recalculate_holidays = function(_) { if (GetRoundState() == 3) Cleanup() } OnGameEvent_mvm_wave_complete = function(_) { Cleanup() } //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 (!ent.IsBotOfType(1337)) return; 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 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; local dmgtypes = params.damage_type; if (victim == null) return if (!(victim.IsPlayer())) return if (!(victim.IsBotOfType(TF_BOT_TYPE))) return 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) } } else if (victim.HasBotTag("nofalldmg") && dmgtypes == 32) { params.damage = 1 } else { // local i = 1 // printl("Adding DMG_CLUB, removing DMG_BUCKSHOT.") // params.damage_type = params.damage_type | 128 // params.damage_type = params.damage_type & ~(DMG_BUCKSHOT) printl("Damage types inside "+params.damage_type+":") PrintDamageTypes(params.damage_type) } } ////////////////////////////////////////////////////////////////////////// //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 PrintDamageTypes(iDmgtypes) { local i = 0 while(iDmgtypes >= (2 << i)) { if (iDmgtypes & 2 << i) { print(2 << i) print("\n") } i = i + 1 } } 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 } function GiveHealthToPlayer(player,amt) { player.SetHealth(player.GetHealth() + amt) SendGlobalGameEvent("player_healonhit", { amount = amt, entindex = player.entindex(), weapon_def_index = 65535 }); } //Restrict a bot to a certain weapon slot. //1 = primary, 2 = secondary, 3 = melee. //No PDA support for now because 1. idk the flag and 2. why would you need it function SetWeaponRestriction(hPlayer, iSlot) { local iRestrictFlag = (iSlot == 3 ? 1 : iSlot == 2 ? 4 : iSlot == 1 ? 2 : 0) if (iRestrictFlag == 0) { printl("SetWeaponRestriction ERROR: invalid slot! Only 1, 2, 3 accepted as primary, secondary, melee!") return; } hPlayer.ClearAllWeaponRestrictions() hPlayer.AddWeaponRestriction(iRestrictFlag) } function GetButtonsThink() { local button_forward = GetPropInt(self, "m_nButtons"); ClientPrint(null,3,"Button: " + button_forward.tostring()) return -1 } function CreateMimic() { 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. function FindIndexOfIcon(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 } function UnUberOnUberThink() { 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; } function SetChargeToZero() { 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") } boss_elder_can_die = false boss_elder_animation_started = false BOSS_ELDER_MAXHEALTH = 22000 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 } //Function taken from the example page, because the math is HARD. function VectorAngles(forward) { local yaw, pitch if (forward.y == 0.0 && forward.x == 0.0) { yaw = 0.0 if (forward.z > 0.0) pitch = 270.0 else pitch = 90.0 } else { yaw = (atan2(forward.y, forward.x) * 180.0 / Constants.Math.Pi) if (yaw < 0.0) yaw += 360.0 pitch = (atan2(-forward.z, forward.Length2D()) * 180.0 / Constants.Math.Pi) if (pitch < 0.0) pitch += 360.0 } return QAngle(pitch, yaw, 0.0) } function DebugText() { 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). function GetNetprops(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"); } } EnableBossThink = function(hBot) { printl("Giving boss buddha...") SetPropInt(hBot, "m_debugOverlays", GetPropInt(hBot, "m_debugOverlays") | OVERLAY_BUDDHA_MODE) local hScope = hBot.GetScriptScope() printl("Declaring scope variables...") } //Spawns a test prop at (0,0,0) function CreateTestProp() { SpawnEntityFromTable("prop_dynamic", { targetname = "testprop" origin = Vector(0,0,0), model = "models/player/scout.mdl" defaultanim = "stand_melee" }) } //Fires a trace to the test prop from caller's position. //FIRE THIS ON !ACTIVATOR function FireTraceToProp(hBot) { local traceStartPos = hBot.GetOrigin() + Vector(0, 0, 65) local traceEndPos = Vector(0, 0, 0) local tbTrace = { start = traceStartPos, end = traceEndPos, mask = 131083 //MASK_NPCWORLDSTATIC: SOLID, WINDOW, MONSTERCLIP, GRATE; aka only world. } TraceLineEx(tbTrace) } function AlwaysAimAtOrigin(hBot) { local hSprite = SpawnEntityFromTable("prop_dynamic", { targetname = "testprp" model = "models/weapons/c_models/c_sniperrifle/c_sniperrifle.mdl" origin = "0 0 0" solid = "0" }) hSprite.ValidateScriptScope() hSprite.GetScriptScope().ParentPlayer <- hBot hSprite.GetScriptScope().AimThink <- function() { if (!(ParentPlayer.IsAlive())) { SetPropString(self, "m_iszScriptThinkFunction", "") self.Destroy() } local vecDir = Vector(0,0,0) - self.GetOrigin() vecDir.Norm() self.SetForwardVector(vecDir) self.SetLocalOrigin(ParentPlayer.GetOrigin() + vecDir * 65 + Vector(0,0,65)) if (Time() % 0.5 == 0) printl(ParentPlayer.GetOrigin()) // local angLook = SeelSpace.VectorAngles(vecDir) // self.SnapEyeAngles(angLook) return -1 } AddThinkToEnt(hSprite,"AimThink") } function CreateFakeLauncher() { 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) } function FireFakeLauncher() { 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() } function MoveFakeLauncher(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) } function CreateWorldLauncher() { if ("FakeLauncher" in getroottable()) { printl("FakeLauncher already exists, don't worry.") return; } } function CreateFireball() { // ForEveryBotWithTag("fireballer", function(player) // { ::FakeLauncher <- CreateByClassname("tf_weapon_rocketlauncher_fireball") SetPropInt(FakeLauncher, "m_AttributeManager.m_Item.m_iItemDefinitionIndex", 1178) //1178 is Dragon's Fury, 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) // Fireball.SetOwner(player) // SetPropEntity(Fireball,"m_hOriginalLauncher",GetItemInSlot(player,SLOT_PRIMARY)) // SetPropEntity(Fireball,"m_hLauncher",GetItemInSlot(player,SLOT_PRIMARY)) ::Fireball <- CreateByClassname("tf_projectile_balloffire") Fireball.SetTeam(3) Fireball.SetAbsOrigin(Vector(1450,-100,-60)) 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)) Fireball.DispatchSpawn() printl(GetPropEntity(Fireball,"m_hLauncher")) // }) } //Give a spell to caller. Type defaults to overheal. function GiveSpell(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 function CompareNetProps(b_compare = 1, b_datamaps = 0) { CompareNetprops(b_compare, b_datamaps) } function CompareNetprops(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"); } } function CompareNetpropsOfEnt(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) } function GetOutputs(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. function AlwaysAttack() { 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; } function CreateWeapon(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") } function CreateRainbowMedal() { ClientPrint(null,3,"Attempting to create a fancy rainbow medal!") local model_name = "models/workshop/player/items/all_class/robotarm_donator_rainbow/robotarm_donator_rainbow_gem.mdl" local origin = GetListenServerHost().GetOrigin() local paint = 12059645 local wearable = Entities.CreateByClassname("tf_wearable") NetProps.SetPropBool(wearable, "m_AttributeManager.m_Item.m_bInitialized", true) wearable.SetAbsOrigin(origin) wearable.AddAttribute("set item tint RGB", paint, -1) wearable.DispatchSpawn() wearable.EnableDraw() // local playerprop = SpawnEntityFromTable("prop_dynamic", // { // targetname = "testprop" // origin = origin, // model = "models/player/scout.mdl" // defaultanim = "stand_melee" // }) local prop = SpawnEntityFromTable("prop_dynamic", //_ornament { targetname = "testcosmetic" origin = origin, model = model_name // InitialOwner = "testprop" }) prop.SetOwner(wearable) EntFireByHandle(wearable,"SetParent","!activator",0,GetListenServerHost(),GetListenServerHost()) } 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] ] function LineOfSightFromLite() { 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. function PrintIfSeeEnemyThink() { 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; } function CTFPlayer_ToggleBotModel() { 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.") } } } function CTFPlayer_SetForwardVelocity(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. function CTFPlayer_AddAttr(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) } function CTFPlayer_MakeSnowfall() { 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)) } function CTFPlayer_MakeSnowstorm() { 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 }) } function CTFPlayer_SpawnBossSpell() { 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 SeelSpace ) { if ( typeof( value ) == "function" && startswith( key, "CTFPlayer_" ) ) { local func_name = key.slice(10); CTFPlayer[ func_name ] <- value; CTFBot[ func_name ] <- value; delete SeelSpace[ key ]; } } __CollectGameEventCallbacks(SeelSpace); PrecacheModel("models/neo_fixed_bots/bots/soldier/bot_soldier.mdl") local botModel_Soldier = "models/neo_fixed_bots/bots/soldier/bot_soldier.mdl" ////////////////////////////////////////////////////////////////////////// //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)) ////////////////////////////////////////////////////////////////////////// //DIRECT CHANGES ////////////////////////////////////////////////////////////////////////// Convars.SetValue("tf_forced_holiday", 9) local ParticleSpawner = Entities.CreateByClassname("trigger_particle"); ParticleSpawner.KeyValueFromInt("spawnflags", 64);