├── .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 | }
--------------------------------------------------------------------------------