::CONST <- getconsttable() ::ROOT <- getroottable() ::MAX_CLIENTS <- MaxClients().tointeger() 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 } foreach(k, v in ::Entities.getclass()) if (k != "IsValid" && !(k in ROOT)) ROOT[k] <- ::Entities[k].bindenv(::Entities) foreach(k, v in ::NetProps.getclass()) if (k != "IsValid" && !(k in ROOT)) ROOT[k] <- ::NetProps[k].bindenv(::NetProps) local OTHER_CONSTANTS = { MASK_ALL = -1 MASK_SPLITAREAPORTAL = 48 MASK_SOLID_BRUSHONLY = 16395 MASK_WATER = 16432 MASK_BLOCKLOS = 16449 MASK_OPAQUE = 16513 MASK_DEADSOLID = 65547 MASK_PLAYERSOLID_BRUSHONLY = 81931 MASK_NPCWORLDSTATIC = 131083 MASK_NPCSOLID_BRUSHONLY = 147467 MASK_CURRENT = 16515072 MASK_SHOT_PORTAL = 33570819 MASK_SOLID = 33570827 MASK_BLOCKLOS_AND_NPCS = 33570881 MASK_OPAQUE_AND_NPCS = 33570945 MASK_VISIBLE_AND_NPCS = 33579137 MASK_PLAYERSOLID = 33636363 MASK_NPCSOLID = 33701899 MASK_SHOT_HULL = 100679691 MASK_SHOT = 1174421507 LIFE_ALIVE = 0 LIFE_DYING = 1 LIFE_DEAD = 2 LIFE_RESPAWNABLE = 3 LIFE_DISCARDBODY = 4 TF_STUN_NONE = 0 TF_STUN_MOVEMENT = 1 TF_STUN_CONTROLS = 2 TF_STUN_MOVEMENT_FORWARD_ONLY = 4 TF_STUN_SPECIAL_SOUND = 8 TF_STUN_DODGE_COOLDOWN = 16 TF_STUN_NO_EFFECTS = 32 TF_STUN_LOSER_STATE = 64 TF_STUN_BY_TRIGGER = 128 TF_STUN_SOUND = 256 SND_NOFLAGS = 0 SND_CHANGE_VOL = 1 SND_CHANGE_PITCH = 2 SND_STOP = 4 SND_SPAWNING = 8 SND_DELAY = 16 SND_STOP_LOOPING = 32 SND_SPEAKER = 64 SND_SHOULDPAUSE = 128 SND_IGNORE_PHONEMES = 256 SND_IGNORE_NAME = 512 SND_DO_NOT_OVERWRITE_EXISTING_ON_CHANNEL = 1024 DEG2RAD = 0.0174532924 RAD2DEG = 57.295779513 FLT_MIN = 1.175494e-38 FLT_MAX = 3.402823466e+38 INT_MIN = -2147483648 INT_MAX = 2147483647 // damagefilter redefinitions DMG_USE_HITLOCATIONS = DMG_AIRBOAT DMG_HALF_FALLOFF = DMG_RADIATION DMG_CRITICAL = DMG_ACID DMG_RADIUS_MAX = DMG_ENERGYBEAM DMG_IGNITE = DMG_PLASMA DMG_USEDISTANCEMOD = DMG_SLOWBURN DMG_NOCLOSEDISTANCEMOD = DMG_POISON DMG_MELEE = DMG_BLAST_SURFACE DMG_DONT_COUNT_DAMAGE_TOWARDS_CRIT_RATE = DMG_DISSOLVE } foreach(k,v in OTHER_CONSTANTS) if(!(k in ROOT)) { CONST[k] <- v ROOT[k] <- v } PrecacheScriptSound("Halloween.GhostBoo") local hPlayerManager = FindByClassname(null, "tf_player_manager") ::GraveyardGhost <- { function OnGameEvent_recalculate_holidays(_) { if(GetRoundState() == 3) { Convars.SetValue("tf_ghost_xy_speed", 300) delete ::GraveyardGhost }} bInWave = false function OnGameEvent_mvm_begin_wave(_) { bInWave = true for(local i = 1; i <= MAX_CLIENTS; i++) { local hPlayer = PlayerInstanceFromIndex(i) if(hPlayer && !hPlayer.IsBotOfType(TF_BOT_TYPE) && hPlayer.GetTeam() == 2) { hPlayer.AddEFlags(EFL_NO_MEGAPHYSCANNON_RAGDOLL) if(!hPlayer.IsAlive()) hPlayer.ForceRespawn() } } } function OnGameEvent_mvm_wave_complete(_) { bInWave = false for(local i = 1; i <= MAX_CLIENTS; i++) { local hPlayer = PlayerInstanceFromIndex(i) if(hPlayer && hPlayer.InCond(TF_COND_HALLOWEEN_GHOST_MODE) && hPlayer.GetTeam() == 2) hPlayer.ForceRespawn() } } function OnGameEvent_player_death(params) { local hVictim = GetPlayerFromUserID(params.userid) local bBot = hVictim.IsBotOfType(TF_BOT_TYPE) if(!bBot && bInWave) { local vecOrigin = hVictim.GetOrigin() local angEyes = hVictim.EyeAngles() EntFire("bignet", "RunScriptCode", format("if(activator.IsValid()) GraveyardGhost.BecomeGhost(activator, Vector(%f, %f, %f), QAngle(%f, %f, 0))", vecOrigin.x, vecOrigin.y, vecOrigin.z, angEyes.x, angEyes.y), 6, hVictim) local iAliveReds = 0 for(local i = 1; i <= MAX_CLIENTS; i++) { local hPlayer = PlayerInstanceFromIndex(i) if(hPlayer && hPlayer.IsAlive() && !hPlayer.IsBotOfType(TF_BOT_TYPE) && !hPlayer.InCond(TF_COND_HALLOWEEN_GHOST_MODE) && hPlayer.GetHealth() > 0) iAliveReds++ } if(iAliveReds == 0) for(local hWin; hWin = FindByClassname(hWin, "game_round_win");) if(hWin.GetTeam() == TF_TEAM_BLUE) { hWin.AcceptInput("RoundWin", null, null, null); break } } } function OnGameEvent_player_spawn(params) { local hPlayer = GetPlayerFromUserID(params.userid) local bBot = hPlayer.IsBotOfType(TF_BOT_TYPE) if(bInWave && !bBot && !(hPlayer.GetEFlags() & EFL_NO_MEGAPHYSCANNON_RAGDOLL)) { local vecOrigin = hPlayer.GetOrigin() local angEyes = hPlayer.EyeAngles() EntFire("bignet", "RunScriptCode", format("if(activator.IsValid()) { SetPropInt(activator, `m_lifeState`, LIFE_DEAD); GraveyardGhost.BecomeGhost(activator, Vector(%f, %f, %f), QAngle(%f, %f, 0)) }", vecOrigin.x, vecOrigin.y, vecOrigin.z, angEyes.x, angEyes.y), -1, hPlayer) } } function SetAlwaysTransmit(hEnt) { local hEdict = SpawnEntityFromTable("info_target", {}) hEdict.AddEFlags(EFL_IN_SKYBOX | EFL_FORCE_CHECK_TRANSMIT) hEdict.AcceptInput("SetParent", "!activator", hEnt, null) hEdict.SetLocalOrigin(Vector()) return hEdict } function DispatchParticleEffectOn(entity, name) { if(entity == null) return if(name == null) { entity.AcceptInput("DispatchEffect", "ParticleEffectStop", null, null); return } local hParticle = CreateByClassname("trigger_particle") SetAlwaysTransmit(hParticle) hParticle.KeyValueFromString("particle_name", name) hParticle.KeyValueFromInt("attachment_type", 1) hParticle.KeyValueFromInt("spawnflags", 64) hParticle.DispatchSpawn() hParticle.AcceptInput("StartTouch", null, entity, entity) hParticle.Kill() } function BecomeGhost(hPlayer, vecOrigin, angEyes) { if(!hPlayer || !hPlayer.IsValid() || hPlayer.IsAlive()) return DispatchParticleEffect("ghost_appearation", vecOrigin, Vector(1)) hPlayer.AddEFlags(EFL_NO_MEGAPHYSCANNON_RAGDOLL) hPlayer.ForceRespawn() hPlayer.RemoveEFlags(EFL_NO_MEGAPHYSCANNON_RAGDOLL) hPlayer.SetAbsOrigin(vecOrigin) hPlayer.SnapEyeAngles(angEyes) hPlayer.AddCond(TF_COND_HALLOWEEN_GHOST_MODE) hPlayer.AddCond(TF_COND_TEAM_GLOWS) hPlayer.SetModelScale(0.5, 0) hPlayer.AddCustomAttribute("voice pitch scale", 1.75, -1) hPlayer.AddCustomAttribute("ignored by bots", 1, -1) hPlayer.AddCustomAttribute("mult credit collect range", 0, -1) EntFireByHandle(hPlayer, "Color", "255 65 25", -1, null, null) EntFireByHandle(hPlayer, "Color", "255 65 25", 0.033, null, null) DispatchParticleEffectOn(hPlayer, "ghost_glow_red") SetPropIntArray(hPlayer, "m_nModelIndexOverrides", PrecacheModel("models/props_graveyard/healing_ghost.mdl"), 0) local hTouch = SpawnEntityFromTable("dispenser_touch_trigger", { targetname = "ghosttrigger" spawnflags = 1 }) hTouch.SetSolid(SOLID_BBOX) hTouch.SetSize(Vector(-128, -128, -128), Vector(128, 128, 128)) local hDisp = SpawnEntityFromTable("pd_dispenser", { origin = Vector(0, 0, 32) defaultupgrade = 0 teamnum = TF_TEAM_RED touch_trigger = "ghosttrigger" "$ratemult" : 0.6 }) hDisp.ValidateScriptScope() local hDisp_scope = hDisp.GetScriptScope() hDisp_scope.LocationInfo <- { origin = Vector(), eyeangles = QAngle() } hDisp_scope.flBooTime <- 0 hDisp_scope.iButtonsLast <- 0 hDisp_scope.bCamera <- false local iEntIndex = hPlayer.entindex() hDisp_scope.Think <- function() { if(!hPlayer.IsAlive() || !hPlayer.InCond(TF_COND_HALLOWEEN_GHOST_MODE) || hPlayer.GetTeam() != 2) { self.Kill(); return } local iButtons = GetPropInt(hPlayer, "m_nButtons") local iButtonsChanged = iButtonsLast ^ iButtons local iButtonsPressed = iButtonsChanged & iButtons local iButtonsReleased = iButtonsChanged & (~iButtons) iButtonsLast = iButtons if(iButtonsPressed & IN_ATTACK && Time() >= flBooTime) { flBooTime = Time() + 1 hPlayer.EmitSound("Halloween.GhostBoo") } EmitSoundEx({ sound_name = "misc/null.wav" flags = SND_CHANGE_PITCH | SND_IGNORE_NAME pitch = 120 entity = hDisp }) EntFireByHandle(self, "RunScriptCode", format("SetPropBoolArray(activator, `m_bAlive`, false, %i)", iEntIndex), -1, hPlayerManager, null) local bCameraNow = false for(local hCamera; hCamera = FindByClassname(hCamera, "point_viewcontrol");) if(GetPropInt(hCamera, "m_state") == 1) { bCameraNow = true; break} if(bCameraNow && !bCamera) // ghosts dont like cameras { bCamera = true local hCameraFix = SpawnEntityFromTable("point_viewcontrol", {}) hCameraFix.AcceptInput("Enable", null, hPlayer, hPlayer) hCameraFix.AcceptInput("Disable", null, hPlayer, hPlayer) hCameraFix.Kill() } else if(!bCameraNow && bCamera) bCamera = false return -1 } AddThinkToEnt(hDisp, "Think") SetPropEntity(hDisp, "m_hMovePeer", hPlayer.FirstMoveChild()) SetPropEntity(hPlayer, "m_hMoveChild", hDisp) SetPropEntity(hDisp, "m_hMoveParent", hPlayer) SetPropString(hTouch, "m_iName", "") SetPropEntity(hTouch, "m_hMovePeer", hDisp.FirstMoveChild()) SetPropEntity(hDisp, "m_hMoveChild", hTouch) SetPropEntity(hTouch, "m_hMoveParent", hDisp) SetDestroyCallback(hDisp, function() { if(hPlayer.IsValid()) { SetPropIntArray(hPlayer, "m_nModelIndexOverrides", 0, 0) } }) } function SetDestroyCallback(entity, callback) { entity.ValidateScriptScope(); local scope = entity.GetScriptScope(); scope.setdelegate({}.setdelegate({ parent = scope.getdelegate() id = entity.GetScriptId() index = entity.entindex() callback = callback _get = function(k) { return parent[k]; } _delslot = function(k) { if (k == id) { entity = EntIndexToHScript(index); local scope = entity.GetScriptScope(); scope.self <- entity; callback.pcall(scope); } delete parent[k]; } }) ); } } __CollectGameEventCallbacks(GraveyardGhost) Convars.SetValue("tf_ghost_xy_speed", 400)