├── .gitignore ├── README.md ├── autochess ├── README.md ├── a-star.nut ├── board-ui.nut ├── board.nut ├── cursors.nut ├── gamemode.nut ├── sounds.txt └── units │ └── base.nut ├── lib ├── chat.nut ├── commands.nut ├── cursors.nut ├── debug.nut ├── events.nut ├── math.nut ├── players.nut ├── polyfills.nut ├── precache.nut ├── templates.nut ├── timers.nut └── weapons.nut ├── portal ├── entry.nut ├── gamemode.nut ├── player.nut ├── portal.nut └── test_portal.vmf └── vip ├── README.md ├── gamemode.nut ├── lib ├── chat.nut ├── debug.nut ├── events.nut ├── math.nut ├── money.nut ├── players.nut └── ui.nut ├── resources ├── materials │ └── models │ │ └── vip │ │ └── helicopter │ │ ├── helicopter_rescue.vmt │ │ ├── helicopter_rescue.vtf │ │ ├── helicopter_rescue_windows.vmt │ │ └── helicopter_rescue_windows.vtf ├── models │ ├── hostage │ │ ├── v_vip_arm.dx90.vtx │ │ ├── v_vip_arm.mdl │ │ ├── v_vip_arm.vvd │ │ ├── vip_carry.dx90.vtx │ │ ├── vip_carry.mdl │ │ ├── vip_carry.vvd │ │ ├── vip_ground.dx90.vtx │ │ ├── vip_ground.mdl │ │ ├── vip_ground.phy │ │ └── vip_ground.vvd │ ├── player │ │ └── custom_player │ │ │ └── legacy │ │ │ ├── ctm_heavy2.dx90.vtx │ │ │ ├── ctm_heavy2.mdl │ │ │ ├── ctm_heavy2.phy │ │ │ └── ctm_heavy2.vvd │ └── vip │ │ ├── helicopter.ani │ │ ├── helicopter.dx90.vtx │ │ ├── helicopter.mdl │ │ ├── helicopter.phy │ │ └── helicopter.vvd └── sound │ ├── vip.wav │ └── vip │ ├── fx_roundend_12seconds.wav │ ├── fx_roundend_12seconds_flatline.wav │ ├── fx_vipdown.wav │ └── vip.wav └── vmfs ├── as_militia_v2.vmf ├── instance_vip_entities.vmf ├── instance_vip_rescue.vmf ├── test_vip.kv ├── test_vip.vmf └── test_vip_dev02.vmf /.gitignore: -------------------------------------------------------------------------------- 1 | 2v2/ 2 | apc/ 3 | ar_shoots/ 4 | ar_dizzy/ 5 | birds/ 6 | control_point_master/ 7 | coop/ 8 | de_bank/ 9 | de_cbble/ 10 | de_dust2/ 11 | de_nuke/ 12 | de_overpass/ 13 | de_safehouse/ 14 | de_stmarc/ 15 | de_train/ 16 | de_vertigo/ 17 | dev/ 18 | flyguydev/ 19 | gd_rialto/ 20 | training/ 21 | mapspawn.nut 22 | vip/vmfs/*.log 23 | vip/vmfs/*.bsp 24 | vip/vmfs/*.prt 25 | vip/vmfs/*.vmx 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A collection of VScripts for CS:GO. 2 | 3 | You're probably going to be most interested in the `lib/` folder - this is where I keep track of all the utility stuff I've extracted into library scripts, which you can use as you see fit. 4 | 5 | If you want to use this code for learning VScript, then you should know that all the code here is a mixture of *terrible* and fairly decent code. I've learned a decent bit as I've gotten more comfortable with Squirrel, but I wouldn't recommend attempting to duplicate my code style. 6 | 7 | If you need help with VScripts, I can suggest joining [the Source Engine Discord](https://discordapp.com/invite/sourceengine). People there do tons of stuff I wouldn't even dream of being able to. 8 | 9 | ## Installation 10 | 11 | Download this repo, extract it to `steamapps\common\Counter-Strike Global Offensive\csgo\scripts\vscripts`. 12 | 13 | Further installation may be necessary depending on the mod. Make sure you check each folder's README. 14 | 15 | ## Notes on Squirrel 16 | 17 | Developing VScripts is cumbersome, mostly due to the poor documentation. Here are a few tips and tricks I've ran into: 18 | 19 |
20 |
CS:GO's Squirrel is old
21 |
The version of Squirrel that is embedded in CS:GO is Squirrel 2.2. This means that some of the newer features (e.g. .find() or in on arrays) aren't available.
22 |
Script files are executed every round, but root table remains unchanged
23 |
At the start of every round, every entity's script is executed. However, the root table still contains any data set from previous runs. Any code you want to run every round (e.g. creating entities, which are invalidated at the end of every round) is fine with this, but any code you want to keep across rounds (e.g. keeping track of state) should be placed behind a guard and store its state in the root table.
24 |
VScripts are fucked
25 |

Valve's code tends to be... interesting. The VScript implementation is no exception. Whenever you rely on some Valve-implemented API, make sure you test that it returns what you expect.

26 |

An example of this: if you loop through entities using Entities.First()/Entities.Next(ent) and check ent.GetClassname() == "player" on each entity, you'll get all bots and players. If you instead loop through entities using Entities.FindByClassname(ent, "player"), then you'll only get players and not bots. Why? Because that's just the way it is. Welcome to the Source engine.

27 |
Outputs are asynchronous
28 |
Be careful when triggering outputs on entities (e.g. showing messages using an env_hudhint) - they will not execute until your Squirrel code is finished. You can wait until the next think cycle to work around this.
29 |
Entities will not react to script-created keyvalues by themselves
30 |
If you have an entity that is supposed to do something without being triggered by an input (e.g. a logic_eventlistener) then you cannot set it up in your VScript, as the __KeyValueFrom* functions do not execute any related logic. You must instead set it up in Hammer.
31 | If you have an entity that reads its keyvalues on input (e.g. a env_hudhint) then it's fine to set it up in your VScript, as it will read the keyvalue directly when you trigger it. Experiment a bit, as it's sometimes difficult to know which is which.
32 |
33 | -------------------------------------------------------------------------------- /autochess/README.md: -------------------------------------------------------------------------------- 1 | Ye... I don't know why I made this. 2 | 3 | Far from finished. I mostly just got it to a point where I could [shitpost on Reddit](https://old.reddit.com/r/GlobalOffensive/comments/c4f415/it_is_time_to_bring_csgo_into_2019/). 4 | -------------------------------------------------------------------------------- /autochess/a-star.nut: -------------------------------------------------------------------------------- 1 | /** 2 | * Implements the A* search algorithm for finding paths on the board 3 | */ 4 | 5 | ::GeneratePath <- function(board, start, end) { 6 | // during execution each node is identified by the string "x-y", e.g. "2-5" 7 | // we maintain a map of those strings to _astar_node's 8 | local startNode = _astar_node(start, 0, _astar_estimator(start, end)); 9 | startNode.open = true; 10 | local open = [startNode]; 11 | local closed = []; 12 | local nodeMap = {}; 13 | nodeMap[_astar_identifier(start)] <- startNode; 14 | 15 | while (open.len() > 0) { 16 | // find the node with the lowest forwards cost 17 | local current = { node=null, fcost=999, idx=-1 }; 18 | foreach (idx,node in open) { 19 | if (node.forwardsCost < current.fcost) { 20 | current.node = node; 21 | current.fcost = node.forwardsCost; 22 | current.idx = idx; 23 | } 24 | } 25 | local curVec = current.node.vec; 26 | if (curVec.x == end.x && curVec.y == end.y) { 27 | // Log("[A*] Found path from "+_astar_identifier(start)+" to "+_astar_identifier(current.node.vec)); 28 | return _astar_reconstructor(current.node); 29 | } 30 | 31 | open.remove(current.idx); 32 | current.node.closed = true; 33 | local neighbors = current.node.FindNeighbors(board, nodeMap, start, end); 34 | foreach (neighbor in neighbors) { 35 | if (neighbor.closed) { continue; } 36 | 37 | local neighborBackCost = current.node.backwardsCost + 1; 38 | if (!neighbor.open) { 39 | neighbor.open = true; 40 | open.push(neighbor); 41 | } else if (neighborBackCost >= neighbor.backwardsCost) { 42 | continue; 43 | } 44 | 45 | neighbor.previous = current.node; 46 | neighbor.backwardsCost = neighborBackCost; 47 | neighbor.forwardsCost = _astar_estimator(neighbor.vec, end); 48 | } 49 | } 50 | // Log("[A*] Couldn't find path from "+_astar_identifier(start)+" to "+_astar_identifier(end)); 51 | return []; 52 | }; 53 | 54 | ::_astar_reconstructor <- function(node) { 55 | local path = []; 56 | while (node != null) { 57 | path.push(node.vec); 58 | node = node.previous; 59 | } 60 | return path; 61 | } 62 | 63 | ::_astar_identifier <- function(vec) { 64 | return vec.x + "-" + vec.y; 65 | }; 66 | 67 | ::_astar_estimator <- function(start, end) { 68 | local dX = start.x - end.x; 69 | local dY = start.y - end.y; 70 | return abs(dX) + abs(dY); 71 | }; 72 | 73 | class _astar_node { 74 | backwardsCost = 999; // cost from start to here 75 | forwardsCost = 999; // cost of getting from here to end 76 | previous = null; 77 | open = false; 78 | closed = false; 79 | 80 | vec = null; 81 | 82 | constructor(vect, backCost=999, forwCost=999) { 83 | this.vec = vect; 84 | this.backwardsCost = backCost; 85 | this.forwardsCost = forwCost; 86 | } 87 | 88 | /** Returns an array of neighbors */ 89 | function FindNeighbors(board, nodeMap, start, end) { 90 | local outp = []; 91 | for (local x = -1; x < 2; x++) { 92 | for (local y = -1; y < 2; y++) { 93 | if (x == 0 && y == 0) { continue; } 94 | local targetVec = this.vec + Vector(x, y, 0); 95 | if (targetVec.x < 0 || targetVec.x >= 8 || targetVec.y < 0 || targetVec.y >= 8) { continue; } 96 | if (board[targetVec.x][targetVec.y] != null 97 | && !(targetVec.x == start.x && targetVec.y == start.y) 98 | && !(targetVec.x == end.x && targetVec.y == end.y)) { 99 | Log("[A*] Found non-empty on-board square "+_astar_identifier(targetVec)); 100 | continue; 101 | } 102 | local identifier = _astar_identifier(targetVec); 103 | local node = null; 104 | if (identifier in nodeMap) { 105 | node = nodeMap[identifier]; 106 | } else { 107 | node = _astar_node(targetVec); 108 | nodeMap[identifier] <- node; 109 | } 110 | outp.push(node); 111 | } 112 | } 113 | return outp; 114 | } 115 | } -------------------------------------------------------------------------------- /autochess/board-ui.nut: -------------------------------------------------------------------------------- 1 | /** 2 | * Handles the UI and binding user actions to changes on the underlying board structure 3 | * Exposes: 4 | * - FindBoardOfPlayer(player) 5 | * - AssignBoardToPlayer(player) 6 | * - RemoveBoardFromPlayer(player) 7 | */ 8 | DoIncludeScript("lib/debug.nut", null); 9 | DoIncludeScript("lib/polyfills.nut", null); 10 | DoIncludeScript("autochess/cursors.nut", null); 11 | DoIncludeScript("autochess/board.nut", null); 12 | 13 | ::BOARD_SQUARE_SIZE <- 64; 14 | ::BOARD_BENCH_OFFSET <- Vector(0, -128, -12); 15 | ::BOARD_SHOP_OFFSET <- Vector(0, -256, 0); 16 | ::BOARD_PLAYER_OFFSET <- Vector(0, -448, 368); 17 | 18 | class BoardUI { 19 | board = null; 20 | userid = null; 21 | ePlayer = null; 22 | cursor = null; 23 | 24 | origin = Vector(0, 0, 0); 25 | lowerLeft = Vector(0, 0, 0); 26 | benchLowerLeft = Vector(0, 0, 0); 27 | shopLowerLeft = Vector(0, 0, 0); 28 | 29 | highlightedSquare = null; 30 | selectedSquare = null; 31 | 32 | nextClockTime = -5; 33 | 34 | constructor(usrid, orig) { 35 | Log("[BoardUI] My origin is "+orig); 36 | this.board = Board(this); 37 | this.userid = usrid; 38 | this.origin = orig; 39 | this.lowerLeft = this.origin - Vector(4 * BOARD_SQUARE_SIZE, 4 * BOARD_SQUARE_SIZE, 0); 40 | this.benchLowerLeft = this.lowerLeft + BOARD_BENCH_OFFSET; 41 | this.shopLowerLeft = this.lowerLeft + BOARD_SHOP_OFFSET; 42 | 43 | this.OnRoundStart(); 44 | } 45 | 46 | /** Called when the round starts, and we need to do stuff again (e.g. place the user) */ 47 | function OnRoundStart() { 48 | this.ePlayer = ::Players.FindByUserid(this.userid); 49 | Log("[BoardUI] Finding player for our userid "+this.userid+", got "+this.ePlayer); 50 | if (this.ePlayer == null) { return; } // we'll be called again in a bit by our Think 51 | this.nextClockTime = -5; 52 | this.board.OnRoundStart(); 53 | this.cursor = ::FindCursorOfPlayer(this.ePlayer); 54 | this.PlacePlayer(); 55 | } 56 | 57 | function Think() { 58 | // after a round restart, we might need to wait a bit before we can grab our user 59 | if (this.ePlayer == null) { 60 | this.OnRoundStart(); 61 | return; 62 | } 63 | 64 | // we play the clock sound from our player entity 65 | local roundTime = ::gamemode_autochess.GetRoundTime(); 66 | if (roundTime < 0 && roundTime > this.nextClockTime) { 67 | this.ePlayer.EmitSound("AutoChess.Clock"); 68 | this.nextClockTime = this.nextClockTime + 1; 69 | } 70 | 71 | 72 | this.board.Think(); 73 | if (this.board.isLive) { return; } // user shouldn't be able to interact while we're fighting 74 | 75 | // update our reading of what the player is aiming at 76 | if (this.cursor == null) { this.cursor = ::FindCursorOfPlayer(this.ePlayer); } 77 | if (this.cursor != null) { 78 | local lookingAt = this.cursor.GetLookingAt(); 79 | local lookingAtSquare = this.GetSquareOfPosition(lookingAt); 80 | if (lookingAtSquare != null && this.ShouldHighlightSquare(lookingAtSquare)) { 81 | this.HighlightSquare(lookingAtSquare); 82 | } else { 83 | this.HighlightSquare(null); 84 | } 85 | } else { 86 | Log("[BoardUI] Couldn't find cursor of player "+player); 87 | } 88 | 89 | // higlight the square the user is aiming at, or has selected 90 | // TODO: replace with model-based highlighting 91 | if (this.selectedSquare != null) { 92 | local position = this.GetPositionOfSquare(this.selectedSquare); 93 | local size = Vector(BOARD_SQUARE_SIZE, BOARD_SQUARE_SIZE, 16); 94 | DebugDrawBox( 95 | position + Vector(0, 0, 9), 96 | size * -0.5, 97 | size * 0.5, 98 | 0, 99 | 0, 100 | 255, 101 | 0, 102 | 0.15 103 | ); 104 | } 105 | if (this.highlightedSquare != null) { 106 | local position = this.GetPositionOfSquare(this.highlightedSquare); 107 | local size = Vector(BOARD_SQUARE_SIZE, BOARD_SQUARE_SIZE, 16); 108 | DebugDrawBox( 109 | position + Vector(0, 0, 8), 110 | size * -0.5, 111 | size * 0.5, 112 | 0, 113 | 255, 114 | 0, 115 | 0, 116 | 0.15 117 | ); 118 | } 119 | } 120 | 121 | function ShouldHighlightSquare(square) { 122 | if (square.y >= 4) { return false; } 123 | if (this.selectedSquare != null) { return true; } 124 | return this.board.GetUnitAtSquare(square) != null; 125 | } 126 | 127 | /** Sets the square that we are highlighting */ 128 | function HighlightSquare(square) { 129 | this.highlightedSquare = square; 130 | } 131 | 132 | /** Sets the square that we have selected */ 133 | function SelectSquare(square) { 134 | Log("[BoardUI] "+this.ePlayer+" selected "+square); 135 | if (square != null) { 136 | this.ePlayer.EmitSound("AutoChess.SelectUnit"); 137 | } 138 | this.selectedSquare = square; 139 | } 140 | 141 | /** Deselects the currently active square */ 142 | function DeselectSquare() { 143 | this.selectedSquare = null; 144 | } 145 | 146 | /** Handles the player clicking. Decides if we should select a square, move a unit, etc. */ 147 | function OnClicked(position) { 148 | if (this.board.isLive) { return; } 149 | local clickedSquare = GetSquareOfPosition(position); 150 | // if we clicked outside of the board, deselect the current selection 151 | if (clickedSquare == null) { 152 | if (this.selectedSquare != null) { 153 | Log("[BoardUI] " + this.ePlayer + " deselected by clicking outside of board"); 154 | this.DeselectSquare(); 155 | } 156 | return; 157 | } 158 | 159 | // == if we clicked on a square, we have to make a decision 160 | 161 | // if we don't have a selected square, we make the clicked square the selected on 162 | if (this.selectedSquare == null) { 163 | // we don't want to select empty squares (because they don't do anything) 164 | if (this.board.GetUnitAtSquare(clickedSquare) != null) { 165 | this.SelectSquare(clickedSquare); 166 | } 167 | return; 168 | } 169 | 170 | // if we clicked the selected square, we want to deselect it 171 | if (clickedSquare.x == this.selectedSquare.x && clickedSquare.y == this.selectedSquare.y) { 172 | Log("[BoardUI] "+this.ePlayer+" deselected by clicking same square ("+clickedSquare+","+this.selectedSquare+")"); 173 | this.DeselectSquare(); 174 | return; 175 | } 176 | 177 | // otherwise we clicked a square while having a square selected - do something 178 | // we can't move to top half of board, or to shop 179 | if (clickedSquare.y >= 4 || clickedSquare.y == -2) { return; } 180 | local unit = this.board.GetUnitAtSquare(this.selectedSquare); 181 | this.board.MoveUnitToSquare(unit, clickedSquare, true); 182 | this.DeselectSquare(); 183 | this.ePlayer.EmitSound("AutoChess.SwapUnits"); 184 | } 185 | 186 | /** Sets our player to the position we want him to start in */ 187 | function PlacePlayer() { 188 | Log("[BoardUI] Moving "+this.ePlayer+" to board"); 189 | this.ePlayer.SetOrigin(this.origin + BOARD_PLAYER_OFFSET); 190 | } 191 | 192 | /** Returns the square that a particular position is on, if it is on the board */ 193 | function GetBoardSquareOfPosition(position) { 194 | // board squares range from [0,8] in both x and y 195 | local dX = position.x - this.lowerLeft.x; 196 | local dY = position.y - this.lowerLeft.y; 197 | if (dX < 0 || dY < 0) { return null; } 198 | 199 | local x = (dX / BOARD_SQUARE_SIZE).tointeger(); 200 | local y = (dY / BOARD_SQUARE_SIZE).tointeger(); 201 | 202 | if (x >= 8 || y >= 8) { return null; } 203 | return Vector(x, y, 0); 204 | } 205 | 206 | /** Returns the square that a particular position is on, if it is on the bench */ 207 | function GetBenchSquareOfPosition(position) { 208 | // bench squares range from [0,8] in x, but are -1 in y 209 | local dX = position.x - this.benchLowerLeft.x; 210 | local dY = position.y - this.benchLowerLeft.y; 211 | if (dX < 0 || dY < 0) { return null; } 212 | 213 | local x = (dX / BOARD_SQUARE_SIZE).tointeger(); 214 | local y = (dY / BOARD_SQUARE_SIZE).tointeger(); 215 | 216 | if (x >= 8 || y >= 1) { return null; } 217 | return Vector(x, y - 1, 0); 218 | } 219 | 220 | /** Returns the square that a particular position is on, if it is in the shop */ 221 | function GetShopSquareOfPosition(position) { 222 | // shop squares range from [0,5] in x, but are -2 in y 223 | local dX = position.x - this.shopLowerLeft.x; 224 | local dY = position.y - this.shopLowerLeft.y; 225 | if (dX < 0 || dY < 0) { return null; } 226 | 227 | local x = (dX / BOARD_SQUARE_SIZE).tointeger(); 228 | local y = (dY / BOARD_SQUARE_SIZE).tointeger(); 229 | 230 | if (x >= 5 || y >= 1) { return null; } 231 | return Vector(x, y - 2, 0); 232 | } 233 | 234 | /** 235 | * Returns the square that a particular position resides in 236 | * Returned square is a vector, or null if outside of board 237 | */ 238 | function GetSquareOfPosition(position) { 239 | local square = this.GetBoardSquareOfPosition(position); 240 | if (square != null) { return square; } 241 | square = this.GetBenchSquareOfPosition(position); 242 | if (square != null) { return square; } 243 | square = this.GetShopSquareOfPosition(position); 244 | return square; 245 | } 246 | 247 | /** 248 | * Returns the center of a square. Square is a vector 249 | */ 250 | function GetPositionOfSquare(square) { 251 | local offset = Vector((square.x + 0.5) * BOARD_SQUARE_SIZE, (square.y + 0.5) * BOARD_SQUARE_SIZE, 0); 252 | // handle board squares 253 | if (square.y >= 0) { return this.lowerLeft + offset; } 254 | // handle bench squares 255 | if (square.y == -1) { return this.benchLowerLeft + Vector(0, BOARD_SQUARE_SIZE, 0) + offset; } 256 | // handle shop squares 257 | if (square.y == -2) { return this.shopLowerLeft + Vector(0, BOARD_SQUARE_SIZE * 2, 0) + offset; } 258 | 259 | Log("[BoardUI] Attempted to get position of invalid square "+square); 260 | return null; 261 | } 262 | } -------------------------------------------------------------------------------- /autochess/board.nut: -------------------------------------------------------------------------------- 1 | /** 2 | * The actually underlying board representation 3 | * Deals with keeping track of units and delegating thinks to them 4 | */ 5 | DoIncludeScript("lib/debug.nut", null); 6 | DoIncludeScript("lib/polyfills.nut", null); 7 | DoIncludeScript("autochess/units/base.nut", null); 8 | 9 | class Board { 10 | parentUI = null; 11 | 12 | isLive = false; 13 | startTime = null; 14 | 15 | board = null; 16 | bench = null; 17 | shop = null; 18 | 19 | constructor(ui) { 20 | this.parentUI = ui; 21 | } 22 | 23 | function Think() { 24 | if (this.startTime != null && Time() > this.startTime) { 25 | this.isLive = true; 26 | this.startTime = null; 27 | } 28 | 29 | if (!this.isLive) { return; } 30 | 31 | foreach (col in this.board) { 32 | foreach (unit in col) { 33 | if (unit != null) { 34 | unit.Think(); 35 | } 36 | } 37 | } 38 | } 39 | 40 | function OnRoundStart() { 41 | this.board = [ // x,y 42 | array(8, null), 43 | array(8, null), 44 | array(8, null), 45 | array(8, null), 46 | array(8, null), 47 | array(8, null), 48 | array(8, null), 49 | array(8, null) 50 | ]; 51 | this.bench = array(8, null); 52 | this.shop = array(5, null); 53 | 54 | // TODO: don't generate fake units 55 | this.bench[0] = BaseUnit(this, true); 56 | this.bench[0].MoveToSquare(Vector(0, -1, 0), true); 57 | 58 | this.board[7][7] = BaseUnit(this, false); 59 | this.board[7][7].MoveToSquare(Vector(7, 7, 0), true); 60 | 61 | this.isLive = false; 62 | this.startTime = Time() + 10; 63 | } 64 | 65 | /** Finds the closest unit of the opposite alliance to a square */ 66 | function FindEnemyClosestTo(square, friendly) { 67 | local lookingFor = !friendly; 68 | for (local dist = 1; dist <= 8; dist++) { 69 | local seenUnits = this.FindUnitsAtDistance(square, dist); 70 | local enemies = []; 71 | foreach (unit in seenUnits) { 72 | if (unit.friendly == lookingFor) { enemies.push(unit); } 73 | } 74 | if (enemies.len() > 0) { 75 | return enemies[RandomInt(0, enemies.len() - 1)]; 76 | } 77 | } 78 | } 79 | 80 | /** Returns an array of all units that are a specific distance from a square */ 81 | function FindUnitsAtDistance(startSquare, distance) { 82 | local output = []; 83 | local lowerLeft = startSquare - Vector(distance, distance, 0); 84 | local upperRight = startSquare + Vector(distance, distance, 0); 85 | // check horizontally 86 | for (local x = lowerLeft.x; x <= upperRight.x; x++) { 87 | if (x < 0 || x >= 8) { continue; } 88 | local square = null; 89 | local unit = null; 90 | if (lowerLeft.y >= 0) { 91 | local square = Vector(x, lowerLeft.y, 0); 92 | local unit = this.GetUnitAtSquare(square); 93 | if (unit != null) { output.push(unit); } 94 | } 95 | if (upperRight.y < 8) { 96 | local square = Vector(x, upperRight.y, 0); 97 | local unit = this.GetUnitAtSquare(square); 98 | if (unit != null) { output.push(unit); } 99 | } 100 | } 101 | // check vertically 102 | for (local y = lowerLeft.y + 1; y <= upperRight.y - 1; y++) { 103 | if (y < 0 || y >= 8) { continue; } 104 | local square = null; 105 | local unit = null; 106 | if (lowerLeft.x >= 0) { 107 | local square = Vector(lowerLeft.x, y, 0); 108 | local unit = this.GetUnitAtSquare(square); 109 | if (unit != null) { output.push(unit); } 110 | } 111 | if (upperRight.x < 8) { 112 | local square = Vector(upperRight.x, y, 0); 113 | local unit = this.GetUnitAtSquare(square); 114 | if (unit != null) { output.push(unit); } 115 | } 116 | } 117 | return output; 118 | } 119 | 120 | /** Gets the distance between two squares */ 121 | function GetDistance(from, to) { 122 | local dX = abs(to.x - from.x); 123 | local dY = abs(to.y - from.y); 124 | if (dX < dY) { return dY; } 125 | return dX; 126 | } 127 | 128 | /** Get the unit that occupies a square, or null if none */ 129 | function GetUnitAtSquare(square) { 130 | if (square.y == -2) { return this.shop[square.x]; } 131 | if (square.y == -1) { return this.bench[square.x]; } 132 | return this.board[square.x][square.y]; 133 | } 134 | 135 | /** Sets the unit that is at a square - be careful, overwrites whatever is there already */ 136 | function SetUnitAtSquare(square, unit) { 137 | if (square.y == -2) { 138 | this.shop[square.x] = unit; 139 | return; 140 | } 141 | if (square.y == -1) { 142 | this.bench[square.x] = unit; 143 | return; 144 | } 145 | this.board[square.x][square.y] = unit; 146 | } 147 | 148 | /** Moves a unit to a square, swapping with whatever is on there */ 149 | function MoveUnitToSquare(unit, square, noAnim=false) { 150 | if (unit == null) { 151 | Log("[BoardUI] -- Attempted to move null piece!"); 152 | return; 153 | } 154 | local fromSquare = unit.position; 155 | local unitAtTarget = this.GetUnitAtSquare(square); 156 | this.SetUnitAtSquare(fromSquare, unitAtTarget); 157 | this.SetUnitAtSquare(square, unit); 158 | unit.MoveToSquare(square, noAnim); 159 | if (unitAtTarget != null) { 160 | unitAtTarget.MoveToSquare(fromSquare, noAnim); 161 | } 162 | } 163 | } -------------------------------------------------------------------------------- /autochess/cursors.nut: -------------------------------------------------------------------------------- 1 | /** 2 | * Dynamically adds logic_measure_movements to map to track what each player is looking at 3 | * Exposes: 4 | * - FindCursorOfPlayer(player) 5 | * Each cursor instance exposes: 6 | * - GetAngles() 7 | * - GetLookingAt() 8 | */ 9 | 10 | DoIncludeScript("lib/debug.nut", null); 11 | DoIncludeScript("lib/events.nut", null); 12 | DoIncludeScript("lib/math.nut", null); 13 | DoIncludeScript("lib/players.nut", null); 14 | DoIncludeScript("lib/timers.nut", null); 15 | 16 | /** 17 | * Gets the cursor that is bound to a particular player entity 18 | * If no cursor is bound, returns null 19 | */ 20 | ::FindCursorOfPlayer <- function(player) { 21 | if (player == null) { return null; } 22 | if (!player.ValidateScriptScope()) { return null; } 23 | local scope = player.GetScriptScope(); 24 | if ("cursor" in scope) { 25 | return scope.cursor; 26 | } 27 | return ::AssignCursorToPlayer(player); 28 | }; 29 | 30 | /** 31 | * Assigns a cursor to a player - automatically done when it's spawned or when a cursor is requested for a player 32 | */ 33 | ::AssignCursorToPlayer <- function(ply) { 34 | if (ply == null) { return; } 35 | if (!ply.ValidateScriptScope()) { return; } 36 | local scope = ply.GetScriptScope(); 37 | if ("cursor" in scope) { 38 | scope.cursor.Regenerate(); 39 | return; 40 | } 41 | 42 | scope.cursor <- PlayerCursor(ply); 43 | }; 44 | 45 | class PlayerCursor { 46 | eMeasureMovement = null; 47 | eRef = null; 48 | refName = null; 49 | eTarget = null; 50 | targetName = null; 51 | 52 | ePlayer = null; 53 | tmpPlayerName = null; 54 | oldPlayerName = null; 55 | 56 | isAwaitingBind = false; 57 | 58 | constructor(player) { 59 | printl("[Cursor] Generating cursor for "+player); 60 | this.ePlayer = player; 61 | this.Regenerate(); 62 | } 63 | 64 | function Regenerate() { 65 | this.Destroy(); 66 | 67 | this.eMeasureMovement = Entities.CreateByClassname("logic_measure_movement"); 68 | EntFireByHandle(this.eMeasureMovement, "AddOutput", "MeasureType 1", 0.0, null, null); 69 | this.eRef = Entities.CreateByClassname("info_target"); 70 | this.eTarget = Entities.CreateByClassname("info_target"); 71 | local origin = Vector(0, 0, 0); 72 | this.eRef.SetOrigin(origin); 73 | this.eTarget.SetOrigin(origin); 74 | 75 | // measure_movement uses targetnames - we create some here 76 | oldPlayerName = this.ePlayer.GetName(); 77 | tmpPlayerName = UniqueString("cursor_player"); 78 | refName = UniqueString("cursor_ref"); 79 | targetName = UniqueString("cursor_target"); 80 | EntFireByHandle(this.ePlayer, "AddOutput", "targetname "+tmpPlayerName, 0.0, null, null); 81 | EntFireByHandle(this.eRef, "AddOutput", "targetname "+refName, 0.0, null, null); 82 | EntFireByHandle(this.eTarget, "AddOutput", "targetname "+targetName, 0.0, null, null); 83 | 84 | // this is required for measure_movement to work - don't ask me why 85 | this.eMeasureMovement.__KeyValueFromString("MeasureReference", refName); 86 | this.eMeasureMovement.__KeyValueFromString("MeasureTarget", refName); 87 | this.eMeasureMovement.__KeyValueFromString("Target", targetName); 88 | this.eMeasureMovement.__KeyValueFromString("TargetReference", refName); 89 | 90 | // we actually tell the measure_movement about our entities in the next frame 91 | this.isAwaitingBind = true; 92 | } 93 | 94 | function BindEntities() { 95 | Log("[Cursor] Binding cursor { ply="+ePlayer+", ref="+eRef+", target="+eTarget+" }"); 96 | 97 | EntFireByHandle(this.eMeasureMovement, "SetMeasureReference", refName, 0.0, null, null); 98 | EntFireByHandle(this.eMeasureMovement, "SetTargetReference", refName, 0.0, null, null); 99 | EntFireByHandle(this.eMeasureMovement, "Target", targetName, 0.0, null, null); 100 | EntFireByHandle(this.eMeasureMovement, "SetMeasureTarget", ePlayer.GetName(), 0.0, null, null); 101 | EntFireByHandle(this.eMeasureMovement, "Target", targetName, 0.0, null, null); 102 | EntFireByHandle(this.eMeasureMovement, "Enable", "", 0.0, null, null); 103 | 104 | this.isAwaitingBind = false; 105 | } 106 | 107 | function Destroy() { 108 | if (this.eMeasureMovement != null && this.eMeasureMovement.IsValid()) { 109 | this.eMeasureMovement.Destroy(); 110 | this.eMeasureMovement = null; 111 | } 112 | if (this.eRef != null && this.eRef.IsValid()) { 113 | this.eRef.Destroy(); 114 | this.eRef = null; 115 | } 116 | if (this.eTarget != null && this.eTarget.IsValid()) { 117 | this.eTarget.Destroy(); 118 | this.eTarget = null; 119 | } 120 | } 121 | 122 | function GetAngles() { 123 | return this.eTarget.GetForwardVector(); 124 | } 125 | 126 | function GetLookingAt() { 127 | return ::TraceInDirection(this.ePlayer.EyePosition(), this.GetAngles(), this.ePlayer); 128 | } 129 | } 130 | 131 | if (!("_LOADED_MODULE_CURSORS" in getroottable())) { 132 | ::_LOADED_MODULE_CURSORS <- true; 133 | 134 | ::_cursors_Think <- function() { 135 | local players = ::Players.GetPlayers(); 136 | foreach (ply in players) { 137 | local cursor = ::FindCursorOfPlayer(ply); 138 | if (cursor != null && cursor.isAwaitingBind) { 139 | cursor.BindEntities(); 140 | } 141 | } 142 | }; 143 | 144 | ::AddEventListener("player_spawn", function(data) { 145 | local ply = ::Players.FindByUserid(data.userid); 146 | ::AssignCursorToPlayer(ply); 147 | }); 148 | } -------------------------------------------------------------------------------- /autochess/gamemode.nut: -------------------------------------------------------------------------------- 1 | /** 2 | * Entrypoint for the Auto Chess gamemode 3 | * Handles assigning boards to users, handling inputs and precaching/delegating think functions 4 | */ 5 | DoIncludeScript("lib/debug.nut", null); 6 | DoIncludeScript("lib/precache.nut", null); 7 | DoIncludeScript("autochess/cursors.nut", null); 8 | DoIncludeScript("autochess/board-ui.nut", null); 9 | 10 | ::BOARD_POSITIONS <- [ // update this to match the position of the boards in your map 11 | Vector(0, 0, 16) 12 | ]; 13 | 14 | RegisterPrecacheSound("Autochess.Clock"); 15 | RegisterPrecacheSound("AutoChess.SelectUnit"); 16 | RegisterPrecacheSound("AutoChess.SwapUnits"); 17 | 18 | function Precache() { 19 | PerformPrecache(self); 20 | } 21 | 22 | function Think() { 23 | local root = getroottable(); 24 | if ("Players" in root) { 25 | ::Players.Think(); 26 | } 27 | if ("_LOADED_MODULE_TIMER" in root) { 28 | ::_timer_Think(); 29 | } 30 | if ("_LOADED_MODULE_CURSORS" in root) { 31 | ::_cursors_Think(); 32 | } 33 | if ("gamemode_autochess" in root) { 34 | ::gamemode_autochess.Think(); 35 | } 36 | } 37 | 38 | function OnAttack1() { 39 | local cursor = ::FindCursorOfPlayer(activator); 40 | if (cursor == null) { return; } 41 | local board = ::gamemode_autochess.FindBoardOfPlayer(activator); 42 | if (board == null) { return; } 43 | 44 | board.OnClicked(cursor.GetLookingAt()); 45 | } 46 | 47 | class GamemodeAutoChess { 48 | assigned_boards = null; 49 | free_board_positions = null; 50 | 51 | startTime = null; 52 | roundTime = null; 53 | isLive = false; 54 | 55 | constructor() { 56 | this.assigned_boards = {}; 57 | this.free_board_positions = []; 58 | foreach (position in ::BOARD_POSITIONS) { 59 | this.free_board_positions.push(position); 60 | } 61 | 62 | this.OnRoundStart(); 63 | } 64 | 65 | /** Get the time of the current round, negative if in preparation and positive if is live */ 66 | function GetRoundTime() { 67 | return Time() - this.roundTime; 68 | } 69 | 70 | function OnRoundStart() { 71 | this.startTime = Time(); 72 | this.roundTime = this.startTime + 10; 73 | this.isLive = false; 74 | 75 | foreach (board in this.assigned_boards) { 76 | board.OnRoundStart(); 77 | } 78 | } 79 | 80 | function Think() { 81 | if (!this.isLive && this.GetRoundTime() >= 0) { 82 | this.isLive = true; 83 | } 84 | 85 | foreach (board in this.assigned_boards) { 86 | board.Think(); 87 | } 88 | } 89 | 90 | /** 91 | * Gets the board that is bound to a particular player entity 92 | * If no board is bound, returns null 93 | */ 94 | function FindBoardOfPlayer(player) { 95 | if (player == null) { return null; } 96 | if (!player.ValidateScriptScope()) { return null; } 97 | local scope = player.GetScriptScope(); 98 | if (!("userid" in scope)) { 99 | Log("[AutoChess] Tried to find board of player "+player+" that has no userid"); 100 | return null; 101 | } 102 | local userid = scope.userid; 103 | if (!(userid in this.assigned_boards)) { 104 | Log("[AutoChess] Tried to find board of player "+player+" that has no board"); 105 | return null; 106 | } 107 | return this.assigned_boards[userid]; 108 | }; 109 | 110 | /** 111 | * Assigns a free board to a player and returns it 112 | * If no free board is available, returns null 113 | * If player already has a board, it is returned without assigning a new one 114 | */ 115 | function AssignBoardToUserid(userid) { 116 | if (this.free_board_positions.len() == 0) { return null; } 117 | Log("[AutoChess] Assigning board to "+userid); 118 | if (userid in this.assigned_boards) { 119 | Log("[AutoChess] Player already has a board"); 120 | return this.assigned_boards[userid]; 121 | } 122 | 123 | local boardPosition = this.free_board_positions.pop(); 124 | this.assigned_boards[userid] <- BoardUI(userid, boardPosition); 125 | return this.assigned_boards[userid]; 126 | } 127 | 128 | /** 129 | * Removes a board from a player, returning it to the pool of free boards 130 | * If player doesn't have a board, nothing is done 131 | */ 132 | function RemoveBoardFromUserid(userid) { 133 | Log("[AutoChess] Removing board from "+userid); 134 | if (!(userid in this.assigned_boards)) { 135 | Log("[AutoChess] Player "+userid+" has no board"); 136 | return; 137 | } 138 | local board = this.assigned_boards[userid]; 139 | this.free_board_positions.push(board.origin); 140 | delete this.assigned_boards[userid]; 141 | } 142 | } 143 | 144 | if (!("_LOADED_GAMEMODE_AUTOCHESS" in getroottable())) { 145 | ::_LOADED_GAMEMODE_AUTOCHESS <- true; 146 | ::gamemode_autochess <- GamemodeAutoChess(); 147 | 148 | ::AddEventListener("player_spawn", function(data) { 149 | local player = ::Players.FindByUserid(data.userid); 150 | if (player == null) { return; } 151 | local board = ::gamemode_autochess.AssignBoardToUserid(data.userid); 152 | Log("[AutoChess] Assigned board "+board+" to "+player+" ("+data.userid+")"); 153 | }); 154 | } else { 155 | ::gamemode_autochess.OnRoundStart(); 156 | } -------------------------------------------------------------------------------- /autochess/sounds.txt: -------------------------------------------------------------------------------- 1 | AutoChess.Clock { 2 | wave autochess/clock_low.wav 3 | } 4 | 5 | AutoChess.ShopOpen { 6 | wave autochess/shop_available.wav 7 | } 8 | 9 | AutoChess.ShopClosed { 10 | wave autochess/shop_unavailable.wav 11 | } 12 | 13 | AutoChess.SelectUnit { 14 | volume 0.5 15 | wave autochess/select_unit_01.wav 16 | } 17 | 18 | AutoChess.SwapUnits { 19 | wave autochess/swap_unit_01.wav 20 | } 21 | 22 | AutoChess.Knife { 23 | volume 0.1 24 | wave weapons/knife/knife_hit1.wav 25 | } -------------------------------------------------------------------------------- /autochess/units/base.nut: -------------------------------------------------------------------------------- 1 | DoIncludeScript("lib/debug.nut", null); 2 | DoIncludeScript("lib/timers.nut", null); 3 | DoIncludeScript("lib/math.nut", null); 4 | DoIncludeScript("lib/precache.nut", null); 5 | DoIncludeScript("autochess/a-star.nut", null); 6 | 7 | RegisterPrecacheModel("models/player/ctm_fbi.mdl"); 8 | RegisterPrecacheModel("models/weapons/w_snip_awp.mdl"); 9 | RegisterPrecacheSound("AutoChess.Knife"); 10 | 11 | class BaseUnit { 12 | eModel = null; 13 | eWeapon = null; 14 | position = Vector(0, 0, 0); 15 | board = null; 16 | 17 | moveTime = null; // time at which we should move again 18 | moveFrom = null; // Vector we are moving from 19 | moveTo = null; // Vector we are moving to 20 | moveStart = null; // time at which we started moving 21 | moveTimer = null; // TimerHandler we use for animating the move 22 | 23 | attackTime = null; 24 | 25 | friendly = false; 26 | target = null; 27 | hp = 0; 28 | 29 | MODEL_NAME = "models/player/ctm_fbi.mdl"; 30 | MOVE_DURATION = 0.75; // time it takes to move from one space to another 31 | MAX_HP = 100; 32 | ATTACK_RANGE = 1; // distance at which we can attack 33 | ATTACK_DELAY = 0.5; // seconds between each attack 34 | ATTACK_DMG = 1; // amount of damage we deal to the enemy 35 | 36 | constructor(brd, isFriend) { 37 | this.board = brd; 38 | this.friendly = isFriend; 39 | this.hp = this.MAX_HP; 40 | 41 | this.GenerateModel(); 42 | if (this.friendly) { 43 | this.eModel.SetAngles(0, 90, 0); 44 | } else { 45 | this.eModel.SetAngles(0, -90, 0); 46 | } 47 | } 48 | 49 | function GenerateModel() { 50 | local modelName = UniqueString(); 51 | this.eModel = CreateProp("prop_dynamic", Vector(0, 0, 0), this.MODEL_NAME, 0); 52 | EntFireByHandle(this.eModel, "AddOutput", "targetname "+modelName, 0.0, null, null); 53 | this.eWeapon = CreateProp("prop_dynamic", Vector(0, 0, 64), "models/weapons/w_snip_awp.mdl", 0); 54 | EntFireByHandle(this.eWeapon, "SetParent", modelName, 0.1, null, null); 55 | EntFireByHandle(this.eWeapon, "SetParentAttachment", "weapon_hand_R", 0.2, null, null); 56 | } 57 | 58 | /** Called when we are live and should do something - gets targets, attacks and moves */ 59 | function Think() { 60 | if (this.target == null) { 61 | this.AcquireTarget(); 62 | } 63 | 64 | if (this.target) { 65 | local distance = this.board.GetDistance(this.position, this.target.position); 66 | if (distance <= this.ATTACK_RANGE) { 67 | if (this.moveStart == null) { 68 | if (this.attackTime == null) { this.attackTime = Time(); } 69 | if (this.attackTime < Time()) { 70 | this.attackTime = Time() + this.ATTACK_DELAY; 71 | this.PerformAttack(); 72 | } 73 | } 74 | } else { 75 | if (this.moveTime == null) { this.moveTime = Time(); } 76 | if (this.moveTime < Time()) { 77 | this.moveTime = Time() + this.MOVE_DURATION; 78 | this.PerformMove(); 79 | } 80 | } 81 | } 82 | } 83 | 84 | /** Calculates which move we want to do and executes it */ 85 | function PerformMove() { 86 | local targetSquare = this.GetMove(); 87 | if (targetSquare == null) { return; } 88 | local ourPos = this.board.parentUI.GetPositionOfSquare(this.position); 89 | local theirPos = this.board.parentUI.GetPositionOfSquare(this.target.position); 90 | /*::DrawLine( 91 | ourPos + Vector(0, 0, 12), 92 | theirPos + Vector(0, 0, 12), 93 | Vector(255, 0, 0), 94 | 1 95 | );*/ 96 | local angles = AngleBetweenPoints(ourPos, theirPos); 97 | this.eModel.SetAngles(angles.x, angles.y, angles.z + 8); 98 | EntFireByHandle(this.eModel, "SetPlaybackRate", ""+(1 / this.MOVE_DURATION), 0.0, null, null); 99 | EntFireByHandle(this.eModel, "SetAnimation", "Hop", 0.0, null, null); 100 | this.board.MoveUnitToSquare(this, targetSquare); 101 | } 102 | 103 | /** Performs an attack towards our target */ 104 | function PerformAttack() { 105 | Log("[BaseUnit] Dealing "+this.ATTACK_DMG+" to target"); 106 | this.target.SetHealth(this.target.hp - this.ATTACK_DMG); 107 | local ourPos = this.board.parentUI.GetPositionOfSquare(this.position); 108 | local theirPos = this.board.parentUI.GetPositionOfSquare(this.target.position); 109 | local angles = AngleBetweenPoints(ourPos, theirPos); 110 | this.eModel.SetAngles(angles.x, angles.y + 90, angles.z); 111 | this.eModel.SetOrigin(this.board.parentUI.GetPositionOfSquare(this.position) + Vector(0, 0, 8)); 112 | this.eModel.EmitSound("AutoChess.Knife"); 113 | EntFireByHandle(this.eModel, "SetPlaybackRate", "1.0", 0.0, null, null); 114 | EntFireByHandle(this.eModel, "SetAnimation", "Reload_AWP", 0.0, null, null); 115 | } 116 | 117 | /** Performs the animation and logic for dying */ 118 | function PerformDeath() { 119 | Log("[BaseUnit] Died :("); 120 | } 121 | 122 | function AnimThink() { 123 | if (this.moveStart != null) { 124 | local progress = (Time() - this.moveStart) / this.MOVE_DURATION; 125 | local pos = null; 126 | if (progress >= 1) { 127 | this.moveStart = null; 128 | this.moveTimer.Destroy(); 129 | this.moveTimer = null; 130 | EntFireByHandle(this.eModel, "SetAnimation", "testIdle", 0.0, null, null); 131 | pos = this.moveTo; 132 | } else { 133 | local delta = (this.moveFrom - this.moveTo) * (1 - progress); 134 | pos = this.moveTo + delta + Vector(0, 0, 8); 135 | } 136 | this.eModel.SetOrigin(pos); 137 | } 138 | } 139 | 140 | /** Get the square we want to move to */ 141 | function GetMove() { 142 | if (this.target == null) { return null; } 143 | 144 | // get path to enemy 145 | local path = ::GeneratePath(this.board.board, this.position, this.target.position); 146 | /*for (local i = 0; i < path.len() - 1; i++) { 147 | ::DrawLine( 148 | this.board.parentUI.GetPositionOfSquare(path[i]) + Vector(0, 0, 8), 149 | this.board.parentUI.GetPositionOfSquare(path[i+1]) + Vector(0, 0, 8), 150 | Vector(255,180,200), 151 | 1 152 | ); 153 | }*/ 154 | 155 | // if we have only one move, that would put us on top of the enemy - in those cases don't move 156 | if (path.len() < 3) { 157 | return null; 158 | } 159 | 160 | // return the next step 161 | return path[path.len() - 2]; 162 | } 163 | 164 | function AcquireTarget() { 165 | // Log("[BaseUnit] Finding target for "+this); 166 | this.target = this.board.FindEnemyClosestTo(this.position, this.friendly); 167 | if (this.target == null) { 168 | Log("[BaseUnit] Unable to find target for "+this); 169 | return; 170 | } 171 | /*::DrawLine( 172 | this.board.parentUI.GetPositionOfSquare(this.position) + Vector(0, 0, 8), 173 | this.board.parentUI.GetPositionOfSquare(this.target.position) + Vector(0, 0, 8), 174 | Vector(0,0,255), 175 | 10 176 | );*/ 177 | } 178 | 179 | function MoveToSquare(square, noAnim=false) { 180 | if (this.moveTimer != null) { 181 | this.moveTimer.Destroy(); 182 | this.moveTimer = null; 183 | } 184 | if (!noAnim) { 185 | this.moveFrom = this.board.parentUI.GetPositionOfSquare(this.position); 186 | this.moveTo = this.board.parentUI.GetPositionOfSquare(square); 187 | this.moveStart = Time(); 188 | this.moveTimer = TimerHandler(0.01, this.AnimThink.bindenv(this)); 189 | } else { 190 | this.eModel.SetOrigin(this.board.parentUI.GetPositionOfSquare(square)); 191 | } 192 | this.position = square; 193 | } 194 | 195 | function SetHealth(health) { 196 | local color = (health / this.MAX_HP) * 255; 197 | local colStr = color + " 0"; 198 | if (this.friendly) { 199 | colStr = "0 "+colStr; 200 | } else { 201 | colStr = colStr+" 0"; 202 | } 203 | this.hp = health; 204 | // EntFireByHandle(this.eModel, "Color", colStr, 0.0, null, null); 205 | 206 | if (this.hp <= 0) { 207 | this.PerformDeath(); 208 | } 209 | } 210 | 211 | function Destroy() { 212 | if (this.eModel != null && this.eModel.IsValid()) { 213 | this.eModel.Destroy(); 214 | } 215 | } 216 | } -------------------------------------------------------------------------------- /lib/chat.nut: -------------------------------------------------------------------------------- 1 | // https://github.com/birjolaxew/csgo-vscripts 2 | // == Chat messages 3 | // Handles displaying stuff to players 4 | // You can colorize your messages using " {green}test{yellow}ing{blue} this{red} thing" 5 | // (remember an empty space at the start or colors won't work - thanks Valve!) 6 | // Exposes: 7 | // ::COLORS{} 8 | // ::ChatMessageAll(msg) 9 | // ::ChatMessageCT(msg) 10 | // ::ChatMessageT(msg) 11 | 12 | DoIncludeScript("lib/players.nut", null); 13 | DoIncludeScript("lib/polyfills.nut", null); 14 | 15 | ::COLORS <- { 16 | invisible = "\x00" 17 | white = "\x01" 18 | dark_red = "\x02" 19 | purple = "\x03" 20 | green = "\x04" 21 | light_green = "\x05" 22 | lime_green = "\x06" 23 | red = "\x07" 24 | grey = "\x08" 25 | yellow = "\x09" 26 | light_blue = "\x0a" 27 | blue = "\x0b" 28 | dark_blue = "\x0c" 29 | light_blue2 = "\x0d" 30 | pink = "\x0e" 31 | light_red = "\x0f" 32 | orange = "\x10" 33 | }; 34 | 35 | ::_chat_colorRegexp <- regexp("{([^}]+)}"); 36 | ::_chat_format <- function(msg) { 37 | local i = 0; 38 | local result = null; 39 | local start = 0; 40 | while ((result = ::_chat_colorRegexp.capture(msg, start)) != null && i < 100) { 41 | local color = msg.slice(result[1].begin, result[1].end); 42 | if (color in ::COLORS) { 43 | msg = msg.slice(0,result[0].begin) + ::COLORS[color] + msg.slice(result[0].end); 44 | start = result[0].begin + ::COLORS[color].len(); 45 | } else { 46 | start = result[0].end; 47 | } 48 | ++i; 49 | } 50 | return msg; 51 | } 52 | ::ChatMessageAll <- function(msg) { 53 | local msgs = split(msg, "\n"); 54 | foreach(msg in msgs) { 55 | ScriptPrintMessageChatAll(::_chat_format(msg)); 56 | } 57 | } 58 | ::ChatMessageCT <- function(msg) { 59 | ScriptPrintMessageChatTeam(TEAM_CT, ::_chat_format(msg)); 60 | } 61 | ::ChatMessageT <- function(msg) { 62 | ScriptPrintMessageChatTeam(TEAM_T, ::_chat_format(msg)); 63 | } -------------------------------------------------------------------------------- /lib/commands.nut: -------------------------------------------------------------------------------- 1 | DoIncludeScript("lib/debug.nut", null); 2 | DoIncludeScript("lib/events.nut", null); 3 | DoIncludeScript("lib/polyfills.nut", null); 4 | 5 | ::_command_prefixes <- ["!", "/", "."]; 6 | ::_command_HandleChat <- function(msg, userid) { 7 | local prefix = msg.slice(0,1); 8 | if (::find_in_array(::_command_prefixes, prefix) == null) { 9 | return; 10 | } 11 | 12 | local cmds = split(strip(msg.slice(1)), " "); 13 | if (cmds.len() == 0) { return; } 14 | local cmd = cmds[0].tolower(); 15 | if (cmd in ::_command_registered) { 16 | if (cmds.len() > 1) { 17 | ::_command_registered[cmd](userid, cmds.slice(1)); 18 | } else { 19 | ::_command_registered[cmd](userid); 20 | } 21 | } 22 | }; 23 | 24 | ::RegisterCommand <- function(name, callback) { 25 | if (typeof name == "array") { 26 | foreach(n in name) { 27 | ::RegisterCommand(n, callback); 28 | } 29 | return; 30 | } 31 | 32 | if (name in ::_command_registered) { 33 | Warn("[commands] Attempting to register command '"+name+"', but is already registered"); 34 | return; 35 | } 36 | ::_command_registered[name] <- callback; 37 | } 38 | 39 | if (!("_LOADED_MODULE_COMMANDS" in getroottable())) { 40 | ::_LOADED_MODULE_COMMANDS <- true; 41 | ::_command_registered <- {}; 42 | ::AddEventListener("player_say", function(data){ 43 | ::_command_HandleChat(data.text, data.userid); 44 | }); 45 | } -------------------------------------------------------------------------------- /lib/cursors.nut: -------------------------------------------------------------------------------- 1 | /** 2 | * https://github.com/birjolaxew/csgo-vscripts 3 | * Dynamically adds logic_measure_movements to map to track what each player is looking at 4 | * You must call _cursors_Think() in your Think 5 | * If you want to listen to Attack1/Attack2, you need to add a point_template that spawns game_ui's 6 | * This point_template should have the name "_cursors_gameui_template" 7 | * It should spawn a game_ui with the flags you want (usually none) 8 | * Exposes: 9 | * - FindCursorOfPlayer(player) 10 | * Each cursor instance exposes: 11 | * - GetAngles() 12 | * - GetLookingAt() 13 | * - AddAttack1Listener(cb) 14 | * - RemoveAttack1Listener(cb) 15 | * - AddAttack2Listener(cb) 16 | * - RemoveAttack2Listener(cb) 17 | */ 18 | 19 | DoIncludeScript("lib/debug.nut", null); 20 | DoIncludeScript("lib/events.nut", null); 21 | DoIncludeScript("lib/math.nut", null); 22 | DoIncludeScript("lib/players.nut", null); 23 | DoIncludeScript("lib/timers.nut", null); 24 | DoIncludeScript("lib/polyfills.nut", null); 25 | 26 | /** 27 | * Gets the cursor that is bound to a particular player entity 28 | * If no cursor is bound, returns null 29 | */ 30 | ::FindCursorOfPlayer <- function(player) { 31 | if (player == null) { return null; } 32 | if (!player.ValidateScriptScope()) { return null; } 33 | local scope = player.GetScriptScope(); 34 | if ("cursor" in scope && scope.cursor.IsValid()) { 35 | return scope.cursor; 36 | } 37 | return ::AssignCursorToPlayer(player); 38 | }; 39 | 40 | /** 41 | * Assigns a cursor to a player - automatically done when it's spawned or when a cursor is requested for a player 42 | */ 43 | ::AssignCursorToPlayer <- function(ply) { 44 | if (ply == null) { return; } 45 | if (!ply.ValidateScriptScope()) { return; } 46 | local scope = ply.GetScriptScope(); 47 | if ("cursor" in scope) { 48 | scope.cursor.Regenerate(); 49 | return scope.cursor; 50 | } 51 | 52 | scope.cursor <- PlayerCursor(ply); 53 | return scope.cursor; 54 | }; 55 | 56 | class PlayerCursor { 57 | eMeasureMovement = null; 58 | eRef = null; 59 | refName = null; 60 | eTarget = null; 61 | targetName = null; 62 | 63 | eGameUI = null; 64 | attack1CbName = null; 65 | attack2CbName = null; 66 | attack1Listeners = []; 67 | attack2Listeners = []; 68 | 69 | ePlayer = null; 70 | tmpPlayerName = null; 71 | oldPlayerName = null; 72 | 73 | isAwaitingBind = false; 74 | 75 | constructor(player) { 76 | Log("[Cursor] Generating cursor for "+player); 77 | this.ePlayer = player; 78 | 79 | this.attack1CbName = "cursor_ui_cb_"+UniqueString(); 80 | this.attack2CbName = "cursor_ui_cb_"+UniqueString(); 81 | getroottable()[this.attack1CbName] <- this.OnAttack1.bindenv(this); 82 | getroottable()[this.attack2CbName] <- this.OnAttack2.bindenv(this); 83 | 84 | this.Regenerate(); 85 | } 86 | 87 | function IsValid() { 88 | return (this.eMeasureMovement && this.eRef && this.eTarget) 89 | && (this.eMeasureMovement.IsValid() && this.eRef.IsValid() && this.eTarget.IsValid()); 90 | } 91 | 92 | function Regenerate() { 93 | this.Destroy(); 94 | 95 | this.eMeasureMovement = Entities.CreateByClassname("logic_measure_movement"); 96 | EntFireByHandle(this.eMeasureMovement, "AddOutput", "MeasureType 1", 0.0, null, null); 97 | this.eRef = Entities.CreateByClassname("info_target"); 98 | this.eTarget = Entities.CreateByClassname("info_target"); 99 | local origin = this.ePlayer.GetOrigin(); 100 | this.eRef.SetOrigin(origin); 101 | this.eTarget.SetOrigin(origin); 102 | 103 | // create a game_ui for ourself, if a point_template exists 104 | local uiTemplate = Entities.FindByName(null, "_cursors_gameui_template"); 105 | if (uiTemplate) { 106 | this.BindUiTemplate(uiTemplate); 107 | ::_cursors_awaitingUi.push(this); 108 | EntFireByHandle(uiTemplate, "ForceSpawn", "", 0.0, null, null); 109 | } 110 | 111 | // measure_movement uses targetnames - we create some here 112 | oldPlayerName = this.ePlayer.GetName(); 113 | tmpPlayerName = UniqueString("cursor_player"); 114 | refName = UniqueString("cursor_ref"); 115 | targetName = UniqueString("cursor_target"); 116 | EntFireByHandle(this.ePlayer, "AddOutput", "targetname "+tmpPlayerName, 0.0, null, null); 117 | EntFireByHandle(this.eRef, "AddOutput", "targetname "+refName, 0.0, null, null); 118 | EntFireByHandle(this.eTarget, "AddOutput", "targetname "+targetName, 0.0, null, null); 119 | 120 | // this is required for measure_movement to work - don't ask me why 121 | this.eMeasureMovement.__KeyValueFromString("MeasureReference", refName); 122 | this.eMeasureMovement.__KeyValueFromString("MeasureTarget", refName); 123 | this.eMeasureMovement.__KeyValueFromString("Target", targetName); 124 | this.eMeasureMovement.__KeyValueFromString("TargetReference", refName); 125 | 126 | // we actually tell the measure_movement about our entities in the next frame 127 | this.isAwaitingBind = true; 128 | } 129 | 130 | function BindUiTemplate(eTemplate) { 131 | if (!eTemplate.ValidateScriptScope()) { return; } 132 | local scope = eTemplate.GetScriptScope(); 133 | if (!("PreSpawnInstance" in scope)) { 134 | // PreSpawnInstance must be called for PostSpawn to be called too 135 | scope.PreSpawnInstance <- function(entClass, entName) {}; 136 | } 137 | scope.PostSpawn <- function(ents) { 138 | foreach (handle in ents) { 139 | if (handle.GetClassname() == "game_ui") { 140 | ::_cursors_OnGameUI(handle); 141 | } 142 | } 143 | } 144 | } 145 | 146 | function BindEntities() { 147 | Log("[Cursor] Binding cursor { ply="+ePlayer+", ref="+eRef+", target="+eTarget+" }"); 148 | EntFireByHandle(this.eMeasureMovement, "SetMeasureReference", refName, 0.0, null, null); 149 | EntFireByHandle(this.eMeasureMovement, "SetTargetReference", refName, 0.0, null, null); 150 | EntFireByHandle(this.eMeasureMovement, "Target", targetName, 0.0, null, null); 151 | EntFireByHandle(this.eMeasureMovement, "SetMeasureTarget", ePlayer.GetName(), 0.0, null, null); 152 | EntFireByHandle(this.eMeasureMovement, "Target", targetName, 0.0, null, null); 153 | EntFireByHandle(this.eMeasureMovement, "Enable", "", 0.0, null, null); 154 | 155 | this.isAwaitingBind = false; 156 | } 157 | 158 | function OnGameUI(ui) { 159 | Log("[Cursors] Received game_ui"); 160 | this.eGameUI = ui; 161 | 162 | EntFireByHandle(this.eGameUI, "Activate", "", 0.0, this.ePlayer, this.ePlayer); 163 | EntFireByHandle(this.eGameUI, "AddOutput", "PressedAttack !self:RunScriptCode:"+this.attack1CbName+"():0:-1", 0.0, this.ePlayer, this.ePlayer); 164 | EntFireByHandle(this.eGameUI, "AddOutput", "PressedAttack2 !self:RunScriptCode:"+this.attack2CbName+"():0:-1", 0.0, this.ePlayer, this.ePlayer); 165 | } 166 | 167 | function Destroy() { 168 | if (this.eMeasureMovement != null && this.eMeasureMovement.IsValid()) { 169 | this.eMeasureMovement.Destroy(); 170 | this.eMeasureMovement = null; 171 | } 172 | if (this.eRef != null && this.eRef.IsValid()) { 173 | this.eRef.Destroy(); 174 | this.eRef = null; 175 | } 176 | if (this.eTarget != null && this.eTarget.IsValid()) { 177 | this.eTarget.Destroy(); 178 | this.eTarget = null; 179 | } 180 | if (this.eGameUI != null && this.eGameUI.IsValid()) { 181 | this.eGameUI.Destroy(); 182 | this.eGameUI = null; 183 | } 184 | } 185 | 186 | function GetAngles() { 187 | return this.eTarget.GetForwardVector(); 188 | } 189 | 190 | function GetLookingAt() { 191 | return ::TraceInDirection(this.ePlayer.EyePosition(), this.GetAngles(), this.ePlayer); 192 | } 193 | 194 | function AddAttack1Listener(cb) { 195 | this.attack1Listeners.push(cb); 196 | } 197 | function RemoveAttack1Listener(cb) { 198 | ::remove_elm_from_array(this.attack1Listeners, cb); 199 | } 200 | function OnAttack1() { 201 | foreach(cb in this.attack1Listeners) { 202 | cb(); 203 | } 204 | } 205 | 206 | function AddAttack2Listener(cb) { 207 | this.attack2Listeners.push(cb); 208 | } 209 | function RemoveAttack2Listener(cb) { 210 | ::remove_elm_from_array(this.attack2Listeners, cb); 211 | } 212 | function OnAttack2() { 213 | foreach(cb in this.attack2Listeners) { 214 | cb(); 215 | } 216 | } 217 | } 218 | 219 | ::_cursors_awaitingUi <- []; 220 | ::_cursors_OnGameUI <- function(ui) { 221 | if (::_cursors_awaitingUi.len() > 0) { 222 | local target = _cursors_awaitingUi[0]; 223 | ::remove_elm_from_array(_cursors_awaitingUi, target); 224 | target.OnGameUI(ui); 225 | } else { 226 | Warn("Got game_ui but has no cursor ready to accept it; something went wrong here"); 227 | } 228 | } 229 | 230 | if (!("_LOADED_MODULE_CURSORS" in getroottable())) { 231 | ::_LOADED_MODULE_CURSORS <- true; 232 | 233 | ::_cursors_Think <- function() { 234 | local players = ::Players.GetPlayers(); 235 | foreach (ply in players) { 236 | local cursor = ::FindCursorOfPlayer(ply); 237 | if (cursor != null && cursor.isAwaitingBind) { 238 | cursor.BindEntities(); 239 | } 240 | } 241 | }; 242 | 243 | ::AddEventListener("player_spawn", function(data) { 244 | local ply = ::Players.FindByUserid(data.userid); 245 | ::AssignCursorToPlayer(ply); 246 | }); 247 | } -------------------------------------------------------------------------------- /lib/debug.nut: -------------------------------------------------------------------------------- 1 | ::IsDebug <- function() { 2 | return GetDeveloperLevel() > 0; 3 | } 4 | 5 | ::Log <- function(msg) { 6 | if (IsDebug()) { 7 | printl(msg); 8 | } 9 | } 10 | 11 | ::Warn <- function(msg) { 12 | Log("[warn] "+msg); 13 | } 14 | 15 | ::PrintTable <- function(tbl, indent="", printfunc=::Log) { 16 | if (tbl == null) { 17 | printfunc("null"); 18 | return; 19 | } 20 | printfunc(indent + "table {"); 21 | foreach (key,val in tbl) { 22 | local v = val; 23 | if (val == null) { 24 | v = "null"; 25 | } else { 26 | v = val.tostring(); 27 | } 28 | printfunc(indent + " "+key+": "+v); 29 | } 30 | printfunc(indent + "}"); 31 | } 32 | 33 | ::DrawBox <- function(origin, size=Vector(16,16,16), color=Vector(255,0,0), duration=5) { 34 | DebugDrawBox( 35 | origin, 36 | size * -0.5, 37 | size * 0.5, 38 | color.x, 39 | color.y, 40 | color.z, 41 | 0, 42 | duration 43 | ); 44 | } 45 | 46 | ::DrawLine <- function(start, end, color=Vector(255,0,0), duration=5) { 47 | DebugDrawLine( 48 | start, 49 | end, 50 | color.x, 51 | color.y, 52 | color.z, 53 | true, 54 | duration 55 | ); 56 | } 57 | 58 | ::DrawCursor <- function(pos, duration=0.1) { 59 | DrawLine(pos, pos + Vector(0, 0, 16), Vector(0, 0, 255), duration); 60 | DrawLine(pos, pos + Vector(0, 16, 0), Vector(0, 255, 0), duration); 61 | DrawLine(pos, pos + Vector(16, 0, 0), Vector(255, 0, 0), duration); 62 | } -------------------------------------------------------------------------------- /lib/events.nut: -------------------------------------------------------------------------------- 1 | /** 2 | * https://github.com/birjolaxew/csgo-vscripts 3 | * == Event listener 4 | * Listen for in-game events 5 | * The callback functions are called directly with the event data 6 | * In order for this to work, you must manually add logic_eventlistener to your world 7 | * This logic_eventlistener should have the following keyvalues: 8 | * - EventName the_event_you_want_to_listen_to 9 | * - FetchEventData 1 10 | * It should also have the following output: 11 | * - OnEventFired name_of_your_logic_eventlistener RunScriptCode ::TriggerEvent(THE_EVENT_YOU_WANT_TO_LISTEN_TO) 12 | * Note that !self does *not* work for name_of_your_logic_eventlistener. 13 | * 14 | * SPECIAL CASE: If an event is triggered twice in the same frame, the data will be that of the last trigger 15 | * Notably this happens on multikills (killing two players with one bullet) and player_death event. 16 | * For that case, create a trigger_brush in your map with the name "game_playerdie" 17 | * This will trigger the event "game_playerdie" with the victim ent as the only parameter 18 | * 19 | * Exposes: 20 | * ::AddEventListener("event-name", function(){}) 21 | * ::RemoveEventListener("event-name", function(){}) 22 | * ::TriggerEvent("event-name") 23 | */ 24 | 25 | ::ITEM_EQUIP <- "item_equip"; 26 | ::PLAYER_USE <- "player_use"; 27 | ::PLAYER_SAY <- "player_say"; 28 | ::PLAYER_DEATH <- "player_death"; 29 | ::PLAYER_CONNECT <- "player_connect"; 30 | ::PLAYER_CONNECT_FULL <- "player_connect_full"; 31 | ::PLAYER_CONNECT_CLIENT <- "player_connect_client"; 32 | ::PLAYER_DISCONNECT <- "player_disconnect"; 33 | ::PLAYER_CHANGENAME <- "player_changename"; 34 | ::PLAYER_TEAM <- "player_team"; 35 | ::ROUND_START <- "round_start"; 36 | ::ROUND_FREEZE_END <- "round_freeze_end"; 37 | ::PLAYER_SPAWN <- "player_spawn"; 38 | ::PLAYER_HURT <- "player_hurt"; 39 | ::BOT_TAKEOVER <- "bot_takeover"; 40 | ::INSPECT_WEAPON <- "inspect_weapon"; 41 | ::ROUND_END <- "round_end"; 42 | ::HOSTAGE_FOLLOWS <- "hostage_follows"; 43 | ::HOSTAGE_STOPS_FOLLOWING <- "hostage_stops_following"; 44 | ::SERVER_CVAR <- "server_cvar"; 45 | ::WEAPON_FIRE <- "weapon_fire"; 46 | 47 | DoIncludeScript("lib/debug.nut",null); 48 | 49 | if (!("_eventsScope" in getroottable())) { 50 | // each event is an array of listeners 51 | ::_eventsScope <- {}; 52 | ::_eventsScope.listeners <- {}; 53 | 54 | ::AddEventListener <- function(name, cb) { 55 | Log("[Events] Adding event listener for " + name); 56 | if (!(name in ::_eventsScope.listeners)) { 57 | ::_eventsScope.listeners[name] <- []; 58 | } 59 | ::_eventsScope.listeners[name].push(cb); 60 | }; 61 | ::RemoveEventListener <- function(name, cb) { 62 | if (name in ::_eventsScope.listeners) { 63 | local ind = ::_eventsScope.listeners[name].find(cb); 64 | if (ind != null) { 65 | ::_eventsScope.listeners[name].remove(ind); 66 | } 67 | } 68 | }; 69 | ::TriggerEvent <- function(name, data=null) { 70 | Log("[Events] Triggering event for " + name); 71 | local listener = Entities.FindByName(null, name+"_listener"); 72 | local event_data = data; 73 | if (event_data == null && listener != null) { 74 | if (listener.ValidateScriptScope()) { 75 | event_data = listener.GetScriptScope().event_data; 76 | } 77 | } 78 | if (name in ::_eventsScope.listeners) { 79 | foreach (listener in ::_eventsScope.listeners[name]) { 80 | listener(event_data); 81 | } 82 | } 83 | } 84 | 85 | Log("[Events] Initialized"); 86 | } 87 | 88 | // bind game_playerdie (see special case) 89 | local ent = null; 90 | while ((ent = Entities.FindByName(ent, "game_playerdie")) != null) { 91 | Log("[Events] Found game_playerdie "+ent+", binding"); 92 | if (ent.ValidateScriptScope()) { 93 | local scope = ent.GetScriptScope(); 94 | scope.OnPlayerDie <- function() { 95 | ::TriggerEvent("game_playerdie", activator); 96 | }; 97 | ent.ConnectOutput("OnUse", "OnPlayerDie"); 98 | } 99 | } -------------------------------------------------------------------------------- /lib/math.nut: -------------------------------------------------------------------------------- 1 | // Trace in a direction, returning the point where the line hits terrain 2 | ::TraceInDirection <- function(start, direction, ignore = null) { 3 | direction.Norm(); 4 | local end = start + (direction * 10000); 5 | local progress = TraceLine(start, end, ignore); 6 | return start + (end - start) * progress; 7 | } 8 | 9 | /** Returns the angle an entity should have when at "from" to point towards "to" */ 10 | ::AngleBetweenPoints <- function(from, to) { 11 | // TODO: extend to support Z axis 12 | local dX = to.x - from.x; 13 | local dY = to.y - from.y; 14 | local rads = atan2(dY, dX); 15 | local degrees = (rads * 180) / PI; 16 | return Vector(0, degrees, 0); 17 | } 18 | 19 | /** Returns the normal vector of a point that is on a surface. Note that this is pretty expensive. 20 | * The point *must* be 1 unit offset away from the wall - otherwise you might get the inverse normal. 21 | * Precision indicates how precise we should be. 0 means round to 90 degrees, 1 to 45 degrees, 2 to 22.5 degrees, etc. */ 22 | const NORMAL_CHECK_LEN = 4; 23 | const NORMAL_OFFSET_DIST = 0.5; // prolly should be <= 0.5, but higher = better precision 24 | ::NormalOfSurface <- function(point, precision=0) { 25 | local min = { dir = null, len = 999 }; 26 | // check the 6 base directions first 27 | local count = 0; 28 | foreach (dir in [Vector(0, 0, 1), Vector(0, 0, -1), Vector(0, 1, 0), Vector(0, -1, 0), Vector(1, 0, 0), Vector(-1, 0, 0)]) { 29 | local start = point; 30 | local end = point + (dir * NORMAL_CHECK_LEN); 31 | local len = TraceLine(start, end, null); 32 | if (len < min.len) { 33 | min.dir = dir; 34 | min.len = len; 35 | } 36 | } 37 | 38 | // we want to find 3 points on the surface so we can generate a formula for the plane 39 | // we just move our direction vector a bit and hope that works ¯\_(ツ)_/¯ 40 | // better solution would be to implement vector rotation or spherical coordinates, but I'm lazy 41 | local dir1 = null; 42 | local dir2 = null; 43 | if (min.dir.y == 0 && min.dir.z == 0) { 44 | dir1 = min.dir + Vector(-min.dir.x * NORMAL_OFFSET_DIST, NORMAL_OFFSET_DIST, NORMAL_OFFSET_DIST); 45 | dir2 = min.dir + Vector(-min.dir.x * NORMAL_OFFSET_DIST, -NORMAL_OFFSET_DIST, NORMAL_OFFSET_DIST); 46 | } else if (min.dir.x == 0 && min.dir.z == 0) { 47 | dir1 = min.dir + Vector(NORMAL_OFFSET_DIST, -min.dir.y * NORMAL_OFFSET_DIST, NORMAL_OFFSET_DIST); 48 | dir2 = min.dir + Vector(-NORMAL_OFFSET_DIST, -min.dir.y * NORMAL_OFFSET_DIST, NORMAL_OFFSET_DIST); 49 | } else { 50 | dir1 = min.dir + Vector(NORMAL_OFFSET_DIST, NORMAL_OFFSET_DIST, -min.dir.z * NORMAL_OFFSET_DIST); 51 | dir2 = min.dir + Vector(-NORMAL_OFFSET_DIST, NORMAL_OFFSET_DIST, -min.dir.z * NORMAL_OFFSET_DIST); 52 | } 53 | local dir3 = min.dir; 54 | dir1.Norm(); dir2.Norm(); dir3.Norm(); 55 | local point1 = point + dir1 * TraceLine(point, point + dir1 * NORMAL_CHECK_LEN, null); 56 | local point2 = point + dir2 * TraceLine(point, point + dir2 * NORMAL_CHECK_LEN, null); 57 | local point3 = point + dir3 * TraceLine(point, point + dir3 * NORMAL_CHECK_LEN, null); 58 | 59 | local vec1 = point3 - point1; 60 | local vec2 = point3 - point2; 61 | local normal = vec1.Cross(vec2); 62 | normal.Norm(); 63 | 64 | // we might've gotten the normal that points into the surface - inverse it if so 65 | if ((min.dir - normal).LengthSqr() < (min.dir + normal).LengthSqr()) { 66 | normal *= -1; 67 | } 68 | 69 | return normal; 70 | } 71 | 72 | /** Gets the reflection of a (normalized) vector, given the normal of the surface */ 73 | ::GetReflection <- function(vec, normal) { 74 | return ( 75 | (normal * (2 * normal.Dot(vec))) - vec 76 | ) * -1; // TODO: figure out why we gotta * -1. I do not have the smahts :( 77 | }; 78 | 79 | /** Turns direction vectors into a relative vector that can then be applied to another vector later */ 80 | ::GetRelativeVector <- function(baseVec, otherVec, debugPos=null) { 81 | // first we generate the two axis' needed to define the other vec in relative spherical coordinates 82 | local tmpVec = Vector(0, 0, 1); 83 | if (baseVec.x == 0 && baseVec.y == 0) { 84 | tmpVec = Vector(1, 0, 0); 85 | } 86 | local xVec = baseVec.Cross(tmpVec); 87 | local yVec = baseVec.Cross(xVec); 88 | local zVec = Vector(baseVec.x, baseVec.y, baseVec.z); 89 | xVec.Norm(); 90 | yVec.Norm(); 91 | zVec.Norm(); 92 | 93 | if (debugPos) { 94 | DrawLine(debugPos, debugPos + xVec * 16, Vector(255, 0, 0), 5.0); 95 | DrawLine(debugPos, debugPos + yVec * 16, Vector(0, 255, 0), 5.0); 96 | DrawLine(debugPos, debugPos + zVec * 16, Vector(0, 0, 255), 5.0); 97 | } 98 | 99 | // then calculate the x/y/height components 100 | local x = otherVec.Dot(xVec); 101 | local y = otherVec.Dot(yVec); 102 | local z = otherVec.Dot(zVec); 103 | 104 | if (debugPos) { 105 | DrawLine(debugPos, debugPos + otherVec * 16, Vector(255, 0, 255), 5.0); 106 | DrawLine(debugPos, debugPos + Vector(x, y, z) * 16, Vector(255, 255, 0), 5.0); 107 | } 108 | 109 | return Vector(x, y, z); 110 | }; 111 | 112 | /** Applies a relative vector (from ::CreateRelativeVector) to another vector */ 113 | ::ApplyRelativeVector <- function(baseVec, relativeVec, debugPos=null) { 114 | // first we generate the two axis' needed to define the other vec in relative cylindrical coordinates 115 | local tmpVec = Vector(0, 0, 1); 116 | if (baseVec.x == 0 && baseVec.y == 0) { 117 | tmpVec = Vector(1, 0, 0); 118 | } 119 | local xVec = baseVec.Cross(tmpVec); 120 | local yVec = baseVec.Cross(xVec); 121 | local zVec = Vector(baseVec.x, baseVec.y, baseVec.z); 122 | xVec.Norm(); 123 | yVec.Norm(); 124 | zVec.Norm(); 125 | 126 | if (debugPos) { 127 | DrawLine(debugPos, debugPos + xVec * 16, Vector(255, 0, 0), 5.0); 128 | DrawLine(debugPos, debugPos + yVec * 16, Vector(0, 255, 0), 5.0); 129 | DrawLine(debugPos, debugPos + zVec * 16, Vector(0, 0, 255), 5.0); 130 | } 131 | 132 | // then apply the x/y/height components 133 | local outp = Vector(0,0,0) + (xVec * relativeVec.x) + (yVec * relativeVec.y) + (zVec * relativeVec.z); 134 | 135 | if (debugPos) { 136 | DrawLine(debugPos, debugPos + outp * 16, Vector(255, 255, 0), 5.0); 137 | } 138 | 139 | return outp; 140 | } -------------------------------------------------------------------------------- /lib/players.nut: -------------------------------------------------------------------------------- 1 | // https://github.com/birjolaxew/csgo-vscripts 2 | // == PlayerManager 3 | // Keeps track of players, allowing you to find players by team or userid 4 | // Note that in order for the Userid binding to work, you *must* have your VMF 5 | // setup such that we can attach a listener to each of the following (see events.nut): 6 | // - player_use (used for binding userids) 7 | // - player_connect (used for getting player names) 8 | // - player_changename (used for getting player names) 9 | // - player_team (used for getting player names) 10 | // 11 | // Exposes: 12 | // ::Players (instance of PlayerManager) 13 | 14 | ::TEAM_SPEC <- 1; 15 | ::TEAM_T <- 2; 16 | ::TEAM_CT <- 3; 17 | 18 | DoIncludeScript("lib/events.nut",null); 19 | DoIncludeScript("lib/debug.nut",null); 20 | 21 | // represents a single player, bound to a player entity 22 | class Player { 23 | ent = null; 24 | userid = null; 25 | name = null; 26 | bot = false; 27 | 28 | constructor(ply) { 29 | ent = ply; 30 | ply.ValidateScriptScope(); 31 | local scope = ply.GetScriptScope(); 32 | scope.player_instance <- this; 33 | } 34 | 35 | function SetUserid(usrid) { userid = usrid; } 36 | function GetUserid() { 37 | if (userid == null) { 38 | return -1; 39 | } 40 | return userid; 41 | } 42 | 43 | function SetDisplayName(nme) { name = nme; } 44 | function GetDisplayName() { 45 | if (name == null) { 46 | return "[Unknown]"; 47 | } 48 | return name; 49 | } 50 | 51 | function SetBot(bt) { bot = bt; } 52 | function IsBot() { 53 | return bot; 54 | } 55 | } 56 | 57 | class PlayerManager { 58 | eventProxy = null; 59 | eventProxy_boundPlayer = null; 60 | userIdMappings = {}; 61 | 62 | constructor() { 63 | GenerateEventProxy(); 64 | } 65 | 66 | function GenerateEventProxy() { 67 | // create an info_game_event_proxy - we use this to fake player_use events 68 | eventProxy = Entities.CreateByClassname("info_game_event_proxy"); 69 | eventProxy.__KeyValueFromString("event_name", "player_use"); 70 | eventProxy.__KeyValueFromInt("range", 0); 71 | eventProxy_boundPlayer = null; 72 | 73 | // reset everything so that we don't have lingering outputs giving us false userids 74 | local ent = Entities.First();; 75 | while (ent != null) { 76 | if (ent.GetClassname() == "player" && ent.IsValid() && ent.ValidateScriptScope()) { 77 | local scope = ent.GetScriptScope(); 78 | if ("userid" in scope) { 79 | delete scope.userid; 80 | } 81 | if ("generating_userid" in scope) { 82 | delete scope.generating_userid; 83 | } 84 | } 85 | ent = Entities.Next(ent); 86 | } 87 | } 88 | 89 | function FindByUserid(userid) { 90 | local ent = Entities.First(); 91 | while (ent != null) { 92 | if (ent.GetClassname() == "player" && ent.ValidateScriptScope()) { 93 | local scope = ent.GetScriptScope(); 94 | if (("userid" in scope) && scope.userid == userid) { 95 | return ent; 96 | } 97 | } 98 | ent = Entities.Next(ent); 99 | } 100 | return null; 101 | } 102 | 103 | function FindInstanceByEntity(ent) { 104 | if (ent == null) { return null; } 105 | if (!ent.ValidateScriptScope()) { return null; } 106 | local scope = ent.GetScriptScope(); 107 | if (!("player_instance" in scope)) { return null; } 108 | return scope.player_instance; 109 | } 110 | 111 | function FindDisplayName(ent) { 112 | local instance = FindInstanceByEntity(ent); 113 | if (instance == null) { return ""; } 114 | return instance.GetDisplayName(); 115 | } 116 | 117 | function FindIsBot(ent) { 118 | local instance = FindInstanceByEntity(ent); 119 | if (instance == null) { 120 | Log("[Players] Couldn't find entity while checking bot " + ent); 121 | return false; 122 | } 123 | return instance.IsBot(); 124 | } 125 | 126 | function GetPlayers(filter = null) { 127 | local outp = []; 128 | local ent = Entities.First(); 129 | while (ent != null) { 130 | if (ent.GetClassname() == "player" && (filter == null || filter(ent))) { 131 | outp.push(ent); 132 | } 133 | ent = Entities.Next(ent); 134 | } 135 | return outp; 136 | } 137 | 138 | function GetPlayersInRadius(origin, radius, filter = null) { 139 | local outp = []; 140 | local players = PlayerManager.GetPlayers(); 141 | foreach (ply in players) { 142 | local deltaVector = origin - ply.GetOrigin(); 143 | if (deltaVector.Length() <= radius) { 144 | outp.push(ply); 145 | } 146 | } 147 | return outp; 148 | } 149 | 150 | function GetCTs() { 151 | return GetPlayers(function(ply){ 152 | return ply.GetTeam() == TEAM_CT; 153 | }); 154 | } 155 | 156 | function GetTs() { 157 | return GetPlayers(function(ply){ 158 | return ply.GetTeam() == TEAM_T; 159 | }); 160 | } 161 | 162 | function Think() { 163 | // only check if we have an entity to check with 164 | if (eventProxy == null || !eventProxy.IsValid()) { 165 | return; 166 | } 167 | local ent = Entities.First(); 168 | while (ent != null) { 169 | if (ent.GetClassname() == "player" && ent.IsValid() && ent.ValidateScriptScope()) { 170 | local scope = ent.GetScriptScope(); 171 | if (!("userid" in scope) && !("generating_userid" in scope)) { 172 | // Log("[Players] Found new player "+ent+" - getting his userid"); 173 | scope.generating_userid <- true; 174 | eventProxy_boundPlayer = ent; 175 | EntFireByHandle(eventProxy, "GenerateGameEvent", "", 0.0, ent, null); 176 | return; // can only bind one per think because we need the output to fire first 177 | } else { 178 | if ("userid" in scope) { 179 | // Log("[Players] Already know userid of player "+ent+": "+scope.userid); 180 | } else { 181 | // Log("[Players] Awaiting userid of player "+ent); 182 | } 183 | } 184 | } 185 | ent = Entities.Next(ent); 186 | } 187 | } 188 | } 189 | 190 | if (!("Players" in getroottable())) { 191 | Log("[Players] Binding"); 192 | ::_players_instances <- []; 193 | ::_players_userid_to_name <- {}; 194 | ::_players_userid_to_bot <- {}; 195 | 196 | // listen for name changes, or whether a player is a bot 197 | ::_players_name_updater <- function(userid, name) { 198 | Log("[Players] Setting name of "+userid+" to "+name); 199 | ::_players_userid_to_name[userid] <- name; 200 | local instance = ::Players.FindInstanceByEntity(::Players.FindByUserid(userid)); 201 | if (instance != null) { 202 | instance.SetDisplayName(name); 203 | } 204 | }; 205 | ::_players_bot_updater <- function(userid, isbot) { 206 | Log("[Players] Setting bot of "+userid+" to "+isbot); 207 | ::_players_userid_to_bot[userid] <- isbot; 208 | local instance = ::Players.FindInstanceByEntity(::Players.FindByUserid(userid)); 209 | if (instance != null) { 210 | instance.SetBot(isbot); 211 | } 212 | }; 213 | 214 | ::AddEventListener("player_connect", function(data) { 215 | ::_players_name_updater(data.userid, data.name); 216 | }); 217 | ::AddEventListener("player_changename", function(data) { 218 | ::_players_name_updater(data.userid, data.newname); 219 | }); 220 | ::AddEventListener("player_team", function(data) { 221 | if ("isbot" in data) { 222 | ::_players_bot_updater(data.userid, data.isbot); 223 | } 224 | if ("name" in data) { 225 | ::_players_name_updater(data.userid, data.name); 226 | } 227 | }); 228 | 229 | // listen for our fake player_use, which gives us userid 230 | ::AddEventListener("player_use", function(data){ 231 | // if this is caused by our fake event 232 | if (::Players.eventProxy_boundPlayer != null && data.entity == 0) { 233 | local ply = ::Players.eventProxy_boundPlayer; 234 | Log("[Players] Got player "+ply+" for userid "+data.userid); 235 | local scope = ply.GetScriptScope(); 236 | if ("generating_userid" in scope) { 237 | scope.userid <- data.userid; 238 | delete scope.generating_userid; 239 | local instance = Player(ply); 240 | instance.SetUserid(data.userid); 241 | if (data.userid in ::_players_userid_to_name) { 242 | instance.SetDisplayName(::_players_userid_to_name[data.userid]); 243 | } 244 | if (data.userid in ::_players_userid_to_bot) { 245 | instance.SetBot(::_players_userid_to_bot[data.userid]); 246 | } 247 | ::_players_instances.append(instance); 248 | ::Players.eventProxy_boundPlayer = null; 249 | if (ply.GetHealth() > 0) { 250 | ::TriggerEvent("player_spawn", { userid = scope.userid }); 251 | } 252 | } 253 | } 254 | }); 255 | 256 | ::Players <- PlayerManager(); 257 | } else { 258 | Log("[Players] Already has global instance - not rebinding"); 259 | ::Players.GenerateEventProxy(); 260 | } -------------------------------------------------------------------------------- /lib/polyfills.nut: -------------------------------------------------------------------------------- 1 | // https://github.com/birjolaxew/csgo-vscripts 2 | // Stuff that should've been in the language 3 | // Keep in mind that the version of Squirrel that comes with CS:GO is 2.2, so it's a bit old 4 | 5 | /** 6 | * Returns the index of an element in an array 7 | * Returns null if not found 8 | */ 9 | ::find_in_array <- function(arr, elm) { 10 | for (local i = 0; i < arr.len(); i++) { 11 | if (arr[i] == elm) { return i; } 12 | } 13 | return null; 14 | }; 15 | 16 | /** 17 | * Removes an element from an array. Only returns one instance 18 | * Returns null if not found, returns the element if is found 19 | */ 20 | ::remove_elm_from_array <- function(arr, elm) { 21 | local idx = find_in_array(arr, elm); 22 | if (idx == null) { return null; } 23 | arr.remove(idx); 24 | return elm; 25 | }; 26 | 27 | /** 28 | * Returns a copy of an array with only the elements that match a specific filter 29 | */ 30 | ::filter_array <- function(arr, filter) { 31 | local outp = []; 32 | foreach (elm in arr) { 33 | if (filter(elm)) { outp.push(elm); } 34 | } 35 | return outp; 36 | } 37 | 38 | /** 39 | * Converts an array into a string by inserting the joiner between each element 40 | */ 41 | ::join <- function(arr, joiner=",") { 42 | local outp = ""; 43 | foreach(i,elm in arr) { 44 | if (i != 0) { 45 | outp += joiner; 46 | } 47 | outp += elm; 48 | } 49 | return outp; 50 | } 51 | 52 | /** Rounds a number */ 53 | ::round <- function(num) { 54 | if (num%1 < 0.5) { 55 | return floor(num); 56 | } 57 | return ceil(num); 58 | } -------------------------------------------------------------------------------- /lib/precache.nut: -------------------------------------------------------------------------------- 1 | // https://github.com/birjolaxew/csgo-vscripts 2 | // Handles gathering all the stuff that should be precached 3 | // Note that you *must* call PerformPrecache(self) in your main script's Precache() 4 | // Exposes: 5 | // - RegisterPrecacheModel(str) 6 | // - RegisterPrecacheSound(str) 7 | // - PerformPrecache() 8 | 9 | DoIncludeScript("lib/debug.nut", null); 10 | 11 | if (!("_LOADED_MODULE_CURSORS" in getroottable())) { 12 | ::_precache_models <- []; 13 | ::_precache_sounds <- []; 14 | } 15 | 16 | ::RegisterPrecacheModel <- function(model) { 17 | ::_precache_models.push(model); 18 | } 19 | 20 | ::RegisterPrecacheSound <- function(sound) { 21 | ::_precache_sounds.push(sound); 22 | } 23 | 24 | function PerformPrecache(target) { 25 | foreach (model in ::_precache_models) { 26 | target.PrecacheModel(model); 27 | } 28 | foreach (sound in ::_precache_sounds) { 29 | target.PrecacheSoundScript(sound); 30 | } 31 | } -------------------------------------------------------------------------------- /lib/templates.nut: -------------------------------------------------------------------------------- 1 | /** 2 | * https://github.com/birjolaxew/csgo-vscripts 3 | * Lets you spawn stuff from a template and get the spawned entities. 4 | * Exposes: 5 | * - SpawnTemplate(eTemplate, cb) 6 | */ 7 | 8 | ::_template_awaiting <- {}; 9 | 10 | /** Spawns a specific template, and calls cb with a table of name=>ent (or null if spawning fails) */ 11 | ::SpawnTemplate <- function(eTemplate, cb) { 12 | if (!eTemplate.ValidateScriptScope()) { cb(null); return; } 13 | local scope = eTemplate.GetScriptScope(); 14 | // bind PostSpawn so we can get the entities after they're spawned 15 | if (!("_template_bound" in scope)) { 16 | scope._template_bound <- "template_id_"+UniqueString(); 17 | ::_template_awaiting[scope._template_bound] <- []; 18 | 19 | // PreSpawnInstance must be called for PostSpawn to be called too 20 | if (!("PreSpawnInstance" in scope)) { 21 | scope.PreSpawnInstance <- function(entClass, entName) {}; 22 | } 23 | 24 | // bind PostSpawn 25 | if ("PostSpawn" in scope) { 26 | scope._template_postspawn <- scope.PostSpawn; 27 | } 28 | scope.PostSpawn <- (function(ents) { 29 | PrintTable(ents); 30 | ::_template_postspawn(this._template_bound, ents); 31 | }).bindenv(scope); 32 | } 33 | 34 | ::_template_awaiting[scope._template_bound].push(cb); 35 | EntFireByHandle(eTemplate, "ForceSpawn", "", 0.0, null, null); 36 | } 37 | 38 | /** Called by PostSpawn of each individual template */ 39 | ::_template_postspawn <- function(id, ents) { 40 | if (!(id in ::_template_awaiting)) { 41 | Warn("PostSpawn of unknown template "+id+" called; ignoring"); 42 | return; 43 | } 44 | if (::_template_awaiting[id].len() > 0) { 45 | local target = _template_awaiting[id][0]; 46 | ::remove_elm_from_array(_template_awaiting[id], target); 47 | target(ents); 48 | } else { 49 | Warn("PostSpawn of template "+id+" called without"); 50 | } 51 | Log("Spawned entities for id "+id); 52 | 53 | } -------------------------------------------------------------------------------- /lib/timers.nut: -------------------------------------------------------------------------------- 1 | DoIncludeScript("lib/debug.nut",null); 2 | 3 | ::CallNextFrame <- function(cb) { 4 | TimerHandler(0, cb, true); 5 | } 6 | 7 | class TimerHandler { 8 | uid = null; 9 | cb = null; 10 | eTimer = null; 11 | once = false; 12 | 13 | constructor(frequency, callback, once=false) { 14 | this.uid = UniqueString("-timer"); 15 | this.cb = callback; 16 | this.once = once; 17 | ::_timer_handler_map[this.uid] <- this; 18 | 19 | this.eTimer = Entities.CreateByClassname("logic_timer"); 20 | EntFireByHandle(this.eTimer, "RefireTime", frequency.tostring(), 0.0, null, null); 21 | EntFireByHandle(this.eTimer, "AddOutput", "OnTimer !self:RunScriptCode:_timer_callback(\""+this.uid+"\"):0:-1", 0.0, null, null); 22 | EntFireByHandle(this.eTimer, "Enable", "", 0.0, null, null); 23 | } 24 | 25 | function Trigger() { 26 | this.cb(); 27 | if (this.once) { 28 | this.Destroy(); 29 | } 30 | } 31 | 32 | function Destroy() { 33 | if (this.eTimer != null && this.eTimer.IsValid()) { 34 | this.eTimer.Destroy(); 35 | } 36 | this.cb = null; 37 | delete ::_timer_handler_map[this.uid]; 38 | } 39 | 40 | function IsValid() { 41 | return this.eTimer && this.eTimer.IsValid(); 42 | } 43 | } 44 | 45 | ::_timer_handler_map <- {}; 46 | 47 | ::_timer_callback <- function(uid) { 48 | if (uid in _timer_handler_map) { 49 | _timer_handler_map[uid].Trigger(); 50 | } else { 51 | Log("[Animation] Timer fired for unknown timer "+uid); 52 | } 53 | } -------------------------------------------------------------------------------- /lib/weapons.nut: -------------------------------------------------------------------------------- 1 | /** 2 | * https://github.com/birjolaxew/csgo-vscripts 3 | * Handles getting and settings weapons on a player 4 | * In order to be able to get active weapon, you must add a listener for item_equip to your map (see events.nut) 5 | * You must also add the listeners required by players.nut 6 | * Exposes: 7 | * - GetActiveWeapon(player) 8 | * - GetWeapons(player) 9 | */ 10 | 11 | DoIncludeScript("lib/players.nut", null); 12 | 13 | /* Gets the active weapon of a player - returns class string (e.g. weapon_glock), or null if unknown */ 14 | ::GetActiveWeapon <- function(player) { 15 | local scope = player.GetScriptScope(); 16 | if (!("weapon_data" in scope)) { return null; } 17 | return "weapon_"+scope.weapon_data.item; 18 | } 19 | 20 | /** Gets all weapons a player is currently carrying - returns array of class strings (e.g. weapon_glock) */ 21 | ::GetWeapons <- function(player) { 22 | // loop through move children 23 | local weapons = []; 24 | local ent = player.FirstMoveChild(); 25 | while (ent != null) { 26 | if (ent.GetClassname().find("weapon_") != null) { 27 | weapons.push(ent.GetClassname()); 28 | } 29 | ent = ent.NextMovePeer(); 30 | } 31 | return weapons; 32 | } 33 | 34 | if (!("_LOADED_MODULE_WEAPONS" in getroottable())) { 35 | ::_LOADED_MODULE_WEAPONS <- true; 36 | 37 | ::AddEventListener("item_equip", function(data){ 38 | local player = ::Players.FindByUserid(data.userid); 39 | if (!player) { return; } 40 | if (!player.ValidateScriptScope()) { return; } 41 | local scope = player.GetScriptScope(); 42 | scope.weapon_data <- data; 43 | }); 44 | } -------------------------------------------------------------------------------- /portal/entry.nut: -------------------------------------------------------------------------------- 1 | /** 2 | * https://github.com/birjolaxew/csgo-vscripts 3 | * Entrypoint for the Portal gamemode 4 | */ 5 | DoIncludeScript("lib/debug.nut", null); 6 | DoIncludeScript("lib/events.nut", null); 7 | DoIncludeScript("lib/precache.nut", null); 8 | DoIncludeScript("portal/gamemode.nut", null); 9 | 10 | function Precache() { 11 | PerformPrecache(self); 12 | } 13 | 14 | function Think() { 15 | local root = getroottable(); 16 | if ("Players" in root) { 17 | ::Players.Think(); 18 | } 19 | if ("_LOADED_MODULE_TIMER" in root) { 20 | ::_timer_Think(); 21 | } 22 | if ("_LOADED_MODULE_CURSORS" in root) { 23 | ::_cursors_Think(); 24 | } 25 | if ("gamemode_portal" in root) { 26 | ::gamemode_portal.Think(); 27 | } 28 | 29 | local ent = null; 30 | while (ent = Entities.FindByClassname(ent, "hegrenade_projectile")) { 31 | Log(" "+ent+" | "+ent.GetName()); 32 | } 33 | } 34 | 35 | if (!("_LOADED_GAMEMODE_PORTAL" in getroottable())) { 36 | ::_LOADED_GAMEMODE_PORTAL <- true; 37 | ::gamemode_portal <- GamemodePortal(); 38 | } else { 39 | ::gamemode_portal.OnRoundStart(); 40 | } -------------------------------------------------------------------------------- /portal/gamemode.nut: -------------------------------------------------------------------------------- 1 | /** 2 | * https://github.com/birjolaxew/csgo-vscripts 3 | * The actual state holder and delegator for the Portal gamemode 4 | */ 5 | 6 | DoIncludeScript("lib/debug.nut", null); 7 | DoIncludeScript("portal/player.nut", null); 8 | 9 | class GamemodePortal { 10 | constructor() { 11 | } 12 | 13 | function Think() { 14 | // bind each player that we don't know of yet 15 | foreach (player in ::Players.GetPlayers()) { 16 | if (!player.ValidateScriptScope()) { continue; } 17 | local scope = player.GetScriptScope(); 18 | if ("_portal_bound" in scope) { continue; } 19 | scope._portal_bound <- PortalPlayer(player); 20 | } 21 | } 22 | 23 | function OnRoundStart() { 24 | foreach (player in ::Players.GetPlayers()) { 25 | if (!player.ValidateScriptScope()) { continue; } 26 | local scope = player.GetScriptScope(); 27 | if (!("_portal_bound" in scope)) { continue; } 28 | scope._portal_bound.Bind(); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /portal/player.nut: -------------------------------------------------------------------------------- 1 | /** 2 | * https://github.com/birjolaxew/csgo-vscripts 3 | * A representation of a player in the Portal gamemode 4 | */ 5 | 6 | DoIncludeScript("lib/debug.nut", null); 7 | DoIncludeScript("lib/cursors.nut", null); 8 | DoIncludeScript("lib/math.nut", null); 9 | DoIncludeScript("lib/templates.nut", null); 10 | DoIncludeScript("lib/timers.nut", null); 11 | DoIncludeScript("portal/portal.nut", null); 12 | 13 | ::ePortalTesterTmpl <- Entities.FindByName(null, "portal_portalcheckertemplate"); 14 | ::ePortalTmpl <- Entities.FindByName(null, "portal_portaltemplate"); 15 | 16 | enum PORTAL_TYPES { 17 | FIRST, 18 | SECOND 19 | } 20 | 21 | const PORTAL_FIRE_DELAY = 0.3; 22 | class PortalPlayer { 23 | ePlayer = null; 24 | cursor = null; 25 | colors = ["255 0 0", "0 0 255"]; 26 | portals = [null, null]; 27 | 28 | cbAttack1 = null; 29 | cbAttack2 = null; 30 | 31 | lastPortalTime = 0; 32 | portalTarget = null; 33 | portalSurfaceNormal = null; 34 | portalType = null; 35 | ePortalTester = null; 36 | 37 | constructor(player) { 38 | this.ePlayer = player; 39 | this.Bind(); 40 | this.cbAttack1 = this.OnAttack1.bindenv(this); 41 | this.cbAttack2 = this.OnAttack2.bindenv(this); 42 | this.cursor.AddAttack1Listener(this.cbAttack1); 43 | this.cursor.AddAttack2Listener(this.cbAttack2); 44 | } 45 | 46 | function Destroy() { this.cursor.Destroy(); } 47 | function Bind() { this.cursor = ::FindCursorOfPlayer(this.ePlayer); } 48 | 49 | /** Fires a portal at where our player is looking */ 50 | function FirePortal(type) { 51 | if (lastPortalTime && Time() - lastPortalTime < PORTAL_FIRE_DELAY) { return; } 52 | lastPortalTime = Time(); 53 | local lookingAt = this.cursor.GetLookingAt(); 54 | local delta = lookingAt - this.ePlayer.EyePosition(); 55 | delta.Norm(); 56 | local traceFrom = lookingAt - delta * 1; 57 | local normal = ::NormalOfSurface(traceFrom); 58 | 59 | if (!::ePortalTesterTmpl || !::ePortalTesterTmpl.IsValid()) { 60 | Warn("Cannot find portal_portalcheckertemplate ("+::ePortalTesterTmpl+")"); 61 | } 62 | this.portalTarget = lookingAt; 63 | this.portalSurfaceNormal = normal; 64 | this.portalType = type; 65 | // checking if it's possible to place a portal is... messy 66 | // we spawn a tester, move it to our target, wait a frame, then test if a trigger has renamed it 67 | ::SpawnTemplate(::ePortalTesterTmpl, (function(ents) { 68 | foreach(ent in ents) { 69 | if (ent.GetName() == "portal_portalchecker") { 70 | this.ePortalTester = ent; 71 | local target = this.portalTarget; 72 | ent.SetOrigin(target); 73 | TimerHandler(0.1, (function(){ 74 | if (!this.ePortalTester) { 75 | Warn("Unknown portal tester!"); 76 | return; 77 | } 78 | if (this.ePortalTester.GetName() != "portal_portalchecker") { 79 | this.SpawnPortal(); 80 | } 81 | this.ePortalTester.Destroy(); 82 | this.ePortalTester = null; 83 | }).bindenv(this), true); 84 | } 85 | } 86 | }).bindenv(this)); 87 | } 88 | 89 | /** Spawns a portal (type previously set) at our portal location */ 90 | function SpawnPortal() { 91 | ::SpawnTemplate(::ePortalTmpl, (function(ents) { 92 | if (this.portals[this.portalType]) { 93 | this.portals[this.portalType].Destroy(); 94 | } 95 | EntFireByHandle(ents["portal_portal"], "Color", this.colors[this.portalType], 0.0, null, null); 96 | local portal = Portal(ents, this, this.portalSurfaceNormal, this.portalTarget); 97 | local otherPortal = this.portals[1 - this.portalType]; 98 | if (otherPortal) { 99 | otherPortal.RegisterPartner(portal); 100 | portal.RegisterPartner(otherPortal); 101 | } 102 | this.portals[this.portalType] = portal; 103 | }).bindenv(this)); 104 | } 105 | 106 | function OnAttack1() { 107 | this.FirePortal(PORTAL_TYPES.FIRST); 108 | } 109 | function OnAttack2() { 110 | local lookingAt = this.cursor.GetLookingAt(); 111 | local delta = lookingAt - this.ePlayer.EyePosition(); 112 | delta.Norm(); 113 | local traceFrom = lookingAt - delta * 1; 114 | local normal = ::NormalOfSurface(traceFrom); 115 | local reflected = ::GetReflection(delta, normal * -1); 116 | local relative = ::GetRelativeVector(normal, delta); 117 | 118 | ::DrawLine(lookingAt, lookingAt + normal * 16, Vector(255,0,0)); 119 | ::DrawLine(lookingAt, lookingAt + delta * 16, Vector(0,255,0)); 120 | ::DrawLine(lookingAt, lookingAt + reflected * 16, Vector(0,0,255)); 121 | ::DrawLine(lookingAt, lookingAt + (normal * -1) * 16, Vector(0,255,255)); 122 | ::DrawLine(lookingAt, lookingAt + ::ApplyRelativeVector(normal, relative) * 16, Vector(255,0,255)); 123 | 124 | this.FirePortal(PORTAL_TYPES.SECOND); 125 | } 126 | } -------------------------------------------------------------------------------- /portal/portal.nut: -------------------------------------------------------------------------------- 1 | /** 2 | * https://github.com/birjolaxew/csgo-vscripts 3 | * A representation of a portal in the Portal gamemode 4 | * Also includes some global stuff for interacting with it 5 | */ 6 | DoIncludeScript("lib/math.nut", null); 7 | 8 | ::_portal_map <- {}; 9 | 10 | ::OnEnteredTeleport <- function(teleScope) { 11 | if (!("_portal_uid" in teleScope)) { 12 | Warn("Entered portal that wasn't bound to portal instance"); 13 | return; 14 | } 15 | local uid = teleScope._portal_uid; 16 | if (!(uid in ::_portal_map)) { 17 | Warn("Entered portal, but cannot find its portal instance"); 18 | return; 19 | } 20 | ::_portal_map[uid].OnEnter(activator); 21 | }; 22 | 23 | class Portal { 24 | player = null; // the Player instance 25 | ents = null; 26 | normal = null; 27 | origin = null; 28 | partner = null; // the Portal instance we connect to 29 | uid = ""; 30 | 31 | constructor(ents, player, normal, origin) { 32 | this.ents = ents; 33 | this.normal = normal; 34 | this.origin = origin; 35 | this.uid = "portal_trigger_"+UniqueString(); 36 | ::_portal_map[this.uid] <- this; 37 | 38 | foreach(ent in ents) { 39 | ent.SetOrigin(origin); 40 | ent.SetForwardVector(normal); 41 | } 42 | 43 | // bind to the teleport trigger 44 | if (!("portal_portaltele" in ents) || !ents["portal_portaltele"].ValidateScriptScope()) { 45 | Warn("Couldn't find tele for portal instance"); 46 | } else { 47 | local teleScope = ents["portal_portaltele"].GetScriptScope(); 48 | teleScope._portal_uid <- this.uid; 49 | } 50 | } 51 | 52 | function RegisterPartner(partner) { 53 | this.partner = partner; 54 | } 55 | 56 | function OnEnter(player) { 57 | // we use a 0.5 cooldown per player to make sure we don't teleport them right after they've gotten emitted from us 58 | if (!player.ValidateScriptScope()) { return; } 59 | local scope = player.GetScriptScope(); 60 | if ((this.uid in scope) && Time() - scope[this.uid] < 0.5) { return; } 61 | 62 | local vel = player.GetVelocity(); 63 | local relativeVel = ::GetRelativeVector(this.normal, vel, this.origin); 64 | if (this.partner && this.partner.IsValid()) { 65 | this.partner.Emit(player, relativeVel); 66 | } 67 | } 68 | 69 | function Emit(player, relativeVel) { 70 | // we use a 0.5 cooldown per player to make sure we don't teleport them right after they've gotten emitted from us 71 | if (!player.ValidateScriptScope()) { return; } 72 | local scope = player.GetScriptScope(); 73 | scope[this.uid] <- Time(); 74 | 75 | Log("Emitting "+player+" from "+this.uid); 76 | local vel = ::ApplyRelativeVector(this.normal, relativeVel, this.origin); 77 | player.SetOrigin(this.origin + this.normal * 64); 78 | player.SetVelocity(vel * -1); 79 | } 80 | 81 | function IsValid() { 82 | foreach(ent in this.ents) { 83 | if (ent && !ent.IsValid()) { 84 | return false; 85 | } 86 | } 87 | return true; 88 | } 89 | 90 | function Destroy() { 91 | foreach(ent in this.ents) { 92 | if (ent && ent.IsValid()) { 93 | ent.Destroy(); 94 | } 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /vip/README.md: -------------------------------------------------------------------------------- 1 | # VIP gamemode 2 | 3 | CTs take the VIP to the helicopter. Ts kill him. 4 | 5 | The VIP has 200 HP, but can only use pistols. If he tries to buy another weapon (or has one from a previous round) he'll drop it if he switches to it twice. 6 | 7 | ## Usage 8 | 9 | Extract `lib/` and `gamemode.nut` to `steamapps/Counter-Strike: Global Offensive/csgo/scripts/vscripts/vip` (create the last folder if it doesn't exist). 10 | Extract `resources/*` to `steamapps/Counter-Strike: Global Offensive/csgo` (so `models` merges with CS:GO's `models`). 11 | 12 | When mapping, insert a `func_instance` entity into your map, pointing to `vmfs/instance_vip_entities.vmf` - make sure "Entity Name Fix Up" is set to "None". This adds all the stuff needed to make the gamemode work to your map. 13 | Then insert another `func_instance` pointing to `vmfs/instance_vip_rescue.vmf`. This will add a VIP rescue zone to your map. If you want multiple rescue zones, you want "Entity Name Fix Up" to be "Prefix". 14 | 15 | **Important:** In order for the VIP to be able to see his own arms, your map needs a `.kv` file. You can copy-paste the `vmfs/test_vip.kv` file from this repo and rename it to match your map. 16 | 17 | ## Known bugs 18 | 19 | - **Bots are able to use weapons while VIP** 20 | 21 | _This is because bots do not respond to client commands, so we cannot make them switch weapons when using illegal weapons. Any ideas for workarounds are appreciated._ 22 | 23 | - **Bots don't know where to go** 24 | 25 | _Not a whole lot can be done about teaching bots the gamemode, but could try spawning an Hostage entity inside Helicopter to "bait" CTs into the Helicopter, and making the hostage invisible/untargatable. This would of course cause some other weird problems such as Terrorist bots saying things like "Gonna camp the hostage" and such. No elegant solution._ 26 | 27 | - **Quickswitch may drop pistol on certain ocasions** 28 | 29 | _This is caused by the delay between server and client. If client switches to another weapon before it receives the `drop` command from the server, then said weapon will be dropped instead. Fix would be to send a `slot1` (or whatever slot the illegal weapon is in) before sending the `drop`._ 30 | 31 | - **When a player swaps from T to CT, and happens to replace the bot that was suposedly VIP some shit happens.** 32 | 33 | _The replaced bot still spawns for the first frame aparently, and if he happens to have been select to be VIP, the player spawns, the VIP is dead in that same position inside the hostage entity._ 34 | 35 | - **[NOT A BUG] Could probably make mode more friendly towards spectator.** 36 | 37 | _Small things like printing messages from Ts and CTs to spectator as well, ex.: When VIP countdown is happening. To make the spectator experience atleast decent._ 38 | -------------------------------------------------------------------------------- /vip/lib/chat.nut: -------------------------------------------------------------------------------- 1 | // https://github.com/birjolaxew/csgo-vscripts 2 | // == Chat messages 3 | // Handles displaying stuff to players 4 | // Exposes: 5 | // ::COLORS{} 6 | // ::ChatMessageAll(msg) 7 | // ::ChatMessageCT(msg) 8 | // ::ChatMessageT(msg) 9 | 10 | DoIncludeScript("vip/lib/players.nut", null); 11 | 12 | ::COLORS <- { 13 | invisible = "\x00" 14 | white = "\x01" 15 | dark_red = "\x02" 16 | purple = "\x03" 17 | green = "\x04" 18 | light_green = "\x05" 19 | lime_green = "\x06" 20 | red = "\x07" 21 | grey = "\x08" 22 | yellow = "\x09" 23 | light_blue = "\x0a" 24 | blue = "\x0b" 25 | dark_blue = "\x0c" 26 | light_blue2 = "\x0d" 27 | pink = "\x0e" 28 | light_red = "\x0f" 29 | orange = "\x10" 30 | }; 31 | 32 | ::ChatMessageAll <- function(msg) { 33 | ScriptPrintMessageChatAll(msg); 34 | } 35 | ::ChatMessageCT <- function(msg) { 36 | ScriptPrintMessageChatTeam(TEAM_CT, msg); 37 | } 38 | ::ChatMessageT <- function(msg) { 39 | ScriptPrintMessageChatTeam(TEAM_T, msg); 40 | } -------------------------------------------------------------------------------- /vip/lib/debug.nut: -------------------------------------------------------------------------------- 1 | ::IsDebug <- function() { 2 | return GetDeveloperLevel() > 0; 3 | } 4 | 5 | ::Log <- function(msg) { 6 | if (IsDebug()) { 7 | printl(msg); 8 | } 9 | } 10 | 11 | ::PrintTable <- function(tbl, indent="", printfunc=::Log) { 12 | if (tbl == null) { 13 | printfunc("null"); 14 | return; 15 | } 16 | printfunc(indent + "table {"); 17 | foreach (key,val in tbl) { 18 | local v = val; 19 | if (val == null) { 20 | v = "null"; 21 | } else { 22 | v = val.tostring(); 23 | } 24 | printfunc(indent + " "+key+": "+v); 25 | } 26 | printfunc(indent + "}"); 27 | } 28 | 29 | ::DrawBox <- function(origin, size=Vector(16,16,16), color=Vector(255,0,0), duration=5) { 30 | DebugDrawBox( 31 | origin, 32 | size * -0.5, 33 | size * 0.5, 34 | color.x, 35 | color.y, 36 | color.z, 37 | 0, 38 | duration 39 | ); 40 | } 41 | 42 | ::DrawLine <- function(start, end, color=Vector(255,0,0), duration=5) { 43 | DebugDrawLine( 44 | start, 45 | end, 46 | color.x, 47 | color.y, 48 | color.z, 49 | true, 50 | duration 51 | ); 52 | } -------------------------------------------------------------------------------- /vip/lib/events.nut: -------------------------------------------------------------------------------- 1 | // https://github.com/birjolaxew/csgo-vscripts 2 | // == Event listener 3 | // Listen for in-game events 4 | // The callback functions are called directly with the event data 5 | // In order for this to work, you must manually add logic_eventlistener to your world 6 | // This logic_eventlistener should have the following keyvalues: 7 | // - targetname the_event_you_want_to_listen_to_listener 8 | // - EventName the_event_you_want_to_listen_to 9 | // - FetchEventData 1 10 | // It should also have the following output: 11 | // - OnEventFired name_of_your_logic_script RunScriptCode ::TriggerEvent(THE_EVENT_YOU_WANT_TO_LISTEN_TO) 12 | // 13 | // SPECIAL CASE: If an event is triggered twice in the same frame, the data will be that of the last trigger 14 | // Notably this happens on multikills (killing two players with one bullet) and player_death event. 15 | // For that case, create a trigger_brush in your map with the name "game_playerdie" 16 | // This will trigger the event "game_playerdie" with the victim ent as the only parameter 17 | // 18 | // Exposes: 19 | // ::AddEventListener("event-name", function(){}) 20 | // ::RemoveEventListener("event-name", function(){}) 21 | // ::TriggerEvent("event-name") 22 | // == 23 | 24 | ::ITEM_EQUIP <- "item_equip"; 25 | ::PLAYER_USE <- "player_use"; 26 | ::PLAYER_DEATH <- "player_death"; 27 | ::PLAYER_CONNECT <- "player_connect"; 28 | ::PLAYER_CONNECT_FULL <- "player_connect_full"; 29 | ::PLAYER_CONNECT_CLIENT <- "player_connect_client"; 30 | ::PLAYER_DISCONNECT <- "player_disconnect"; 31 | ::PLAYER_CHANGENAME <- "player_changename"; 32 | ::PLAYER_TEAM <- "player_team"; 33 | ::ROUND_START <- "round_start"; 34 | ::ROUND_FREEZE_END <- "round_freeze_end"; 35 | ::PLAYER_SPAWN <- "player_spawn"; 36 | ::PLAYER_HURT <- "player_hurt"; 37 | ::BOT_TAKEOVER <- "bot_takeover"; 38 | ::INSPECT_WEAPON <- "inspect_weapon"; 39 | ::ROUND_END <- "round_end"; 40 | ::HOSTAGE_FOLLOWS <- "hostage_follows"; 41 | ::HOSTAGE_STOPS_FOLLOWING <- "hostage_stops_following"; 42 | 43 | DoIncludeScript("vip/lib/debug.nut",null); 44 | 45 | if (!("_eventsScope" in getroottable())) { 46 | // each event is an array of listeners 47 | ::_eventsScope <- {}; 48 | ::_eventsScope.listeners <- {}; 49 | 50 | ::AddEventListener <- function(name, cb) { 51 | Log("[Events] Adding event listener for " + name); 52 | if (!(name in ::_eventsScope.listeners)) { 53 | ::_eventsScope.listeners[name] <- []; 54 | } 55 | ::_eventsScope.listeners[name].push(cb); 56 | }; 57 | ::RemoveEventListener <- function(name, cb) { 58 | if (name in ::_eventsScope.listeners) { 59 | local ind = ::_eventsScope.listeners[name].find(cb); 60 | if (ind != null) { 61 | ::_eventsScope.listeners[name].remove(ind); 62 | } 63 | } 64 | }; 65 | ::TriggerEvent <- function(name, data=null) { 66 | Log("[Events] Triggering event for " + name); 67 | local listener = Entities.FindByName(null, name+"_listener"); 68 | local event_data = data; 69 | if (event_data == null && listener != null) { 70 | if (listener.ValidateScriptScope()) { 71 | event_data = listener.GetScriptScope().event_data; 72 | } 73 | } 74 | if (name in ::_eventsScope.listeners) { 75 | foreach (listener in ::_eventsScope.listeners[name]) { 76 | listener(event_data); 77 | } 78 | } 79 | } 80 | 81 | Log("[Events] Initialized"); 82 | } 83 | 84 | // bind game_playerdie (see special case) 85 | local ent = null; 86 | while ((ent = Entities.FindByName(ent, "game_playerdie")) != null) { 87 | Log("[Events] Found game_playerdie "+ent+", binding"); 88 | if (ent.ValidateScriptScope()) { 89 | local scope = ent.GetScriptScope(); 90 | scope.OnPlayerDie <- function() { 91 | ::TriggerEvent("game_playerdie", activator); 92 | }; 93 | ent.ConnectOutput("OnUse", "OnPlayerDie"); 94 | } 95 | } -------------------------------------------------------------------------------- /vip/lib/math.nut: -------------------------------------------------------------------------------- 1 | // Trace in a direction, returning the point where the line hits terrain 2 | ::TraceInDirection <- function(start, direction, ignore = null) { 3 | direction.Norm(); 4 | local end = start + (direction * 5000); 5 | local progress = TraceLine(start, end, ignore); 6 | return start + (end - start) * progress; 7 | } -------------------------------------------------------------------------------- /vip/lib/money.nut: -------------------------------------------------------------------------------- 1 | // https://github.com/birjolaxew/csgo-vscripts 2 | // == Money 3 | // Handles giving money to players 4 | // Exposes: 5 | // ::GiveMoney(amount, player) 6 | // ::GiveMoneySome(amount, filter) 7 | // ::GiveMoneyCT(amount, msg) 8 | // ::GiveMoneyT(amount, msg) 9 | 10 | DoIncludeScript("vip/lib/chat.nut", null); 11 | DoIncludeScript("vip/lib/players.nut", null); 12 | DoIncludeScript("vip/lib/debug.nut", null); 13 | 14 | if (!("_LOADED_MODULE_MONEY" in getroottable())) { 15 | ::_LOADED_MODULE_MONEY <- true; 16 | ::_money_fire <- function(amount, target, msg="", output="AddMoneyPlayer") { 17 | local eMoney = Entities.CreateByClassname("game_money"); 18 | eMoney.__KeyValueFromString("AwardText", msg); 19 | eMoney.__KeyValueFromInt("Money", amount); 20 | EntFireByHandle(eMoney, output, "", 0.0, target, target); 21 | EntFireByHandle(eMoney, "Kill", "", 0.5, null, null); 22 | }; 23 | 24 | ::GiveMoney <- function(amount, player) { 25 | Log("[Money] Giving "+amount+" to "+player); 26 | 27 | ::_money_fire(amount, player); 28 | } 29 | 30 | ::GiveMoneySome <- function(msg, filter) { 31 | local ent = Entities.First(); 32 | while (ent != null) { 33 | if (ent.GetClassname() == "player") { 34 | if (filter == null || filter(ent)) { 35 | ::GiveMoney(msg, ent, delay); 36 | } 37 | } 38 | ent = Entities.Next(ent); 39 | } 40 | } 41 | 42 | ::GiveMoneyCT <- function(amount, msg) { 43 | Log("[Money] Giving "+amount+" to CTs"); 44 | ::_money_fire(amount, null, msg, "AddTeamMoneyCT"); 45 | } 46 | ::GiveMoneyT <- function(amount, msg, delay=0.0) { 47 | Log("[Money] Giving "+amount+" to Ts"); 48 | ::_money_fire(amount, null, msg, "AddTeamMoneyTerrorist"); 49 | } 50 | } -------------------------------------------------------------------------------- /vip/lib/players.nut: -------------------------------------------------------------------------------- 1 | // https://github.com/birjolaxew/csgo-vscripts 2 | // == PlayerManager 3 | // Keeps track of players, allowing you to find players by team or userid 4 | // Note that in order for the Userid binding to work, you *must* have your VMF 5 | // setup such that we can attach a listener to each of the following (see events.nut): 6 | // - player_use (used for binding userids) 7 | // - player_changename (used for getting player names) 8 | // - player_team (used for getting player names) 9 | // 10 | // Exposes: 11 | // ::Players (instance of PlayerManager) 12 | 13 | ::TEAM_SPEC <- 1; 14 | ::TEAM_T <- 2; 15 | ::TEAM_CT <- 3; 16 | 17 | DoIncludeScript("vip/lib/events.nut",null); 18 | DoIncludeScript("vip/lib/debug.nut",null); 19 | 20 | // represents a single player, bound to a player entity 21 | class Player { 22 | ent = null; 23 | userid = null; 24 | name = null; 25 | bot = false; 26 | 27 | constructor(ply) { 28 | ent = ply; 29 | ply.ValidateScriptScope(); 30 | local scope = ply.GetScriptScope(); 31 | scope.player_instance <- this; 32 | } 33 | 34 | function SetUserid(usrid) { userid = usrid; } 35 | function GetUserid() { 36 | if (userid == null) { 37 | return -1; 38 | } 39 | return userid; 40 | } 41 | 42 | function SetDisplayName(nme) { name = nme; } 43 | function GetDisplayName() { 44 | if (name == null) { 45 | return "[Unknown]"; 46 | } 47 | return name; 48 | } 49 | 50 | function SetBot(bt) { bot = bt; } 51 | function IsBot() { 52 | return bot; 53 | } 54 | } 55 | 56 | class PlayerManager { 57 | eventProxy = null; 58 | eventProxy_boundPlayer = null; 59 | userIdMappings = {}; 60 | 61 | constructor() { 62 | GenerateEventProxy(); 63 | } 64 | 65 | function GenerateEventProxy() { 66 | // create an info_game_event_proxy - we use this to fake player_use events 67 | eventProxy = Entities.CreateByClassname("info_game_event_proxy"); 68 | eventProxy.__KeyValueFromString("event_name", "player_use"); 69 | eventProxy.__KeyValueFromInt("range", 0); 70 | eventProxy_boundPlayer = null; 71 | 72 | // reset everything so that we don't have lingering outputs giving us false userids 73 | local ent = Entities.First();; 74 | while (ent != null) { 75 | if (ent.GetClassname() == "player" && ent.IsValid() && ent.ValidateScriptScope()) { 76 | local scope = ent.GetScriptScope(); 77 | if ("userid" in scope) { 78 | delete scope.userid; 79 | } 80 | if ("generating_userid" in scope) { 81 | delete scope.generating_userid; 82 | } 83 | } 84 | ent = Entities.Next(ent); 85 | } 86 | } 87 | 88 | function FindByUserid(userid) { 89 | local ent = Entities.First(); 90 | while (ent != null) { 91 | if (ent.GetClassname() == "player" && ent.ValidateScriptScope()) { 92 | local scope = ent.GetScriptScope(); 93 | if (("userid" in scope) && scope.userid == userid) { 94 | return ent; 95 | } 96 | } 97 | ent = Entities.Next(ent); 98 | } 99 | return null; 100 | } 101 | 102 | function FindInstanceByEntity(ent) { 103 | if (ent == null) { return null; } 104 | if (!ent.ValidateScriptScope()) { return null; } 105 | local scope = ent.GetScriptScope(); 106 | if (!("player_instance" in scope)) { return null; } 107 | return scope.player_instance; 108 | } 109 | 110 | function FindDisplayName(ent) { 111 | local instance = FindInstanceByEntity(ent); 112 | if (instance == null) { return ""; } 113 | return instance.GetDisplayName(); 114 | } 115 | 116 | function FindIsBot(ent) { 117 | local instance = FindInstanceByEntity(ent); 118 | if (instance == null) { 119 | Log("[Players] Couldn't find entity while checking bot " + ent); 120 | return false; 121 | } 122 | return instance.IsBot(); 123 | } 124 | 125 | function GetPlayers(filter = null) { 126 | local outp = []; 127 | local ent = Entities.First(); 128 | while (ent != null) { 129 | if (ent.GetClassname() == "player" && (filter == null || filter(ent))) { 130 | outp.push(ent); 131 | } 132 | ent = Entities.Next(ent); 133 | } 134 | return outp; 135 | } 136 | 137 | function GetPlayersInRadius(origin, radius, filter = null) { 138 | local outp = []; 139 | local players = PlayerManager.GetPlayers(); 140 | foreach (ply in players) { 141 | local deltaVector = origin - ply.GetOrigin(); 142 | if (deltaVector.Length() <= radius) { 143 | outp.push(ply); 144 | } 145 | } 146 | return outp; 147 | } 148 | 149 | function GetCTs() { 150 | return GetPlayers(function(ply){ 151 | return ply.GetTeam() == TEAM_CT; 152 | }); 153 | } 154 | 155 | function GetTs() { 156 | return GetPlayers(function(ply){ 157 | return ply.GetTeam() == TEAM_T; 158 | }); 159 | } 160 | 161 | function Think() { 162 | // only check if we have an entity to check with 163 | if (eventProxy == null || !eventProxy.IsValid()) { 164 | return; 165 | } 166 | local ent = Entities.First(); 167 | while (ent != null) { 168 | if (ent.GetClassname() == "player" && ent.IsValid() && ent.ValidateScriptScope()) { 169 | local scope = ent.GetScriptScope(); 170 | if (!("userid" in scope) && !("generating_userid" in scope)) { 171 | // Log("[Players] Found new player "+ent+" - getting his userid"); 172 | scope.generating_userid <- true; 173 | eventProxy_boundPlayer = ent; 174 | EntFireByHandle(eventProxy, "GenerateGameEvent", "", 0.0, ent, null); 175 | return; // can only bind one per think because we need the output to fire first 176 | } else { 177 | if ("userid" in scope) { 178 | // Log("[Players] Already know userid of player "+ent+": "+scope.userid); 179 | } else { 180 | // Log("[Players] Awaiting userid of player "+ent); 181 | } 182 | } 183 | } 184 | ent = Entities.Next(ent); 185 | } 186 | } 187 | } 188 | 189 | if (!("Players" in getroottable())) { 190 | Log("[Players] Binding"); 191 | ::_players_instances <- []; 192 | ::_players_userid_to_name <- {}; 193 | ::_players_userid_to_bot <- {}; 194 | 195 | // listen for name changes, or whether a player is a bot 196 | ::_players_name_updater <- function(userid, name) { 197 | Log("[Players] Setting name of "+userid+" to "+name); 198 | ::_players_userid_to_name[userid] <- name; 199 | local instance = ::Players.FindInstanceByEntity(::Players.FindByUserid(userid)); 200 | if (instance != null) { 201 | instance.SetDisplayName(name); 202 | } 203 | }; 204 | ::_players_bot_updater <- function(userid, isbot) { 205 | Log("[Players] Setting bot of "+userid+" to "+isbot); 206 | ::_players_userid_to_bot[userid] <- isbot; 207 | local instance = ::Players.FindInstanceByEntity(::Players.FindByUserid(userid)); 208 | if (instance != null) { 209 | instance.SetBot(isbot); 210 | } 211 | }; 212 | 213 | ::AddEventListener("player_connect", function(data) { 214 | ::_players_name_updater(data.userid, data.name); 215 | }); 216 | ::AddEventListener("player_changename", function(data) { 217 | ::_players_name_updater(data.userid, data.newname); 218 | }); 219 | ::AddEventListener("player_team", function(data) { 220 | if ("isbot" in data) { 221 | ::_players_bot_updater(data.userid, data.isbot); 222 | } 223 | if ("name" in data) { 224 | ::_players_name_updater(data.userid, data.name); 225 | } 226 | }); 227 | 228 | // listen for our fake player_use, which gives us userid 229 | ::AddEventListener("player_use", function(data){ 230 | // if this is caused by our fake event 231 | if (::Players.eventProxy_boundPlayer != null && data.entity == 0) { 232 | local ply = ::Players.eventProxy_boundPlayer; 233 | Log("[Players] Got player "+ply+" for userid "+data.userid); 234 | local scope = ply.GetScriptScope(); 235 | if ("generating_userid" in scope) { 236 | scope.userid <- data.userid; 237 | delete scope.generating_userid; 238 | if (ply.GetHealth() > 0) { 239 | ::TriggerEvent("player_spawn", { userid = scope.userid }); 240 | } 241 | local instance = Player(ply); 242 | instance.SetUserid(data.userid); 243 | if (data.userid in ::_players_userid_to_name) { 244 | instance.SetDisplayName(::_players_userid_to_name[data.userid]); 245 | } 246 | if (data.userid in ::_players_userid_to_bot) { 247 | instance.SetBot(::_players_userid_to_bot[data.userid]); 248 | } 249 | ::_players_instances.append(instance); 250 | ::Players.eventProxy_boundPlayer = null; 251 | } 252 | } 253 | }); 254 | 255 | ::Players <- PlayerManager(); 256 | } else { 257 | Log("[Players] Already has global instance - not rebinding"); 258 | ::Players.GenerateEventProxy(); 259 | } -------------------------------------------------------------------------------- /vip/lib/ui.nut: -------------------------------------------------------------------------------- 1 | // https://github.com/birjolaxew/csgo-vscripts 2 | // == UI messages 3 | // Handles displaying stuff to players 4 | // Exposes: 5 | // ::ShowMessage(msg, player, style="", delay=0.0) 6 | // ::ShowMessageSome(msg, filter, style="", delay=0.0) 7 | 8 | DoIncludeScript("vip/lib/players.nut", null); 9 | 10 | if (!("_LOADED_MODULE_UI" in getroottable())) { 11 | ::_LOADED_MODULE_UI <- true; 12 | ::ShowMessage <- function(msg, player, style="", delay=0.0) { 13 | local hudhint = Entities.CreateByClassname("env_hudhint"); 14 | hudhint.__KeyValueFromString("message", ""+msg+""); 15 | EntFireByHandle(hudhint, "ShowHudHint", "", delay, player, null); 16 | EntFireByHandle(hudhint, "Kill", "", 0.5, null, null); 17 | } 18 | 19 | ::ShowMessageSome <- function(msg, filter, style="") { 20 | local ent = Entities.First(); 21 | while (ent != null) { 22 | if (ent.GetClassname() == "player") { 23 | if (filter == null || filter(ent)) { 24 | ::ShowMessage(msg, ent, style); 25 | } 26 | } 27 | ent = Entities.Next(ent); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /vip/resources/materials/models/vip/helicopter/helicopter_rescue.vmt: -------------------------------------------------------------------------------- 1 | VertexLitGeneric 2 | { 3 | $baseTexture "models/vip/helicopter/helicopter_rescue" 4 | } 5 | -------------------------------------------------------------------------------- /vip/resources/materials/models/vip/helicopter/helicopter_rescue.vtf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/materials/models/vip/helicopter/helicopter_rescue.vtf -------------------------------------------------------------------------------- /vip/resources/materials/models/vip/helicopter/helicopter_rescue_windows.vmt: -------------------------------------------------------------------------------- 1 | VertexLitGeneric 2 | { 3 | $baseTexture "models/vip/helicopter/helicopter_rescue_windows" 4 | 5 | $nocull 1 6 | 7 | $envmap env_cubemap 8 | $envmaptint "[.4 .4 .4]" 9 | } 10 | -------------------------------------------------------------------------------- /vip/resources/materials/models/vip/helicopter/helicopter_rescue_windows.vtf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/materials/models/vip/helicopter/helicopter_rescue_windows.vtf -------------------------------------------------------------------------------- /vip/resources/models/hostage/v_vip_arm.dx90.vtx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/hostage/v_vip_arm.dx90.vtx -------------------------------------------------------------------------------- /vip/resources/models/hostage/v_vip_arm.mdl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/hostage/v_vip_arm.mdl -------------------------------------------------------------------------------- /vip/resources/models/hostage/v_vip_arm.vvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/hostage/v_vip_arm.vvd -------------------------------------------------------------------------------- /vip/resources/models/hostage/vip_carry.dx90.vtx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/hostage/vip_carry.dx90.vtx -------------------------------------------------------------------------------- /vip/resources/models/hostage/vip_carry.mdl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/hostage/vip_carry.mdl -------------------------------------------------------------------------------- /vip/resources/models/hostage/vip_carry.vvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/hostage/vip_carry.vvd -------------------------------------------------------------------------------- /vip/resources/models/hostage/vip_ground.dx90.vtx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/hostage/vip_ground.dx90.vtx -------------------------------------------------------------------------------- /vip/resources/models/hostage/vip_ground.mdl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/hostage/vip_ground.mdl -------------------------------------------------------------------------------- /vip/resources/models/hostage/vip_ground.phy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/hostage/vip_ground.phy -------------------------------------------------------------------------------- /vip/resources/models/hostage/vip_ground.vvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/hostage/vip_ground.vvd -------------------------------------------------------------------------------- /vip/resources/models/player/custom_player/legacy/ctm_heavy2.dx90.vtx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/player/custom_player/legacy/ctm_heavy2.dx90.vtx -------------------------------------------------------------------------------- /vip/resources/models/player/custom_player/legacy/ctm_heavy2.mdl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/player/custom_player/legacy/ctm_heavy2.mdl -------------------------------------------------------------------------------- /vip/resources/models/player/custom_player/legacy/ctm_heavy2.phy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/player/custom_player/legacy/ctm_heavy2.phy -------------------------------------------------------------------------------- /vip/resources/models/player/custom_player/legacy/ctm_heavy2.vvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/player/custom_player/legacy/ctm_heavy2.vvd -------------------------------------------------------------------------------- /vip/resources/models/vip/helicopter.ani: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/vip/helicopter.ani -------------------------------------------------------------------------------- /vip/resources/models/vip/helicopter.dx90.vtx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/vip/helicopter.dx90.vtx -------------------------------------------------------------------------------- /vip/resources/models/vip/helicopter.mdl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/vip/helicopter.mdl -------------------------------------------------------------------------------- /vip/resources/models/vip/helicopter.phy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/vip/helicopter.phy -------------------------------------------------------------------------------- /vip/resources/models/vip/helicopter.vvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/models/vip/helicopter.vvd -------------------------------------------------------------------------------- /vip/resources/sound/vip.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/sound/vip.wav -------------------------------------------------------------------------------- /vip/resources/sound/vip/fx_roundend_12seconds.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/sound/vip/fx_roundend_12seconds.wav -------------------------------------------------------------------------------- /vip/resources/sound/vip/fx_roundend_12seconds_flatline.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/sound/vip/fx_roundend_12seconds_flatline.wav -------------------------------------------------------------------------------- /vip/resources/sound/vip/fx_vipdown.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/sound/vip/fx_vipdown.wav -------------------------------------------------------------------------------- /vip/resources/sound/vip/vip.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birjj/csgo-vscripts/7c2cf3553e475ba98a9eb75c48248ec65bdbca68/vip/resources/sound/vip/vip.wav -------------------------------------------------------------------------------- /vip/vmfs/instance_vip_entities.vmf: -------------------------------------------------------------------------------- 1 | versioninfo 2 | { 3 | "editorversion" "400" 4 | "editorbuild" "8075" 5 | "mapversion" "95" 6 | "formatversion" "100" 7 | "prefab" "0" 8 | } 9 | visgroups 10 | { 11 | } 12 | viewsettings 13 | { 14 | "bSnapToGrid" "1" 15 | "bShowGrid" "1" 16 | "bShowLogicalGrid" "0" 17 | "nGridSpacing" "8" 18 | "bShow3DGrid" "1" 19 | } 20 | world 21 | { 22 | "id" "1" 23 | "mapversion" "95" 24 | "classname" "worldspawn" 25 | "detailmaterial" "detail/detailsprites" 26 | "detailvbsp" "detail.vbsp" 27 | "maxpropscreenwidth" "-1" 28 | "skyname" "sky_dust" 29 | } 30 | entity 31 | { 32 | "id" "1988" 33 | "classname" "ambient_generic" 34 | "health" "10" 35 | "message" "vip/fx_roundend_12seconds_flatline.wav" 36 | "pitch" "100" 37 | "pitchstart" "100" 38 | "radius" "1024" 39 | "spawnflags" "17" 40 | "targetname" "vip_snd_fx_bleedout" 41 | "origin" "120 -8 8.21867" 42 | editor 43 | { 44 | "color" "220 30 220" 45 | "visgroupshown" "1" 46 | "visgroupautoshown" "1" 47 | "logicalpos" "[0 5500]" 48 | } 49 | } 50 | entity 51 | { 52 | "id" "1991" 53 | "classname" "ambient_generic" 54 | "health" "10" 55 | "message" "vip/fx_vipdown.wav" 56 | "pitch" "100" 57 | "pitchstart" "100" 58 | "radius" "1024" 59 | "spawnflags" "17" 60 | "targetname" "vip_snd_fx_downed" 61 | "origin" "120 -24 8.21867" 62 | editor 63 | { 64 | "color" "220 30 220" 65 | "visgroupshown" "1" 66 | "visgroupautoshown" "1" 67 | "logicalpos" "[0 5500]" 68 | } 69 | } 70 | entity 71 | { 72 | "id" "1705" 73 | "classname" "ambient_generic" 74 | "health" "10" 75 | "message" "vip/fx_vipdown.wav" 76 | "pitch" "100" 77 | "pitchstart" "100" 78 | "radius" "1024" 79 | "spawnflags" "16" 80 | "targetname" "vip_snd_protect_2" 81 | "origin" "-120 -8 8.21867" 82 | editor 83 | { 84 | "color" "220 30 220" 85 | "visgroupshown" "1" 86 | "visgroupautoshown" "1" 87 | "logicalpos" "[0 5500]" 88 | } 89 | } 90 | entity 91 | { 92 | "id" "1708" 93 | "classname" "ambient_generic" 94 | "health" "10" 95 | "message" "vip/fx_roundend_12seconds.wav" 96 | "pitch" "100" 97 | "pitchstart" "100" 98 | "radius" "1024" 99 | "spawnflags" "16" 100 | "targetname" "vip_snd_protect_3" 101 | "origin" "-120 -24 8.21867" 102 | editor 103 | { 104 | "color" "220 30 220" 105 | "visgroupshown" "1" 106 | "visgroupautoshown" "1" 107 | "logicalpos" "[0 5500]" 108 | } 109 | } 110 | entity 111 | { 112 | "id" "1796" 113 | "classname" "ambient_generic" 114 | "health" "10" 115 | "message" "vip/fx_vipdown.wav" 116 | "pitch" "100" 117 | "pitchstart" "100" 118 | "radius" "1024" 119 | "spawnflags" "17" 120 | "targetname" "vip_snd_downed_1" 121 | "origin" "-72 8 8.21867" 122 | editor 123 | { 124 | "color" "220 30 220" 125 | "visgroupshown" "1" 126 | "visgroupautoshown" "1" 127 | "logicalpos" "[0 5500]" 128 | } 129 | } 130 | entity 131 | { 132 | "id" "1805" 133 | "classname" "ambient_generic" 134 | "health" "10" 135 | "message" "vip/vip.wav" 136 | "pitch" "100" 137 | "pitchstart" "100" 138 | "radius" "1024" 139 | "spawnflags" "17" 140 | "targetname" "vip_snd_killed_1" 141 | "origin" "-24 8 8.21867" 142 | editor 143 | { 144 | "color" "220 30 220" 145 | "visgroupshown" "1" 146 | "visgroupautoshown" "1" 147 | "logicalpos" "[0 5500]" 148 | } 149 | } 150 | entity 151 | { 152 | "id" "1817" 153 | "classname" "ambient_generic" 154 | "health" "10" 155 | "message" "vip/vip.wav" 156 | "pitch" "100" 157 | "pitchstart" "100" 158 | "radius" "1024" 159 | "spawnflags" "17" 160 | "targetname" "vip_snd_escaped_1" 161 | "origin" "24 8 8.21867" 162 | editor 163 | { 164 | "color" "220 30 220" 165 | "visgroupshown" "1" 166 | "visgroupautoshown" "1" 167 | "logicalpos" "[0 5500]" 168 | } 169 | } 170 | entity 171 | { 172 | "id" "1826" 173 | "classname" "ambient_generic" 174 | "health" "10" 175 | "message" "vip/vip.wav" 176 | "pitch" "100" 177 | "pitchstart" "100" 178 | "radius" "1024" 179 | "spawnflags" "17" 180 | "targetname" "vip_snd_late_1" 181 | "origin" "72 8 8.21867" 182 | editor 183 | { 184 | "color" "220 30 220" 185 | "visgroupshown" "1" 186 | "visgroupautoshown" "1" 187 | "logicalpos" "[0 5500]" 188 | } 189 | } 190 | entity 191 | { 192 | "id" "1835" 193 | "classname" "ambient_generic" 194 | "health" "10" 195 | "message" "vip/fx_roundend_12seconds.wav" 196 | "pitch" "100" 197 | "pitchstart" "100" 198 | "radius" "1024" 199 | "spawnflags" "17" 200 | "targetname" "vip_snd_fx_timeout" 201 | "origin" "120 8 8.21867" 202 | editor 203 | { 204 | "color" "220 30 220" 205 | "visgroupshown" "1" 206 | "visgroupautoshown" "1" 207 | "logicalpos" "[0 5500]" 208 | } 209 | } 210 | entity 211 | { 212 | "id" "1904" 213 | "classname" "ambient_generic" 214 | "health" "10" 215 | "message" "vip/vip.wav" 216 | "pitch" "100" 217 | "pitchstart" "100" 218 | "radius" "1024" 219 | "spawnflags" "17" 220 | "targetname" "vip_snd_downed_2" 221 | "origin" "-72 -8 8.21867" 222 | editor 223 | { 224 | "color" "220 30 220" 225 | "visgroupshown" "1" 226 | "visgroupautoshown" "1" 227 | "logicalpos" "[0 5500]" 228 | } 229 | } 230 | entity 231 | { 232 | "id" "1907" 233 | "classname" "ambient_generic" 234 | "health" "10" 235 | "message" "vip/fx_roundend_12seconds_flatline.wav" 236 | "pitch" "100" 237 | "pitchstart" "100" 238 | "radius" "1024" 239 | "spawnflags" "17" 240 | "targetname" "vip_snd_downed_3" 241 | "origin" "-72 -24 8.21867" 242 | editor 243 | { 244 | "color" "220 30 220" 245 | "visgroupshown" "1" 246 | "visgroupautoshown" "1" 247 | "logicalpos" "[0 5500]" 248 | } 249 | } 250 | entity 251 | { 252 | "id" "994" 253 | "classname" "point_worldtext" 254 | "angles" "75 90 0" 255 | "color" "255 255 255" 256 | "message" "Sound Entities" 257 | "rendermode" "10" 258 | "spawnflags" "1" 259 | "targetname" "text" 260 | "textsize" "32" 261 | "origin" "-128 32 16" 262 | editor 263 | { 264 | "color" "220 30 220" 265 | "visgroupshown" "1" 266 | "visgroupautoshown" "1" 267 | "logicalpos" "[0 0]" 268 | } 269 | } 270 | entity 271 | { 272 | "id" "1022" 273 | "classname" "point_worldtext" 274 | "angles" "60 90 0" 275 | "color" "255 255 255" 276 | "message" "Event Listeners" 277 | "rendermode" "10" 278 | "spawnflags" "1" 279 | "targetname" "text" 280 | "textsize" "32" 281 | "origin" "-128 224 16" 282 | editor 283 | { 284 | "color" "220 30 220" 285 | "visgroupshown" "1" 286 | "visgroupautoshown" "1" 287 | "logicalpos" "[0 0]" 288 | } 289 | } 290 | entity 291 | { 292 | "id" "1032" 293 | "classname" "point_worldtext" 294 | "angles" "75 90 0" 295 | "color" "255 255 255" 296 | "message" "Logic Entities" 297 | "rendermode" "10" 298 | "spawnflags" "1" 299 | "targetname" "text" 300 | "textsize" "32" 301 | "origin" "-128 128 16" 302 | editor 303 | { 304 | "color" "220 30 220" 305 | "visgroupshown" "1" 306 | "visgroupautoshown" "1" 307 | "logicalpos" "[0 0]" 308 | } 309 | } 310 | entity 311 | { 312 | "id" "957" 313 | "classname" "trigger_brush" 314 | "StartDisabled" "0" 315 | "targetname" "game_playerdie" 316 | solid 317 | { 318 | "id" "955" 319 | side 320 | { 321 | "id" "1" 322 | "plane" "(88 192 16) (88 208 16) (104 208 16)" 323 | "material" "TOOLS/TOOLSTRIGGER" 324 | "uaxis" "[1 0 0 -32] 0.25" 325 | "vaxis" "[0 -1 0 0] 0.25" 326 | "rotation" "0" 327 | "lightmapscale" "16" 328 | "smoothing_groups" "0" 329 | } 330 | side 331 | { 332 | "id" "2" 333 | "plane" "(88 208 0) (88 192 0) (104 192 0)" 334 | "material" "TOOLS/TOOLSTRIGGER" 335 | "uaxis" "[1 0 0 -32] 0.25" 336 | "vaxis" "[0 -1 0 0] 0.25" 337 | "rotation" "0" 338 | "lightmapscale" "16" 339 | "smoothing_groups" "0" 340 | } 341 | side 342 | { 343 | "id" "3" 344 | "plane" "(88 192 0) (88 208 0) (88 208 16)" 345 | "material" "TOOLS/TOOLSTRIGGER" 346 | "uaxis" "[0 1 0 0] 0.25" 347 | "vaxis" "[0 0 -1 0] 0.25" 348 | "rotation" "0" 349 | "lightmapscale" "16" 350 | "smoothing_groups" "0" 351 | } 352 | side 353 | { 354 | "id" "4" 355 | "plane" "(104 208 0) (104 192 0) (104 192 16)" 356 | "material" "TOOLS/TOOLSTRIGGER" 357 | "uaxis" "[0 1 0 0] 0.25" 358 | "vaxis" "[0 0 -1 0] 0.25" 359 | "rotation" "0" 360 | "lightmapscale" "16" 361 | "smoothing_groups" "0" 362 | } 363 | side 364 | { 365 | "id" "5" 366 | "plane" "(88 208 0) (104 208 0) (104 208 16)" 367 | "material" "TOOLS/TOOLSTRIGGER" 368 | "uaxis" "[1 0 0 -32] 0.25" 369 | "vaxis" "[0 0 -1 0] 0.25" 370 | "rotation" "0" 371 | "lightmapscale" "16" 372 | "smoothing_groups" "0" 373 | } 374 | side 375 | { 376 | "id" "6" 377 | "plane" "(104 192 0) (88 192 0) (88 192 16)" 378 | "material" "TOOLS/TOOLSTRIGGER" 379 | "uaxis" "[1 0 0 -32] 0.25" 380 | "vaxis" "[0 0 -1 0] 0.25" 381 | "rotation" "0" 382 | "lightmapscale" "16" 383 | "smoothing_groups" "0" 384 | } 385 | editor 386 | { 387 | "color" "220 220 220" 388 | "visgroupshown" "1" 389 | "visgroupautoshown" "1" 390 | } 391 | } 392 | editor 393 | { 394 | "color" "220 30 220" 395 | "visgroupshown" "1" 396 | "visgroupautoshown" "1" 397 | "logicalpos" "[0 500]" 398 | } 399 | } 400 | entity 401 | { 402 | "id" "747" 403 | "classname" "filter_activator_name" 404 | "filtername" "vip_vip" 405 | "Negated" "1" 406 | "targetname" "@vip_filter_notvip" 407 | "origin" "-48 80 8" 408 | editor 409 | { 410 | "color" "220 30 220" 411 | "visgroupshown" "1" 412 | "visgroupautoshown" "1" 413 | "logicalpos" "[0 9000]" 414 | } 415 | } 416 | entity 417 | { 418 | "id" "749" 419 | "classname" "filter_activator_name" 420 | "filtername" "vip_vip" 421 | "Negated" "Allow entities that match criteria" 422 | "targetname" "@vip_filter_onlyvip" 423 | "origin" "-24 80 8" 424 | editor 425 | { 426 | "color" "220 30 220" 427 | "visgroupshown" "1" 428 | "visgroupautoshown" "1" 429 | "logicalpos" "[0 -11268]" 430 | } 431 | } 432 | entity 433 | { 434 | "id" "882" 435 | "classname" "filter_activator_team" 436 | "filterteam" "2" 437 | "Negated" "Allow entities that match criteria" 438 | "targetname" "@vip_filter_onlyts" 439 | "origin" "-96 80 8" 440 | editor 441 | { 442 | "color" "220 30 220" 443 | "visgroupshown" "1" 444 | "visgroupautoshown" "1" 445 | "logicalpos" "[0 6000]" 446 | } 447 | } 448 | entity 449 | { 450 | "id" "908" 451 | "classname" "logic_eventlistener" 452 | "EventName" "player_connect_full" 453 | "FetchEventData" "1" 454 | "IsEnabled" "1" 455 | "targetname" "player_connect_full_listener" 456 | "TeamNum" "-1" 457 | connections 458 | { 459 | "OnEventFired" "vip_scriptRunScriptCodeTriggerEvent(PLAYER_CONNECT_FULL)0-1" 460 | } 461 | "origin" "72 176 8" 462 | editor 463 | { 464 | "color" "220 30 220" 465 | "visgroupshown" "1" 466 | "visgroupautoshown" "1" 467 | "logicalpos" "[0 1500]" 468 | } 469 | } 470 | entity 471 | { 472 | "id" "918" 473 | "classname" "logic_eventlistener" 474 | "EventName" "player_connect_client" 475 | "FetchEventData" "1" 476 | "IsEnabled" "1" 477 | "targetname" "player_connect_client_listener" 478 | "TeamNum" "-1" 479 | connections 480 | { 481 | "OnEventFired" "vip_scriptRunScriptCodeTriggerEvent(PLAYER_CONNECT_CLIENT)0-1" 482 | } 483 | "origin" "72 200 8" 484 | editor 485 | { 486 | "color" "220 30 220" 487 | "visgroupshown" "1" 488 | "visgroupautoshown" "1" 489 | "logicalpos" "[0 1500]" 490 | } 491 | } 492 | entity 493 | { 494 | "id" "693" 495 | "classname" "logic_eventlistener" 496 | "EventName" "player_connect" 497 | "FetchEventData" "1" 498 | "IsEnabled" "1" 499 | "targetname" "player_connect_listener" 500 | "TeamNum" "-1" 501 | connections 502 | { 503 | "OnEventFired" "vip_scriptRunScriptCodeTriggerEvent(PLAYER_CONNECT)0-1" 504 | } 505 | "origin" "48 176 8" 506 | editor 507 | { 508 | "color" "220 30 220" 509 | "visgroupshown" "1" 510 | "visgroupautoshown" "1" 511 | "logicalpos" "[0 1500]" 512 | } 513 | } 514 | entity 515 | { 516 | "id" "646" 517 | "classname" "logic_eventlistener" 518 | "EventName" "player_changename" 519 | "FetchEventData" "1" 520 | "IsEnabled" "1" 521 | "targetname" "player_changename_listener" 522 | "TeamNum" "-1" 523 | connections 524 | { 525 | "OnEventFired" "vip_scriptRunScriptCodeTriggerEvent(PLAYER_CHANGENAME)0-1" 526 | } 527 | "origin" "24 176 8" 528 | editor 529 | { 530 | "color" "220 30 220" 531 | "visgroupshown" "1" 532 | "visgroupautoshown" "1" 533 | "logicalpos" "[0 1500]" 534 | } 535 | } 536 | entity 537 | { 538 | "id" "658" 539 | "classname" "logic_eventlistener" 540 | "EventName" "player_team" 541 | "FetchEventData" "1" 542 | "IsEnabled" "1" 543 | "targetname" "player_team_listener" 544 | "TeamNum" "-1" 545 | connections 546 | { 547 | "OnEventFired" "vip_scriptRunScriptCodeTriggerEvent(PLAYER_TEAM)0-1" 548 | } 549 | "origin" "48 200 8" 550 | editor 551 | { 552 | "color" "220 30 220" 553 | "visgroupshown" "1" 554 | "visgroupautoshown" "1" 555 | "logicalpos" "[0 1500]" 556 | } 557 | } 558 | entity 559 | { 560 | "id" "607" 561 | "classname" "logic_eventlistener" 562 | "EventName" "hostage_stops_following" 563 | "FetchEventData" "1" 564 | "IsEnabled" "1" 565 | "targetname" "hostage_stops_following_listener" 566 | "TeamNum" "-1" 567 | connections 568 | { 569 | "OnEventFired" "vip_scriptRunScriptCodeTriggerEvent(HOSTAGE_STOPS_FOLLOWING)0-1" 570 | } 571 | "origin" "0 176 8" 572 | editor 573 | { 574 | "color" "220 30 220" 575 | "visgroupshown" "1" 576 | "visgroupautoshown" "1" 577 | "logicalpos" "[0 1500]" 578 | } 579 | } 580 | entity 581 | { 582 | "id" "541" 583 | "classname" "filter_damage_type" 584 | "damagetype" "512" 585 | "Negated" "Allow entities that match criteria" 586 | "targetname" "vip_filter_damage" 587 | "origin" "-120 80 8" 588 | editor 589 | { 590 | "color" "220 30 220" 591 | "visgroupshown" "1" 592 | "visgroupautoshown" "1" 593 | "logicalpos" "[0 500]" 594 | } 595 | } 596 | entity 597 | { 598 | "id" "433" 599 | "classname" "logic_eventlistener" 600 | "EventName" "hostage_follows" 601 | "FetchEventData" "1" 602 | "IsEnabled" "1" 603 | "targetname" "hostage_follows_listener" 604 | "TeamNum" "-1" 605 | connections 606 | { 607 | "OnEventFired" "vip_scriptRunScriptCodeTriggerEvent(HOSTAGE_FOLLOWS)0-1" 608 | } 609 | "origin" "24 200 8" 610 | editor 611 | { 612 | "color" "220 30 220" 613 | "visgroupshown" "1" 614 | "visgroupautoshown" "1" 615 | "logicalpos" "[0 1500]" 616 | } 617 | } 618 | entity 619 | { 620 | "id" "435" 621 | "classname" "filter_damage_type" 622 | "damagetype" "2" 623 | "Negated" "1" 624 | "targetname" "vip_hostage_damage_filter" 625 | "origin" "-72 80 8" 626 | editor 627 | { 628 | "color" "220 30 220" 629 | "visgroupshown" "1" 630 | "visgroupautoshown" "1" 631 | "logicalpos" "[0 2500]" 632 | } 633 | } 634 | entity 635 | { 636 | "id" "437" 637 | "classname" "hostage_entity" 638 | "angles" "0 270 0" 639 | "HostageSpawnRandomFactor" "1" 640 | "targetname" "vip_hostage" 641 | "origin" "112 96 0" 642 | editor 643 | { 644 | "color" "220 30 220" 645 | "visgroupshown" "1" 646 | "visgroupautoshown" "1" 647 | "comments" "This is Alfred. Alfred is a nice guy." 648 | "logicalpos" "[0 500]" 649 | } 650 | } 651 | entity 652 | { 653 | "id" "439" 654 | "classname" "env_entity_maker" 655 | "angles" "0 0 0" 656 | "EntityTemplate" "vip_template" 657 | "PostSpawnDirection" "0 0 0" 658 | "PostSpawnDirectionVariance" "0.15" 659 | "spawnflags" "0" 660 | "targetname" "vip_entity_maker" 661 | "origin" "72 104 8" 662 | editor 663 | { 664 | "color" "220 30 220" 665 | "visgroupshown" "1" 666 | "visgroupautoshown" "1" 667 | "logicalpos" "[0 0]" 668 | } 669 | } 670 | entity 671 | { 672 | "id" "481" 673 | "classname" "point_template" 674 | "spawnflags" "2" 675 | "targetname" "vip_template" 676 | "Template01" "vip_hostage" 677 | "origin" "72 80 8" 678 | editor 679 | { 680 | "color" "220 30 220" 681 | "visgroupshown" "1" 682 | "visgroupautoshown" "1" 683 | "logicalpos" "[0 0]" 684 | } 685 | } 686 | entity 687 | { 688 | "id" "203" 689 | "classname" "game_text" 690 | "channel" "1" 691 | "color" "255 255 255" 692 | "color2" "49 82 102" 693 | "effect" "2" 694 | "fadein" "0.02" 695 | "fadeout" "1" 696 | "fxtime" "0.25" 697 | "holdtime" "4" 698 | "message" "You are the VIP!" 699 | "spawnflags" "0" 700 | "targetname" "vip_text_notification" 701 | "x" "-1" 702 | "y" "0.4" 703 | "origin" "-24 104 8" 704 | editor 705 | { 706 | "color" "220 30 220" 707 | "visgroupshown" "1" 708 | "visgroupautoshown" "1" 709 | "logicalpos" "[0 0]" 710 | } 711 | } 712 | entity 713 | { 714 | "id" "265" 715 | "classname" "game_text" 716 | "channel" "1" 717 | "color" "255 255 255" 718 | "color2" "0 0 0" 719 | "effect" "2" 720 | "fadein" "0.02" 721 | "fadeout" "1" 722 | "fxtime" "0.25" 723 | "holdtime" "10" 724 | "message" "The VIP was assassinated!" 725 | "spawnflags" "1" 726 | "targetname" "vip_text_round_end" 727 | "x" "-1" 728 | "y" "0.3" 729 | "origin" "-48 104 8" 730 | editor 731 | { 732 | "color" "220 30 220" 733 | "visgroupshown" "1" 734 | "visgroupautoshown" "1" 735 | "logicalpos" "[0 0]" 736 | } 737 | } 738 | entity 739 | { 740 | "id" "409" 741 | "classname" "game_text" 742 | "channel" "1" 743 | "color" "255 255 255" 744 | "color2" "0 0 0" 745 | "fadein" "0.5" 746 | "fadeout" "1" 747 | "fxtime" "0.25" 748 | "holdtime" "10" 749 | "message" "The VIP was assassinated!" 750 | "spawnflags" "1" 751 | "targetname" "vip_text_backup" 752 | "x" "-1" 753 | "y" "0.3" 754 | "origin" "-72 104 8" 755 | editor 756 | { 757 | "color" "220 30 220" 758 | "visgroupshown" "1" 759 | "visgroupautoshown" "1" 760 | "logicalpos" "[0 0]" 761 | } 762 | } 763 | entity 764 | { 765 | "id" "114" 766 | "classname" "logic_eventlistener" 767 | "EventName" "round_end" 768 | "FetchEventData" "1" 769 | "IsEnabled" "1" 770 | "targetname" "round_end_listener" 771 | "TeamNum" "-1" 772 | connections 773 | { 774 | "OnEventFired" "vip_scriptRunScriptCodeTriggerEvent(ROUND_END)0-1" 775 | } 776 | "origin" "0 200 8" 777 | editor 778 | { 779 | "color" "220 30 220" 780 | "visgroupshown" "1" 781 | "visgroupautoshown" "1" 782 | "logicalpos" "[0 0]" 783 | } 784 | } 785 | entity 786 | { 787 | "id" "74" 788 | "classname" "logic_eventlistener" 789 | "EventName" "inspect_weapon" 790 | "FetchEventData" "1" 791 | "IsEnabled" "1" 792 | "targetname" "inspect_weapon_listener" 793 | "TeamNum" "-1" 794 | connections 795 | { 796 | "OnEventFired" "vip_scriptRunScriptCodeTriggerEvent(INSPECT_WEAPON)0-1" 797 | } 798 | "origin" "-24 200 8" 799 | editor 800 | { 801 | "color" "220 30 220" 802 | "visgroupshown" "1" 803 | "visgroupautoshown" "1" 804 | "logicalpos" "[0 0]" 805 | } 806 | } 807 | entity 808 | { 809 | "id" "76" 810 | "classname" "logic_eventlistener" 811 | "EventName" "bot_takeover" 812 | "FetchEventData" "1" 813 | "IsEnabled" "1" 814 | "targetname" "bot_takeover_listener" 815 | "TeamNum" "-1" 816 | connections 817 | { 818 | "OnEventFired" "vip_scriptRunScriptCodeTriggerEvent(BOT_TAKEOVER)0-1" 819 | } 820 | "origin" "-24 176 8" 821 | editor 822 | { 823 | "color" "220 30 220" 824 | "visgroupshown" "1" 825 | "visgroupautoshown" "1" 826 | "logicalpos" "[0 0]" 827 | } 828 | } 829 | entity 830 | { 831 | "id" "2" 832 | "classname" "logic_eventlistener" 833 | "EventName" "player_disconnect" 834 | "FetchEventData" "1" 835 | "IsEnabled" "1" 836 | "targetname" "player_disconnect_listener" 837 | "TeamNum" "-1" 838 | connections 839 | { 840 | "OnEventFired" "vip_scriptRunScriptCodeTriggerEvent(PLAYER_DISCONNECT)0-1" 841 | } 842 | "origin" "-48 176 8" 843 | editor 844 | { 845 | "color" "220 30 220" 846 | "visgroupshown" "1" 847 | "visgroupautoshown" "1" 848 | "logicalpos" "[0 1500]" 849 | } 850 | } 851 | entity 852 | { 853 | "id" "4" 854 | "classname" "logic_eventlistener" 855 | "EventName" "round_start" 856 | "FetchEventData" "1" 857 | "IsEnabled" "1" 858 | "targetname" "round_start_listener" 859 | "TeamNum" "-1" 860 | connections 861 | { 862 | "OnEventFired" "vip_scriptRunScriptCodeTriggerEvent(ROUND_START)0-1" 863 | } 864 | "origin" "-48 200 8" 865 | editor 866 | { 867 | "color" "220 30 220" 868 | "visgroupshown" "1" 869 | "visgroupautoshown" "1" 870 | "logicalpos" "[0 2000]" 871 | } 872 | } 873 | entity 874 | { 875 | "id" "6" 876 | "classname" "logic_eventlistener" 877 | "EventName" "player_spawn" 878 | "FetchEventData" "1" 879 | "IsEnabled" "1" 880 | "targetname" "player_spawn_listener" 881 | "TeamNum" "-1" 882 | connections 883 | { 884 | "OnEventFired" "vip_scriptRunScriptCodeTriggerEvent(PLAYER_SPAWN)0-1" 885 | } 886 | "origin" "-72 176 8" 887 | editor 888 | { 889 | "color" "220 30 220" 890 | "visgroupshown" "1" 891 | "visgroupautoshown" "1" 892 | "logicalpos" "[0 2500]" 893 | } 894 | } 895 | entity 896 | { 897 | "id" "8" 898 | "classname" "logic_eventlistener" 899 | "EventName" "player_hurt" 900 | "FetchEventData" "1" 901 | "IsEnabled" "1" 902 | "targetname" "player_hurt_listener" 903 | "TeamNum" "-1" 904 | connections 905 | { 906 | "OnEventFired" "vip_scriptRunScriptCodeTriggerEvent(PLAYER_HURT)0-1" 907 | } 908 | "origin" "-72 200 8" 909 | editor 910 | { 911 | "color" "220 30 220" 912 | "visgroupshown" "1" 913 | "visgroupautoshown" "1" 914 | "logicalpos" "[0 3500]" 915 | } 916 | } 917 | entity 918 | { 919 | "id" "10" 920 | "classname" "logic_eventlistener" 921 | "EventName" "player_use" 922 | "FetchEventData" "1" 923 | "IsEnabled" "1" 924 | "targetname" "player_use_listener" 925 | "TeamNum" "-1" 926 | connections 927 | { 928 | "OnEventFired" "vip_scriptRunScriptCodeTriggerEvent(PLAYER_USE)0-1" 929 | } 930 | "origin" "-96 176 8" 931 | editor 932 | { 933 | "color" "220 30 220" 934 | "visgroupshown" "1" 935 | "visgroupautoshown" "1" 936 | "logicalpos" "[0 8500]" 937 | } 938 | } 939 | entity 940 | { 941 | "id" "12" 942 | "classname" "logic_eventlistener" 943 | "EventName" "player_death" 944 | "FetchEventData" "1" 945 | "IsEnabled" "1" 946 | "targetname" "player_death_listener" 947 | "TeamNum" "-1" 948 | connections 949 | { 950 | "OnEventFired" "vip_scriptRunScriptCodeTriggerEvent(PLAYER_DEATH)0-1" 951 | } 952 | "origin" "-96 200 8" 953 | editor 954 | { 955 | "color" "220 30 220" 956 | "visgroupshown" "1" 957 | "visgroupautoshown" "1" 958 | "logicalpos" "[0 7000]" 959 | } 960 | } 961 | entity 962 | { 963 | "id" "14" 964 | "classname" "logic_eventlistener" 965 | "EventName" "item_equip" 966 | "FetchEventData" "1" 967 | "IsEnabled" "1" 968 | "targetname" "item_equip_listener" 969 | "TeamNum" "-1" 970 | connections 971 | { 972 | "OnEventFired" "vip_scriptRunScriptCodeTriggerEvent(ITEM_EQUIP)0-1" 973 | } 974 | "origin" "-120 176 8" 975 | editor 976 | { 977 | "color" "220 30 220" 978 | "visgroupshown" "1" 979 | "visgroupautoshown" "1" 980 | "logicalpos" "[0 8000]" 981 | } 982 | } 983 | entity 984 | { 985 | "id" "16" 986 | "classname" "logic_eventlistener" 987 | "EventName" "round_freeze_end" 988 | "FetchEventData" "1" 989 | "IsEnabled" "1" 990 | "targetname" "round_freeze_end_listener" 991 | "TeamNum" "-1" 992 | connections 993 | { 994 | "OnEventFired" "vip_scriptRunScriptCodeTriggerEvent(ROUND_FREEZE_END)0-1" 995 | } 996 | "origin" "-120 200 8" 997 | editor 998 | { 999 | "color" "220 30 220" 1000 | "visgroupshown" "1" 1001 | "visgroupautoshown" "1" 1002 | "logicalpos" "[0 6500]" 1003 | } 1004 | } 1005 | entity 1006 | { 1007 | "id" "18" 1008 | "classname" "logic_script" 1009 | "targetname" "vip_script" 1010 | "thinkfunction" "Think" 1011 | "vscripts" "vip/gamemode.nut" 1012 | "origin" "-120 104 8" 1013 | editor 1014 | { 1015 | "color" "220 30 220" 1016 | "visgroupshown" "1" 1017 | "visgroupautoshown" "1" 1018 | "logicalpos" "[0 7500]" 1019 | } 1020 | } 1021 | entity 1022 | { 1023 | "id" "22" 1024 | "classname" "ambient_generic" 1025 | "health" "10" 1026 | "message" "vip/vip.wav" 1027 | "pitch" "100" 1028 | "pitchstart" "100" 1029 | "radius" "1024" 1030 | "spawnflags" "16" 1031 | "targetname" "vip_snd_protect_1" 1032 | "origin" "-120 8 8.21867" 1033 | editor 1034 | { 1035 | "color" "220 30 220" 1036 | "visgroupshown" "1" 1037 | "visgroupautoshown" "1" 1038 | "logicalpos" "[0 5500]" 1039 | } 1040 | } 1041 | cameras 1042 | { 1043 | "activecamera" "-1" 1044 | } 1045 | cordons 1046 | { 1047 | "active" "0" 1048 | } 1049 | -------------------------------------------------------------------------------- /vip/vmfs/instance_vip_rescue.vmf: -------------------------------------------------------------------------------- 1 | versioninfo 2 | { 3 | "editorversion" "400" 4 | "editorbuild" "8075" 5 | "mapversion" "104" 6 | "formatversion" "100" 7 | "prefab" "0" 8 | } 9 | visgroups 10 | { 11 | } 12 | viewsettings 13 | { 14 | "bSnapToGrid" "1" 15 | "bShowGrid" "1" 16 | "bShowLogicalGrid" "0" 17 | "nGridSpacing" "8" 18 | "bShow3DGrid" "0" 19 | } 20 | world 21 | { 22 | "id" "1" 23 | "mapversion" "104" 24 | "classname" "worldspawn" 25 | "comment" "Decompiled by BSPSource v1.3.24 from test_vip" 26 | "detailmaterial" "detail/detailsprites" 27 | "detailvbsp" "detail.vbsp" 28 | "maxpropscreenwidth" "-1" 29 | "skyname" "office" 30 | solid 31 | { 32 | "id" "2173" 33 | side 34 | { 35 | "id" "483" 36 | "plane" "(320 -56 192) (192 -56 192) (192 -48 192)" 37 | "material" "TOOLS/TOOLSCLIP" 38 | "uaxis" "[1 0 0 0] 0.25" 39 | "vaxis" "[0 -1 0 0] 0.25" 40 | "rotation" "0" 41 | "lightmapscale" "16" 42 | "smoothing_groups" "0" 43 | } 44 | side 45 | { 46 | "id" "484" 47 | "plane" "(320 -40 6.83591e-07) (192 -48 6.83591e-07) (192 -56 6.83591e-07)" 48 | "material" "TOOLS/TOOLSCLIP" 49 | "uaxis" "[1 0 0 0] 0.25" 50 | "vaxis" "[0 -1 0 0] 0.25" 51 | "rotation" "0" 52 | "lightmapscale" "16" 53 | "smoothing_groups" "0" 54 | } 55 | side 56 | { 57 | "id" "485" 58 | "plane" "(192 -56 6.83591e-07) (192 -48 6.83591e-07) (192 -48 192)" 59 | "material" "TOOLS/TOOLSCLIP" 60 | "uaxis" "[0 1 0 0] 0.25" 61 | "vaxis" "[0 0 -1 0] 0.25" 62 | "rotation" "0" 63 | "lightmapscale" "16" 64 | "smoothing_groups" "0" 65 | } 66 | side 67 | { 68 | "id" "486" 69 | "plane" "(320 -56 192) (320 -40 192) (320 -40 6.83591e-07)" 70 | "material" "TOOLS/TOOLSCLIP" 71 | "uaxis" "[0 1 0 0] 0.25" 72 | "vaxis" "[0 0 -1 0] 0.25" 73 | "rotation" "0" 74 | "lightmapscale" "16" 75 | "smoothing_groups" "0" 76 | } 77 | side 78 | { 79 | "id" "487" 80 | "plane" "(192 -48 6.83591e-07) (320 -40 6.83591e-07) (320 -40 192)" 81 | "material" "TOOLS/TOOLSCLIP" 82 | "uaxis" "[1 0 0 0] 0.25" 83 | "vaxis" "[0 0 -1 0] 0.25" 84 | "rotation" "0" 85 | "lightmapscale" "16" 86 | "smoothing_groups" "0" 87 | } 88 | side 89 | { 90 | "id" "488" 91 | "plane" "(192 -56 192) (320 -56 192) (320 -56 6.83591e-07)" 92 | "material" "TOOLS/TOOLSCLIP" 93 | "uaxis" "[1 0 0 0] 0.25" 94 | "vaxis" "[0 0 -1 0] 0.25" 95 | "rotation" "0" 96 | "lightmapscale" "16" 97 | "smoothing_groups" "0" 98 | } 99 | editor 100 | { 101 | "color" "0 123 152" 102 | "visgroupshown" "1" 103 | "visgroupautoshown" "1" 104 | } 105 | } 106 | solid 107 | { 108 | "id" "2178" 109 | side 110 | { 111 | "id" "500" 112 | "plane" "(320 40 192) (192 48 192) (192 56 192)" 113 | "material" "TOOLS/TOOLSCLIP" 114 | "uaxis" "[1 0 0 0] 0.25" 115 | "vaxis" "[0 -1 0 0] 0.25" 116 | "rotation" "0" 117 | "lightmapscale" "16" 118 | "smoothing_groups" "0" 119 | } 120 | side 121 | { 122 | "id" "499" 123 | "plane" "(320 56 6.83591e-07) (192 56 6.83591e-07) (192 48 6.83591e-07)" 124 | "material" "TOOLS/TOOLSCLIP" 125 | "uaxis" "[1 0 0 0] 0.25" 126 | "vaxis" "[0 -1 0 0] 0.25" 127 | "rotation" "0" 128 | "lightmapscale" "16" 129 | "smoothing_groups" "0" 130 | } 131 | side 132 | { 133 | "id" "498" 134 | "plane" "(192 48 6.83591e-07) (192 56 6.83591e-07) (192 56 192)" 135 | "material" "TOOLS/TOOLSCLIP" 136 | "uaxis" "[0 1 0 0] 0.25" 137 | "vaxis" "[0 0 -1 0] 0.25" 138 | "rotation" "0" 139 | "lightmapscale" "16" 140 | "smoothing_groups" "0" 141 | } 142 | side 143 | { 144 | "id" "497" 145 | "plane" "(320 40 192) (320 56 192) (320 56 6.83591e-07)" 146 | "material" "TOOLS/TOOLSCLIP" 147 | "uaxis" "[0 1 0 0] 0.25" 148 | "vaxis" "[0 0 -1 0] 0.25" 149 | "rotation" "0" 150 | "lightmapscale" "16" 151 | "smoothing_groups" "0" 152 | } 153 | side 154 | { 155 | "id" "496" 156 | "plane" "(192 56 6.83591e-07) (320 56 6.83591e-07) (320 56 192)" 157 | "material" "TOOLS/TOOLSCLIP" 158 | "uaxis" "[1 0 0 0] 0.25" 159 | "vaxis" "[0 0 -1 0] 0.25" 160 | "rotation" "0" 161 | "lightmapscale" "16" 162 | "smoothing_groups" "0" 163 | } 164 | side 165 | { 166 | "id" "495" 167 | "plane" "(192 48 192) (320 40 192) (320 40 6.83591e-07)" 168 | "material" "TOOLS/TOOLSCLIP" 169 | "uaxis" "[1 0 0 0] 0.25" 170 | "vaxis" "[0 0 -1 0] 0.25" 171 | "rotation" "0" 172 | "lightmapscale" "16" 173 | "smoothing_groups" "0" 174 | } 175 | editor 176 | { 177 | "color" "0 123 152" 178 | "visgroupshown" "1" 179 | "visgroupautoshown" "1" 180 | } 181 | } 182 | solid 183 | { 184 | "id" "2213" 185 | side 186 | { 187 | "id" "529" 188 | "plane" "(-104 56 191.986) (-104 120 191.986) (-72 136 191.986)" 189 | "material" "TOOLS/TOOLSCLIP" 190 | "uaxis" "[1 0 0 0] 0.25" 191 | "vaxis" "[0 -1 0 0] 0.25" 192 | "rotation" "0" 193 | "lightmapscale" "16" 194 | "smoothing_groups" "0" 195 | } 196 | side 197 | { 198 | "id" "528" 199 | "plane" "(-104 120 -0.0140228) (-104 56 -0.0140228) (176 56 -0.0140228)" 200 | "material" "TOOLS/TOOLSCLIP" 201 | "uaxis" "[1 0 0 0] 0.25" 202 | "vaxis" "[0 -1 0 0] 0.25" 203 | "rotation" "0" 204 | "lightmapscale" "16" 205 | "smoothing_groups" "0" 206 | } 207 | side 208 | { 209 | "id" "527" 210 | "plane" "(-104 56 -0.0148247) (-104 120 -0.0148256) (-104 120 191.986)" 211 | "material" "TOOLS/TOOLSCLIP" 212 | "uaxis" "[0 1 0 0] 0.25" 213 | "vaxis" "[0 0 -1 0] 0.25" 214 | "rotation" "0" 215 | "lightmapscale" "16" 216 | "smoothing_groups" "0" 217 | } 218 | side 219 | { 220 | "id" "526" 221 | "plane" "(-72 136 -0.0148229) (136 136 -0.0148258) (136 136 191.986)" 222 | "material" "TOOLS/TOOLSCLIP" 223 | "uaxis" "[1 0 0 0] 0.25" 224 | "vaxis" "[0 0 -1 0] 0.25" 225 | "rotation" "0" 226 | "lightmapscale" "16" 227 | "smoothing_groups" "0" 228 | } 229 | side 230 | { 231 | "id" "525" 232 | "plane" "(176 56 -0.0148214) (-104 56 -0.0148254) (-104 56 191.986)" 233 | "material" "TOOLS/TOOLSCLIP" 234 | "uaxis" "[1 0 0 0] 0.25" 235 | "vaxis" "[0 0 -1 0] 0.25" 236 | "rotation" "0" 237 | "lightmapscale" "16" 238 | "smoothing_groups" "0" 239 | } 240 | side 241 | { 242 | "id" "524" 243 | "plane" "(-104 120 -0.0148234) (-72 136 -0.0148239) (-72 136 191.986)" 244 | "material" "TOOLS/TOOLSCLIP" 245 | "uaxis" "[1 0 0 0] 0.25" 246 | "vaxis" "[0 0 -1 0] 0.25" 247 | "rotation" "0" 248 | "lightmapscale" "16" 249 | "smoothing_groups" "0" 250 | } 251 | side 252 | { 253 | "id" "523" 254 | "plane" "(136 136 -0.0148253) (168 128 -0.0148258) (168 128 191.986)" 255 | "material" "TOOLS/TOOLSCLIP" 256 | "uaxis" "[1 0 0 0] 0.25" 257 | "vaxis" "[0 0 -1 0] 0.25" 258 | "rotation" "0" 259 | "lightmapscale" "16" 260 | "smoothing_groups" "0" 261 | } 262 | side 263 | { 264 | "id" "522" 265 | "plane" "(168 128 -0.0148224) (176 56 -0.0148234) (176 56 191.986)" 266 | "material" "TOOLS/TOOLSCLIP" 267 | "uaxis" "[0 1 0 0] 0.25" 268 | "vaxis" "[0 0 -1 0] 0.25" 269 | "rotation" "0" 270 | "lightmapscale" "16" 271 | "smoothing_groups" "0" 272 | } 273 | editor 274 | { 275 | "color" "0 177 190" 276 | "visgroupshown" "1" 277 | "visgroupautoshown" "1" 278 | } 279 | } 280 | solid 281 | { 282 | "id" "2214" 283 | side 284 | { 285 | "id" "545" 286 | "plane" "(176 -56 192.001) (168 -128 192.001) (136 -136 192.001)" 287 | "material" "TOOLS/TOOLSCLIP" 288 | "uaxis" "[1 0 0 0] 0.25" 289 | "vaxis" "[0 1 0 0] 0.25" 290 | "rotation" "0" 291 | "lightmapscale" "16" 292 | "smoothing_groups" "0" 293 | } 294 | side 295 | { 296 | "id" "544" 297 | "plane" "(-72 -136 0.000808716) (136 -136 0.000808716) (168 -128 0.000808716)" 298 | "material" "TOOLS/TOOLSCLIP" 299 | "uaxis" "[1 0 0 0] 0.25" 300 | "vaxis" "[0 1 0 0] 0.25" 301 | "rotation" "0" 302 | "lightmapscale" "16" 303 | "smoothing_groups" "0" 304 | } 305 | side 306 | { 307 | "id" "543" 308 | "plane" "(-104 -56 192.001) (-104 -120 192.001) (-104 -120 5.93532e-06)" 309 | "material" "TOOLS/TOOLSCLIP" 310 | "uaxis" "[0 -1 0 0] 0.25" 311 | "vaxis" "[0 0 -1 0.0593262] 0.25" 312 | "rotation" "0" 313 | "lightmapscale" "16" 314 | "smoothing_groups" "0" 315 | } 316 | side 317 | { 318 | "id" "542" 319 | "plane" "(-72 -136 192.001) (136 -136 192.001) (136 -136 5.70901e-06)" 320 | "material" "TOOLS/TOOLSCLIP" 321 | "uaxis" "[1 0 0 0] 0.25" 322 | "vaxis" "[0 0 -1 0.0593262] 0.25" 323 | "rotation" "0" 324 | "lightmapscale" "16" 325 | "smoothing_groups" "0" 326 | } 327 | side 328 | { 329 | "id" "541" 330 | "plane" "(176 -56 192.001) (-104 -56 192.001) (-104 -56 6.16163e-06)" 331 | "material" "TOOLS/TOOLSCLIP" 332 | "uaxis" "[1 0 0 0] 0.25" 333 | "vaxis" "[0 0 -1 0.0593262] 0.25" 334 | "rotation" "0" 335 | "lightmapscale" "16" 336 | "smoothing_groups" "0" 337 | } 338 | side 339 | { 340 | "id" "540" 341 | "plane" "(-104 -120 192.001) (-72 -136 192.001) (-72 -136 7.67969e-06)" 342 | "material" "TOOLS/TOOLSCLIP" 343 | "uaxis" "[1 0 0 0] 0.25" 344 | "vaxis" "[0 0 -1 0.0593262] 0.25" 345 | "rotation" "0" 346 | "lightmapscale" "16" 347 | "smoothing_groups" "0" 348 | } 349 | side 350 | { 351 | "id" "539" 352 | "plane" "(136 -136 192.001) (168 -128 192.001) (168 -128 5.76768e-06)" 353 | "material" "TOOLS/TOOLSCLIP" 354 | "uaxis" "[1 0 0 0] 0.25" 355 | "vaxis" "[0 0 -1 0.0593262] 0.25" 356 | "rotation" "0" 357 | "lightmapscale" "16" 358 | "smoothing_groups" "0" 359 | } 360 | side 361 | { 362 | "id" "538" 363 | "plane" "(168 -128 192.001) (176 -56 192.001) (176 -56 8.14069e-06)" 364 | "material" "TOOLS/TOOLSCLIP" 365 | "uaxis" "[0 -1 0 0] 0.25" 366 | "vaxis" "[0 0 -1 0.0593262] 0.25" 367 | "rotation" "0" 368 | "lightmapscale" "16" 369 | "smoothing_groups" "0" 370 | } 371 | editor 372 | { 373 | "color" "0 177 190" 374 | "visgroupshown" "1" 375 | "visgroupautoshown" "1" 376 | } 377 | } 378 | solid 379 | { 380 | "id" "2216" 381 | side 382 | { 383 | "id" "546" 384 | "plane" "(-88 -48 191.986) (192 -48 191.986) (192 -56 191.986)" 385 | "material" "TOOLS/TOOLSCLIP" 386 | "uaxis" "[1 0 0 0] 0.25" 387 | "vaxis" "[0 -1 0 0] 0.25" 388 | "rotation" "0" 389 | "lightmapscale" "16" 390 | "smoothing_groups" "0" 391 | } 392 | side 393 | { 394 | "id" "547" 395 | "plane" "(-88 -56 -0.0140228) (192 -56 -0.0140228) (192 -48 -0.0140228)" 396 | "material" "TOOLS/TOOLSCLIP" 397 | "uaxis" "[1 0 0 0] 0.25" 398 | "vaxis" "[0 -1 0 0] 0.25" 399 | "rotation" "0" 400 | "lightmapscale" "16" 401 | "smoothing_groups" "0" 402 | } 403 | side 404 | { 405 | "id" "548" 406 | "plane" "(-88 -48 191.986) (-88 -56 191.986) (-88 -56 -0.0140228)" 407 | "material" "TOOLS/TOOLSCLIP" 408 | "uaxis" "[0 1 0 0] 0.25" 409 | "vaxis" "[0 0 -1 0] 0.25" 410 | "rotation" "0" 411 | "lightmapscale" "16" 412 | "smoothing_groups" "0" 413 | } 414 | side 415 | { 416 | "id" "549" 417 | "plane" "(192 -48 -0.0140228) (192 -56 -0.0140228) (192 -56 191.986)" 418 | "material" "TOOLS/TOOLSCLIP" 419 | "uaxis" "[0 1 0 0] 0.25" 420 | "vaxis" "[0 0 -1 0] 0.25" 421 | "rotation" "0" 422 | "lightmapscale" "16" 423 | "smoothing_groups" "0" 424 | } 425 | side 426 | { 427 | "id" "550" 428 | "plane" "(192 -48 191.986) (-88 -48 191.986) (-88 -48 -0.0140228)" 429 | "material" "TOOLS/TOOLSCLIP" 430 | "uaxis" "[1 0 0 0] 0.25" 431 | "vaxis" "[0 0 -1 0] 0.25" 432 | "rotation" "0" 433 | "lightmapscale" "16" 434 | "smoothing_groups" "0" 435 | } 436 | side 437 | { 438 | "id" "551" 439 | "plane" "(192 -56 -0.0140228) (-88 -56 -0.0140228) (-88 -56 191.986)" 440 | "material" "TOOLS/TOOLSCLIP" 441 | "uaxis" "[1 0 0 0] 0.25" 442 | "vaxis" "[0 0 -1 0] 0.25" 443 | "rotation" "0" 444 | "lightmapscale" "16" 445 | "smoothing_groups" "0" 446 | } 447 | editor 448 | { 449 | "color" "0 185 238" 450 | "visgroupshown" "1" 451 | "visgroupautoshown" "1" 452 | } 453 | } 454 | solid 455 | { 456 | "id" "2217" 457 | side 458 | { 459 | "id" "552" 460 | "plane" "(-88 56 191.986) (192 56 191.986) (192 48 191.986)" 461 | "material" "TOOLS/TOOLSCLIP" 462 | "uaxis" "[1 0 0 0] 0.25" 463 | "vaxis" "[0 -1 0 0] 0.25" 464 | "rotation" "0" 465 | "lightmapscale" "16" 466 | "smoothing_groups" "0" 467 | } 468 | side 469 | { 470 | "id" "553" 471 | "plane" "(-88 48 -0.0140228) (192 48 -0.0140228) (192 56 -0.0140228)" 472 | "material" "TOOLS/TOOLSCLIP" 473 | "uaxis" "[1 0 0 0] 0.25" 474 | "vaxis" "[0 -1 0 0] 0.25" 475 | "rotation" "0" 476 | "lightmapscale" "16" 477 | "smoothing_groups" "0" 478 | } 479 | side 480 | { 481 | "id" "554" 482 | "plane" "(-88 56 191.986) (-88 48 191.986) (-88 48 -0.0140228)" 483 | "material" "TOOLS/TOOLSCLIP" 484 | "uaxis" "[0 1 0 0] 0.25" 485 | "vaxis" "[0 0 -1 0] 0.25" 486 | "rotation" "0" 487 | "lightmapscale" "16" 488 | "smoothing_groups" "0" 489 | } 490 | side 491 | { 492 | "id" "555" 493 | "plane" "(192 56 -0.0140228) (192 48 -0.0140228) (192 48 191.986)" 494 | "material" "TOOLS/TOOLSCLIP" 495 | "uaxis" "[0 1 0 0] 0.25" 496 | "vaxis" "[0 0 -1 0] 0.25" 497 | "rotation" "0" 498 | "lightmapscale" "16" 499 | "smoothing_groups" "0" 500 | } 501 | side 502 | { 503 | "id" "556" 504 | "plane" "(192 56 191.986) (-88 56 191.986) (-88 56 -0.0140228)" 505 | "material" "TOOLS/TOOLSCLIP" 506 | "uaxis" "[1 0 0 0] 0.25" 507 | "vaxis" "[0 0 -1 0] 0.25" 508 | "rotation" "0" 509 | "lightmapscale" "16" 510 | "smoothing_groups" "0" 511 | } 512 | side 513 | { 514 | "id" "557" 515 | "plane" "(192 48 -0.0140228) (-88 48 -0.0140228) (-88 48 191.986)" 516 | "material" "TOOLS/TOOLSCLIP" 517 | "uaxis" "[1 0 0 0] 0.25" 518 | "vaxis" "[0 0 -1 0] 0.25" 519 | "rotation" "0" 520 | "lightmapscale" "16" 521 | "smoothing_groups" "0" 522 | } 523 | editor 524 | { 525 | "color" "0 143 120" 526 | "visgroupshown" "1" 527 | "visgroupautoshown" "1" 528 | } 529 | } 530 | solid 531 | { 532 | "id" "2234" 533 | side 534 | { 535 | "id" "578" 536 | "plane" "(-248 32 191.986) (-200 56 191.986) (-88 56 191.986)" 537 | "material" "TOOLS/TOOLSCLIP" 538 | "uaxis" "[1 0 0 0] 0.25" 539 | "vaxis" "[0 -1 0 0] 0.25" 540 | "rotation" "0" 541 | "lightmapscale" "16" 542 | "smoothing_groups" "0" 543 | } 544 | side 545 | { 546 | "id" "577" 547 | "plane" "(-248 -32 -0.0140228) (-200 -56 -0.0140228) (-88 -56 -0.0140228)" 548 | "material" "TOOLS/TOOLSCLIP" 549 | "uaxis" "[1 0 0 0] 0.25" 550 | "vaxis" "[0 -1 0 0] 0.25" 551 | "rotation" "0" 552 | "lightmapscale" "16" 553 | "smoothing_groups" "0" 554 | } 555 | side 556 | { 557 | "id" "576" 558 | "plane" "(-248 32 -0.0140228) (-248 32 191.986) (-248 -32 191.986)" 559 | "material" "TOOLS/TOOLSCLIP" 560 | "uaxis" "[0 1 0 0] 0.25" 561 | "vaxis" "[0 0 -1 0] 0.25" 562 | "rotation" "0" 563 | "lightmapscale" "16" 564 | "smoothing_groups" "0" 565 | } 566 | side 567 | { 568 | "id" "575" 569 | "plane" "(-88 -56 -0.0140228) (-88 -56 191.986) (-88 56 191.986)" 570 | "material" "TOOLS/TOOLSCLIP" 571 | "uaxis" "[0 1 0 0] 0.25" 572 | "vaxis" "[0 0 -1 0] 0.25" 573 | "rotation" "0" 574 | "lightmapscale" "16" 575 | "smoothing_groups" "0" 576 | } 577 | side 578 | { 579 | "id" "574" 580 | "plane" "(-88 56 -0.0140228) (-88 56 191.986) (-200 56 191.986)" 581 | "material" "TOOLS/TOOLSCLIP" 582 | "uaxis" "[1 0 0 0] 0.25" 583 | "vaxis" "[0 0 -1 0] 0.25" 584 | "rotation" "0" 585 | "lightmapscale" "16" 586 | "smoothing_groups" "0" 587 | } 588 | side 589 | { 590 | "id" "573" 591 | "plane" "(-200 -56 -0.0140228) (-200 -56 191.986) (-88 -56 191.986)" 592 | "material" "TOOLS/TOOLSCLIP" 593 | "uaxis" "[1 0 0 0] 0.25" 594 | "vaxis" "[0 0 -1 0] 0.25" 595 | "rotation" "0" 596 | "lightmapscale" "16" 597 | "smoothing_groups" "0" 598 | } 599 | side 600 | { 601 | "id" "572" 602 | "plane" "(-248 -32 -0.0140228) (-248 -32 191.986) (-200 -56 191.986)" 603 | "material" "TOOLS/TOOLSCLIP" 604 | "uaxis" "[1 0 0 0] 0.25" 605 | "vaxis" "[0 0 -1 0] 0.25" 606 | "rotation" "0" 607 | "lightmapscale" "16" 608 | "smoothing_groups" "0" 609 | } 610 | side 611 | { 612 | "id" "571" 613 | "plane" "(-200 56 -0.0140228) (-200 56 191.986) (-248 32 191.986)" 614 | "material" "TOOLS/TOOLSCLIP" 615 | "uaxis" "[1 0 0 0] 0.25" 616 | "vaxis" "[0 0 -1 0] 0.25" 617 | "rotation" "0" 618 | "lightmapscale" "16" 619 | "smoothing_groups" "0" 620 | } 621 | editor 622 | { 623 | "color" "0 105 102" 624 | "visgroupshown" "1" 625 | "visgroupautoshown" "1" 626 | } 627 | } 628 | solid 629 | { 630 | "id" "2248" 631 | side 632 | { 633 | "id" "585" 634 | "plane" "(-88 -48 40) (-88 48 40) (224 48 40)" 635 | "material" "TOOLS/TOOLSCLIP" 636 | "uaxis" "[1 0 0 0] 0.25" 637 | "vaxis" "[0 -1 0 0] 0.25" 638 | "rotation" "0" 639 | "lightmapscale" "16" 640 | "smoothing_groups" "0" 641 | } 642 | side 643 | { 644 | "id" "586" 645 | "plane" "(-88 48 0) (-88 -48 0) (224 -48 0)" 646 | "material" "TOOLS/TOOLSCLIP" 647 | "uaxis" "[1 0 0 0] 0.25" 648 | "vaxis" "[0 -1 0 0] 0.25" 649 | "rotation" "0" 650 | "lightmapscale" "16" 651 | "smoothing_groups" "0" 652 | } 653 | side 654 | { 655 | "id" "587" 656 | "plane" "(-88 -48 0) (-88 48 0) (-88 48 40)" 657 | "material" "TOOLS/TOOLSCLIP" 658 | "uaxis" "[0 1 0 0] 0.25" 659 | "vaxis" "[0 0 -1 0] 0.25" 660 | "rotation" "0" 661 | "lightmapscale" "16" 662 | "smoothing_groups" "0" 663 | } 664 | side 665 | { 666 | "id" "588" 667 | "plane" "(224 48 0) (224 -48 0) (224 -48 40)" 668 | "material" "TOOLS/TOOLSCLIP" 669 | "uaxis" "[0 1 0 0] 0.25" 670 | "vaxis" "[0 0 -1 0] 0.25" 671 | "rotation" "0" 672 | "lightmapscale" "16" 673 | "smoothing_groups" "0" 674 | } 675 | side 676 | { 677 | "id" "589" 678 | "plane" "(-88 48 0) (224 48 0) (224 48 40)" 679 | "material" "TOOLS/TOOLSCLIP" 680 | "uaxis" "[1 0 0 0] 0.25" 681 | "vaxis" "[0 0 -1 0] 0.25" 682 | "rotation" "0" 683 | "lightmapscale" "16" 684 | "smoothing_groups" "0" 685 | } 686 | side 687 | { 688 | "id" "590" 689 | "plane" "(224 -48 0) (-88 -48 0) (-88 -48 40)" 690 | "material" "TOOLS/TOOLSCLIP" 691 | "uaxis" "[1 0 0 0] 0.25" 692 | "vaxis" "[0 0 -1 0] 0.25" 693 | "rotation" "0" 694 | "lightmapscale" "16" 695 | "smoothing_groups" "0" 696 | } 697 | editor 698 | { 699 | "color" "0 188 141" 700 | "visgroupshown" "1" 701 | "visgroupautoshown" "1" 702 | } 703 | } 704 | solid 705 | { 706 | "id" "2273" 707 | side 708 | { 709 | "id" "601" 710 | "plane" "(304 48 0) (224 48 0) (224 -48 0)" 711 | "material" "TOOLS/TOOLSCLIP" 712 | "uaxis" "[1 0 0 0] 0.25" 713 | "vaxis" "[0 -1 0 0] 0.25" 714 | "rotation" "0" 715 | "lightmapscale" "16" 716 | "smoothing_groups" "0" 717 | } 718 | side 719 | { 720 | "id" "600" 721 | "plane" "(224 48 0) (304 48 0) (224 48 40)" 722 | "material" "TOOLS/TOOLSCLIP" 723 | "uaxis" "[1 0 0 0] 0.25" 724 | "vaxis" "[0 0 -1 0] 0.25" 725 | "rotation" "0" 726 | "lightmapscale" "16" 727 | "smoothing_groups" "0" 728 | } 729 | side 730 | { 731 | "id" "599" 732 | "plane" "(304 -48 0) (224 -48 0) (224 -48 40)" 733 | "material" "TOOLS/TOOLSCLIP" 734 | "uaxis" "[1 0 0 0] 0.25" 735 | "vaxis" "[0 0 -1 0] 0.25" 736 | "rotation" "0" 737 | "lightmapscale" "16" 738 | "smoothing_groups" "0" 739 | } 740 | side 741 | { 742 | "id" "598" 743 | "plane" "(224 -48 40) (224 48 40) (304 48 0)" 744 | "material" "TOOLS/TOOLSCLIP" 745 | "uaxis" "[1 0 0 0] 0.25" 746 | "vaxis" "[0 -1 0 0] 0.25" 747 | "rotation" "0" 748 | "lightmapscale" "16" 749 | "smoothing_groups" "0" 750 | } 751 | side 752 | { 753 | "id" "597" 754 | "plane" "(224 -48 0) (224 48 0) (224 48 40)" 755 | "material" "TOOLS/TOOLSCLIP" 756 | "uaxis" "[0 1 0 0] 0.25" 757 | "vaxis" "[0 0 -1 0] 0.25" 758 | "rotation" "0" 759 | "lightmapscale" "16" 760 | "smoothing_groups" "0" 761 | } 762 | editor 763 | { 764 | "color" "0 212 105" 765 | "visgroupshown" "1" 766 | "visgroupautoshown" "1" 767 | } 768 | } 769 | } 770 | entity 771 | { 772 | "id" "2292" 773 | "classname" "prop_dynamic" 774 | "angles" "0 330 0" 775 | "fademindist" "-1" 776 | "fadescale" "1" 777 | "MaxAnimTime" "10" 778 | "MinAnimTime" "5" 779 | "model" "models/props/de_dust/hr_dust/dust_crates/dust_crate_style_01_32x64x64.mdl" 780 | "parentname" "vip_heli" 781 | "renderamt" "255" 782 | "rendercolor" "255 255 255" 783 | "skin" "0" 784 | "solid" "6" 785 | "spawnflags" "0" 786 | "targetname" "vip_blocker" 787 | "origin" "128 16 40" 788 | editor 789 | { 790 | "color" "220 30 220" 791 | "visgroupshown" "1" 792 | "visgroupautoshown" "1" 793 | "logicalpos" "[0 2500]" 794 | } 795 | } 796 | entity 797 | { 798 | "id" "2352" 799 | "classname" "hostage_entity" 800 | "angles" "0 0 0" 801 | "HostageSpawnRandomFactor" "1" 802 | "targetname" "vip_bait" 803 | "origin" "-16 0 40" 804 | editor 805 | { 806 | "color" "220 30 220" 807 | "visgroupshown" "1" 808 | "visgroupautoshown" "1" 809 | "logicalpos" "[0 0]" 810 | } 811 | } 812 | entity 813 | { 814 | "id" "2397" 815 | "classname" "prop_dynamic" 816 | "angles" "0 285 90" 817 | "fademindist" "-1" 818 | "fadescale" "1" 819 | "MaxAnimTime" "10" 820 | "MinAnimTime" "5" 821 | "model" "models/props/de_dust/hr_dust/dust_crates/dust_crate_style_02_32x16x64.mdl" 822 | "parentname" "vip_heli" 823 | "renderamt" "255" 824 | "rendercolor" "255 255 255" 825 | "skin" "0" 826 | "solid" "6" 827 | "spawnflags" "0" 828 | "targetname" "vip_blocker" 829 | "origin" "152 -24 72" 830 | editor 831 | { 832 | "color" "220 30 220" 833 | "visgroupshown" "1" 834 | "visgroupautoshown" "1" 835 | "logicalpos" "[0 2500]" 836 | } 837 | } 838 | entity 839 | { 840 | "id" "2128" 841 | "classname" "ambient_generic" 842 | "health" "10" 843 | "message" "vehicles/insertion/helicopter.wav" 844 | "pitch" "100" 845 | "pitchstart" "100" 846 | "radius" "1250" 847 | "spawnflags" "48" 848 | "targetname" "vip_heli_sound_takeoff" 849 | "origin" "16 0 232" 850 | editor 851 | { 852 | "color" "220 30 220" 853 | "visgroupshown" "1" 854 | "visgroupautoshown" "1" 855 | "logicalpos" "[0 3000]" 856 | } 857 | } 858 | entity 859 | { 860 | "id" "2107" 861 | "classname" "logic_relay" 862 | "spawnflags" "0" 863 | "targetname" "vip_relay_heli_takeoff" 864 | connections 865 | { 866 | "OnTrigger" "vip_heliSetAnimation4lift0-1" 867 | "OnTrigger" "vip_heli_sound_takeoffPlaySound0-1" 868 | "OnTrigger" "vip_trigger_pushDisable1-1" 869 | "OnTrigger" "vip_trigger_barrierDisable1-1" 870 | "OnTrigger" "vip_heli_bladeshurtDisable0-1" 871 | "OnTrigger" "vip_baitKill0-1" 872 | } 873 | "origin" "192 0 128" 874 | editor 875 | { 876 | "color" "220 30 220" 877 | "visgroupshown" "1" 878 | "visgroupautoshown" "1" 879 | "logicalpos" "[0 0]" 880 | } 881 | } 882 | entity 883 | { 884 | "id" "2079" 885 | "classname" "prop_dynamic" 886 | "angles" "90 0 0" 887 | "disableshadowdepth" "1" 888 | "disableshadows" "1" 889 | "fademindist" "-1" 890 | "fadescale" "1" 891 | "MaxAnimTime" "10" 892 | "MinAnimTime" "5" 893 | "model" "models/props/crates/csgo_drop_crate_armsdeal3.mdl" 894 | "parentname" "vip_heli" 895 | "renderamt" "255" 896 | "rendercolor" "255 255 255" 897 | "rendermode" "10" 898 | "skin" "0" 899 | "solid" "0" 900 | "spawnflags" "256" 901 | "targetname" "vip_fixup" 902 | "origin" "-64 0 64" 903 | editor 904 | { 905 | "color" "220 30 220" 906 | "visgroupshown" "1" 907 | "visgroupautoshown" "1" 908 | "logicalpos" "[0 1000]" 909 | } 910 | } 911 | entity 912 | { 913 | "id" "1817" 914 | "classname" "func_hostage_rescue" 915 | "origin" "0 0 152" 916 | "spawnflags" "4097" 917 | "StartDisabled" "0" 918 | "targetname" "vip_rescuezone" 919 | solid 920 | { 921 | "id" "2153" 922 | side 923 | { 924 | "id" "482" 925 | "plane" "(64 -32 160) (-64 -32 160) (-64 32 160)" 926 | "material" "TOOLS/TOOLSTRIGGER" 927 | "uaxis" "[1 0 0 0] 0.25" 928 | "vaxis" "[0 -1 0 0] 0.25" 929 | "rotation" "0" 930 | "lightmapscale" "16" 931 | "smoothing_groups" "0" 932 | } 933 | side 934 | { 935 | "id" "481" 936 | "plane" "(64 32 144) (-64 32 144) (-64 -32 144)" 937 | "material" "TOOLS/TOOLSTRIGGER" 938 | "uaxis" "[1 0 0 0] 0.25" 939 | "vaxis" "[0 -1 0 0] 0.25" 940 | "rotation" "0" 941 | "lightmapscale" "16" 942 | "smoothing_groups" "0" 943 | } 944 | side 945 | { 946 | "id" "480" 947 | "plane" "(-64 32 144) (64 32 144) (64 32 160)" 948 | "material" "TOOLS/TOOLSTRIGGER" 949 | "uaxis" "[1 0 0 0] 0.25" 950 | "vaxis" "[0 0 -1 0] 0.25" 951 | "rotation" "0" 952 | "lightmapscale" "16" 953 | "smoothing_groups" "0" 954 | } 955 | side 956 | { 957 | "id" "479" 958 | "plane" "(64 -32 144) (-64 -32 144) (-64 -32 160)" 959 | "material" "TOOLS/TOOLSTRIGGER" 960 | "uaxis" "[1 0 0 0] 0.25" 961 | "vaxis" "[0 0 -1 0] 0.25" 962 | "rotation" "0" 963 | "lightmapscale" "16" 964 | "smoothing_groups" "0" 965 | } 966 | side 967 | { 968 | "id" "478" 969 | "plane" "(64 32 144) (64 -32 144) (64 -32 160)" 970 | "material" "TOOLS/TOOLSTRIGGER" 971 | "uaxis" "[0 1 0 0] 0.25" 972 | "vaxis" "[0 0 -1 0] 0.25" 973 | "rotation" "0" 974 | "lightmapscale" "16" 975 | "smoothing_groups" "0" 976 | } 977 | side 978 | { 979 | "id" "477" 980 | "plane" "(-64 -32 144) (-64 32 144) (-64 32 160)" 981 | "material" "TOOLS/TOOLSTRIGGER" 982 | "uaxis" "[0 1 0 0] 0.25" 983 | "vaxis" "[0 0 -1 0] 0.25" 984 | "rotation" "0" 985 | "lightmapscale" "16" 986 | "smoothing_groups" "0" 987 | } 988 | editor 989 | { 990 | "color" "220 30 220" 991 | "visgroupshown" "1" 992 | "visgroupautoshown" "1" 993 | } 994 | } 995 | editor 996 | { 997 | "color" "220 30 220" 998 | "visgroupshown" "1" 999 | "visgroupautoshown" "1" 1000 | "logicalpos" "[0 500]" 1001 | } 1002 | } 1003 | entity 1004 | { 1005 | "id" "1726" 1006 | "classname" "point_viewcontrol" 1007 | "acceleration" "500" 1008 | "angles" "-10 285 0" 1009 | "deceleration" "500" 1010 | "fov" "90" 1011 | "fov_rate" "1" 1012 | "interpolatepositiontoplayer" "0" 1013 | "spawnflags" "60" 1014 | "speed" "0" 1015 | "target" "vip_heli" 1016 | "targetattachment" "starboard_light" 1017 | "targetname" "vip_helicopter_camera" 1018 | "trackspeed" "40" 1019 | "wait" "10" 1020 | "origin" "-128 384 62.1188" 1021 | editor 1022 | { 1023 | "color" "220 30 220" 1024 | "visgroupshown" "1" 1025 | "visgroupautoshown" "1" 1026 | "logicalpos" "[0 10500]" 1027 | } 1028 | } 1029 | entity 1030 | { 1031 | "id" "1789" 1032 | "classname" "logic_auto" 1033 | "spawnflags" "1" 1034 | connections 1035 | { 1036 | "OnMapSpawn" "vip_helicopter_cameraDisable0-1" 1037 | "OnMapSpawn" "vip_rescueDisable0-1" 1038 | "OnMapSpawn" "vip_fixupSetParentAttachmentMaintainOffsetstrobe_red0.02-1" 1039 | "OnMapSpawn" "vip_blockerSetParentAttachmentMaintainOffsetstrobe_red0.02-1" 1040 | "OnMapSpawn" "vip_baitDisableDraw0-1" 1041 | "OnMapSpawn" "vip_baitAddOutputsolid 40-1" 1042 | } 1043 | "origin" "16 0 248" 1044 | editor 1045 | { 1046 | "color" "220 30 220" 1047 | "visgroupshown" "1" 1048 | "visgroupautoshown" "1" 1049 | "logicalpos" "[0 0]" 1050 | } 1051 | } 1052 | entity 1053 | { 1054 | "id" "1661" 1055 | "classname" "trigger_hurt" 1056 | "damage" "100000" 1057 | "damagecap" "20" 1058 | "damagemodel" "0" 1059 | "damagetype" "4" 1060 | "nodmgforce" "0" 1061 | "origin" "0 0 199" 1062 | "spawnflags" "4097" 1063 | "StartDisabled" "0" 1064 | "targetname" "vip_heli_bladeshurt" 1065 | solid 1066 | { 1067 | "id" "233" 1068 | side 1069 | { 1070 | "id" "234" 1071 | "plane" "(0 448 191) (-317 317 191) (-448 0 191)" 1072 | "material" "TOOLS/TOOLSTRIGGER" 1073 | "uaxis" "[1 0 0 0] 0.25" 1074 | "vaxis" "[0 -1 0 0] 0.25" 1075 | "rotation" "0" 1076 | "lightmapscale" "16" 1077 | "smoothing_groups" "0" 1078 | } 1079 | side 1080 | { 1081 | "id" "235" 1082 | "plane" "(0 -448 207) (-317 -317 207) (-448 0 207)" 1083 | "material" "TOOLS/TOOLSTRIGGER" 1084 | "uaxis" "[1 0 0 0] 0.25" 1085 | "vaxis" "[0 -1 0 0] 0.25" 1086 | "rotation" "0" 1087 | "lightmapscale" "16" 1088 | "smoothing_groups" "0" 1089 | } 1090 | side 1091 | { 1092 | "id" "236" 1093 | "plane" "(0 -448 191) (-317 -317 191) (-317 -317 207)" 1094 | "material" "TOOLS/TOOLSTRIGGER" 1095 | "uaxis" "[1 0 0 0] 0.25" 1096 | "vaxis" "[0 0 -1 -4] 0.25" 1097 | "rotation" "0" 1098 | "lightmapscale" "16" 1099 | "smoothing_groups" "0" 1100 | } 1101 | side 1102 | { 1103 | "id" "237" 1104 | "plane" "(-317 -317 191) (-448 0 191) (-448 0 207)" 1105 | "material" "TOOLS/TOOLSTRIGGER" 1106 | "uaxis" "[0 1 0 0] 0.25" 1107 | "vaxis" "[0 0 -1 -4] 0.25" 1108 | "rotation" "0" 1109 | "lightmapscale" "16" 1110 | "smoothing_groups" "0" 1111 | } 1112 | side 1113 | { 1114 | "id" "238" 1115 | "plane" "(-448 0 191) (-317 317 191) (-317 317 207)" 1116 | "material" "TOOLS/TOOLSTRIGGER" 1117 | "uaxis" "[0 1 0 0] 0.25" 1118 | "vaxis" "[0 0 -1 -4] 0.25" 1119 | "rotation" "0" 1120 | "lightmapscale" "16" 1121 | "smoothing_groups" "0" 1122 | } 1123 | side 1124 | { 1125 | "id" "239" 1126 | "plane" "(-317 317 191) (0 448 191) (0 448 207)" 1127 | "material" "TOOLS/TOOLSTRIGGER" 1128 | "uaxis" "[1 0 0 0] 0.25" 1129 | "vaxis" "[0 0 -1 -4] 0.25" 1130 | "rotation" "0" 1131 | "lightmapscale" "16" 1132 | "smoothing_groups" "0" 1133 | } 1134 | side 1135 | { 1136 | "id" "240" 1137 | "plane" "(448 0 191) (317 -317 191) (317 -317 207)" 1138 | "material" "TOOLS/TOOLSTRIGGER" 1139 | "uaxis" "[0 1 0 0] 0.25" 1140 | "vaxis" "[0 0 -1 -4] 0.25" 1141 | "rotation" "0" 1142 | "lightmapscale" "16" 1143 | "smoothing_groups" "0" 1144 | } 1145 | side 1146 | { 1147 | "id" "241" 1148 | "plane" "(317 -317 191) (0 -448 191) (0 -448 207)" 1149 | "material" "TOOLS/TOOLSTRIGGER" 1150 | "uaxis" "[1 0 0 0] 0.25" 1151 | "vaxis" "[0 0 -1 -4] 0.25" 1152 | "rotation" "0" 1153 | "lightmapscale" "16" 1154 | "smoothing_groups" "0" 1155 | } 1156 | side 1157 | { 1158 | "id" "242" 1159 | "plane" "(0 448 191) (317 317 191) (317 317 207)" 1160 | "material" "TOOLS/TOOLSTRIGGER" 1161 | "uaxis" "[1 0 0 0] 0.25" 1162 | "vaxis" "[0 0 -1 -4] 0.25" 1163 | "rotation" "0" 1164 | "lightmapscale" "16" 1165 | "smoothing_groups" "0" 1166 | } 1167 | side 1168 | { 1169 | "id" "244" 1170 | "plane" "(317 317 191) (448 0 191) (448 0 207)" 1171 | "material" "TOOLS/TOOLSTRIGGER" 1172 | "uaxis" "[0 1 0 0] 0.25" 1173 | "vaxis" "[0 0 -1 -4] 0.25" 1174 | "rotation" "0" 1175 | "lightmapscale" "16" 1176 | "smoothing_groups" "0" 1177 | } 1178 | editor 1179 | { 1180 | "color" "220 30 220" 1181 | "visgroupshown" "1" 1182 | "visgroupautoshown" "1" 1183 | } 1184 | } 1185 | editor 1186 | { 1187 | "color" "220 30 220" 1188 | "visgroupshown" "1" 1189 | "visgroupautoshown" "1" 1190 | "logicalpos" "[0 0]" 1191 | } 1192 | } 1193 | entity 1194 | { 1195 | "id" "1374" 1196 | "classname" "trigger_softbarrier" 1197 | "filtername" "@vip_filter_onlyts" 1198 | "origin" "232 0 55.5" 1199 | "pushdir" "0 1 0" 1200 | "spawnflags" "4097" 1201 | "StartDisabled" "0" 1202 | "targetname" "vip_trigger_barrier" 1203 | solid 1204 | { 1205 | "id" "246" 1206 | side 1207 | { 1208 | "id" "248" 1209 | "plane" "(272 -48 0) (272 48 0) (272 48 111)" 1210 | "material" "TOOLS/TOOLSTRIGGER" 1211 | "uaxis" "[0 1 0 0] 0.25" 1212 | "vaxis" "[0 0 -1 0] 0.433594" 1213 | "rotation" "0" 1214 | "lightmapscale" "16" 1215 | "smoothing_groups" "0" 1216 | } 1217 | side 1218 | { 1219 | "id" "250" 1220 | "plane" "(304 48 0) (304 -48 0) (304 -48 111)" 1221 | "material" "TOOLS/TOOLSTRIGGER" 1222 | "uaxis" "[0 1 0 0] 0.25" 1223 | "vaxis" "[0 0 -1 0] 0.433594" 1224 | "rotation" "0" 1225 | "lightmapscale" "16" 1226 | "smoothing_groups" "0" 1227 | } 1228 | side 1229 | { 1230 | "id" "252" 1231 | "plane" "(304 -48 0) (272 -48 0) (272 -48 111)" 1232 | "material" "TOOLS/TOOLSTRIGGER" 1233 | "uaxis" "[1 0 0 -32] 0.25" 1234 | "vaxis" "[0 0 -1 0] 0.433594" 1235 | "rotation" "0" 1236 | "lightmapscale" "16" 1237 | "smoothing_groups" "0" 1238 | } 1239 | side 1240 | { 1241 | "id" "254" 1242 | "plane" "(272 48 0) (304 48 0) (304 48 111)" 1243 | "material" "TOOLS/TOOLSTRIGGER" 1244 | "uaxis" "[1 0 0 -32] 0.25" 1245 | "vaxis" "[0 0 -1 0] 0.433594" 1246 | "rotation" "0" 1247 | "lightmapscale" "16" 1248 | "smoothing_groups" "0" 1249 | } 1250 | side 1251 | { 1252 | "id" "256" 1253 | "plane" "(272 -48 0) (304 -48 0) (304 48 0)" 1254 | "material" "TOOLS/TOOLSTRIGGER" 1255 | "uaxis" "[1 0 0 -32] 0.25" 1256 | "vaxis" "[0 -1 0 0] 0.25" 1257 | "rotation" "0" 1258 | "lightmapscale" "16" 1259 | "smoothing_groups" "0" 1260 | } 1261 | side 1262 | { 1263 | "id" "258" 1264 | "plane" "(272 48 111) (304 48 111) (304 -48 111)" 1265 | "material" "TOOLS/TOOLSTRIGGER" 1266 | "uaxis" "[1 0 0 -32] 0.25" 1267 | "vaxis" "[0 -1 0 0] 0.25" 1268 | "rotation" "0" 1269 | "lightmapscale" "16" 1270 | "smoothing_groups" "0" 1271 | } 1272 | editor 1273 | { 1274 | "color" "220 30 220" 1275 | "visgroupshown" "1" 1276 | "visgroupautoshown" "1" 1277 | } 1278 | } 1279 | editor 1280 | { 1281 | "color" "220 30 220" 1282 | "visgroupshown" "1" 1283 | "visgroupautoshown" "1" 1284 | "logicalpos" "[0 3000]" 1285 | } 1286 | } 1287 | entity 1288 | { 1289 | "id" "1035" 1290 | "classname" "trigger_once" 1291 | "filtername" "@vip_filter_onlyvip" 1292 | "origin" "-38.44 -97.29 78.99" 1293 | "spawnflags" "4097" 1294 | "StartDisabled" "0" 1295 | "targetname" "vip_rescue" 1296 | connections 1297 | { 1298 | "OnTrigger" "!selfRunScriptCode::gamemode_vip.OnVIPRescued()0-1" 1299 | "OnTrigger" "!activatorRemoveHealth0-1" 1300 | "OnTrigger" "vip_helicopter_cameraEnable0-1" 1301 | "OnTrigger" "!activatorSetParentvip_fixup0-1" 1302 | "OnStartTouch" "vip_relay_heli_takeoffTrigger0-1" 1303 | } 1304 | solid 1305 | { 1306 | "id" "260" 1307 | side 1308 | { 1309 | "id" "262" 1310 | "plane" "(-112 -48 111) (-112 -48 47) (-112 48 47)" 1311 | "material" "TOOLS/TOOLSTRIGGER" 1312 | "uaxis" "[0.9177 -0.3974 0 -23.3891] 0.261452" 1313 | "vaxis" "[0 0 -1 -4] 0.25" 1314 | "rotation" "0" 1315 | "lightmapscale" "16" 1316 | "smoothing_groups" "0" 1317 | } 1318 | side 1319 | { 1320 | "id" "264" 1321 | "plane" "(208 48 111) (208 48 47) (208 -48 47)" 1322 | "material" "TOOLS/TOOLSTRIGGER" 1323 | "uaxis" "[-0.9177 0.3974 0 23.3891] 0.261452" 1324 | "vaxis" "[0 0 -1 -4] 0.25" 1325 | "rotation" "0" 1326 | "lightmapscale" "16" 1327 | "smoothing_groups" "0" 1328 | } 1329 | side 1330 | { 1331 | "id" "266" 1332 | "plane" "(208 -48 111) (208 -48 47) (-112 -48 47)" 1333 | "material" "TOOLS/TOOLSTRIGGER" 1334 | "uaxis" "[0.61 0.7924 0 -12.3591] 0.255565" 1335 | "vaxis" "[0 0 -1 -4] 0.25" 1336 | "rotation" "0" 1337 | "lightmapscale" "16" 1338 | "smoothing_groups" "0" 1339 | } 1340 | side 1341 | { 1342 | "id" "268" 1343 | "plane" "(-112 48 111) (-112 48 47) (208 48 47)" 1344 | "material" "TOOLS/TOOLSTRIGGER" 1345 | "uaxis" "[-0.61 -0.7924 0 12.3591] 0.255565" 1346 | "vaxis" "[0 0 -1 -4] 0.25" 1347 | "rotation" "0" 1348 | "lightmapscale" "16" 1349 | "smoothing_groups" "0" 1350 | } 1351 | side 1352 | { 1353 | "id" "270" 1354 | "plane" "(-112 48 47) (-112 -48 47) (208 -48 47)" 1355 | "material" "TOOLS/TOOLSTRIGGER" 1356 | "uaxis" "[0.866 -0.5 0 -31.3437] 0.260395" 1357 | "vaxis" "[0.5 0.866 0 -12.446] 0.25383" 1358 | "rotation" "0" 1359 | "lightmapscale" "16" 1360 | "smoothing_groups" "0" 1361 | } 1362 | side 1363 | { 1364 | "id" "272" 1365 | "plane" "(-112 -48 111) (-112 48 111) (208 48 111)" 1366 | "material" "TOOLS/TOOLSTRIGGER" 1367 | "uaxis" "[-0.866 0.5 0 31.3437] 0.260395" 1368 | "vaxis" "[0.5 0.866 0 -12.446] 0.25383" 1369 | "rotation" "0" 1370 | "lightmapscale" "16" 1371 | "smoothing_groups" "0" 1372 | } 1373 | editor 1374 | { 1375 | "color" "220 30 220" 1376 | "visgroupshown" "1" 1377 | "visgroupautoshown" "1" 1378 | } 1379 | } 1380 | editor 1381 | { 1382 | "color" "220 30 220" 1383 | "visgroupshown" "1" 1384 | "visgroupautoshown" "1" 1385 | "logicalpos" "[0 4000]" 1386 | } 1387 | } 1388 | entity 1389 | { 1390 | "id" "1038" 1391 | "classname" "trigger_push" 1392 | "alternateticksfix" "0" 1393 | "FallingSpeedThreshold" "-150" 1394 | "filtername" "@vip_filter_onlyts" 1395 | "origin" "64 0 55.5" 1396 | "pushdir" "0 0 0" 1397 | "spawnflags" "1" 1398 | "speed" "400" 1399 | "StartDisabled" "0" 1400 | "targetname" "vip_trigger_push" 1401 | solid 1402 | { 1403 | "id" "273" 1404 | side 1405 | { 1406 | "id" "274" 1407 | "plane" "(-88 -48 0) (272 -48 0) (272 48 0)" 1408 | "material" "TOOLS/TOOLSTRIGGER" 1409 | "uaxis" "[1 0 0 -2.26416] 0.25" 1410 | "vaxis" "[0 -1 0 -32] 0.25" 1411 | "rotation" "0" 1412 | "lightmapscale" "16" 1413 | "smoothing_groups" "0" 1414 | } 1415 | side 1416 | { 1417 | "id" "275" 1418 | "plane" "(-88 48 111) (272 48 111) (272 -48 111)" 1419 | "material" "TOOLS/TOOLSTRIGGER" 1420 | "uaxis" "[1 0 0 2.26416] 0.25" 1421 | "vaxis" "[0 -1 0 -32] 0.25" 1422 | "rotation" "0" 1423 | "lightmapscale" "16" 1424 | "smoothing_groups" "0" 1425 | } 1426 | side 1427 | { 1428 | "id" "276" 1429 | "plane" "(-88 48 0) (-88 48 111) (-88 -48 111)" 1430 | "material" "TOOLS/TOOLSTRIGGER" 1431 | "uaxis" "[0 -1 0 0] 0.25" 1432 | "vaxis" "[0 0 -1 0] 0.433594" 1433 | "rotation" "0" 1434 | "lightmapscale" "16" 1435 | "smoothing_groups" "0" 1436 | } 1437 | side 1438 | { 1439 | "id" "277" 1440 | "plane" "(272 -48 0) (272 -48 111) (272 48 111)" 1441 | "material" "TOOLS/TOOLSTRIGGER" 1442 | "uaxis" "[-0.9838 0.1793 0 58.6753] 0.25" 1443 | "vaxis" "[0 0 -1 0] 0.433594" 1444 | "rotation" "0" 1445 | "lightmapscale" "16" 1446 | "smoothing_groups" "0" 1447 | } 1448 | side 1449 | { 1450 | "id" "278" 1451 | "plane" "(-88 -48 0) (-88 -48 111) (272 -48 111)" 1452 | "material" "TOOLS/TOOLSTRIGGER" 1453 | "uaxis" "[0.8774 0.4798 0 -49.5295] 0.25" 1454 | "vaxis" "[0 0 -1 0] 0.433594" 1455 | "rotation" "0" 1456 | "lightmapscale" "16" 1457 | "smoothing_groups" "0" 1458 | } 1459 | side 1460 | { 1461 | "id" "279" 1462 | "plane" "(272 48 0) (272 48 111) (-88 48 111)" 1463 | "material" "TOOLS/TOOLSTRIGGER" 1464 | "uaxis" "[-0.8774 -0.4798 0 49.5295] 0.25" 1465 | "vaxis" "[0 0 -1 0] 0.433594" 1466 | "rotation" "0" 1467 | "lightmapscale" "16" 1468 | "smoothing_groups" "0" 1469 | } 1470 | editor 1471 | { 1472 | "color" "220 30 220" 1473 | "visgroupshown" "1" 1474 | "visgroupautoshown" "1" 1475 | } 1476 | } 1477 | editor 1478 | { 1479 | "color" "220 30 220" 1480 | "visgroupshown" "1" 1481 | "visgroupautoshown" "1" 1482 | "logicalpos" "[0 4500]" 1483 | } 1484 | } 1485 | entity 1486 | { 1487 | "id" "1041" 1488 | "classname" "prop_dynamic" 1489 | "angles" "0 180 0" 1490 | "DefaultAnim" "3ready" 1491 | "fademindist" "-1" 1492 | "fadescale" "1" 1493 | "MaxAnimTime" "10" 1494 | "MinAnimTime" "5" 1495 | "model" "models/vip/helicopter.mdl" 1496 | "renderamt" "255" 1497 | "rendercolor" "255 255 255" 1498 | "skin" "0" 1499 | "solid" "6" 1500 | "spawnflags" "0" 1501 | "targetname" "vip_heli" 1502 | "origin" "16 0 -1" 1503 | editor 1504 | { 1505 | "color" "220 30 220" 1506 | "visgroupshown" "1" 1507 | "visgroupautoshown" "1" 1508 | "logicalpos" "[0 5000]" 1509 | } 1510 | } 1511 | entity 1512 | { 1513 | "id" "1045" 1514 | "classname" "ambient_generic" 1515 | "health" "10" 1516 | "message" "vehicles/loud_helicopter_lp_01.wav" 1517 | "pitch" "100" 1518 | "pitchstart" "100" 1519 | "radius" "1024" 1520 | "spawnflags" "0" 1521 | "origin" "16 0 216" 1522 | editor 1523 | { 1524 | "color" "220 30 220" 1525 | "visgroupshown" "1" 1526 | "visgroupautoshown" "1" 1527 | "logicalpos" "[0 5500]" 1528 | } 1529 | } 1530 | cameras 1531 | { 1532 | "activecamera" "0" 1533 | camera 1534 | { 1535 | "position" "[137.053 -49.6958 223.692]" 1536 | "look" "[-106.575 45.777 273.607]" 1537 | } 1538 | camera 1539 | { 1540 | "position" "[-540.716 -480 -39.7614]" 1541 | "look" "[-288 -480 -124]" 1542 | } 1543 | camera 1544 | { 1545 | "position" "[-476.716 -448 -39.7614]" 1546 | "look" "[-224 -448 -124]" 1547 | } 1548 | camera 1549 | { 1550 | "position" "[-412.716 -416 -39.7614]" 1551 | "look" "[-160 -416 -124]" 1552 | } 1553 | camera 1554 | { 1555 | "position" "[-412.716 -480 -39.7614]" 1556 | "look" "[-160 -480 -124]" 1557 | } 1558 | camera 1559 | { 1560 | "position" "[-284.716 -416 -39.7614]" 1561 | "look" "[-32 -416 -124]" 1562 | } 1563 | camera 1564 | { 1565 | "position" "[-284.716 -480 -39.7614]" 1566 | "look" "[-32 -480 -124]" 1567 | } 1568 | camera 1569 | { 1570 | "position" "[-348.716 -448 -39.7614]" 1571 | "look" "[-96 -448 -124]" 1572 | } 1573 | camera 1574 | { 1575 | "position" "[-284.716 480 -39.7614]" 1576 | "look" "[-32 480 -124]" 1577 | } 1578 | camera 1579 | { 1580 | "position" "[-284.716 416 -39.7614]" 1581 | "look" "[-32 416 -124]" 1582 | } 1583 | camera 1584 | { 1585 | "position" "[-348.716 448 -39.7614]" 1586 | "look" "[-96 448 -124]" 1587 | } 1588 | camera 1589 | { 1590 | "position" "[-412.716 480 -39.7614]" 1591 | "look" "[-160 480 -124]" 1592 | } 1593 | camera 1594 | { 1595 | "position" "[-412.716 416 -39.7614]" 1596 | "look" "[-160 416 -124]" 1597 | } 1598 | camera 1599 | { 1600 | "position" "[-476.716 448 -39.7614]" 1601 | "look" "[-224 448 -124]" 1602 | } 1603 | camera 1604 | { 1605 | "position" "[-540.716 416 -39.7614]" 1606 | "look" "[-288 416 -124]" 1607 | } 1608 | camera 1609 | { 1610 | "position" "[-540.716 480 -39.7614]" 1611 | "look" "[-288 480 -124]" 1612 | } 1613 | camera 1614 | { 1615 | "position" "[29.8907 102.892 273.966]" 1616 | "look" "[15.8209 -148.889 188.118]" 1617 | } 1618 | camera 1619 | { 1620 | "position" "[-540.716 -480 -39.7614]" 1621 | "look" "[-288 -480 -124]" 1622 | } 1623 | camera 1624 | { 1625 | "position" "[-476.716 -448 -39.7614]" 1626 | "look" "[-224 -448 -124]" 1627 | } 1628 | camera 1629 | { 1630 | "position" "[-412.716 -416 -39.7614]" 1631 | "look" "[-160 -416 -124]" 1632 | } 1633 | camera 1634 | { 1635 | "position" "[-412.716 -480 -39.7614]" 1636 | "look" "[-160 -480 -124]" 1637 | } 1638 | camera 1639 | { 1640 | "position" "[-284.716 -416 -39.7614]" 1641 | "look" "[-32 -416 -124]" 1642 | } 1643 | camera 1644 | { 1645 | "position" "[-284.716 -480 -39.7614]" 1646 | "look" "[-32 -480 -124]" 1647 | } 1648 | camera 1649 | { 1650 | "position" "[-348.716 -448 -39.7614]" 1651 | "look" "[-96 -448 -124]" 1652 | } 1653 | camera 1654 | { 1655 | "position" "[-284.716 480 -39.7614]" 1656 | "look" "[-32 480 -124]" 1657 | } 1658 | camera 1659 | { 1660 | "position" "[-284.716 416 -39.7614]" 1661 | "look" "[-32 416 -124]" 1662 | } 1663 | camera 1664 | { 1665 | "position" "[-348.716 448 -39.7614]" 1666 | "look" "[-96 448 -124]" 1667 | } 1668 | camera 1669 | { 1670 | "position" "[-412.716 480 -39.7614]" 1671 | "look" "[-160 480 -124]" 1672 | } 1673 | camera 1674 | { 1675 | "position" "[-412.716 416 -39.7614]" 1676 | "look" "[-160 416 -124]" 1677 | } 1678 | camera 1679 | { 1680 | "position" "[-476.716 448 -39.7614]" 1681 | "look" "[-224 448 -124]" 1682 | } 1683 | camera 1684 | { 1685 | "position" "[-540.716 416 -39.7614]" 1686 | "look" "[-288 416 -124]" 1687 | } 1688 | camera 1689 | { 1690 | "position" "[-540.716 480 -39.7614]" 1691 | "look" "[-288 480 -124]" 1692 | } 1693 | } 1694 | cordons 1695 | { 1696 | "active" "0" 1697 | } 1698 | -------------------------------------------------------------------------------- /vip/vmfs/test_vip.kv: -------------------------------------------------------------------------------- 1 | "test_vip" 2 | { 3 | "name" "VIP Testings" 4 | "imagename" "test-vip-overall" 5 | "t_arms" "models/weapons/t_arms_separatist.mdl" 6 | "ct_arms" "models/weapons/ct_arms_fbi.mdl" 7 | "t_models" 8 | { 9 | "tm_separatist""" 10 | "tm_separatist_variantA""" 11 | "tm_separatist_variantB""" 12 | "tm_separatist_variantC""" 13 | "tm_separatist_variantD""" 14 | } 15 | "ct_models" 16 | { 17 | "ctm_fbi""" 18 | "ctm_fbi_variantA""" 19 | "ctm_fbi_variantB""" 20 | "ctm_fbi_variantC""" 21 | "ctm_fbi_variantD""" 22 | } 23 | } --------------------------------------------------------------------------------