├── .github
├── FUNDING.yml
├── dependabot.yml
└── workflows
│ ├── build-shared.yml
│ ├── formatting.yml
│ └── plugin-build.yml
├── .gitignore
├── LICENSE
├── README.md
├── RetakesPlugin
├── .gitignore
├── Modules
│ ├── Configs
│ │ ├── JsonConverters
│ │ │ ├── QAngleJsonConverter.cs
│ │ │ └── VectorJsonConverter.cs
│ │ ├── MapConfig.cs
│ │ ├── MapConfigData.cs
│ │ ├── RetakesConfig.cs
│ │ ├── RetakesConfigData.cs
│ │ └── Spawn.cs
│ ├── Helpers.cs
│ ├── Managers
│ │ ├── AllocationManager.cs
│ │ ├── BreakerManager.cs
│ │ ├── GameManager.cs
│ │ ├── QueueManager.cs
│ │ └── SpawnManager.cs
│ ├── RetakesPluginEventSender.cs
│ └── Translator.cs
├── RetakesPlugin.cs
├── RetakesPlugin.csproj
├── lang
│ ├── ar.json
│ ├── da.json
│ ├── de.json
│ ├── en.json
│ ├── es.json
│ ├── fi.json
│ ├── fr.json
│ ├── hu.json
│ ├── no.json
│ ├── pl.json
│ ├── pt-BR.json
│ ├── pt-PT.json
│ ├── ru.json
│ ├── sv.json
│ ├── tr.json
│ ├── uz.json
│ ├── zh-Hans.json
│ └── zh-Hant.json
└── map_config
│ ├── de_ancient.json
│ ├── de_anubis.json
│ ├── de_cache.json
│ ├── de_dust2.json
│ ├── de_inferno.json
│ ├── de_mirage.json
│ ├── de_nuke.json
│ ├── de_overpass.json
│ ├── de_train.json
│ └── de_vertigo.json
└── RetakesPluginShared
├── .gitignore
├── Enums
└── Bombsite.cs
├── Events
├── AllocateEvent.cs
├── AnnounceBombsiteEvent.cs
└── IRetakesPluginEvent.cs
├── IRetakesPluginEventSender.cs
└── RetakesPluginShared.csproj
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Add patreon
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | custom: https://paypal.me/b3none
9 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "nuget" # See documentation for possible values
9 | directory: "/RetakesPlugin" # Location of package manifests
10 | schedule:
11 | interval: "daily"
12 | - package-ecosystem: "nuget" # See documentation for possible values
13 | directory: "/RetakesPluginShared" # Location of package manifests
14 | schedule:
15 | interval: "daily"
16 |
--------------------------------------------------------------------------------
/.github/workflows/build-shared.yml:
--------------------------------------------------------------------------------
1 | name: Build RetakesPluginShared
2 |
3 | on:
4 | push:
5 | branches: [ "master" ]
6 | paths:
7 | - '**.cs'
8 | - '**.csproj'
9 | pull_request:
10 | branches: [ "master" ]
11 | paths:
12 | - '**.cs'
13 | - '**.csproj'
14 | release:
15 | types:
16 | - created
17 | workflow_dispatch:
18 |
19 | jobs:
20 | build:
21 | runs-on: ubuntu-latest
22 |
23 | steps:
24 | - uses: actions/checkout@v4
25 | - name: Setup .NET
26 | uses: actions/setup-dotnet@v4
27 | with:
28 | dotnet-version: 8.0.x
29 | - name: Change directory and build
30 | run: |
31 | cd RetakesPluginShared
32 | dotnet build
33 | - name: Create output directory
34 | run: |
35 | mkdir -p output/addons/counterstrikesharp/shared/RetakesPluginShared
36 | mv ./RetakesPluginShared/bin/Debug/net8.0/RetakesPluginShared.dll output/addons/counterstrikesharp/shared/RetakesPluginShared/
37 | mv ./RetakesPluginShared/bin/Debug/net8.0/RetakesPluginShared.pdb output/addons/counterstrikesharp/shared/RetakesPluginShared/
38 | mv ./RetakesPluginShared/bin/Debug/net8.0/RetakesPluginShared.deps.json output/addons/counterstrikesharp/shared/RetakesPluginShared/
39 | - name: Publish artifact
40 | uses: actions/upload-artifact@v4
41 | with:
42 | name: RetakesPluginShared-${{ github.sha }}
43 | path: output
44 |
45 | release:
46 | needs: build
47 | permissions: write-all
48 | runs-on: ubuntu-latest
49 | if: github.event_name == 'release'
50 |
51 | steps:
52 | - name: Download build artifact
53 | uses: actions/download-artifact@v4
54 | with:
55 | name: RetakesPluginShared-${{ github.sha }}
56 |
57 | - name: Create release assets
58 | run: |
59 | zip -r cs2-retakes-shared-${{ github.event.release.tag_name }}.zip ./addons
60 |
61 | - name: Upload release assets
62 | uses: softprops/action-gh-release@v2
63 | with:
64 | files: |
65 | ./cs2-retakes-shared-${{ github.event.release.tag_name }}.zip
66 | env:
67 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
68 |
--------------------------------------------------------------------------------
/.github/workflows/formatting.yml:
--------------------------------------------------------------------------------
1 | name: Check the format of .cs files
2 |
3 | on:
4 | push:
5 | branches: [ "master" ]
6 | paths:
7 | - '**.cs'
8 | - '**.csproj'
9 | pull_request:
10 | branches: [ "master" ]
11 | paths:
12 | - '**.cs'
13 | - '**.csproj'
14 |
15 | jobs:
16 | dotnet-format:
17 | runs-on: ubuntu-latest
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v4
21 |
22 | - name: .NET Format (RetakesPlugin)
23 | uses: zyactions/dotnet-format@v1
24 | with:
25 | workspace: RetakesPlugin
26 | implicit-restore: true
27 |
28 | - name: .NET Format (RetakesPluginShared)
29 | uses: zyactions/dotnet-format@v1
30 | with:
31 | workspace: RetakesPluginShared
32 | implicit-restore: true
33 |
34 |
--------------------------------------------------------------------------------
/.github/workflows/plugin-build.yml:
--------------------------------------------------------------------------------
1 | name: Build RetakesPlugin
2 |
3 | on:
4 | push:
5 | branches: [ "master" ]
6 | paths:
7 | - '**.cs'
8 | - '**.csproj'
9 | pull_request:
10 | branches: [ "master" ]
11 | paths:
12 | - '**.cs'
13 | - '**.csproj'
14 | release:
15 | types:
16 | - created
17 | workflow_dispatch:
18 |
19 | jobs:
20 | build:
21 | runs-on: ubuntu-latest
22 |
23 | steps:
24 | - uses: actions/checkout@v4
25 | - name: Setup .NET
26 | uses: actions/setup-dotnet@v4
27 | with:
28 | dotnet-version: 8.0.x
29 | - name: Change directory and build
30 | run: |
31 | cd RetakesPlugin
32 | dotnet build
33 | - name: Create output directory
34 | run: |
35 | mkdir -p output/RetakesPlugin
36 | mv ./RetakesPlugin/bin/Debug/net8.0/RetakesPlugin.dll output/RetakesPlugin/
37 | mv ./RetakesPlugin/bin/Debug/net8.0/RetakesPlugin.pdb output/RetakesPlugin/
38 | mv ./RetakesPlugin/map_config output/RetakesPlugin/
39 | mv ./RetakesPlugin/lang output/RetakesPlugin/
40 | - name: Copy output (no map configs)
41 | run: |
42 | cp -r output/ output-no-map-configs/
43 | rm -rf output-no-map-configs/RetakesPlugin/map_config
44 | - name: Publish artifact
45 | uses: actions/upload-artifact@v4
46 | with:
47 | name: RetakesPlugin-${{ github.sha }}
48 | path: output
49 | - name: Publish artifact (no map configs)
50 | uses: actions/upload-artifact@v4
51 | with:
52 | name: RetakesPlugin-${{ github.sha }}-no-map-configs
53 | path: output-no-map-configs
54 |
55 | release:
56 | needs: build
57 | permissions: write-all
58 | runs-on: ubuntu-latest
59 | if: github.event_name == 'release'
60 |
61 | steps:
62 | - name: Download build artifact
63 | uses: actions/download-artifact@v4
64 | with:
65 | name: RetakesPlugin-${{ github.sha }}
66 |
67 | - name: Create release assets
68 | run: |
69 | zip -r cs2-retakes-${{ github.event.release.tag_name }}.zip ./RetakesPlugin
70 | rm -rf ./RetakesPlugin/map_config
71 | zip -r cs2-retakes-${{ github.event.release.tag_name }}-no-map-configs.zip ./RetakesPlugin
72 |
73 | - name: Upload release assets
74 | uses: softprops/action-gh-release@v2
75 | with:
76 | files: |
77 | ./cs2-retakes-${{ github.event.release.tag_name }}.zip
78 | ./cs2-retakes-${{ github.event.release.tag_name }}-no-map-configs.zip
79 | env:
80 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
81 |
82 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Rider IDE
2 | /.idea
3 | /RetakesPlugin/.idea
4 | /RetakesPluginShared/.idea
5 | *.DotSettings.user
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/b3none/cs2-retakes/releases/latest)
2 | 
3 |
4 | # CS2 Retakes
5 | CS2 implementation of retakes written in C# for CounterStrikeSharp. Based on the version for CS:GO by Splewis.
6 |
7 | ## Share the love
8 | If you appreciate the project then please take the time to star the repository 🙏
9 |
10 | 
11 |
12 | ## Features / Roadmap
13 | - [x] Bombsite selection
14 | - [x] Per map configurations
15 | - [x] Ability to add spawns
16 | - [x] Spawn system
17 | - [x] Temporary weapon allocation (hard coded)
18 | - [x] Temporary grenade allocation (hard coded)
19 | - [x] Equipment allocation
20 | - [x] Queue manager (Queue system)
21 | - [x] Team manager (with team switch calculations)
22 | - [x] Retakes config file
23 | - [x] Add translations
24 | - [x] Improve bombsite announcement
25 | - [x] Queue priority for VIPs
26 | - [x] Add autoplant
27 | - [x] Add a command to view the spawns for the current bombsite
28 | - [x] Add a command to delete the nearest spawn
29 | - [x] Implement better spawn management system
30 | - [x] Add a release zip file without spawns too
31 |
32 | ## Installation
33 | - Download the zip file from the [latest release](https://github.com/B3none/cs2-retakes/releases/latest), and extract the contents into your `addons/counterstrikesharp/plugins` directory.
34 | - Download the latest shared plugin and put it into your `addons/counterstrikesharp/shared` directory.
35 |
36 | ## Recommendations
37 | I also recommend installing these plugins for an improved player experience
38 | - Instadefuse: https://github.com/B3none/cs2-instadefuse
39 | - Retakes Zones (prevent silly flanks / rotations): https://github.com/oscar-wos/Retakes-Zones
40 | - Clutch Announce: https://github.com/B3none/cs2-clutch-announce
41 | - Instaplant (if not using autoplant): https://github.com/B3none/cs2-instaplant
42 |
43 | ## Allocators
44 | Although this plugin comes with it's own weapon allocation system, I would recommend using **one** of the following plugins for a better experience:
45 | - Yoni's Allocator: https://github.com/yonilerner/cs2-retakes-allocator
46 | - NokkviReyr's Allocator: https://github.com/nokkvireyr/kps-allocator
47 | - Ravid's Allocator: https://github.com/Ravid-A/cs2-retakes-weapon-allocator
48 |
49 | ## Configuration
50 | When the plugin is first loaded it will create a `retakes_config.json` file in the plugin directory. This file contains all of the configuration options for the plugin:
51 |
52 | | Config | Description | Default | Min | Max |
53 | |---------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|------------|------------|------------|
54 | | Version | The retakes config version. This is used to automatically migrate the retakes config file for you. **ignore this value**. | **IGNORE** | **IGNORE** | **IGNORE** |
55 | | MaxPlayers | The maximum number of players allowed in the game at any time. (If you want to increase the max capability you need to add more spawns) | 9 | 2 | 10 |
56 | | TerroristRatio | The percentage of the total players that should be Terrorists. | 0.45 | 0 | 1 |
57 | | RoundsToScramble | The number of rounds won in a row before the teams are scrambled. | 5 | -1 | 99999 |
58 | | IsScrambleEnabled | Whether to scramble the teams once the RoundsToScramble value is met. | true | false | true |
59 | | EnableFallbackAllocation | Whether to enable the fallback weapon allocation. You should set this value to false if you're using a standalone weapon allocator. | true | false | true |
60 | | EnableBombsiteAnnouncementVoices | Whether to play the bombsite announcement voices. The volume for these values is client sided `snd_toolvolume`. | true | false | true |
61 | | EnableBombsiteAnnouncementCenter | Whether to display the bombsite in the center announcement box. | true | false | true |
62 | | ShouldBreakBreakables | Whether to break all breakable props on round start (People are noticing rare crashes when this is enabled). | false | false | true |
63 | | ShouldOpenDoors | Whether to open doors on round start (People are noticing rare crashes when this is enabled). | false | false | true |
64 | | IsAutoPlantEnabled | Whether to enable auto bomb planting at the start of the round or not. | true | false | true |
65 | | QueuePriorityFlag | A comma separated list of CSS flags for queue priority. | @css/vip | n/a | n/a |
66 | | IsDebugMode | Whether to enable debug output to the server console or not. | false | false | true |
67 | | ShouldForceEvenTeamsWhenPlayerCountIsMultipleOf10 | Whether to force even teams when the active players is a multiple of 10 or not. (this means you will get 5v5 @ 10 players / 10v10 @ 20 players) | true | false | true |
68 | | EnableFallbackBombsiteAnnouncement | Whether to enable the fallback bombsite announcement. | true | false | true |
69 | | ShouldRemoveSpectators | When a player is moved to spectators, remove them from all retake queues. Ensures that AFK plugins work as expected. | false | false | true |
70 | | IsBalanceEnabled | Whether to enable the default team balancing mechanic. | true | false | true |
71 | | ShouldPreventTeamChangesMidRound | Whether or not to prevent players from switching teams at any point during the round. | true | false | true |
72 |
73 | ## Commands
74 | | Command | Arguments | Description | Permissions |
75 | |--------------------|-----------------------------------|----------------------------------------------------------------------|-------------|
76 | | !forcebombsite | | Force the retakes to occur from a single bombsite. | @css/root |
77 | | !forcebombsitestop | | Clear the forced bombsite and return back to normal. | @css/root |
78 | | !showspawns | | Show the spawns for the specified bombsite. | @css/root |
79 | | !addspawn | | Adds a retakes spawn point for the bombsite spawns currently shown. | @css/root |
80 | | !removespawn | | Removes the nearest spawn point for the bombsite currently shown. | @css/root |
81 | | !nearestspawn | | Teleports the player to the nearest spawn. | @css/root |
82 | | !hidespawns | | Exits the spawn editing mode. | @css/root |
83 | | !mapconfig | | Forces a specific map config file to load. | @css/root |
84 | | !mapconfigs | | Displays a list of available map configs. | @css/root |
85 | | !scramble | | Scrambles the teams next round. | @css/admin |
86 | | !voices | | Toggles whether or not to hear the bombsite voice announcements. | |
87 | | css_debugqueues | | **SERVER ONLY** Shows the current queue state in the server console. | |
88 |
89 | ## Stay up to date
90 | Subscribe to **release** notifications and stay up to date with the latest features and patches:
91 |
92 | 
93 |
94 | ## Credits
95 | This was inspired by the [CS:GO Retakes project](https://github.com/splewis/csgo-retakes) written by [splewis](https://github.com/splewis).
96 |
97 | ## Server Hosting (Discounted)
98 |
99 | Looking for reliable server hosting? [Dathost](https://dathost.net/r/b3none/cs2-server-hosting) offers top-tier performance, easy server management, and excellent support, with servers available in multiple regions across the globe. Whether you're in North America, Europe, Asia, or anywhere else, [Dathost](https://dathost.net/r/b3none/cs2-server-hosting) has you covered. Use [this link](https://dathost.net/r/b3none/cs2-server-hosting) to get **30% off your first month**. Click [here]( https://dathost.net/r/b3none/cs2-server-hosting) to get started with the discount!
100 |
--------------------------------------------------------------------------------
/RetakesPlugin/.gitignore:
--------------------------------------------------------------------------------
1 | **/obj/
2 | **/obj/**/*.nuget.cache
3 | **/bin/
4 |
--------------------------------------------------------------------------------
/RetakesPlugin/Modules/Configs/JsonConverters/QAngleJsonConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 | using System.Text.Json;
3 | using System.Text.Json.Serialization;
4 | using CounterStrikeSharp.API.Modules.Utils;
5 |
6 | namespace RetakesPlugin.Modules.Configs.JsonConverters;
7 |
8 | public class QAngleJsonConverter : JsonConverter
9 | {
10 | public override QAngle Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
11 | {
12 | if (reader.TokenType != JsonTokenType.String)
13 | {
14 | throw new JsonException("Expected a string value.");
15 | }
16 |
17 | var stringValue = reader.GetString();
18 | if (stringValue == null)
19 | {
20 | throw new JsonException("String value is null.");
21 | }
22 |
23 | var values = stringValue.Split(' '); // Split by space
24 |
25 | if (values.Length != 3)
26 | {
27 | throw new JsonException("String value is not in the correct format (X Y Z).");
28 | }
29 |
30 | if (!float.TryParse(values[0], NumberStyles.Any, CultureInfo.InvariantCulture, out var x) ||
31 | !float.TryParse(values[1], NumberStyles.Any, CultureInfo.InvariantCulture, out var y) ||
32 | !float.TryParse(values[2], NumberStyles.Any, CultureInfo.InvariantCulture, out var z))
33 | {
34 | Helpers.Debug($"Unable to parse QAngle float values for: {stringValue}");
35 | throw new JsonException("Unable to parse QAngle float values.");
36 | }
37 |
38 | return new QAngle(x, y, z);
39 | }
40 |
41 | public override void Write(Utf8JsonWriter writer, QAngle value, JsonSerializerOptions options)
42 | {
43 | var x = value.X.ToString(CultureInfo.InvariantCulture);
44 | var y = value.Y.ToString(CultureInfo.InvariantCulture);
45 | var z = value.Z.ToString(CultureInfo.InvariantCulture);
46 |
47 | writer.WriteStringValue($"{x} {y} {z}");
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/RetakesPlugin/Modules/Configs/JsonConverters/VectorJsonConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 | using System.Text.Json;
3 | using System.Text.Json.Serialization;
4 | using CounterStrikeSharp.API.Modules.Utils;
5 |
6 | namespace RetakesPlugin.Modules.Configs.JsonConverters;
7 |
8 | public class VectorJsonConverter : JsonConverter
9 | {
10 | public override Vector Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
11 | {
12 | if (reader.TokenType != JsonTokenType.String)
13 | {
14 | throw new JsonException("Expected a string value.");
15 | }
16 |
17 | var stringValue = reader.GetString();
18 | if (stringValue == null)
19 | {
20 | throw new JsonException("String value is null.");
21 | }
22 |
23 | var values = stringValue.Split(' '); // Split by space
24 |
25 | if (values.Length != 3)
26 | {
27 | throw new JsonException("String value is not in the correct format (X Y Z).");
28 | }
29 |
30 | if (!float.TryParse(values[0], NumberStyles.Any, CultureInfo.InvariantCulture, out var x) ||
31 | !float.TryParse(values[1], NumberStyles.Any, CultureInfo.InvariantCulture, out var y) ||
32 | !float.TryParse(values[2], NumberStyles.Any, CultureInfo.InvariantCulture, out var z))
33 | {
34 | Helpers.Debug($"Unable to parse Vector float values for: {stringValue}");
35 | throw new JsonException("Unable to parse Vector float values.");
36 | }
37 |
38 | return new Vector(x, y, z);
39 | }
40 |
41 | public override void Write(Utf8JsonWriter writer, Vector value, JsonSerializerOptions options)
42 | {
43 | var x = value.X.ToString(CultureInfo.InvariantCulture);
44 | var y = value.Y.ToString(CultureInfo.InvariantCulture);
45 | var z = value.Z.ToString(CultureInfo.InvariantCulture);
46 |
47 | writer.WriteStringValue($"{x} {y} {z}");
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/RetakesPlugin/Modules/Configs/MapConfig.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 |
3 | namespace RetakesPlugin.Modules.Configs;
4 |
5 | public class MapConfig
6 | {
7 | private readonly string _mapName;
8 | private readonly string _mapConfigDirectory;
9 | private readonly string _mapConfigPath;
10 | private MapConfigData? _mapConfigData;
11 |
12 | public MapConfig(string moduleDirectory, string mapName)
13 | {
14 | _mapName = mapName;
15 | _mapConfigDirectory = Path.Combine(moduleDirectory, "map_config");
16 | _mapConfigPath = Path.Combine(_mapConfigDirectory, $"{mapName}.json");
17 | _mapConfigData = null;
18 | }
19 |
20 | public void Load(bool isViaCommand = false)
21 | {
22 | Helpers.Debug($"Attempting to load map data from {_mapConfigPath}");
23 |
24 | try
25 | {
26 | if (!File.Exists(_mapConfigPath))
27 | {
28 | throw new FileNotFoundException();
29 | }
30 |
31 | var jsonData = File.ReadAllText(_mapConfigPath);
32 | _mapConfigData = JsonSerializer.Deserialize(jsonData, Helpers.JsonSerializerOptions);
33 |
34 | // TODO: Implement validation to make sure the config is valid / has enough spawns.
35 | // if (_mapConfigData!.Spawns == null || _mapConfigData.Spawns.Count < 0)
36 | // {
37 | // throw new Exception("No spawns found in config");
38 | // }
39 |
40 | Helpers.Debug($"Data loaded from {_mapConfigPath}");
41 | }
42 | catch (FileNotFoundException)
43 | {
44 | Helpers.Debug($"No config for map {_mapName}");
45 |
46 | if (!isViaCommand)
47 | {
48 | _mapConfigData = new MapConfigData();
49 | Save();
50 | }
51 | }
52 | catch (Exception ex)
53 | {
54 | Helpers.Debug($"An error occurred while loading data: {ex.Message}");
55 | }
56 | }
57 |
58 | /**
59 | * This function returns a clone of the spawns list. (free to mutate :>)
60 | */
61 | public List GetSpawnsClone()
62 | {
63 | if (_mapConfigData == null)
64 | {
65 | throw new Exception("Map config data is null");
66 | }
67 |
68 | return _mapConfigData.Spawns.ToList();
69 | }
70 |
71 | public bool AddSpawn(Spawn spawn)
72 | {
73 | _mapConfigData ??= new MapConfigData();
74 |
75 | // Check if the spawn already exists based on vector and bombsite
76 | if (_mapConfigData.Spawns.Any(existingSpawn =>
77 | existingSpawn.Vector == spawn.Vector && existingSpawn.Bombsite == spawn.Bombsite))
78 | {
79 | return false; // Spawn already exists, avoid duplication
80 | }
81 |
82 | _mapConfigData.Spawns.Add(spawn);
83 |
84 | Save();
85 | Load();
86 |
87 | return true;
88 | }
89 |
90 | public bool RemoveSpawn(Spawn spawn)
91 | {
92 | _mapConfigData ??= new MapConfigData();
93 |
94 | if (!_mapConfigData.Spawns.Any(existingSpawn =>
95 | existingSpawn.Vector == spawn.Vector && existingSpawn.Bombsite == spawn.Bombsite))
96 | {
97 | return false; // Spawn doesn't exist, avoid removing
98 | }
99 |
100 | _mapConfigData.Spawns.Remove(spawn);
101 |
102 | Save();
103 | Load();
104 |
105 | return true;
106 | }
107 |
108 | private MapConfigData GetSanitisedMapConfigData()
109 | {
110 | if (_mapConfigData == null)
111 | {
112 | throw new Exception("Map config data is null");
113 | }
114 |
115 | // Remove any duplicate spawns in the list
116 | _mapConfigData.Spawns = _mapConfigData.Spawns
117 | .GroupBy(spawn => new { spawn.Vector, spawn.Bombsite })
118 | .Select(group => group.First())
119 | .ToList();
120 |
121 | return _mapConfigData;
122 | }
123 |
124 | private void Save()
125 | {
126 | var jsonString = JsonSerializer.Serialize(GetSanitisedMapConfigData(), Helpers.JsonSerializerOptions);
127 |
128 | try
129 | {
130 | if (!Directory.Exists(_mapConfigDirectory))
131 | {
132 | Directory.CreateDirectory(_mapConfigDirectory);
133 | }
134 |
135 | File.WriteAllText(_mapConfigPath, jsonString);
136 |
137 | Helpers.Debug($"Data has been written to {_mapConfigPath}");
138 | }
139 | catch (IOException e)
140 | {
141 | Helpers.Debug($"An error occurred while writing to the file: {e.Message}");
142 | }
143 | }
144 |
145 | public static bool IsLoaded(MapConfig? mapConfig, string currentMap)
146 | {
147 | if (mapConfig == null || mapConfig._mapName != currentMap)
148 | {
149 | return false;
150 | }
151 |
152 | return true;
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/RetakesPlugin/Modules/Configs/MapConfigData.cs:
--------------------------------------------------------------------------------
1 | namespace RetakesPlugin.Modules.Configs;
2 |
3 | public class MapConfigData
4 | {
5 | public List Spawns { get; set; } = [];
6 | }
7 |
--------------------------------------------------------------------------------
/RetakesPlugin/Modules/Configs/RetakesConfig.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 |
3 | namespace RetakesPlugin.Modules.Configs;
4 |
5 | public class RetakesConfig
6 | {
7 | private readonly string _retakesConfigPath;
8 | public RetakesConfigData? RetakesConfigData;
9 |
10 | public RetakesConfig(string moduleDirectory)
11 | {
12 | _retakesConfigPath = Path.Combine(moduleDirectory, "retakes_config.json");
13 | RetakesConfigData = null;
14 | }
15 |
16 | public void Load()
17 | {
18 | Helpers.Debug($"Attempting to load data from {_retakesConfigPath}");
19 |
20 | try
21 | {
22 | if (!File.Exists(_retakesConfigPath))
23 | {
24 | throw new FileNotFoundException();
25 | }
26 |
27 | var jsonData = File.ReadAllText(_retakesConfigPath);
28 | RetakesConfigData = JsonSerializer.Deserialize(jsonData, Helpers.JsonSerializerOptions);
29 |
30 | if (RetakesConfigData == null)
31 | {
32 | throw new Exception("Retakes config is null after deserialization");
33 | }
34 |
35 | if (RetakesConfigData.Version != RetakesConfigData.CurrentVersion)
36 | {
37 | UpdateVersion();
38 | throw new Exception("Config is outdated");
39 | }
40 |
41 | Helpers.Debug($"Data loaded from {_retakesConfigPath}");
42 | }
43 | catch (FileNotFoundException)
44 | {
45 | Helpers.Debug($"No retakes config.");
46 | RetakesConfigData = new RetakesConfigData();
47 | Save();
48 | }
49 | catch (Exception ex)
50 | {
51 | Helpers.Debug($"An error occurred while loading data: {ex.Message}");
52 | }
53 | }
54 |
55 | private void Save()
56 | {
57 | var jsonString = JsonSerializer.Serialize(RetakesConfigData, Helpers.JsonSerializerOptions);
58 |
59 | try
60 | {
61 | File.WriteAllText(_retakesConfigPath, jsonString);
62 |
63 | Helpers.Debug($"Data has been written to {_retakesConfigPath}");
64 | }
65 | catch (IOException e)
66 | {
67 | Helpers.Debug($"An error occurred while writing to the file: {e.Message}");
68 | }
69 | }
70 |
71 | public static bool IsLoaded(RetakesConfig? retakesConfig)
72 | {
73 | if (retakesConfig == null)
74 | {
75 | Helpers.Debug($"Retakes config is null");
76 | return false;
77 | }
78 |
79 | if (retakesConfig.RetakesConfigData == null)
80 | {
81 | Helpers.Debug($"Retakes config data is null");
82 | return false;
83 | }
84 |
85 | return true;
86 | }
87 |
88 | private bool UpdateVersion()
89 | {
90 | if (RetakesConfigData == null)
91 | {
92 | return false;
93 | }
94 |
95 | if (RetakesConfigData.Version == RetakesConfigData.CurrentVersion)
96 | {
97 | return true;
98 | }
99 |
100 | RetakesConfigData.Version = RetakesConfigData.CurrentVersion;
101 | Save();
102 |
103 | return true;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/RetakesPlugin/Modules/Configs/RetakesConfigData.cs:
--------------------------------------------------------------------------------
1 | namespace RetakesPlugin.Modules.Configs;
2 |
3 | public class RetakesConfigData
4 | {
5 | public static int CurrentVersion = 11;
6 |
7 | public int Version { get; set; } = CurrentVersion;
8 | public int MaxPlayers { get; set; } = 9;
9 | public float TerroristRatio { get; set; } = 0.45f;
10 | public int RoundsToScramble { get; set; } = 5;
11 | public bool IsScrambleEnabled { get; set; } = true;
12 | public bool EnableFallbackAllocation { get; set; } = true;
13 | public bool EnableBombsiteAnnouncementVoices { get; set; } = false;
14 | public bool EnableBombsiteAnnouncementCenter { get; set; } = true;
15 | public bool ShouldBreakBreakables { get; set; } = false;
16 | public bool ShouldOpenDoors { get; set; } = false;
17 | public bool IsAutoPlantEnabled { get; set; } = true;
18 | public string QueuePriorityFlag { get; set; } = "@css/vip";
19 | public bool IsDebugMode { get; set; } = false;
20 | public bool ShouldForceEvenTeamsWhenPlayerCountIsMultipleOf10 { get; set; } = true;
21 | public bool EnableFallbackBombsiteAnnouncement { get; set; } = true;
22 | public bool ShouldRemoveSpectators { get; set; } = true;
23 | public bool IsBalanceEnabled { get; set; } = true;
24 | public bool ShouldPreventTeamChangesMidRound { get; set; } = true;
25 | }
26 |
--------------------------------------------------------------------------------
/RetakesPlugin/Modules/Configs/Spawn.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 | using CounterStrikeSharp.API.Modules.Utils;
3 | using RetakesPlugin.Modules.Configs.JsonConverters;
4 | using RetakesPluginShared.Enums;
5 |
6 | namespace RetakesPlugin.Modules.Configs;
7 |
8 | public class Spawn
9 | {
10 | public Spawn(Vector vector, QAngle qAngle)
11 | {
12 | Vector = vector;
13 | QAngle = qAngle;
14 | }
15 |
16 | [JsonConverter(typeof(VectorJsonConverter))]
17 | public Vector Vector { get; }
18 |
19 | [JsonConverter(typeof(QAngleJsonConverter))]
20 | public QAngle QAngle { get; }
21 |
22 | public CsTeam Team { get; set; }
23 | public Bombsite Bombsite { get; set; }
24 | public bool CanBePlanter { get; set; }
25 | }
26 |
--------------------------------------------------------------------------------
/RetakesPlugin/Modules/Helpers.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using System.Drawing;
3 | using System.Text;
4 | using System.Text.Json;
5 | using CounterStrikeSharp.API;
6 | using CounterStrikeSharp.API.Core;
7 | using CounterStrikeSharp.API.Modules.Admin;
8 | using CounterStrikeSharp.API.Modules.Entities.Constants;
9 | using CounterStrikeSharp.API.Modules.Utils;
10 | using RetakesPlugin.Modules.Configs;
11 | using RetakesPlugin.Modules.Configs.JsonConverters;
12 | using RetakesPluginShared.Enums;
13 |
14 | namespace RetakesPlugin.Modules;
15 |
16 | public static class Helpers
17 | {
18 | internal static readonly Random Random = new();
19 | internal static readonly JsonSerializerOptions JsonSerializerOptions = new()
20 | {
21 | WriteIndented = true,
22 | Converters =
23 | {
24 | new VectorJsonConverter(),
25 | new QAngleJsonConverter()
26 | }
27 | };
28 |
29 | public static bool IsValidPlayer([NotNullWhen(true)] CCSPlayerController? player)
30 | {
31 | return player != null && player.IsValid;
32 | }
33 |
34 | public static bool DoesPlayerHaveAlivePawn(CCSPlayerController? player, bool shouldBeAlive = true)
35 | {
36 | if (!IsValidPlayer(player))
37 | {
38 | return false;
39 | }
40 |
41 | var playerPawn = player!.Pawn.Value;
42 |
43 | if (playerPawn == null || playerPawn is { AbsOrigin: null, AbsRotation: null })
44 | {
45 | return false;
46 | }
47 |
48 | if (shouldBeAlive && !(playerPawn.Health > 0))
49 | {
50 | return false;
51 | }
52 |
53 | return true;
54 | }
55 |
56 | public static List Shuffle(IEnumerable list)
57 | {
58 | var shuffledList = new List(list); // Create a copy of the original list
59 |
60 | var n = shuffledList.Count;
61 | while (n > 1)
62 | {
63 | n--;
64 | var k = Random.Next(n + 1);
65 | T value = shuffledList[k];
66 | shuffledList[k] = shuffledList[n];
67 | shuffledList[n] = value;
68 | }
69 |
70 | return shuffledList;
71 | }
72 |
73 | public static CCSGameRules GetGameRules()
74 | {
75 | var gameRulesEntities = Utilities.FindAllEntitiesByDesignerName("cs_gamerules");
76 | var gameRules = gameRulesEntities.First().GameRules;
77 |
78 | if (gameRules == null)
79 | {
80 | throw new Exception($"{RetakesPlugin.LogPrefix}Game rules not found!");
81 | }
82 |
83 | return gameRules;
84 | }
85 |
86 | public static bool IsPlayerConnected(CCSPlayerController player)
87 | {
88 | return player.Connected == PlayerConnectedState.PlayerConnected;
89 | }
90 |
91 | private const string RetakesCfgDirectory = "/../../../../cfg/cs2-retakes";
92 | private const string RetakesCfgPath = $"{RetakesCfgDirectory}/retakes.cfg";
93 |
94 | public static void ExecuteRetakesConfiguration(string moduleDirectory)
95 | {
96 | if (!File.Exists(moduleDirectory + RetakesCfgPath))
97 | {
98 | // make any directories required too
99 | Directory.CreateDirectory(moduleDirectory + RetakesCfgDirectory);
100 |
101 | var retakesCfg = File.Create(moduleDirectory + RetakesCfgPath);
102 |
103 | var retakesCfgContents = @"
104 | // Things you shouldn't change:
105 | bot_kick
106 | bot_quota 0
107 | mp_autoteambalance 0
108 | mp_forcecamera 1
109 | mp_give_player_c4 0
110 | mp_halftime 0
111 | mp_ignore_round_win_conditions 0
112 | mp_join_grace_time 0
113 | mp_match_can_clinch 0
114 | mp_maxmoney 0
115 | mp_playercashawards 0
116 | mp_respawn_on_death_ct 0
117 | mp_respawn_on_death_t 0
118 | mp_solid_teammates 1
119 | mp_teamcashawards 0
120 | mp_warmup_pausetimer 0
121 | sv_skirmish_id 0
122 |
123 | // Things you can change, and may want to:
124 | mp_roundtime_defuse 0.25
125 | mp_autokick 0
126 | mp_c4timer 40
127 | mp_freezetime 1
128 | mp_friendlyfire 0
129 | mp_round_restart_delay 2
130 | sv_talk_enemy_dead 0
131 | sv_talk_enemy_living 0
132 | sv_deadtalk 1
133 | spec_replay_enable 0
134 | mp_maxrounds 30
135 | mp_match_end_restart 0
136 | mp_timelimit 0
137 | mp_match_restart_delay 10
138 | mp_death_drop_gun 1
139 | mp_death_drop_defuser 1
140 | mp_death_drop_grenade 1
141 | mp_warmuptime 15
142 |
143 | echo [Retakes] Config loaded!
144 | ";
145 |
146 | var retakesCfgBytes = Encoding.UTF8.GetBytes(retakesCfgContents);
147 | retakesCfg.Write(retakesCfgBytes, 0, retakesCfgBytes.Length);
148 |
149 | retakesCfg.Close();
150 | }
151 |
152 | Server.ExecuteCommand("exec cs2-retakes/retakes.cfg");
153 | }
154 |
155 | public static void Debug(string message)
156 | {
157 | if (RetakesPlugin.IsDebugMode)
158 | {
159 | Console.WriteLine($"{RetakesPlugin.LogPrefix}{message}");
160 | }
161 | }
162 |
163 | public static int GetCurrentNumPlayers(CsTeam? csTeam = null)
164 | {
165 | var players = 0;
166 |
167 | foreach (var player in Utilities.GetPlayers()
168 | .Where(player => IsValidPlayer(player)))
169 | {
170 | if (csTeam == null || player.Team == csTeam)
171 | {
172 | players++;
173 | }
174 | }
175 |
176 | return players;
177 | }
178 |
179 | public static void GiveAndSwitchToBomb(CCSPlayerController player)
180 | {
181 | player.GiveNamedItem(CsItem.Bomb);
182 | NativeAPI.IssueClientCommand((int)player.UserId!, "slot5");
183 | }
184 |
185 | public static void RemoveHelmetAndHeavyArmour(CCSPlayerController player)
186 | {
187 | if (player.PlayerPawn.Value == null || player.PlayerPawn.Value.ItemServices == null)
188 | {
189 | return;
190 | }
191 |
192 | var itemServices = new CCSPlayer_ItemServices(player.PlayerPawn.Value.ItemServices.Handle);
193 | itemServices.HasHelmet = false;
194 | itemServices.HasHeavyArmor = false;
195 | }
196 |
197 | public static void RestartGame()
198 | {
199 | if (!GetGameRules().WarmupPeriod)
200 | {
201 | CheckRoundDone();
202 | }
203 |
204 | Server.ExecuteCommand("mp_restartgame 1");
205 | }
206 |
207 | public static void CheckRoundDone()
208 | {
209 | var tHumanCount = GetCurrentNumPlayers(CsTeam.Terrorist);
210 | var ctHumanCount = GetCurrentNumPlayers(CsTeam.CounterTerrorist);
211 |
212 | if (tHumanCount == 0 || ctHumanCount == 0)
213 | {
214 | TerminateRound(RoundEndReason.TerroristsWin);
215 | }
216 | }
217 |
218 | public static void TerminateRound(RoundEndReason roundEndReason)
219 | {
220 | // TODO: Once the signature is updated then this will be redundant
221 | try
222 | {
223 | GetGameRules().TerminateRound(0.1f, roundEndReason);
224 | }
225 | catch
226 | {
227 | Debug(
228 | $"Incorrect signature detected (Can't use TerminateRound) killing all alive players instead.");
229 | var alivePlayers = Utilities.GetPlayers()
230 | .Where(IsValidPlayer)
231 | .Where(player => player.PawnIsAlive)
232 | .ToList();
233 |
234 | foreach (var player in alivePlayers)
235 | {
236 | player.CommitSuicide(false, true);
237 | }
238 | }
239 | }
240 |
241 | public static double GetDistanceBetweenVectors(Vector v1, Vector v2)
242 | {
243 | var dx = v1.X - v2.X;
244 | var dy = v1.Y - v2.Y;
245 |
246 | return Math.Sqrt(Math.Pow(dx, 2) + Math.Pow(dy, 2));
247 | }
248 |
249 | public static void PlantTickingBomb(CCSPlayerController? player, Bombsite bombsite)
250 | {
251 | if (player == null || !player.IsValid)
252 | {
253 | throw new Exception("Player controller is not valid");
254 | }
255 |
256 | var playerPawn = player.Pawn.Value;
257 |
258 | if (playerPawn == null || !playerPawn.IsValid)
259 | {
260 | throw new Exception("Player pawn is not valid");
261 | }
262 |
263 | if (playerPawn.AbsOrigin == null)
264 | {
265 | throw new Exception("Player pawn abs origin is null");
266 | }
267 |
268 | if (playerPawn.AbsRotation == null)
269 | {
270 | throw new Exception("Player pawn abs rotation is null");
271 | }
272 |
273 | var plantedC4 = Utilities.CreateEntityByName("planted_c4");
274 |
275 | if (plantedC4 == null)
276 | {
277 | throw new Exception("c4 is null");
278 | }
279 |
280 | if (plantedC4.AbsOrigin == null)
281 | {
282 | throw new Exception("c4.AbsOrigin is null");
283 | }
284 |
285 | plantedC4.AbsOrigin.X = playerPawn.AbsOrigin.X;
286 | plantedC4.AbsOrigin.Y = playerPawn.AbsOrigin.Y;
287 | plantedC4.AbsOrigin.Z = playerPawn.AbsOrigin.Z;
288 | plantedC4.HasExploded = false;
289 |
290 | plantedC4.BombSite = (int)bombsite;
291 | plantedC4.BombTicking = true;
292 | plantedC4.CannotBeDefused = false;
293 |
294 | plantedC4.DispatchSpawn();
295 |
296 | var gameRules = GetGameRules();
297 | gameRules.BombPlanted = true;
298 | gameRules.BombDefused = false;
299 |
300 | SendBombPlantedEvent(player, bombsite);
301 | }
302 |
303 | private static void SendBombPlantedEvent(CCSPlayerController bombCarrier, Bombsite bombsite)
304 | {
305 | if (!bombCarrier.IsValid || bombCarrier.PlayerPawn.Value == null)
306 | {
307 | return;
308 | }
309 |
310 | var eventPtr = NativeAPI.CreateEvent("bomb_planted", true);
311 | NativeAPI.SetEventPlayerController(eventPtr, "userid", bombCarrier.Handle);
312 | NativeAPI.SetEventInt(eventPtr, "site", (int)bombsite);
313 |
314 | NativeAPI.FireEvent(eventPtr, false);
315 | }
316 |
317 | public static int ShowSpawns(List spawns, Bombsite? bombsite)
318 | {
319 | if (bombsite == null)
320 | {
321 | return -1;
322 | }
323 |
324 | spawns = spawns.Where(spawn => spawn.Bombsite == bombsite).ToList();
325 |
326 | foreach (var spawn in spawns)
327 | {
328 | ShowSpawn(spawn);
329 | }
330 |
331 | Server.PrintToChatAll($"{RetakesPlugin.MessagePrefix}Showing {spawns.Count} spawns for bombsite {bombsite}.");
332 | return spawns.Count;
333 | }
334 |
335 | public static void ShowSpawn(Spawn spawn)
336 | {
337 | var beam = Utilities.CreateEntityByName("beam") ?? throw new Exception("Failed to create beam entity.");
338 | beam.StartFrame = 0;
339 | beam.FrameRate = 0;
340 | beam.LifeState = 1;
341 | beam.Width = 5;
342 | beam.EndWidth = 5;
343 | beam.Amplitude = 0;
344 | beam.Speed = 50;
345 | beam.Flags = 0;
346 | beam.BeamType = BeamType_t.BEAM_HOSE;
347 | beam.FadeLength = 10.0f;
348 |
349 | var color = spawn.Team == CsTeam.Terrorist ? (spawn.CanBePlanter ? Color.Orange : Color.Red) : Color.Blue;
350 | beam.Render = Color.FromArgb(255, color);
351 |
352 | beam.EndPos.X = spawn.Vector.X;
353 | beam.EndPos.Y = spawn.Vector.Y;
354 | beam.EndPos.Z = spawn.Vector.Z + 100.0f;
355 |
356 | beam.Teleport(spawn.Vector, new QAngle(IntPtr.Zero), new Vector(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero));
357 | beam.DispatchSpawn();
358 | }
359 |
360 | public static bool HasQueuePriority(CCSPlayerController player, string[] queuePriorityFlags)
361 | {
362 | foreach (var queuePriorityFlag in queuePriorityFlags)
363 | {
364 | if (AdminManager.PlayerHasPermissions(player, queuePriorityFlag))
365 | {
366 | return true;
367 | }
368 | }
369 |
370 | return false;
371 | }
372 | }
373 |
--------------------------------------------------------------------------------
/RetakesPlugin/Modules/Managers/AllocationManager.cs:
--------------------------------------------------------------------------------
1 | using CounterStrikeSharp.API.Core;
2 | using CounterStrikeSharp.API.Modules.Entities.Constants;
3 | using CounterStrikeSharp.API.Modules.Utils;
4 |
5 | namespace RetakesPlugin.Modules.Managers;
6 |
7 | public static class AllocationManager
8 | {
9 | public static void Allocate(CCSPlayerController player)
10 | {
11 | AllocateEquipment(player);
12 | AllocateWeapons(player);
13 | AllocateGrenades(player);
14 | }
15 |
16 | private static void AllocateEquipment(CCSPlayerController player)
17 | {
18 | player.GiveNamedItem(CsItem.KevlarHelmet);
19 |
20 | if (
21 | player.Team == CsTeam.CounterTerrorist
22 | && player.PlayerPawn.IsValid
23 | && player.PlayerPawn.Value != null
24 | && player.PlayerPawn.Value.IsValid
25 | && player.PlayerPawn.Value.ItemServices != null
26 | )
27 | {
28 | var itemServices = new CCSPlayer_ItemServices(player.PlayerPawn.Value.ItemServices.Handle);
29 | itemServices.HasDefuser = true;
30 | }
31 | }
32 |
33 | private static void AllocateWeapons(CCSPlayerController player)
34 | {
35 | if (player.Team == CsTeam.Terrorist)
36 | {
37 | player.GiveNamedItem(CsItem.AK47);
38 | player.GiveNamedItem(CsItem.Deagle);
39 | }
40 |
41 | if (player.Team == CsTeam.CounterTerrorist)
42 | {
43 | // @klippy
44 | if (player.PlayerName.Trim() == "klip")
45 | {
46 | player.GiveNamedItem(CsItem.M4A4);
47 | }
48 | else
49 | {
50 | player.GiveNamedItem(CsItem.M4A1S);
51 | }
52 |
53 | player.GiveNamedItem(CsItem.Deagle);
54 | }
55 |
56 | player.GiveNamedItem(CsItem.Knife);
57 | }
58 |
59 | private static void AllocateGrenades(CCSPlayerController player)
60 | {
61 | switch (Helpers.Random.Next(4))
62 | {
63 | case 0:
64 | player.GiveNamedItem(CsItem.SmokeGrenade);
65 | break;
66 | case 1:
67 | player.GiveNamedItem(CsItem.Flashbang);
68 | break;
69 | case 2:
70 | player.GiveNamedItem(CsItem.HEGrenade);
71 | break;
72 | case 3:
73 | player.GiveNamedItem(player.Team == CsTeam.Terrorist ? CsItem.Molotov : CsItem.Incendiary);
74 | break;
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/RetakesPlugin/Modules/Managers/BreakerManager.cs:
--------------------------------------------------------------------------------
1 | using CounterStrikeSharp.API;
2 | using CounterStrikeSharp.API.Core;
3 | using CounterStrikeSharp.API.Modules.Entities;
4 | using CounterStrikeSharp.API.Modules.Utils;
5 |
6 | namespace RetakesPlugin.Modules.Managers;
7 |
8 | public class BreakerManager
9 | {
10 | private readonly List<(string designerName, string action)> _entityActions = [];
11 |
12 | private static readonly HashSet MapsWithPropDynamic = [
13 | "de_vertigo",
14 | "de_nuke",
15 | "de_mirage"
16 | ];
17 |
18 | private static readonly HashSet MapsWithFuncButton = ["de_nuke"];
19 |
20 | public BreakerManager(bool? shouldBreakBreakables, bool? shouldOpenDoors)
21 | {
22 | if (shouldBreakBreakables ?? false)
23 | {
24 | _entityActions.AddRange(new List<(string designerName, string action)>
25 | {
26 | ("func_breakable", "Break"),
27 | ("func_breakable_surf", "Break"),
28 | ("prop.breakable.01", "Break"),
29 | ("prop.breakable.02", "Break")
30 | });
31 |
32 | if (MapsWithPropDynamic.Contains(Server.MapName))
33 | {
34 | _entityActions.Add(("prop_dynamic", "Break"));
35 | }
36 |
37 | if (MapsWithFuncButton.Contains(Server.MapName))
38 | {
39 | _entityActions.Add(("func_button", "Kill"));
40 | }
41 | }
42 |
43 | if (shouldOpenDoors ?? false)
44 | {
45 | _entityActions.Add(("prop_door_rotating", "open"));
46 | }
47 | }
48 |
49 | public void Handle()
50 | {
51 | if (_entityActions.Count == 0)
52 | {
53 | return;
54 | }
55 |
56 | var pEntity = new CEntityIdentity(EntitySystem.FirstActiveEntity);
57 | for (; pEntity != null && pEntity.Handle != IntPtr.Zero; pEntity = pEntity.Next)
58 | {
59 | foreach (var (designerName, action) in _entityActions)
60 | {
61 | if (pEntity.DesignerName != designerName)
62 | {
63 | continue;
64 | }
65 |
66 | switch (pEntity.DesignerName)
67 | {
68 | case "func_breakable":
69 | case "func_breakable_surf":
70 | case "prop_dynamic":
71 | case "prop.breakable.01":
72 | case "prop.breakable.02":
73 | var breakableEntity = new PointerTo(pEntity.Handle).Value;
74 |
75 | if (breakableEntity.IsValid)
76 | {
77 | breakableEntity.AcceptInput(action);
78 | }
79 |
80 | break;
81 |
82 | case "func_button":
83 | var button = new PointerTo(pEntity.Handle).Value;
84 |
85 | if (button.IsValid)
86 | {
87 | button.AcceptInput(action);
88 | }
89 |
90 | break;
91 |
92 | case "prop_door_rotating":
93 | var propDoorRotating = new PointerTo(pEntity.Handle).Value;
94 |
95 | if (propDoorRotating.IsValid)
96 | {
97 | propDoorRotating.AcceptInput(action);
98 | }
99 |
100 | break;
101 | }
102 |
103 | break;
104 | }
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/RetakesPlugin/Modules/Managers/GameManager.cs:
--------------------------------------------------------------------------------
1 | using CounterStrikeSharp.API;
2 | using CounterStrikeSharp.API.Core;
3 | using CounterStrikeSharp.API.Modules.Utils;
4 |
5 | namespace RetakesPlugin.Modules.Managers;
6 |
7 | public class GameManager
8 | {
9 | private readonly Translator _translator;
10 | private Dictionary _playerRoundScores = new();
11 | public readonly QueueManager QueueManager;
12 | private readonly int _consecutiveRoundWinsToScramble;
13 | private readonly bool _isScrambleEnabled;
14 | private readonly bool _removeSpectatorsEnabled;
15 | private readonly bool _isBalanceEnabled;
16 | public const int ScoreForKill = 50;
17 | public const int ScoreForAssist = 25;
18 | public const int ScoreForDefuse = 50;
19 |
20 | public GameManager(Translator translator, QueueManager queueManager, int? roundsToScramble, bool? isScrambleEnabled, bool? removeSpectatorsEnabled, bool? isBalanceEnabled)
21 | {
22 | _translator = translator;
23 | QueueManager = queueManager;
24 | _consecutiveRoundWinsToScramble = roundsToScramble ?? 5;
25 | _isScrambleEnabled = isScrambleEnabled ?? true;
26 | _removeSpectatorsEnabled = removeSpectatorsEnabled ?? false;
27 | _isBalanceEnabled = isBalanceEnabled ?? true;
28 | }
29 |
30 | private bool _scrambleNextRound;
31 |
32 | public void ScrambleNextRound(CCSPlayerController? admin = null)
33 | {
34 | _scrambleNextRound = true;
35 | Server.PrintToChatAll(
36 | $"{RetakesPlugin.MessagePrefix}{_translator["retakes.teams.admin_scramble", admin?.PlayerName ?? "The server owner"]}");
37 | }
38 |
39 | private void ScrambleTeams()
40 | {
41 | _scrambleNextRound = false;
42 | _consecutiveRoundsWon = 0;
43 |
44 | var shuffledActivePlayers = Helpers.Shuffle(QueueManager.ActivePlayers);
45 |
46 | var newTerrorists = shuffledActivePlayers.Take(QueueManager.GetTargetNumTerrorists()).ToList();
47 | var newCounterTerrorists = shuffledActivePlayers.Except(newTerrorists).ToList();
48 |
49 | SetTeams(newTerrorists, newCounterTerrorists);
50 | }
51 |
52 | public void ResetPlayerScores()
53 | {
54 | _playerRoundScores = new Dictionary();
55 | }
56 |
57 | public void AddScore(CCSPlayerController player, int score)
58 | {
59 | if (!Helpers.IsValidPlayer(player) || player.UserId == null)
60 | {
61 | return;
62 | }
63 |
64 | var playerId = (int)player.UserId;
65 |
66 | if (!_playerRoundScores.TryAdd(playerId, score))
67 | {
68 | // Add to the player's existing score
69 | _playerRoundScores[playerId] += score;
70 | }
71 | }
72 |
73 | private int _consecutiveRoundsWon;
74 |
75 | private void TerroristRoundWin()
76 | {
77 | _consecutiveRoundsWon++;
78 |
79 | var shouldScrambleNow = _isScrambleEnabled && _consecutiveRoundsWon == _consecutiveRoundWinsToScramble;
80 | var roundsLeftToScramble = _consecutiveRoundWinsToScramble - _consecutiveRoundsWon;
81 | // Almost scramble if 1-2 rounds left to automatic scramble
82 | var shouldAlmostScramble = _isScrambleEnabled && roundsLeftToScramble > 0 && roundsLeftToScramble <= 2;
83 |
84 | if (shouldScrambleNow)
85 | {
86 | Server.PrintToChatAll(
87 | $"{RetakesPlugin.MessagePrefix}{_translator["retakes.teams.scramble", _consecutiveRoundWinsToScramble]}");
88 |
89 | ScrambleTeams();
90 | }
91 | else if (shouldAlmostScramble)
92 | {
93 | Server.PrintToChatAll(
94 | $"{RetakesPlugin.MessagePrefix}{_translator["retakes.teams.almost_scramble", _consecutiveRoundsWon, roundsLeftToScramble]}");
95 | }
96 | else if (_consecutiveRoundsWon >= 3)
97 | {
98 | Server.PrintToChatAll(
99 | $"{RetakesPlugin.MessagePrefix}{_translator["retakes.teams.win_streak", _consecutiveRoundsWon]}");
100 | }
101 | }
102 |
103 | private void CounterTerroristRoundWin()
104 | {
105 | if (_consecutiveRoundsWon >= 3)
106 | {
107 | Server.PrintToChatAll(
108 | $"{RetakesPlugin.MessagePrefix}{_translator["retakes.teams.win_streak_over", _consecutiveRoundsWon]}");
109 | }
110 |
111 | _consecutiveRoundsWon = 0;
112 |
113 | var targetNumTerrorists = QueueManager.GetTargetNumTerrorists();
114 | var sortedCounterTerroristPlayers = GetSortedActivePlayers(CsTeam.CounterTerrorist);
115 |
116 | // Ensure that the players with the scores are set as new terrorists first.
117 | var newTerrorists = sortedCounterTerroristPlayers.Where(player => player.Score > 0).Take(targetNumTerrorists)
118 | .ToList();
119 |
120 | if (newTerrorists.Count < targetNumTerrorists)
121 | {
122 | // Shuffle the other players with 0 score to ensure it's random who is swapped
123 | var playersLeft = Helpers.Shuffle(sortedCounterTerroristPlayers.Except(newTerrorists).ToList());
124 | newTerrorists.AddRange(playersLeft.Take(targetNumTerrorists - newTerrorists.Count));
125 | }
126 |
127 | if (newTerrorists.Count < targetNumTerrorists)
128 | {
129 | // If we still don't have enough terrorists
130 | newTerrorists.AddRange(
131 | GetSortedActivePlayers(CsTeam.Terrorist)
132 | .Take(targetNumTerrorists - newTerrorists.Count)
133 | );
134 | }
135 |
136 | newTerrorists.AddRange(sortedCounterTerroristPlayers.Where(player => player.Score > 0)
137 | .Take(targetNumTerrorists - newTerrorists.Count).ToList());
138 |
139 | var newCounterTerrorists = QueueManager.ActivePlayers.Except(newTerrorists).ToList();
140 |
141 | SetTeams(newTerrorists, newCounterTerrorists);
142 | }
143 |
144 | private void BalanceTeams()
145 | {
146 | List newTerrorists = [];
147 | List newCounterTerrorists = [];
148 |
149 | var currentNumTerrorist = Helpers.GetCurrentNumPlayers(CsTeam.Terrorist);
150 | var numTerroristsNeeded = QueueManager.GetTargetNumTerrorists() - currentNumTerrorist;
151 |
152 | if (numTerroristsNeeded > 0)
153 | {
154 | var sortedCounterTerroristPlayers = GetSortedActivePlayers(CsTeam.CounterTerrorist);
155 |
156 | newTerrorists = sortedCounterTerroristPlayers.Where(player => player.Score > 0)
157 | .Take(numTerroristsNeeded).ToList();
158 |
159 | if (newTerrorists.Count < numTerroristsNeeded)
160 | {
161 | var playersLeft = Helpers.Shuffle(sortedCounterTerroristPlayers.Except(newTerrorists).ToList());
162 | newTerrorists.AddRange(playersLeft.Take(numTerroristsNeeded - newTerrorists.Count));
163 | }
164 | }
165 |
166 | var currentNumCounterTerroristAfterBalance = Helpers.GetCurrentNumPlayers(CsTeam.CounterTerrorist);
167 | var numCounterTerroristsNeeded =
168 | QueueManager.GetTargetNumCounterTerrorists() - currentNumCounterTerroristAfterBalance;
169 |
170 | if (numCounterTerroristsNeeded > 0)
171 | {
172 | var terroristsWithZeroScore = QueueManager.ActivePlayers
173 | .Where(player =>
174 | Helpers.IsValidPlayer(player)
175 | && player.Team == CsTeam.Terrorist
176 | && _playerRoundScores.GetValueOrDefault((int)player.UserId!, 0) == 0
177 | )
178 | .Except(newTerrorists)
179 | .ToList();
180 |
181 | // Shuffle to avoid repetitive swapping of the same players
182 | newCounterTerrorists = Helpers.Shuffle(terroristsWithZeroScore).Take(numCounterTerroristsNeeded).ToList();
183 |
184 | if (numCounterTerroristsNeeded > newCounterTerrorists.Count)
185 | {
186 | // For remaining excess terrorists, move the ones with the lowest score to CT
187 | newCounterTerrorists.AddRange(
188 | QueueManager.ActivePlayers
189 | .Except(newCounterTerrorists)
190 | .Except(newTerrorists)
191 | .Where(player => Helpers.IsValidPlayer(player) && player.Team == CsTeam.Terrorist)
192 | .OrderBy(player => _playerRoundScores.GetValueOrDefault((int)player.UserId!, 0))
193 | .Take(numTerroristsNeeded - newCounterTerrorists.Count)
194 | .ToList()
195 | );
196 | }
197 | }
198 |
199 | SetTeams(newTerrorists, newCounterTerrorists);
200 | }
201 |
202 | public void OnRoundPreStart(CsTeam winningTeam)
203 | {
204 | // Handle team swaps during round pre-start.
205 | switch (winningTeam)
206 | {
207 | case CsTeam.CounterTerrorist:
208 | if (_isBalanceEnabled)
209 | {
210 | CounterTerroristRoundWin();
211 | }
212 | break;
213 |
214 | case CsTeam.Terrorist:
215 | TerroristRoundWin();
216 | break;
217 | }
218 |
219 | if (_scrambleNextRound)
220 | {
221 | ScrambleTeams();
222 | }
223 |
224 | if (_isBalanceEnabled)
225 | {
226 | BalanceTeams();
227 | }
228 | }
229 |
230 | private List GetSortedActivePlayers(CsTeam? team = null)
231 | {
232 | return QueueManager.ActivePlayers
233 | .Where(Helpers.IsValidPlayer)
234 | .Where(player => team == null || player.Team == team)
235 | .OrderByDescending(player => _playerRoundScores.GetValueOrDefault((int)player.UserId!, 0))
236 | .ToList();
237 | }
238 |
239 | private void SetTeams(List? terrorists, List? counterTerrorists)
240 | {
241 | terrorists ??= [];
242 | counterTerrorists ??= [];
243 |
244 | foreach (var player in QueueManager.ActivePlayers.Where(Helpers.IsValidPlayer))
245 | {
246 | if (terrorists.Contains(player))
247 | {
248 | player.SwitchTeam(CsTeam.Terrorist);
249 | }
250 | else if (counterTerrorists.Contains(player))
251 | {
252 | player.SwitchTeam(CsTeam.CounterTerrorist);
253 | }
254 | }
255 | }
256 |
257 | public HookResult RemoveSpectators(EventPlayerTeam @event, HashSet _hasMutedVoices)
258 | {
259 | if (_removeSpectatorsEnabled)
260 | {
261 | CCSPlayerController? player = @event.Userid;
262 |
263 | if (!Helpers.IsValidPlayer(player))
264 | {
265 | return HookResult.Continue;
266 | }
267 | int team = @event.Team;
268 |
269 | if (team == (int)CsTeam.Spectator)
270 | {
271 | // Ensure player is active ingame.
272 | if (QueueManager.ActivePlayers.Contains(player))
273 | {
274 | QueueManager.RemovePlayerFromQueues(player);
275 | _hasMutedVoices.Remove(player);
276 | }
277 | }
278 | }
279 | return HookResult.Continue;
280 | }
281 | }
282 |
--------------------------------------------------------------------------------
/RetakesPlugin/Modules/Managers/QueueManager.cs:
--------------------------------------------------------------------------------
1 | using CounterStrikeSharp.API.Core;
2 | using CounterStrikeSharp.API.Modules.Utils;
3 |
4 | namespace RetakesPlugin.Modules.Managers;
5 |
6 | public class QueueManager
7 | {
8 | private readonly Translator _translator;
9 | private readonly int _maxRetakesPlayers;
10 | private readonly float _terroristRatio;
11 | private readonly string[] _queuePriorityFlags;
12 | private readonly bool _shouldForceEvenTeamsWhenPlayerCountIsMultipleOf10;
13 | private readonly bool _shouldPreventTeamChangesMidRound;
14 |
15 | public HashSet QueuePlayers = [];
16 | public HashSet ActivePlayers = [];
17 |
18 | public QueueManager(
19 | Translator translator,
20 | int? retakesMaxPlayers,
21 | float? retakesTerroristRatio,
22 | string? queuePriorityFlags,
23 | bool? shouldForceEvenTeamsWhenPlayerCountIsMultipleOf10,
24 | bool? shouldPreventTeamChangesMidRound
25 | )
26 | {
27 | _translator = translator;
28 | _maxRetakesPlayers = retakesMaxPlayers ?? 9;
29 | _terroristRatio = retakesTerroristRatio ?? 0.45f;
30 | _queuePriorityFlags = queuePriorityFlags?.Split(",").Select(flag => flag.Trim()).ToArray() ?? ["@css/vip"];
31 | _shouldForceEvenTeamsWhenPlayerCountIsMultipleOf10 = shouldForceEvenTeamsWhenPlayerCountIsMultipleOf10 ?? true;
32 | _shouldPreventTeamChangesMidRound = shouldPreventTeamChangesMidRound ?? true;
33 | }
34 |
35 | public int GetTargetNumTerrorists()
36 | {
37 | var shouldForceEvenTeams = _shouldForceEvenTeamsWhenPlayerCountIsMultipleOf10 && ActivePlayers.Count % 10 == 0;
38 | var ratio = (shouldForceEvenTeams ? 0.5 : _terroristRatio) * ActivePlayers.Count;
39 | var numTerrorists = (int)Math.Round(ratio);
40 |
41 | // Ensure at least one terrorist if the calculated number is zero
42 | return numTerrorists > 0 ? numTerrorists : 1;
43 | }
44 |
45 | public int GetTargetNumCounterTerrorists()
46 | {
47 | return ActivePlayers.Count - GetTargetNumTerrorists();
48 | }
49 |
50 | public HookResult PlayerJoinedTeam(CCSPlayerController player, CsTeam fromTeam, CsTeam toTeam)
51 | {
52 | Helpers.Debug($"[{player.PlayerName}] PlayerTriedToJoinTeam called.");
53 |
54 | if (fromTeam == CsTeam.None && toTeam == CsTeam.Spectator)
55 | {
56 | // This is called when a player first joins.
57 | Helpers.Debug(
58 | $"[{player.PlayerName}] {fromTeam.ToString()} -> {toTeam.ToString()}.");
59 | return HookResult.Continue;
60 | }
61 |
62 | Helpers.Debug($"[{player.PlayerName}] Checking ActivePlayers.");
63 | if (ActivePlayers.Contains(player))
64 | {
65 | Helpers.Debug($"[{player.PlayerName}] Player is an active player.");
66 |
67 | if (toTeam == CsTeam.Spectator)
68 | {
69 | Helpers.Debug($"[{player.PlayerName}] Switching to spectator.");
70 | RemovePlayerFromQueues(player);
71 | Helpers.CheckRoundDone();
72 | return HookResult.Continue;
73 | }
74 |
75 | if (!_shouldPreventTeamChangesMidRound || Helpers.GetGameRules().WarmupPeriod)
76 | {
77 | Helpers.Debug($"[{player.PlayerName}] Preventing team changes mid round is disabled or warmup is active, allowing team change.");
78 | return HookResult.Continue;
79 | }
80 |
81 | if (
82 | _roundTerrorists.Count > 0
83 | && _roundCounterTerrorists.Count > 0
84 | && (
85 | (toTeam == CsTeam.CounterTerrorist && !_roundCounterTerrorists.Contains(player))
86 | || (toTeam == CsTeam.Terrorist && !_roundTerrorists.Contains(player))
87 | )
88 | )
89 | {
90 | Helpers.Debug(
91 | $"[{player.PlayerName}] player is not in round list for {toTeam}, switching to spectator.");
92 | ActivePlayers.Remove(player);
93 | QueuePlayers.Add(player);
94 |
95 | if (player.PawnIsAlive)
96 | {
97 | player.CommitSuicide(false, true);
98 | }
99 |
100 | player.ChangeTeam(CsTeam.Spectator);
101 | return HookResult.Handled;
102 | }
103 |
104 | Helpers.Debug(
105 | $"[{player.PlayerName}] The player tried joining the team they're already on, or, there were not enough players so we don't care. Do nothing.");
106 | Helpers.CheckRoundDone();
107 | return HookResult.Handled;
108 | }
109 |
110 | Helpers.Debug($"[{player.PlayerName}] Checking QueuePlayers.");
111 | if (!QueuePlayers.Contains(player))
112 | {
113 | if (Helpers.GetGameRules().WarmupPeriod && ActivePlayers.Count < _maxRetakesPlayers)
114 | {
115 | Helpers.Debug(
116 | $"[{player.PlayerName}] Not found, adding to ActivePlayers (because in warmup).");
117 | ActivePlayers.Add(player);
118 | return HookResult.Continue;
119 | }
120 |
121 | Helpers.Debug($"[{player.PlayerName}] Not found, adding to QueuePlayers.");
122 | player.PrintToChat($"{RetakesPlugin.MessagePrefix}{_translator["retakes.queue.joined"]}");
123 | QueuePlayers.Add(player);
124 | }
125 | else
126 | {
127 | Helpers.Debug($"[{player.PlayerName}] Already in Queue, do nothing.");
128 | }
129 |
130 | Helpers.CheckRoundDone();
131 | return HookResult.Handled;
132 | }
133 |
134 | private void RemoveDisconnectedPlayers()
135 | {
136 | var disconnectedActivePlayers = ActivePlayers
137 | .Where(player => !Helpers.IsValidPlayer(player) || !Helpers.IsPlayerConnected(player)).ToList();
138 |
139 | if (disconnectedActivePlayers.Count > 0)
140 | {
141 | Helpers.Debug(
142 | $"Removing {disconnectedActivePlayers.Count} disconnected players from ActivePlayers.");
143 | ActivePlayers.RemoveWhere(player => disconnectedActivePlayers.Contains(player));
144 | }
145 |
146 | var disconnectedQueuePlayers = QueuePlayers
147 | .Where(player => !Helpers.IsValidPlayer(player) || !Helpers.IsPlayerConnected(player)).ToList();
148 |
149 | if (disconnectedQueuePlayers.Count > 0)
150 | {
151 | Helpers.Debug(
152 | $"Removing {disconnectedQueuePlayers.Count} disconnected players from QueuePlayers.");
153 | QueuePlayers.RemoveWhere(player => disconnectedQueuePlayers.Contains(player));
154 | }
155 | }
156 |
157 | private void HandleQueuePriority()
158 | {
159 | Helpers.Debug($"handling queue priority.");
160 | if (ActivePlayers.Count != _maxRetakesPlayers)
161 | {
162 | Helpers.Debug($"ActivePlayers.Count != _maxRetakesPlayers, returning.");
163 | return;
164 | }
165 |
166 | var vipQueuePlayers = QueuePlayers
167 | .Where(player => Helpers.HasQueuePriority(player, _queuePriorityFlags)).ToList();
168 |
169 | if (vipQueuePlayers.Count <= 0)
170 | {
171 | Helpers.Debug($"No VIP players found in queue, returning.");
172 | return;
173 | }
174 |
175 | // loop through vipQueuePlayers and add them to ActivePlayers
176 | foreach (var vipQueuePlayer in vipQueuePlayers)
177 | {
178 | // If the player is no longer valid, skip them
179 | if (!Helpers.IsValidPlayer(vipQueuePlayer))
180 | {
181 | continue;
182 | }
183 |
184 | // TODO: We shouldn't really shuffle here, implement a last in first out queue instead.
185 | var nonVipActivePlayers = Helpers.Shuffle(
186 | ActivePlayers
187 | .Where(player => !Helpers.HasQueuePriority(player, _queuePriorityFlags))
188 | .ToList()
189 | );
190 |
191 | if (nonVipActivePlayers.Count == 0)
192 | {
193 | Helpers.Debug($"No non-VIP players found in ActivePlayers, returning.");
194 | break;
195 | }
196 |
197 | var nonVipActivePlayer = nonVipActivePlayers.First();
198 |
199 | // Switching them to spectator will automatically remove them from the queue
200 | nonVipActivePlayer.ChangeTeam(CsTeam.Spectator);
201 | ActivePlayers.Remove(nonVipActivePlayer);
202 | QueuePlayers.Add(nonVipActivePlayer);
203 | nonVipActivePlayer.PrintToChat(
204 | $"{RetakesPlugin.MessagePrefix}{_translator["retakes.queue.replaced_by_vip", vipQueuePlayer.PlayerName]}");
205 |
206 | // Add the new VIP player to ActivePlayers and remove them from QueuePlayers
207 | ActivePlayers.Add(vipQueuePlayer);
208 | QueuePlayers.Remove(vipQueuePlayer);
209 | vipQueuePlayer.ChangeTeam(CsTeam.CounterTerrorist);
210 | vipQueuePlayer.PrintToChat(
211 | $"{RetakesPlugin.MessagePrefix}{_translator["retakes.queue.vip_took_place", nonVipActivePlayer.PlayerName]}");
212 | }
213 | }
214 |
215 | public void Update()
216 | {
217 | RemoveDisconnectedPlayers();
218 |
219 | Helpers.Debug(
220 | $"{_maxRetakesPlayers} max players, {ActivePlayers.Count} active players, {QueuePlayers.Count} players in queue.");
221 | Helpers.Debug($"players to add: {_maxRetakesPlayers - ActivePlayers.Count}");
222 | var playersToAdd = _maxRetakesPlayers - ActivePlayers.Count;
223 |
224 | if (playersToAdd > 0 && QueuePlayers.Count > 0)
225 | {
226 | Helpers.Debug(
227 | $" inside if - this means the game has players to add and players in the queue.");
228 | // Take players from QueuePlayers and add them to ActivePlayers
229 | // Ordered by players with queue priority flag first since they
230 | // have queue priority.
231 | var playersToAddList = QueuePlayers
232 | .OrderBy(player => Helpers.HasQueuePriority(player, _queuePriorityFlags) ? 1 : 0)
233 | .Take(playersToAdd)
234 | .ToList();
235 |
236 | QueuePlayers.RemoveWhere(playersToAddList.Contains);
237 | foreach (var player in playersToAddList)
238 | {
239 | // If the player is no longer valid, skip them
240 | if (!Helpers.IsValidPlayer(player))
241 | {
242 | continue;
243 | }
244 |
245 | ActivePlayers.Add(player);
246 | player.ChangeTeam(CsTeam.CounterTerrorist);
247 | }
248 | }
249 |
250 | HandleQueuePriority();
251 |
252 | if (ActivePlayers.Count == _maxRetakesPlayers && QueuePlayers.Count > 0)
253 | {
254 | var waitingMessage = _translator["retakes.queue.waiting", ActivePlayers.Count];
255 |
256 | foreach (var player in QueuePlayers)
257 | {
258 | player.PrintToChat($"{RetakesPlugin.MessagePrefix}{waitingMessage}");
259 | }
260 | }
261 | }
262 |
263 | public void RemovePlayerFromQueues(CCSPlayerController player)
264 | {
265 | ActivePlayers.Remove(player);
266 | QueuePlayers.Remove(player);
267 | _roundTerrorists.Remove(player);
268 | _roundCounterTerrorists.Remove(player);
269 |
270 | Helpers.CheckRoundDone();
271 | }
272 |
273 | public void DebugQueues(bool isBefore)
274 | {
275 | if (!ActivePlayers.Any())
276 | {
277 | Helpers.Debug(
278 | $"ActivePlayers ({(isBefore ? "BEFORE" : "AFTER")}): No active players.");
279 | }
280 | else
281 | {
282 | Helpers.Debug(
283 | $"ActivePlayers ({(isBefore ? "BEFORE" : "AFTER")}): {string.Join(", ", ActivePlayers.Where(Helpers.IsValidPlayer).Select(player => player.PlayerName))}");
284 | }
285 |
286 | if (!QueuePlayers.Any())
287 | {
288 | Helpers.Debug(
289 | $"QueuePlayers ({(isBefore ? "BEFORE" : "AFTER")}): No players in the queue.");
290 | }
291 | else
292 | {
293 | Helpers.Debug(
294 | $"QueuePlayers ({(isBefore ? "BEFORE" : "AFTER")}): {string.Join(", ", QueuePlayers.Where(Helpers.IsValidPlayer).Select(player => player.PlayerName))}");
295 | }
296 |
297 | if (!_roundTerrorists.Any())
298 | {
299 | Helpers.Debug(
300 | $"_roundTerrorists ({(isBefore ? "BEFORE" : "AFTER")}): No players in the queue.");
301 | }
302 | else
303 | {
304 | Helpers.Debug(
305 | $"_roundTerrorists ({(isBefore ? "BEFORE" : "AFTER")}): {string.Join(", ", _roundTerrorists.Where(Helpers.IsValidPlayer).Select(player => player.PlayerName))}");
306 | }
307 |
308 | if (!_roundCounterTerrorists.Any())
309 | {
310 | Helpers.Debug(
311 | $"_roundCounterTerrorists ({(isBefore ? "BEFORE" : "AFTER")}): No players in the queue.");
312 | }
313 | else
314 | {
315 | Helpers.Debug(
316 | $"_roundCounterTerrorists ({(isBefore ? "BEFORE" : "AFTER")}): {string.Join(", ", _roundCounterTerrorists.Where(Helpers.IsValidPlayer).Select(player => player.PlayerName))}");
317 | }
318 | }
319 |
320 | private List _roundTerrorists = [];
321 | private List _roundCounterTerrorists = [];
322 |
323 | public void ClearRoundTeams()
324 | {
325 | _roundTerrorists.Clear();
326 | _roundCounterTerrorists.Clear();
327 | }
328 |
329 | public void SetRoundTeams()
330 | {
331 | if (!_shouldPreventTeamChangesMidRound)
332 | {
333 | return;
334 | }
335 |
336 | _roundTerrorists = ActivePlayers
337 | .Where(player => Helpers.IsValidPlayer(player) && player.Team == CsTeam.Terrorist).ToList();
338 | _roundCounterTerrorists = ActivePlayers
339 | .Where(player => Helpers.IsValidPlayer(player) && player.Team == CsTeam.CounterTerrorist).ToList();
340 | }
341 | }
342 |
--------------------------------------------------------------------------------
/RetakesPlugin/Modules/Managers/SpawnManager.cs:
--------------------------------------------------------------------------------
1 | using CounterStrikeSharp.API.Core;
2 | using CounterStrikeSharp.API.Modules.Utils;
3 | using RetakesPlugin.Modules.Configs;
4 | using RetakesPluginShared.Enums;
5 |
6 | namespace RetakesPlugin.Modules.Managers;
7 |
8 | public class SpawnManager
9 | {
10 | private readonly MapConfig _mapConfig;
11 | private readonly Dictionary>> _spawns = new();
12 |
13 | public SpawnManager(MapConfig mapConfig)
14 | {
15 | _mapConfig = mapConfig;
16 |
17 | CalculateMapSpawns();
18 | }
19 |
20 | public void CalculateMapSpawns()
21 | {
22 | _spawns.Clear();
23 |
24 | _spawns.Add(Bombsite.A, new Dictionary>()
25 | {
26 | { CsTeam.Terrorist, [] },
27 | { CsTeam.CounterTerrorist, [] }
28 | });
29 | _spawns.Add(Bombsite.B, new Dictionary>()
30 | {
31 | { CsTeam.Terrorist, [] },
32 | { CsTeam.CounterTerrorist, [] }
33 | });
34 |
35 | foreach (var spawn in _mapConfig.GetSpawnsClone())
36 | {
37 | _spawns[spawn.Bombsite][spawn.Team].Add(spawn);
38 | }
39 | }
40 |
41 | public List GetSpawns(Bombsite bombsite, CsTeam? team = null)
42 | {
43 | if (_spawns[bombsite][CsTeam.Terrorist].Count == 0 && _spawns[bombsite][CsTeam.CounterTerrorist].Count == 0)
44 | {
45 | return [];
46 | }
47 |
48 | if (team == null)
49 | {
50 | return _spawns[bombsite].SelectMany(entry => entry.Value).ToList();
51 | }
52 |
53 | return _spawns[bombsite][(CsTeam)team];
54 | }
55 |
56 | /**
57 | * This function returns the player who should be the planter and moves all players to random spawns based on bombsite.
58 | */
59 | public CCSPlayerController? HandleRoundSpawns(Bombsite bombsite, HashSet players)
60 | {
61 | Helpers.Debug($"Moving players to spawns.");
62 |
63 | // Clone the spawns so we can mutate them
64 | var spawns = _spawns[bombsite].ToDictionary(
65 | entry => entry.Key,
66 | entry => entry.Value.ToList()
67 | );
68 |
69 | if (
70 | Helpers.GetCurrentNumPlayers(CsTeam.CounterTerrorist) > spawns[CsTeam.CounterTerrorist].Count ||
71 | Helpers.GetCurrentNumPlayers(CsTeam.Terrorist) > spawns[CsTeam.Terrorist].Count
72 | )
73 | {
74 | // TODO: Potentially update the maxRetakesPlayers on the fly.
75 | throw new Exception($"There are not enough spawns in the map config for Bombsite {bombsite.ToString()}!");
76 | }
77 |
78 | var planterSpawns = spawns[CsTeam.Terrorist].Where(spawn => spawn.CanBePlanter).ToList();
79 |
80 | if (planterSpawns.Count == 0)
81 | {
82 | throw new Exception($"There are no planter spawns for Bombsite {bombsite.ToString()}!");
83 | }
84 |
85 | var randomPlanterSpawn = planterSpawns[Helpers.Random.Next(planterSpawns.Count)];
86 | spawns[CsTeam.Terrorist].Remove(randomPlanterSpawn);
87 |
88 | CCSPlayerController? planter = null;
89 |
90 | foreach (var player in Helpers.Shuffle(players))
91 | {
92 | if (!Helpers.DoesPlayerHaveAlivePawn(player))
93 | {
94 | continue;
95 | }
96 |
97 | var team = player.Team;
98 | if (team != CsTeam.Terrorist && team != CsTeam.CounterTerrorist)
99 | {
100 | continue;
101 | }
102 |
103 | if (planter == null && team == CsTeam.Terrorist)
104 | {
105 | planter = player;
106 | }
107 |
108 | var count = spawns[team].Count;
109 | if (count == 0)
110 | {
111 | continue;
112 | }
113 |
114 | var spawn = player == planter ? randomPlanterSpawn : spawns[team][Helpers.Random.Next(count)];
115 |
116 | player.Pawn.Value!.Teleport(spawn.Vector, spawn.QAngle, new Vector());
117 | spawns[team].Remove(spawn);
118 | }
119 |
120 | Helpers.Debug($"Moving players to spawns COMPLETE.");
121 |
122 | return planter;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/RetakesPlugin/Modules/RetakesPluginEventSender.cs:
--------------------------------------------------------------------------------
1 | using RetakesPluginShared;
2 | using RetakesPluginShared.Events;
3 |
4 | namespace RetakesPlugin.Modules;
5 |
6 | public class RetakesPluginEventSender : IRetakesPluginEventSender
7 | {
8 | public event EventHandler? RetakesPluginEventHandlers;
9 |
10 | public void TriggerEvent(IRetakesPluginEvent @event)
11 | {
12 | RetakesPluginEventHandlers?.Invoke(this, @event);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/RetakesPlugin/Modules/Translator.cs:
--------------------------------------------------------------------------------
1 | using CounterStrikeSharp.API.Modules.Utils;
2 | using Microsoft.Extensions.Localization;
3 |
4 | namespace RetakesPlugin.Modules;
5 |
6 | public class Translator
7 | {
8 | private IStringLocalizer _stringLocalizerImplementation;
9 |
10 | public Translator(IStringLocalizer localizer)
11 | {
12 | _stringLocalizerImplementation = localizer;
13 | }
14 |
15 | public IEnumerable GetAllStrings(bool includeParentCultures)
16 | {
17 | return _stringLocalizerImplementation.GetAllStrings(includeParentCultures);
18 | }
19 |
20 | public string this[string name] => Translate(name);
21 |
22 | public string this[string name, params object[] arguments] => Translate(name, arguments);
23 |
24 | private const string CenterModifier = "center.";
25 | private const string HtmlModifier = "html.";
26 |
27 | private string Translate(string key, params object[] arguments)
28 | {
29 | var isCenter = key.StartsWith(CenterModifier);
30 | var isHtml = key.StartsWith(HtmlModifier);
31 |
32 | if (isCenter)
33 | {
34 | key = key.Substring(CenterModifier.Length);
35 | }
36 | else if (isHtml)
37 | {
38 | key = key.Substring(HtmlModifier.Length);
39 | }
40 |
41 | var localizedString = _stringLocalizerImplementation[key, arguments];
42 |
43 | if (localizedString == null || localizedString.ResourceNotFound)
44 | {
45 | return key;
46 | }
47 |
48 | var translation = localizedString.Value;
49 |
50 | // Handle translation colours
51 | return translation
52 | .Replace("[GREEN]", isCenter ? "" : isHtml ? "" : ChatColors.Green.ToString())
53 | .Replace("[RED]", isCenter ? "" : isHtml ? "" : ChatColors.Red.ToString())
54 | .Replace("[YELLOW]", isCenter ? "" : isHtml ? "" : ChatColors.Yellow.ToString())
55 | .Replace("[BLUE]", isCenter ? "" : isHtml ? "" : ChatColors.Blue.ToString())
56 | .Replace("[PURPLE]", isCenter ? "" : isHtml ? "" : ChatColors.Purple.ToString())
57 | .Replace("[ORANGE]", isCenter ? "" : isHtml ? "" : ChatColors.Orange.ToString())
58 | .Replace("[WHITE]", isCenter ? "" : isHtml ? "" : ChatColors.White.ToString())
59 | .Replace("[NORMAL]", isCenter ? "" : isHtml ? "" : ChatColors.White.ToString())
60 | .Replace("[GREY]", isCenter ? "" : isHtml ? "" : ChatColors.Grey.ToString())
61 | .Replace("[LIGHT_RED]", isCenter ? "" : isHtml ? "" : ChatColors.LightRed.ToString())
62 | .Replace("[LIGHT_BLUE]", isCenter ? "" : isHtml ? "" : ChatColors.LightBlue.ToString())
63 | .Replace("[LIGHT_PURPLE]", isCenter ? "" : isHtml ? "" : ChatColors.LightPurple.ToString())
64 | .Replace("[LIGHT_YELLOW]", isCenter ? "" : isHtml ? "" : ChatColors.LightYellow.ToString())
65 | .Replace("[DARK_RED]", isCenter ? "" : isHtml ? "" : ChatColors.DarkRed.ToString())
66 | .Replace("[DARK_BLUE]", isCenter ? "" : isHtml ? "" : ChatColors.DarkBlue.ToString())
67 | .Replace("[BLUE_GREY]", isCenter ? "" : isHtml ? "" : ChatColors.BlueGrey.ToString())
68 | .Replace("[OLIVE]", isCenter ? "" : isHtml ? "" : ChatColors.Olive.ToString())
69 | .Replace("[LIME]", isCenter ? "" : isHtml ? "" : ChatColors.Lime.ToString())
70 | .Replace("[GOLD]", isCenter ? "" : isHtml ? "" : ChatColors.Gold.ToString())
71 | .Replace("[SILVER]", isCenter ? "" : isHtml ? "" : ChatColors.Silver.ToString())
72 | .Replace("[MAGENTA]", isCenter ? "" : isHtml ? "" : ChatColors.Magenta.ToString());
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/RetakesPlugin/RetakesPlugin.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0
4 | enable
5 | enable
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/RetakesPlugin/lang/ar.json:
--------------------------------------------------------------------------------
1 | {
2 | "retakes.prefix": "[[GREEN]Retakes[NORMAL]] ",
3 |
4 | "retakes.queue.joined": "لقد تم وضعك في قائمة الانتظار.",
5 | "retakes.queue.waiting": "اللعبة ممتلئة حالياً ب[GREEN]{0}[NORMAL] لاعبين. أنت في قائمة الانتظار.",
6 | "retakes.queue.replaced_by_vip": "لقد تم استبدالك بلاعب VIP [GREEN]{0}[NORMAL].",
7 | "retakes.queue.vip_took_place": "لقد أخذت مكان [GREEN]{0}[NORMAL] في اللعبة.",
8 |
9 | "retakes.bombsite.announcement": "إعادة الاستيلاء على [GREEN]{0}[NORMAL]: [LIGHT_RED]{1} من الإرهابيين[NORMAL] مقابل [PURPLE]{2} من قوات مكافحة الإرهاب",
10 |
11 | "retakes.teams.scramble": "فاز الإرهابيون [GREEN]{0}[NORMAL] جولات متتالية. [PURPLE]يتم خلط الفرق!",
12 | "retakes.teams.almost_scramble": "فاز الإرهابيون [GREEN]{0}[NORMAL] جولات متتالية - إذا فازوا [GREEN]{1} مرة أخرى، [PURPLE]سيتم خلط الفرق.",
13 | "retakes.teams.win_streak": "فاز الإرهابيون [GREEN]{0}[NORMAL] جولات متتالية!",
14 | "retakes.teams.win_streak_over": "قوات مكافحة الإرهاب قد أنهت سلسلة انتصارات متتالية [GREEN]{0}[NORMAL] جولة!",
15 | "retakes.teams.admin_scramble": "{0} قام بضبط الفرق لتُخلط في الجولة التالية."
16 | }
17 |
--------------------------------------------------------------------------------
/RetakesPlugin/lang/da.json:
--------------------------------------------------------------------------------
1 | {
2 | "retakes.prefix": "[[GREEN]Retakes[NORMAL]] ",
3 |
4 | "retakes.queue.joined": "Du er blevet placeret i køen.",
5 | "retakes.queue.waiting": "Dette spil er fuldt med [GREEN]{0}[NORMAL] spillere. Du er blevet placeret i køen.",
6 |
7 | "retakes.bombsite.announcement": "Retake [GREEN]{0}[NORMAL]: [LIGHT_RED]{1} T'er[NORMAL] mod [PURPLE]{2} CT'er",
8 |
9 | "retakes.teams.scramble": "Terroristerne har vundet [GREEN]{0}[NORMAL] runder i træk. [PURPLE]Holdende bliver blandet!",
10 | "retakes.teams.almost_scramble": "Terroristerne har vundet [GREEN]{0}[NORMAL] runder i træk - hvis de vinder [GREEN]{1} mere, [PURPLE]bliver holdende blandet.",
11 | "retakes.teams.win_streak": "Terroristerne har vundet [GREEN]{0}[NORMAL] runder i træk!",
12 | "retakes.teams.win_streak_over": "CT'erne har afsluttet en [GREEN]{0}[NORMAL]-runde lang win streak!",
13 | "retakes.teams.admin_scramble": "{0} har sat holdende til at blive blandet."
14 | }
15 |
--------------------------------------------------------------------------------
/RetakesPlugin/lang/de.json:
--------------------------------------------------------------------------------
1 | {
2 | "retakes.prefix": "[[GREEN]Retakes[NORMAL]] ",
3 |
4 | "retakes.queue.joined": "Sie befinden sich in der Warteschlange.",
5 | "retakes.queue.waiting": "Das Spiel ist aktuell voll mit [GREEN]{0}[NORMAL] Spielern. Sie sind in der Warteschlange.",
6 |
7 | "retakes.bombsite.announcement": "Retake [GREEN]{0}[NORMAL]: [LIGHT_RED]{1} Ts[NORMAL] vs [PURPLE]{2} CTs",
8 |
9 | "retakes.teams.scramble": "Die Terroristen haben [GREEN]{0}[NORMAL] Runden in Folge gewonnen. [PURPLE]Teams werden gemischt!",
10 | "retakes.teams.almost_scramble": "Die Terroristen haben [GREEN]{0}[NORMAL] Runden in Folge gewonnen - wenn sie noch [GREEN]{1} gewinnen, [PURPLE]werden die Teams gemischt.",
11 | "retakes.teams.win_streak": "Die Terroristen haben [GREEN]{0}[NORMAL] Runden in Folge gewonnen!",
12 | "retakes.teams.win_streak_over": "Die CTs haben die Sieges-Serie von [GREEN]{0}[NORMAL] Runden beendet!",
13 | "retakes.teams.admin_scramble": "{0} lässt die Teams in der nächsten Runde mischen."
14 | }
15 |
--------------------------------------------------------------------------------
/RetakesPlugin/lang/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "retakes.prefix": "[[GREEN]Retakes[NORMAL]] ",
3 |
4 | "retakes.queue.joined": "You have been placed in the waiting queue.",
5 | "retakes.queue.waiting": "The game is currently full with [GREEN]{0}[NORMAL] players. You are in the waiting queue.",
6 | "retakes.queue.replaced_by_vip": "You have been replaced by VIP player [GREEN]{0}[NORMAL].",
7 | "retakes.queue.vip_took_place": "You have taken [GREEN]{0}[NORMAL]'s place in the game.",
8 |
9 | "retakes.bombsite.announcement": "Retake [GREEN]{0}[NORMAL]: [LIGHT_RED]{1} Ts[NORMAL] vs [PURPLE]{2} CTs",
10 | "retakes.voices.toggle": "You have {0} voice announcements!",
11 |
12 | "retakes.teams.scramble": "The terrorists won [GREEN]{0}[NORMAL] rounds in a row. [PURPLE]Teams are being scrambled!",
13 | "retakes.teams.almost_scramble": "The terrorists have won [GREEN]{0}[NORMAL] rounds in a row - if they win [GREEN]{1} more, [PURPLE]teams will be scrambled.",
14 | "retakes.teams.win_streak": "The terrorists have won [GREEN]{0}[NORMAL] rounds in a row!",
15 | "retakes.teams.win_streak_over": "The CTs have ended a [GREEN]{0}[NORMAL]-round long win streak!",
16 | "retakes.teams.admin_scramble": "{0} has set the teams to be scrambled next round."
17 | }
--------------------------------------------------------------------------------
/RetakesPlugin/lang/es.json:
--------------------------------------------------------------------------------
1 | {
2 | "retakes.prefix": "[[GREEN]Retakes[NORMAL]] ",
3 |
4 | "retakes.queue.joined": "Fuiste puesto en la lista de espera.",
5 | "retakes.queue.waiting": "La partida está llena con [GREEN]{0}[NORMAL] jugadores. Te encuentras en la lista de espera.",
6 |
7 | "retakes.bombsite.announcement": "Retake [GREEN]{0}[NORMAL]: [LIGHT_RED]{1} Ts[NORMAL] vs [PURPLE]{2} CTs",
8 |
9 | "retakes.teams.scramble": "Los terroristas ganaron [GREEN]{0}[NORMAL] rondas seguidas. [PURPLE]Los equipos serán mezclados!",
10 | "retakes.teams.almost_scramble": "Los terroristas van ganando [GREEN]{0}[NORMAL] rondas seguidas - Si ganan [GREEN]{1} más, [PURPLE]los equipos serán a mezclados.",
11 | "retakes.teams.win_streak": "Los terroristas ganaron [GREEN]{0}[NORMAL] rondas seguidas!",
12 | "retakes.teams.win_streak_over": "Los CTs habían perdido [GREEN]{0}[NORMAL] rondas, pero al fin ganaron 1.",
13 | "retakes.teams.admin_scramble": "{0} Los equipos serán a mezclados en la siguiente ronda."
14 | }
15 |
--------------------------------------------------------------------------------
/RetakesPlugin/lang/fi.json:
--------------------------------------------------------------------------------
1 | {
2 | "retakes.prefix": "[[GREEN]Retakes[NORMAL]] ",
3 |
4 | "retakes.queue.joined": "Sinut on laitettu jonoon odottamaan peliin siirtymistä.",
5 | "retakes.queue.waiting": "Peli on tällä hetkellä täysi [GREEN]{0}[NORMAL] pelaajalla. Olet jonossa odottamassa peliin.",
6 | "retakes.queue.replaced_by_vip": "Sinun paikkasi pelissä otti VIP pelaaja [GREEN]{0}[NORMAL].",
7 | "retakes.queue.vip_took_place": "Olet ottanut pelaajan [GREEN]{0}[NORMAL] paikan pelissä.",
8 |
9 | "retakes.bombsite.announcement": "Retake [GREEN]{0}[NORMAL]: [LIGHT_RED]{1} Ts[NORMAL] vs [PURPLE]{2} CTs",
10 |
11 | "retakes.teams.scramble": "Terot ovat voittaneet [GREEN]{0}[NORMAL] kierrosta putkeen. [PURPLE]Tiimit sekoitetaan!",
12 | "retakes.teams.almost_scramble": "Terot ovat voittanut [GREEN]{0}[NORMAL] kierrosta putkeen - Jos he voittavat [GREEN]{1} lisää, [PURPLE]tiimit sekoitetaan.",
13 | "retakes.teams.win_streak": "Terot ovat voittaneet [GREEN]{0}[NORMAL] kierrosta putkeen!",
14 | "retakes.teams.win_streak_over": "CT hävisi [GREEN]{0}[NORMAL]-kierrosta kestäneen voittoputken!",
15 | "retakes.teams.admin_scramble": "{0} asetti tiimit sekoittumaan seuraavalla kierroksella."
16 | }
17 |
--------------------------------------------------------------------------------
/RetakesPlugin/lang/fr.json:
--------------------------------------------------------------------------------
1 | {
2 | "retakes.prefix": "[[GREEN]Retakes[NORMAL]] ",
3 |
4 | "retakes.queue.joined": "Vous avez été placé dans la file d'attente.",
5 | "retakes.queue.waiting": "La partie est actuellement complète avec [GREEN]{0}[NORMAL] joueurs. Vous êtes dans la file d'attente.",
6 |
7 | "retakes.bombsite.announcement": "Reprise [GREEN]{0}[NORMAL]: [LIGHT_RED]{1} Terroristes[NORMAL] contre [PURPLE]{2} CTs",
8 |
9 | "retakes.teams.scramble": "Les terroristes ont gagné [GREEN]{0}[NORMAL] tours d'affilée. [PURPLE]Mélange des équipes en cours !",
10 | "retakes.teams.almost_scramble": "Les terroristes ont gagné [GREEN]{0}[NORMAL] tours d'affilée - s'ils en gagnent encore [GREEN]{1}, [PURPLE]les équipes seront mélangées.",
11 | "retakes.teams.win_streak": "Les terroristes ont gagné [GREEN]{0}[NORMAL] tours d'affilée !",
12 | "retakes.teams.win_streak_over": "Les CTs ont mis fin à [GREEN]{0}[NORMAL] tours gagnés d'affilée !",
13 | "retakes.teams.admin_scramble": "{0} a imposé le mélange des équipes au prochain tour."
14 | }
15 |
--------------------------------------------------------------------------------
/RetakesPlugin/lang/hu.json:
--------------------------------------------------------------------------------
1 | {
2 | "retakes.prefix": "[[GREEN]Retakes[NORMAL]] ",
3 |
4 | "retakes.queue.joined": "Át lettél rakva a várólistába.",
5 | "retakes.queue.waiting": "Játék jelenleg fullon van [GREEN]{0}[NORMAL] játékossal. Bent vagy a várólistában.",
6 |
7 | "retakes.bombsite.announcement": "Retake [GREEN]{0}[NORMAL]: [LIGHT_RED]{1} T-k[NORMAL] vs [PURPLE]{2} TE-k",
8 |
9 | "retakes.teams.scramble": "Terroristák [GREEN]{0}[NORMAL] kört nyertek egymás után. [PURPLE]A csapatok össze lettek keverve!",
10 | "retakes.teams.almost_scramble": "Terroristák [GREEN]{0}[NORMAL] kört nyertek egymás után - ha még nyernek [GREEN]{1} kört, [PURPLE]a csapatok össze lesznek keverve.",
11 | "retakes.teams.win_streak": "Terroristák [GREEN]{0}[NORMAL] kört nyertek egymás után!",
12 | "retakes.teams.win_streak_over": "TE-k megállították a [GREEN]{0}[NORMAL] kör hosszú győzelem sorozatot!",
13 | "retakes.teams.admin_scramble": "{0} beállította, hogy a csapatok össze legyenek keverve a következő körben."
14 | }
15 |
--------------------------------------------------------------------------------
/RetakesPlugin/lang/no.json:
--------------------------------------------------------------------------------
1 | {
2 | "retakes.prefix": "[[GREEN]Retakes[NORMAL]] ",
3 |
4 | "retakes.queue.joined": "Du har blitt plassert i kø for å bli med.",
5 | "retakes.queue.waiting": "Spillet er for øyeblikket fullt med [GREEN]{0}[NORMAL] spillere. Du er nå i køen for å bli med.",
6 |
7 | "retakes.bombsite.announcement": "Retake [GREEN]{0}[NORMAL]: [LIGHT_RED]{1} Ts[NORMAL] mot [PURPLE]{2} CTs",
8 |
9 | "retakes.teams.scramble": "Terroristene har vunnet [GREEN]{0}[NORMAL] runder på rad. [PURPLE]Lagene blir samlet!",
10 | "retakes.teams.almost_scramble": "Terroristene har vunnet [GREEN]{0}[NORMAL] runder på rad - hvis de vinner [GREEN]{1} mer, [PURPLE]vil lagene bli endret.",
11 | "retakes.teams.win_streak": "Terroristene har vunnet [GREEN]{0}[NORMAL] runder på rad!",
12 | "retakes.teams.win_streak_over": "CT har sluttet en [GREEN]{0}[NORMAL]-runders lang seiersrekke!",
13 | "retakes.teams.admin_scramble": "{0} har satt lagene til å bli samlet neste runde."
14 | }
15 |
--------------------------------------------------------------------------------
/RetakesPlugin/lang/pl.json:
--------------------------------------------------------------------------------
1 | {
2 | "retakes.prefix": "[[GREEN]Retakes[NORMAL]] ",
3 |
4 | "retakes.queue.joined": "Zostałeś dodany do kolejki.",
5 | "retakes.queue.waiting": "Gra jest pełna z [GREEN]{0}[NORMAL] graczami. Jesteś aktualnie w kolejce.",
6 |
7 | "retakes.bombsite.announcement": "Retake [GREEN]{0}[NORMAL]: [LIGHT_RED]{1} Ts[NORMAL] vs [PURPLE]{2} CTs",
8 |
9 | "retakes.teams.scramble": "Terroryści wygrali [GREEN]{0}[NORMAL] rund pod rząd. [PURPLE]Drużyny zostaną wymieszane!",
10 | "retakes.teams.almost_scramble": "Terroryści wygrali [GREEN]{0}[NORMAL] rund pod rząd - jeśli wygrają [GREEN]{1} raz(y), [PURPLE]drużyny zostaną wymieszane.",
11 | "retakes.teams.win_streak": "Terroryści wygrali [GREEN]{0}[NORMAL] rund pod rząd!",
12 | "retakes.teams.win_streak_over": "CT zakończyli [GREEN]{0}[NORMAL] zwycięską serie rund!",
13 | "retakes.teams.admin_scramble": "{0} ustawił wymieszanie drużyn na następną rundę."
14 | }
15 |
--------------------------------------------------------------------------------
/RetakesPlugin/lang/pt-BR.json:
--------------------------------------------------------------------------------
1 | {
2 | "retakes.prefix": "[[GREEN]Retakes[NORMAL]] ",
3 |
4 | "retakes.queue.joined": "Você foi colocado na fila de espera, aguarde a próxima rodada.",
5 | "retakes.queue.waiting": "Os times estão cheios no momento com [GREEN]{0}[NORMAL] jogadores. Você está na fila de espera, aguarde!",
6 | "retakes.queue.replaced_by_vip": "Você foi substituído pelo jogador VIP [GREEN]{0}[NORMAL].",
7 | "retakes.queue.vip_took_place": "Você ocupou o lugar de [GREEN]{0}[NORMAL] no jogo.",
8 |
9 | "retakes.bombsite.announcement": "Retake [GREEN]{0}[NORMAL]: [LIGHT_RED]{1} Ts[NORMAL] vs [PURPLE]{2} CTs",
10 | "retakes.voices.toggle": "Você tem {0} anúncios de voz!",
11 |
12 | "retakes.teams.scramble": "Terroristas venceram [GREEN]{0}[NORMAL] rodadas consecutivas. [PURPLE]Times estão sendo embaralhados!",
13 | "retakes.teams.almost_scramble": "Terroristas venceram [GREEN]{0}[NORMAL] rodadas consecutivas. - se ganharem mais [GREEN]{1} rodada, [PURPLE]os times serão embaralhados.",
14 | "retakes.teams.win_streak": "Terroristas venceram [GREEN]{0}[NORMAL] rodadas consecutivas!",
15 | "retakes.teams.win_streak_over": "Contra-Terroristas terminaram uma sequência de [GREEN]{0} rodadas consecutivas[NORMAL]!",
16 | "retakes.teams.admin_scramble": "{0} definiu para os times serem embaralhados na próxima rodada."
17 | }
18 |
--------------------------------------------------------------------------------
/RetakesPlugin/lang/pt-PT.json:
--------------------------------------------------------------------------------
1 | {
2 | "retakes.prefix": "[[GREEN]Retakes[NORMAL]] ",
3 |
4 | "retakes.queue.joined": "Você foi colocado na fila de espera, aguarda a próxima ronda.",
5 | "retakes.queue.waiting": "As equipas estão cheias de momento com [GREEN]{0}[NORMAL] players. Você está na fila de espera.",
6 | "retakes.queue.replaced_by_vip": "Você foi substituído pelo jogador VIP [GREEN]{0}[NORMAL].",
7 | "retakes.queue.vip_took_place": "Você ocupou o lugar de [GREEN]{0}[NORMAL] no jogo.",
8 |
9 | "retakes.bombsite.announcement": "Retake [GREEN]{0}[NORMAL]: [LIGHT_RED]{1} Ts[NORMAL] vs [PURPLE]{2} CTs",
10 | "retakes.voices.toggle": "Você tem {0} anúncios de voz!",
11 |
12 | "retakes.teams.scramble": "Terroristas venceram [GREEN]{0}[NORMAL] rondas consecutivas. [PURPLE]Equipas estão sendo embaralhadas!",
13 | "retakes.teams.almost_scramble": "Terroristas venceram [GREEN]{0}[NORMAL] rondas consecutivas - se ganharem mais [GREEN]{1} ronda, [PURPLE]as equipas serão embaralhadas.",
14 | "retakes.teams.win_streak": "Terroristas venceram [GREEN]{0}[NORMAL] rondas consecutivas!",
15 | "retakes.teams.win_streak_over": "Contra-Terroristas terminaram uma sequência de rondas consecutivas [GREEN]{0}[NORMAL]!",
16 | "retakes.teams.admin_scramble": "{0} definiu para as equipas serem embaralhadas na próxima ronda."
17 | }
18 |
--------------------------------------------------------------------------------
/RetakesPlugin/lang/ru.json:
--------------------------------------------------------------------------------
1 | {
2 | "retakes.prefix": "[[GREEN]Retakes[NORMAL]] ",
3 |
4 | "retakes.queue.joined": "Вы в очереди. Ожидайте.",
5 | "retakes.queue.waiting": "Сейчас играет максимальное количество игроков - [GREEN]{0}[NORMAL]. Вы в очереди.",
6 |
7 | "retakes.bombsite.announcement": "Retake [GREEN]{0}[NORMAL]: [LIGHT_RED]{1} T[NORMAL] против [PURPLE]{2} CT",
8 |
9 | "retakes.teams.scramble": "Террористы выиграли [GREEN]{0}[NORMAL] раундов подряд. [PURPLE]Команды были перемешаны!",
10 | "retakes.teams.almost_scramble": "Террористы выиграли [GREEN]{0}[NORMAL] раундов подряд - если они выиграют еще [GREEN]{1}, то [PURPLE]команды будут перемешаны.",
11 | "retakes.teams.win_streak": "Террористы выиграли [GREEN]{0}[NORMAL] раундов подряд!",
12 | "retakes.teams.win_streak_over": "Контр-Террористы завершили серию побед Террористов из [GREEN]{0}[NORMAL] раундов!",
13 | "retakes.teams.admin_scramble": "{0} включил перемешивание команд со следующего раунда."
14 | }
15 |
--------------------------------------------------------------------------------
/RetakesPlugin/lang/sv.json:
--------------------------------------------------------------------------------
1 | {
2 | "retakes.prefix": "[[GREEN]Retakes[NORMAL]] ",
3 |
4 | "retakes.queue.joined": "Du är placerad i kön.",
5 | "retakes.queue.waiting": "Matchen är just nu full med [GREEN]{0}[NORMAL] spelare. Du är i kö.",
6 |
7 | "retakes.bombsite.announcement": "Retake [GREEN]{0}[NORMAL]: [LIGHT_RED]{1} Terrorister[NORMAL] vs [PURPLE]{2} Anti-terrorister.",
8 |
9 | "retakes.teams.scramble": "Terroristerna har vunnit [GREEN]{0}[NORMAL] rundor i rad. [PURPLE]Blandar om lagen!",
10 | "retakes.teams.almost_scramble": "Terroristerna har vunnit [GREEN]{0}[NORMAL] rundor i rad - vinner de [GREEN]{1} till, [PURPLE]så blandas lagen om.",
11 | "retakes.teams.win_streak": "Terroristerna har vunnit [GREEN]{0}[NORMAL] rundor i rad!",
12 | "retakes.teams.win_streak_over": "Anti-terroristerna har avbrutit terroristernas [GREEN]{0}[NORMAL] raka vinster!",
13 | "retakes.teams.admin_scramble": "{0} har satt lagen att blandas om nästa runda."
14 | }
15 |
--------------------------------------------------------------------------------
/RetakesPlugin/lang/tr.json:
--------------------------------------------------------------------------------
1 | {
2 | "retakes.prefix": "[[GREEN]Retakes[NORMAL]] ",
3 |
4 | "retakes.queue.joined": "Bekleme sırasına alındınız.",
5 | "retakes.queue.waiting": "Oyun şu anda [GREEN]{0}[NORMAL] oyuncuyla dolu. Bekleme sırasındasınız.",
6 | "retakes.queue.replaced_by_vip": "Yerinizi VIP oyuncu [GREEN]{0}[NORMAL] aldı.",
7 | "retakes.queue.vip_took_place": "Oyunda [GREEN]{0}[NORMAL]'in yerini aldınız.",
8 |
9 | "retakes.bombsite.announcement": "Retake [GREEN]{0}[NORMAL]: [LIGHT_RED]{1} T[NORMAL] vs [PURPLE]{2} AT",
10 | "retakes.voices.toggle": "{0} sesli duyurunuz var!",
11 |
12 | "retakes.teams.scramble": "Teröristler [GREEN]{0}[NORMAL] round arka arkaya kazandı. [PURPLE]Takımlar karıştırılıyor!",
13 | "retakes.teams.almost_scramble": "Teröristler [GREEN]{0}[NORMAL] round arka arkaya kazandı - eğer [GREEN]{1} round daha alırlarsa, [PURPLE]takımlar karıştırılacak.",
14 | "retakes.teams.win_streak": "Teröristler [GREEN]{0}[NORMAL] round arka arkaya kazandı!",
15 | "retakes.teams.win_streak_over": "AT'ler [GREEN]{0}[NORMAL] roundluk uzun galibiyet serisine son verdi.",
16 | "retakes.teams.admin_scramble": "{0}, takımları bir sonraki round karıştırılacak şekilde ayarladı."
17 | }
18 |
--------------------------------------------------------------------------------
/RetakesPlugin/lang/uz.json:
--------------------------------------------------------------------------------
1 | {
2 | "retakes.prefix": "[[GREEN]Retakes[NORMAL]] ",
3 |
4 | "retakes.queue.joined": "Siz kutish navbatiga qo‘shildingiz.",
5 | "retakes.queue.waiting": "O‘yin hozirda [GREEN]{0}[NORMAL] o‘yinchilar bilan to‘la. Siz kutish navbatidasiz.",
6 | "retakes.queue.replaced_by_vip": "Siz vip o‘yinchi [GREEN]{0}[NORMAL] bilan almashtirildingiz.",
7 | "retakes.queue.vip_took_place": "Siz [GREEN]{0}[NORMAL]ning o‘rniga o‘yinga kirdingiz.",
8 |
9 | "retakes.bombsite.announcement": "Retake [GREEN]{0}[NORMAL]: [LIGHT_RED]{1} Tlar[NORMAL] va [BLUE]{2} KTlar",
10 | "retakes.voices.toggle": "Siz ovozli e‘lonlarni {0}!",
11 |
12 | "retakes.teams.scramble": "Terrorchilar ketma-ket [GREEN]{0}[NORMAL] raundda g‘alaba qozonishdi. [PURPLE]Jamoalar aralashtirildi!",
13 | "retakes.teams.almost_scramble": "Terrorchilar ketma-ket [GREEN]{0}[NORMAL] raundda g‘alaba qozonishdi - agar ular yana [GREEN]{1} ta g‘alaba qozonishsa, [PURPLE]jamoalar aralashtiriladi.",
14 | "retakes.teams.win_streak": "Terrorchilar ketma-ket [GREEN]{0}[NORMAL] raundda g‘alaba qozonishdi!",
15 | "retakes.teams.win_streak_over": "KTlar [GREEN]{0}[NORMAL]-raundlik uzoq davom etgan g‘alabali seriyasini tugatdi!",
16 | "retakes.teams.admin_scramble": "{0} keyingi raundda jamoalarni aralashtirishni o‘rnatdi."
17 | }
18 |
--------------------------------------------------------------------------------
/RetakesPlugin/lang/zh-Hans.json:
--------------------------------------------------------------------------------
1 | {
2 | "retakes.prefix": "[[GREEN]Retakes[NORMAL]] ",
3 |
4 | "retakes.queue.joined": "您已被放入等待队列。",
5 | "retakes.queue.waiting": "游戏当前已满,共有[GREEN]{0}[NORMAL]名玩家。您正在等待队列中。",
6 | "retakes.queue.replaced_by_vip": "您已经被VIP用户插队[GREEN]{0}[NORMAL]。",
7 | "retakes.queue.vip_took_place": "您已经插队了[GREEN]{0}[NORMAL]的等待位置。",
8 |
9 | "retakes.bombsite.announcement": "回防[GREEN]{0}[NORMAL]包点:[LIGHT_RED]{1}名T[NORMAL]对阵[PURPLE]{2}名CT",
10 | "retakes.voices.toggle": "您已经{0}语音播报!",
11 |
12 | "retakes.teams.scramble": "T连续赢得[GREEN]{0}[NORMAL]个回合。[PURPLE]正在重新打乱队伍!",
13 | "retakes.teams.almost_scramble": "T连续赢得[GREEN]{0}[NORMAL]个回合 - 如果他们再赢[GREEN]{1}个回合,[PURPLE]将重新打乱队伍。",
14 | "retakes.teams.win_streak": "T连续赢得[GREEN]{0}[NORMAL]个回合!",
15 | "retakes.teams.win_streak_over": "CT结束了[GREEN]{0}[NORMAL]个回合的连胜!",
16 | "retakes.teams.admin_scramble": "{0}已设置下一回合重新打乱队伍。"
17 | }
18 |
--------------------------------------------------------------------------------
/RetakesPlugin/lang/zh-Hant.json:
--------------------------------------------------------------------------------
1 | {
2 | "retakes.prefix": "[[GREEN]Retakes[NORMAL]] ",
3 |
4 | "retakes.queue.joined": "您已被放入等待佇列。",
5 | "retakes.queue.waiting": "遊戲目前已滿,共有[GREEN]{0}[NORMAL]名玩家。您正在等待佇列中。",
6 |
7 | "retakes.bombsite.announcement": "回防[GREEN]{0}[NORMAL]炸彈點:[LIGHT_RED]{1}名恐怖份子[NORMAL]對陣[PURPLE]{2}名反恐小组",
8 |
9 | "retakes.teams.scramble": "T連續贏得[GREEN]{0}[NORMAL]個回合。[PURPLE]正在重新打亂隊伍!",
10 | "retakes.teams.almost_scramble": "T連續贏得[GREEN]{0}[NORMAL]個回合 - 如果他們再贏[GREEN]{1}個回合,[PURPLE]將重新打亂隊伍。",
11 | "retakes.teams.win_streak": "T連續贏得[GREEN]{0}[NORMAL]個回合!",
12 | "retakes.teams.win_streak_over": "CT結束了[GREEN]{0}[NORMAL]個回合的連勝!",
13 | "retakes.teams.admin_scramble": "{0}已設定下一回合重新打亂隊伍。"
14 | }
15 |
--------------------------------------------------------------------------------
/RetakesPlugin/map_config/de_ancient.json:
--------------------------------------------------------------------------------
1 | {
2 | "Spawns": [
3 | {
4 | "Vector": "-1,701.56 -1,136.77 -21.34",
5 | "QAngle": "0.00 92.42 0.00",
6 | "Team": 3,
7 | "Bombsite": 0,
8 | "CanBePlanter": false
9 | },
10 | {
11 | "Vector": "-1,870.92 -1,094.40 -25.68",
12 | "QAngle": "0.00 85.14 0.00",
13 | "Team": 3,
14 | "Bombsite": 0,
15 | "CanBePlanter": false
16 | },
17 | {
18 | "Vector": "-1,461.51 -1,156.01 -11.22",
19 | "QAngle": "0.00 120.34 0.00",
20 | "Team": 3,
21 | "Bombsite": 0,
22 | "CanBePlanter": false
23 | },
24 | {
25 | "Vector": "-530.90 -562.85 34.25",
26 | "QAngle": "0.00 126.89 0.00",
27 | "Team": 3,
28 | "Bombsite": 0,
29 | "CanBePlanter": false
30 | },
31 | {
32 | "Vector": "-376.98 -589.89 33.63",
33 | "QAngle": "0.00 140.55 0.00",
34 | "Team": 3,
35 | "Bombsite": 0,
36 | "CanBePlanter": false
37 | },
38 | {
39 | "Vector": "-492.12 78.27 128.83",
40 | "QAngle": "0.00 -126.17 0.00",
41 | "Team": 3,
42 | "Bombsite": 0,
43 | "CanBePlanter": false
44 | },
45 | {
46 | "Vector": "-555.44 669.00 138.08",
47 | "QAngle": "0.00 47.42 0.00",
48 | "Team": 3,
49 | "Bombsite": 0,
50 | "CanBePlanter": false
51 | },
52 | {
53 | "Vector": "143.56 880.04 87.49",
54 | "QAngle": "0.00 -150.13 0.00",
55 | "Team": 3,
56 | "Bombsite": 0,
57 | "CanBePlanter": false
58 | },
59 | {
60 | "Vector": "14.00 906.11 74.03",
61 | "QAngle": "0.00 -111.82 0.00",
62 | "Team": 3,
63 | "Bombsite": 0,
64 | "CanBePlanter": false
65 | },
66 | {
67 | "Vector": "-210.35 723.11 77.66",
68 | "QAngle": "0.00 106.09 0.00",
69 | "Team": 3,
70 | "Bombsite": 0,
71 | "CanBePlanter": false
72 | },
73 | {
74 | "Vector": "-1,226.28 729.97 56.03",
75 | "QAngle": "0.00 178.12 0.00",
76 | "Team": 2,
77 | "Bombsite": 0,
78 | "CanBePlanter": true
79 | },
80 | {
81 | "Vector": "-1,301.03 828.16 54.03",
82 | "QAngle": "0.00 3.14 0.00",
83 | "Team": 2,
84 | "Bombsite": 0,
85 | "CanBePlanter": true
86 | },
87 | {
88 | "Vector": "-1,562.02 731.65 56.03",
89 | "QAngle": "0.00 26.82 0.00",
90 | "Team": 2,
91 | "Bombsite": 0,
92 | "CanBePlanter": true
93 | },
94 | {
95 | "Vector": "-1,527.92 931.31 54.03",
96 | "QAngle": "0.00 -98.86 0.00",
97 | "Team": 2,
98 | "Bombsite": 0,
99 | "CanBePlanter": false
100 | },
101 | {
102 | "Vector": "-1,665.46 1,187.97 52.03",
103 | "QAngle": "0.00 -122.50 0.00",
104 | "Team": 2,
105 | "Bombsite": 0,
106 | "CanBePlanter": false
107 | },
108 | {
109 | "Vector": "-1,466.50 1,148.96 97.45",
110 | "QAngle": "0.00 45.27 0.00",
111 | "Team": 2,
112 | "Bombsite": 0,
113 | "CanBePlanter": false
114 | },
115 | {
116 | "Vector": "-178.52 1,309.92 28.57",
117 | "QAngle": "0.00 -89.77 0.00",
118 | "Team": 3,
119 | "Bombsite": 1,
120 | "CanBePlanter": false
121 | },
122 | {
123 | "Vector": "-315.99 1,304.19 26.23",
124 | "QAngle": "0.00 -72.00 0.00",
125 | "Team": 3,
126 | "Bombsite": 1,
127 | "CanBePlanter": false
128 | },
129 | {
130 | "Vector": "-439.99 1,217.56 50.63",
131 | "QAngle": "0.00 -51.58 0.00",
132 | "Team": 3,
133 | "Bombsite": 1,
134 | "CanBePlanter": false
135 | },
136 | {
137 | "Vector": "-544.63 892.36 103.54",
138 | "QAngle": "0.00 -12.95 0.00",
139 | "Team": 3,
140 | "Bombsite": 1,
141 | "CanBePlanter": false
142 | },
143 | {
144 | "Vector": "-210.55 -705.51 164.03",
145 | "QAngle": "0.00 -12.12 0.00",
146 | "Team": 3,
147 | "Bombsite": 1,
148 | "CanBePlanter": false
149 | },
150 | {
151 | "Vector": "-214.63 -813.57 164.03",
152 | "QAngle": "0.00 0.42 0.00",
153 | "Team": 3,
154 | "Bombsite": 1,
155 | "CanBePlanter": false
156 | },
157 | {
158 | "Vector": "566.91 -808.29 108.01",
159 | "QAngle": "0.00 9.49 0.00",
160 | "Team": 3,
161 | "Bombsite": 1,
162 | "CanBePlanter": false
163 | },
164 | {
165 | "Vector": "647.28 -627.91 93.59",
166 | "QAngle": "0.00 -9.83 0.00",
167 | "Team": 3,
168 | "Bombsite": 1,
169 | "CanBePlanter": false
170 | },
171 | {
172 | "Vector": "1,230.87 -1,253.92 6.90",
173 | "QAngle": "0.00 110.91 0.00",
174 | "Team": 3,
175 | "Bombsite": 1,
176 | "CanBePlanter": false
177 | },
178 | {
179 | "Vector": "944.03 -1,266.52 3.74",
180 | "QAngle": "0.00 66.06 0.00",
181 | "Team": 3,
182 | "Bombsite": 1,
183 | "CanBePlanter": false
184 | },
185 | {
186 | "Vector": "376.47 -870.68 141.37",
187 | "QAngle": "0.00 145.28 0.00",
188 | "Team": 3,
189 | "Bombsite": 1,
190 | "CanBePlanter": false
191 | },
192 | {
193 | "Vector": "797.17 4.97 131.40",
194 | "QAngle": "0.00 -30.60 0.00",
195 | "Team": 2,
196 | "Bombsite": 1,
197 | "CanBePlanter": true
198 | },
199 | {
200 | "Vector": "1,112.24 57.82 134.03",
201 | "QAngle": "0.00 136.66 0.00",
202 | "Team": 2,
203 | "Bombsite": 1,
204 | "CanBePlanter": true
205 | },
206 | {
207 | "Vector": "792.17 152.03 130.03",
208 | "QAngle": "0.00 93.37 0.00",
209 | "Team": 2,
210 | "Bombsite": 1,
211 | "CanBePlanter": true
212 | },
213 | {
214 | "Vector": "320.03 292.97 161.65",
215 | "QAngle": "0.00 -78.06 0.00",
216 | "Team": 2,
217 | "Bombsite": 1,
218 | "CanBePlanter": false
219 | },
220 | {
221 | "Vector": "665.49 -286.00 138.04",
222 | "QAngle": "0.00 2.80 0.00",
223 | "Team": 2,
224 | "Bombsite": 1,
225 | "CanBePlanter": false
226 | },
227 | {
228 | "Vector": "724.94 -106.86 134.03",
229 | "QAngle": "0.00 79.79 0.00",
230 | "Team": 2,
231 | "Bombsite": 1,
232 | "CanBePlanter": false
233 | },
234 | {
235 | "Vector": "1,267.56 323.62 123.47",
236 | "QAngle": "0.00 100.03 0.00",
237 | "Team": 2,
238 | "Bombsite": 1,
239 | "CanBePlanter": false
240 | },
241 | {
242 | "Vector": "1,036.46 259.77 131.03",
243 | "QAngle": "0.00 49.41 0.00",
244 | "Team": 2,
245 | "Bombsite": 1,
246 | "CanBePlanter": false
247 | }
248 | ]
249 | }
--------------------------------------------------------------------------------
/RetakesPlugin/map_config/de_anubis.json:
--------------------------------------------------------------------------------
1 | {
2 | "Spawns": [
3 | {
4 | "Vector": "-1,088.24 -682.20 64.03",
5 | "QAngle": "0.00 121.64 0.00",
6 | "Team": 3,
7 | "Bombsite": 1,
8 | "CanBePlanter": false
9 | },
10 | {
11 | "Vector": "-844.02 -668.25 64.03",
12 | "QAngle": "0.00 130.52 0.00",
13 | "Team": 3,
14 | "Bombsite": 1,
15 | "CanBePlanter": false
16 | },
17 | {
18 | "Vector": "-785.18 -804.54 106.03",
19 | "QAngle": "0.00 127.95 0.00",
20 | "Team": 3,
21 | "Bombsite": 1,
22 | "CanBePlanter": false
23 | },
24 | {
25 | "Vector": "778.68 4.77 -147.14",
26 | "QAngle": "0.00 170.65 0.00",
27 | "Team": 3,
28 | "Bombsite": 1,
29 | "CanBePlanter": false
30 | },
31 | {
32 | "Vector": "799.84 282.08 -145.97",
33 | "QAngle": "0.00 -172.28 0.00",
34 | "Team": 3,
35 | "Bombsite": 1,
36 | "CanBePlanter": false
37 | },
38 | {
39 | "Vector": "260.37 1,513.29 -31.34",
40 | "QAngle": "0.00 177.50 0.00",
41 | "Team": 3,
42 | "Bombsite": 1,
43 | "CanBePlanter": false
44 | },
45 | {
46 | "Vector": "-219.85 2,181.14 24.03",
47 | "QAngle": "0.00 -90.69 0.00",
48 | "Team": 3,
49 | "Bombsite": 1,
50 | "CanBePlanter": false
51 | },
52 | {
53 | "Vector": "-656.04 2,066.58 16.06",
54 | "QAngle": "0.00 -126.17 0.00",
55 | "Team": 3,
56 | "Bombsite": 1,
57 | "CanBePlanter": false
58 | },
59 | {
60 | "Vector": "-727.17 2,256.04 9.15",
61 | "QAngle": "0.00 -129.13 0.00",
62 | "Team": 3,
63 | "Bombsite": 1,
64 | "CanBePlanter": false
65 | },
66 | {
67 | "Vector": "-1,136.03 699.97 -1.97",
68 | "QAngle": "0.00 -98.17 0.00",
69 | "Team": 2,
70 | "Bombsite": 1,
71 | "CanBePlanter": true
72 | },
73 | {
74 | "Vector": "-1,047.91 599.87 -1.97",
75 | "QAngle": "0.00 -51.56 0.00",
76 | "Team": 2,
77 | "Bombsite": 1,
78 | "CanBePlanter": true
79 | },
80 | {
81 | "Vector": "-1,533.19 799.22 38.03",
82 | "QAngle": "0.00 -93.00 0.00",
83 | "Team": 2,
84 | "Bombsite": 1,
85 | "CanBePlanter": false
86 | },
87 | {
88 | "Vector": "-583.09 496.94 38.16",
89 | "QAngle": "0.00 135.10 0.00",
90 | "Team": 2,
91 | "Bombsite": 1,
92 | "CanBePlanter": false
93 | },
94 | {
95 | "Vector": "-635.26 923.00 38.03",
96 | "QAngle": "0.00 -158.08 0.00",
97 | "Team": 2,
98 | "Bombsite": 1,
99 | "CanBePlanter": false
100 | },
101 | {
102 | "Vector": "-1,160.09 270.44 -18.23",
103 | "QAngle": "0.00 9.39 0.00",
104 | "Team": 2,
105 | "Bombsite": 1,
106 | "CanBePlanter": false
107 | },
108 | {
109 | "Vector": "-959.97 645.97 -1.97",
110 | "QAngle": "0.00 -69.80 0.00",
111 | "Team": 2,
112 | "Bombsite": 1,
113 | "CanBePlanter": true
114 | },
115 | {
116 | "Vector": "-1,383.69 635.28 0.03",
117 | "QAngle": "0.00 49.68 0.00",
118 | "Team": 2,
119 | "Bombsite": 1,
120 | "CanBePlanter": false
121 | },
122 | {
123 | "Vector": "-244.01 2,588.58 -94.97",
124 | "QAngle": "0.00 -4.67 0.00",
125 | "Team": 3,
126 | "Bombsite": 0,
127 | "CanBePlanter": false
128 | },
129 | {
130 | "Vector": "-22.06 2,833.01 -135.88",
131 | "QAngle": "0.00 -53.59 0.00",
132 | "Team": 3,
133 | "Bombsite": 0,
134 | "CanBePlanter": false
135 | },
136 | {
137 | "Vector": "-390.34 2,152.14 22.03",
138 | "QAngle": "0.00 -61.34 0.00",
139 | "Team": 3,
140 | "Bombsite": 0,
141 | "CanBePlanter": false
142 | },
143 | {
144 | "Vector": "-250.31 1,200.82 63.24",
145 | "QAngle": "0.00 71.96 0.00",
146 | "Team": 3,
147 | "Bombsite": 0,
148 | "CanBePlanter": false
149 | },
150 | {
151 | "Vector": "-48.46 815.06 0.27",
152 | "QAngle": "0.00 43.60 0.00",
153 | "Team": 3,
154 | "Bombsite": 0,
155 | "CanBePlanter": false
156 | },
157 | {
158 | "Vector": "-114.01 630.31 0.27",
159 | "QAngle": "0.00 50.15 0.00",
160 | "Team": 3,
161 | "Bombsite": 0,
162 | "CanBePlanter": false
163 | },
164 | {
165 | "Vector": "189.05 723.24 -28.97",
166 | "QAngle": "0.00 68.36 0.00",
167 | "Team": 3,
168 | "Bombsite": 0,
169 | "CanBePlanter": false
170 | },
171 | {
172 | "Vector": "800.15 741.65 -149.63",
173 | "QAngle": "0.00 15.62 0.00",
174 | "Team": 3,
175 | "Bombsite": 0,
176 | "CanBePlanter": false
177 | },
178 | {
179 | "Vector": "1,007.39 513.04 -174.19",
180 | "QAngle": "0.00 51.34 0.00",
181 | "Team": 3,
182 | "Bombsite": 0,
183 | "CanBePlanter": false
184 | },
185 | {
186 | "Vector": "1,365.77 477.05 0.03",
187 | "QAngle": "0.00 142.37 0.00",
188 | "Team": 3,
189 | "Bombsite": 0,
190 | "CanBePlanter": false
191 | },
192 | {
193 | "Vector": "964.03 1,739.85 -190.93",
194 | "QAngle": "0.00 0.82 0.00",
195 | "Team": 2,
196 | "Bombsite": 0,
197 | "CanBePlanter": true
198 | },
199 | {
200 | "Vector": "1,131.88 1,978.87 -176.77",
201 | "QAngle": "0.00 109.31 0.00",
202 | "Team": 2,
203 | "Bombsite": 0,
204 | "CanBePlanter": true
205 | },
206 | {
207 | "Vector": "1,312.03 2,174.42 -185.02",
208 | "QAngle": "0.00 -67.34 0.00",
209 | "Team": 2,
210 | "Bombsite": 0,
211 | "CanBePlanter": true
212 | },
213 | {
214 | "Vector": "1,613.31 1,978.65 -70.47",
215 | "QAngle": "0.00 -89.02 0.00",
216 | "Team": 2,
217 | "Bombsite": 0,
218 | "CanBePlanter": false
219 | },
220 | {
221 | "Vector": "1,144.71 2,111.30 -112.95",
222 | "QAngle": "0.00 175.94 0.00",
223 | "Team": 2,
224 | "Bombsite": 0,
225 | "CanBePlanter": false
226 | },
227 | {
228 | "Vector": "943.88 1,925.33 -151.97",
229 | "QAngle": "0.00 -31.75 0.00",
230 | "Team": 2,
231 | "Bombsite": 0,
232 | "CanBePlanter": false
233 | },
234 | {
235 | "Vector": "1,204.95 1,587.60 -151.97",
236 | "QAngle": "0.00 -150.42 0.00",
237 | "Team": 2,
238 | "Bombsite": 0,
239 | "CanBePlanter": false
240 | },
241 | {
242 | "Vector": "1,425.29 1,686.47 -151.97",
243 | "QAngle": "0.00 -51.38 0.00",
244 | "Team": 2,
245 | "Bombsite": 0,
246 | "CanBePlanter": false
247 | }
248 | ]
249 | }
--------------------------------------------------------------------------------
/RetakesPlugin/map_config/de_cache.json:
--------------------------------------------------------------------------------
1 | {
2 | "Spawns": [
3 | {
4 | "Vector": "88.846 2115.3987 1694.6296",
5 | "QAngle": "0 -116.60202 0",
6 | "Team": 2,
7 | "Bombsite": 0,
8 | "CanBePlanter": false
9 | },
10 | {
11 | "Vector": "-426.19086 2163.7827 1687.0312",
12 | "QAngle": "0 -83.966446 0",
13 | "Team": 2,
14 | "Bombsite": 0,
15 | "CanBePlanter": false
16 | },
17 | {
18 | "Vector": "530.5122 -135.95178 1748.0312",
19 | "QAngle": "0 138.06793 0",
20 | "Team": 3,
21 | "Bombsite": 0,
22 | "CanBePlanter": false
23 | },
24 | {
25 | "Vector": "-564.94867 -435.6573 1659.0205",
26 | "QAngle": "0 131.95471 0",
27 | "Team": 3,
28 | "Bombsite": 0,
29 | "CanBePlanter": false
30 | },
31 | {
32 | "Vector": "370.14374 460.2083 1616.3209",
33 | "QAngle": "0 169.67767 0",
34 | "Team": 3,
35 | "Bombsite": 0,
36 | "CanBePlanter": false
37 | },
38 | {
39 | "Vector": "1055.1146 150.50739 1612.0312",
40 | "QAngle": "0 -171.81519 0",
41 | "Team": 3,
42 | "Bombsite": 0,
43 | "CanBePlanter": false
44 | },
45 | {
46 | "Vector": "-965.4935 -486.7514 1612.0938",
47 | "QAngle": "0 89.897 0",
48 | "Team": 3,
49 | "Bombsite": 0,
50 | "CanBePlanter": false
51 | },
52 | {
53 | "Vector": "-52.70166 2028.946 1687.0312",
54 | "QAngle": "0 -129.94046 0",
55 | "Team": 2,
56 | "Bombsite": 0,
57 | "CanBePlanter": true
58 | },
59 | {
60 | "Vector": "-167.70996 2009.9249 1687.0312",
61 | "QAngle": "0 -92.82898 0",
62 | "Team": 2,
63 | "Bombsite": 0,
64 | "CanBePlanter": true
65 | },
66 | {
67 | "Vector": "1675.7158 433.6111 1612.0312",
68 | "QAngle": "0 160.41205 0",
69 | "Team": 3,
70 | "Bombsite": 0,
71 | "CanBePlanter": false
72 | },
73 | {
74 | "Vector": "220.42476 1943.33 1687.2749",
75 | "QAngle": "0 -119.349976 0",
76 | "Team": 2,
77 | "Bombsite": 0,
78 | "CanBePlanter": false
79 | },
80 | {
81 | "Vector": "797.99524 1994.3889 1704.2843",
82 | "QAngle": "0 -98.994026 0",
83 | "Team": 2,
84 | "Bombsite": 0,
85 | "CanBePlanter": false
86 | },
87 | {
88 | "Vector": "-537.23694 -93.11426 1666.0049",
89 | "QAngle": "0 30.952606 0",
90 | "Team": 3,
91 | "Bombsite": 0,
92 | "CanBePlanter": false
93 | },
94 | {
95 | "Vector": "-85.16113 1829.0667 1687.0326",
96 | "QAngle": "0 138.84143 0",
97 | "Team": 2,
98 | "Bombsite": 0,
99 | "CanBePlanter": true
100 | },
101 | {
102 | "Vector": "-454.53986 253.37761 1664.0875",
103 | "QAngle": "0 -93.64712 0",
104 | "Team": 3,
105 | "Bombsite": 0,
106 | "CanBePlanter": false
107 | },
108 | {
109 | "Vector": "112.10849 -645.7037 1612.0312",
110 | "QAngle": "0 -30.481903 0",
111 | "Team": 2,
112 | "Bombsite": 1,
113 | "CanBePlanter": false
114 | },
115 | {
116 | "Vector": "38.96541 -410.5996 1612.0315",
117 | "QAngle": "0 -1.3763733 0",
118 | "Team": 2,
119 | "Bombsite": 1,
120 | "CanBePlanter": false
121 | },
122 | {
123 | "Vector": "-260.18933 -263.54498 1612.0315",
124 | "QAngle": "0 -42.288437 0",
125 | "Team": 2,
126 | "Bombsite": 1,
127 | "CanBePlanter": false
128 | },
129 | {
130 | "Vector": "-39.28589 -1089.934 1659.0312",
131 | "QAngle": "0 159.6159 0",
132 | "Team": 2,
133 | "Bombsite": 1,
134 | "CanBePlanter": true
135 | },
136 | {
137 | "Vector": "204.96872 -1454.9694 1659.7084",
138 | "QAngle": "0 121.152405 0",
139 | "Team": 2,
140 | "Bombsite": 1,
141 | "CanBePlanter": true
142 | },
143 | {
144 | "Vector": "181.90877 -1275.5289 1660.0312",
145 | "QAngle": "0 146.85425 0",
146 | "Team": 2,
147 | "Bombsite": 1,
148 | "CanBePlanter": true
149 | },
150 | {
151 | "Vector": "73.0746 -1449.4857 1659.0312",
152 | "QAngle": "0 148.2114 0",
153 | "Team": 2,
154 | "Bombsite": 1,
155 | "CanBePlanter": true
156 | },
157 | {
158 | "Vector": "-135.96872 -1454.9808 1659.0312",
159 | "QAngle": "0 83.33197 0",
160 | "Team": 2,
161 | "Bombsite": 1,
162 | "CanBePlanter": true
163 | },
164 | {
165 | "Vector": "-547.1575 163.76521 1662.0312",
166 | "QAngle": "0 -178.13199 0",
167 | "Team": 3,
168 | "Bombsite": 1,
169 | "CanBePlanter": false
170 | },
171 | {
172 | "Vector": "-1210.2491 319.78955 1612.2812",
173 | "QAngle": "0 -55.701714 0",
174 | "Team": 3,
175 | "Bombsite": 1,
176 | "CanBePlanter": false
177 | },
178 | {
179 | "Vector": "1786.6544 -703.06866 1612.0759",
180 | "QAngle": "0 178.40802 0",
181 | "Team": 3,
182 | "Bombsite": 1,
183 | "CanBePlanter": false
184 | },
185 | {
186 | "Vector": "-825.3157 606.30457 1633.837",
187 | "QAngle": "0 -92.024574 0",
188 | "Team": 3,
189 | "Bombsite": 1,
190 | "CanBePlanter": false
191 | },
192 | {
193 | "Vector": "-266.07788 399.4657 1613.0312",
194 | "QAngle": "0 -24.039795 0",
195 | "Team": 3,
196 | "Bombsite": 1,
197 | "CanBePlanter": false
198 | },
199 | {
200 | "Vector": "555.2382 653.47833 1613.0312",
201 | "QAngle": "0 -90.55927 0",
202 | "Team": 3,
203 | "Bombsite": 1,
204 | "CanBePlanter": false
205 | },
206 | {
207 | "Vector": "1050.5557 -231.46716 1612.0312",
208 | "QAngle": "0 -104.38248 0",
209 | "Team": 3,
210 | "Bombsite": 1,
211 | "CanBePlanter": false
212 | },
213 | {
214 | "Vector": "-980.65094 1009.6074 1681.4651",
215 | "QAngle": "0 -86.84452 0",
216 | "Team": 3,
217 | "Bombsite": 1,
218 | "CanBePlanter": false
219 | },
220 | {
221 | "Vector": "246.33647 439.47473 1615.7766",
222 | "QAngle": "0 -58.32573 0",
223 | "Team": 3,
224 | "Bombsite": 1,
225 | "CanBePlanter": false
226 | },
227 | {
228 | "Vector": "-300.5824 618.87946 1639.1337",
229 | "QAngle": "0 -58.32573 0",
230 | "Team": 3,
231 | "Bombsite": 1,
232 | "CanBePlanter": false
233 | }
234 | ]
235 | }
--------------------------------------------------------------------------------
/RetakesPlugin/map_config/de_dust2.json:
--------------------------------------------------------------------------------
1 | {
2 | "Spawns": [
3 | {
4 | "Vector": "339.15 2,111.32 -125.04",
5 | "QAngle": "0.00 178.21 0.00",
6 | "Team": 3,
7 | "Bombsite": 1,
8 | "CanBePlanter": false
9 | },
10 | {
11 | "Vector": "335.92 2,291.59 -118.97",
12 | "QAngle": "0.00 -167.78 0.00",
13 | "Team": 3,
14 | "Bombsite": 1,
15 | "CanBePlanter": false
16 | },
17 | {
18 | "Vector": "-505.70 1,753.06 -119.35",
19 | "QAngle": "0.00 98.39 0.00",
20 | "Team": 3,
21 | "Bombsite": 1,
22 | "CanBePlanter": false
23 | },
24 | {
25 | "Vector": "-349.74 1,792.41 -121.33",
26 | "QAngle": "0.00 120.25 0.00",
27 | "Team": 3,
28 | "Bombsite": 1,
29 | "CanBePlanter": false
30 | },
31 | {
32 | "Vector": "-384.31 1,426.28 -126.24",
33 | "QAngle": "0.00 -179.82 0.00",
34 | "Team": 3,
35 | "Bombsite": 1,
36 | "CanBePlanter": false
37 | },
38 | {
39 | "Vector": "-676.93 1,472.06 -112.09",
40 | "QAngle": "0.00 -163.95 0.00",
41 | "Team": 3,
42 | "Bombsite": 1,
43 | "CanBePlanter": false
44 | },
45 | {
46 | "Vector": "-1,925.20 176.37 4.68",
47 | "QAngle": "0.00 64.86 0.00",
48 | "Team": 3,
49 | "Bombsite": 1,
50 | "CanBePlanter": false
51 | },
52 | {
53 | "Vector": "-1,511.12 171.17 2.02",
54 | "QAngle": "0.00 103.50 0.00",
55 | "Team": 3,
56 | "Bombsite": 1,
57 | "CanBePlanter": false
58 | },
59 | {
60 | "Vector": "-2,093.97 3,117.97 35.71",
61 | "QAngle": "0.00 -85.79 0.00",
62 | "Team": 2,
63 | "Bombsite": 1,
64 | "CanBePlanter": false
65 | },
66 | {
67 | "Vector": "-2,100.94 2,637.79 39.31",
68 | "QAngle": "0.00 -8.22 0.00",
69 | "Team": 2,
70 | "Bombsite": 1,
71 | "CanBePlanter": false
72 | },
73 | {
74 | "Vector": "-1,366.03 2,565.97 4.63",
75 | "QAngle": "0.00 -137.57 0.00",
76 | "Team": 2,
77 | "Bombsite": 1,
78 | "CanBePlanter": true
79 | },
80 | {
81 | "Vector": "-1,684.03 2,546.56 7.28",
82 | "QAngle": "0.00 89.41 0.00",
83 | "Team": 2,
84 | "Bombsite": 1,
85 | "CanBePlanter": true
86 | },
87 | {
88 | "Vector": "-1,589.59 2,758.31 65.03",
89 | "QAngle": "0.00 -68.12 0.00",
90 | "Team": 2,
91 | "Bombsite": 1,
92 | "CanBePlanter": true
93 | },
94 | {
95 | "Vector": "-1,364.03 2,755.03 17.68",
96 | "QAngle": "0.00 -173.45 0.00",
97 | "Team": 2,
98 | "Bombsite": 1,
99 | "CanBePlanter": true
100 | },
101 | {
102 | "Vector": "-1,672.21 1,620.03 2.53",
103 | "QAngle": "0.00 59.01 0.00",
104 | "Team": 2,
105 | "Bombsite": 1,
106 | "CanBePlanter": false
107 | },
108 | {
109 | "Vector": "1,235.97 2,348.03 99.72",
110 | "QAngle": "0.00 -85.47 0.00",
111 | "Team": 2,
112 | "Bombsite": 0,
113 | "CanBePlanter": true
114 | },
115 | {
116 | "Vector": "1,235.97 2,460.97 97.90",
117 | "QAngle": "0.00 -88.72 0.00",
118 | "Team": 2,
119 | "Bombsite": 0,
120 | "CanBePlanter": true
121 | },
122 | {
123 | "Vector": "1,069.03 2,348.03 99.20",
124 | "QAngle": "0.00 -154.03 0.00",
125 | "Team": 2,
126 | "Bombsite": 0,
127 | "CanBePlanter": true
128 | },
129 | {
130 | "Vector": "987.97 2,444.03 96.53",
131 | "QAngle": "0.00 -140.64 0.00",
132 | "Team": 2,
133 | "Bombsite": 0,
134 | "CanBePlanter": true
135 | },
136 | {
137 | "Vector": "1,091.15 3,052.68 130.77",
138 | "QAngle": "0.00 -99.80 0.00",
139 | "Team": 2,
140 | "Bombsite": 0,
141 | "CanBePlanter": false
142 | },
143 | {
144 | "Vector": "1,491.10 2,909.15 121.87",
145 | "QAngle": "0.00 -138.43 0.00",
146 | "Team": 2,
147 | "Bombsite": 0,
148 | "CanBePlanter": false
149 | },
150 | {
151 | "Vector": "499.64 66.23 -4.87",
152 | "QAngle": "0.00 59.27 0.00",
153 | "Team": 3,
154 | "Bombsite": 0,
155 | "CanBePlanter": false
156 | },
157 | {
158 | "Vector": "296.67 166.38 -0.73",
159 | "QAngle": "0.00 16.27 0.00",
160 | "Team": 3,
161 | "Bombsite": 0,
162 | "CanBePlanter": false
163 | },
164 | {
165 | "Vector": "-178.91 764.76 1.98",
166 | "QAngle": "0.00 88.59 0.00",
167 | "Team": 3,
168 | "Bombsite": 0,
169 | "CanBePlanter": false
170 | },
171 | {
172 | "Vector": "-754.60 1,426.94 -112.29",
173 | "QAngle": "0.00 -0.34 0.00",
174 | "Team": 3,
175 | "Bombsite": 0,
176 | "CanBePlanter": false
177 | },
178 | {
179 | "Vector": "-523.51 1,823.55 -120.31",
180 | "QAngle": "0.00 42.92 0.00",
181 | "Team": 3,
182 | "Bombsite": 0,
183 | "CanBePlanter": false
184 | },
185 | {
186 | "Vector": "-549.88 2,344.89 -108.92",
187 | "QAngle": "0.00 -25.31 0.00",
188 | "Team": 3,
189 | "Bombsite": 0,
190 | "CanBePlanter": false
191 | }
192 | ]
193 | }
--------------------------------------------------------------------------------
/RetakesPlugin/map_config/de_inferno.json:
--------------------------------------------------------------------------------
1 | {
2 | "Spawns": [
3 | {
4 | "Vector": "-339.74 874.60 2.41",
5 | "QAngle": "0.00 3.34 0.00",
6 | "Team": 3,
7 | "Bombsite": 1,
8 | "CanBePlanter": false
9 | },
10 | {
11 | "Vector": "-348.59 790.64 4.40",
12 | "QAngle": "0.00 17.77 0.00",
13 | "Team": 3,
14 | "Bombsite": 1,
15 | "CanBePlanter": false
16 | },
17 | {
18 | "Vector": "135.08 520.68 82.00",
19 | "QAngle": "0.00 89.61 0.00",
20 | "Team": 3,
21 | "Bombsite": 1,
22 | "CanBePlanter": false
23 | },
24 | {
25 | "Vector": "388.67 556.29 85.98",
26 | "QAngle": "0.00 145.25 0.00",
27 | "Team": 3,
28 | "Bombsite": 1,
29 | "CanBePlanter": false
30 | },
31 | {
32 | "Vector": "2,325.63 2,070.42 134.66",
33 | "QAngle": "0.00 120.37 0.00",
34 | "Team": 3,
35 | "Bombsite": 1,
36 | "CanBePlanter": false
37 | },
38 | {
39 | "Vector": "2,447.89 2,333.10 129.16",
40 | "QAngle": "0.00 148.16 0.00",
41 | "Team": 3,
42 | "Bombsite": 1,
43 | "CanBePlanter": false
44 | },
45 | {
46 | "Vector": "1,635.22 2,101.05 176.03",
47 | "QAngle": "0.00 89.88 0.00",
48 | "Team": 3,
49 | "Bombsite": 1,
50 | "CanBePlanter": false
51 | },
52 | {
53 | "Vector": "2,496.89 2,330.43 128.24",
54 | "QAngle": "0.00 152.12 0.00",
55 | "Team": 3,
56 | "Bombsite": 1,
57 | "CanBePlanter": false
58 | },
59 | {
60 | "Vector": "176.47 2,768.02 164.03",
61 | "QAngle": "0.00 0.09 0.00",
62 | "Team": 2,
63 | "Bombsite": 1,
64 | "CanBePlanter": true
65 | },
66 | {
67 | "Vector": "530.32 2,937.17 161.53",
68 | "QAngle": "0.00 -56.58 0.00",
69 | "Team": 2,
70 | "Bombsite": 1,
71 | "CanBePlanter": true
72 | },
73 | {
74 | "Vector": "172.64 2,938.53 161.53",
75 | "QAngle": "0.00 -44.60 0.00",
76 | "Team": 2,
77 | "Bombsite": 1,
78 | "CanBePlanter": true
79 | },
80 | {
81 | "Vector": "1.17 2,614.14 161.03",
82 | "QAngle": "0.00 5.67 0.00",
83 | "Team": 2,
84 | "Bombsite": 1,
85 | "CanBePlanter": false
86 | },
87 | {
88 | "Vector": "24.11 3,146.27 161.03",
89 | "QAngle": "0.00 -43.52 0.00",
90 | "Team": 2,
91 | "Bombsite": 1,
92 | "CanBePlanter": false
93 | },
94 | {
95 | "Vector": "433.23 3,181.69 162.91",
96 | "QAngle": "0.00 57.42 0.00",
97 | "Team": 2,
98 | "Bombsite": 1,
99 | "CanBePlanter": false
100 | },
101 | {
102 | "Vector": "2,428.34 2,220.58 131.88",
103 | "QAngle": "0.00 -96.94 0.00",
104 | "Team": 3,
105 | "Bombsite": 0,
106 | "CanBePlanter": false
107 | },
108 | {
109 | "Vector": "2,265.74 2,221.95 134.22",
110 | "QAngle": "0.00 -84.80 0.00",
111 | "Team": 3,
112 | "Bombsite": 0,
113 | "CanBePlanter": false
114 | },
115 | {
116 | "Vector": "1,833.85 1,801.65 160.03",
117 | "QAngle": "0.00 -93.14 0.00",
118 | "Team": 3,
119 | "Bombsite": 0,
120 | "CanBePlanter": false
121 | },
122 | {
123 | "Vector": "145.05 525.70 81.97",
124 | "QAngle": "0.00 -3.18 0.00",
125 | "Team": 3,
126 | "Bombsite": 0,
127 | "CanBePlanter": false
128 | },
129 | {
130 | "Vector": "158.91 702.40 82.69",
131 | "QAngle": "0.00 -11.97 0.00",
132 | "Team": 3,
133 | "Bombsite": 0,
134 | "CanBePlanter": false
135 | },
136 | {
137 | "Vector": "766.77 96.12 89.29",
138 | "QAngle": "0.00 63.38 0.00",
139 | "Team": 3,
140 | "Bombsite": 0,
141 | "CanBePlanter": false
142 | },
143 | {
144 | "Vector": "856.75 -652.57 89.03",
145 | "QAngle": "0.00 0.12 0.00",
146 | "Team": 3,
147 | "Bombsite": 0,
148 | "CanBePlanter": false
149 | },
150 | {
151 | "Vector": "1,224.24 -671.15 128.03",
152 | "QAngle": "0.00 89.79 0.00",
153 | "Team": 3,
154 | "Bombsite": 0,
155 | "CanBePlanter": false
156 | },
157 | {
158 | "Vector": "2,046.40 -195.81 262.03",
159 | "QAngle": "0.00 179.83 0.00",
160 | "Team": 2,
161 | "Bombsite": 0,
162 | "CanBePlanter": false
163 | },
164 | {
165 | "Vector": "2,114.27 -394.94 96.59",
166 | "QAngle": "0.00 79.18 0.00",
167 | "Team": 2,
168 | "Bombsite": 0,
169 | "CanBePlanter": false
170 | },
171 | {
172 | "Vector": "2,528.94 -420.20 98.57",
173 | "QAngle": "0.00 120.91 0.00",
174 | "Team": 2,
175 | "Bombsite": 0,
176 | "CanBePlanter": false
177 | },
178 | {
179 | "Vector": "2,111.01 179.37 160.03",
180 | "QAngle": "0.00 91.83 0.00",
181 | "Team": 2,
182 | "Bombsite": 0,
183 | "CanBePlanter": true
184 | },
185 | {
186 | "Vector": "1,819.03 693.97 160.01",
187 | "QAngle": "0.00 -49.49 0.00",
188 | "Team": 2,
189 | "Bombsite": 0,
190 | "CanBePlanter": true
191 | },
192 | {
193 | "Vector": "1,819.03 273.68 161.03",
194 | "QAngle": "0.00 44.56 0.00",
195 | "Team": 2,
196 | "Bombsite": 0,
197 | "CanBePlanter": true
198 | },
199 | {
200 | "Vector": "2,599.97 559.97 216.03",
201 | "QAngle": "0.00 -177.30 0.00",
202 | "Team": 2,
203 | "Bombsite": 0,
204 | "CanBePlanter": false
205 | }
206 | ]
207 | }
--------------------------------------------------------------------------------
/RetakesPlugin/map_config/de_mirage.json:
--------------------------------------------------------------------------------
1 | {
2 | "Spawns": [
3 | {
4 | "Vector": "1,229.99 -631.64 -204.70",
5 | "QAngle": "0.00 -108.09 0.00",
6 | "Team": 3,
7 | "Bombsite": 0,
8 | "CanBePlanter": false
9 | },
10 | {
11 | "Vector": "-321.03 -2,036.16 -174.56",
12 | "QAngle": "0.00 132.62 0.00",
13 | "Team": 2,
14 | "Bombsite": 0,
15 | "CanBePlanter": true
16 | },
17 | {
18 | "Vector": "-554.97 -2,103.69 -179.97",
19 | "QAngle": "0.00 -71.46 0.00",
20 | "Team": 2,
21 | "Bombsite": 0,
22 | "CanBePlanter": true
23 | },
24 | {
25 | "Vector": "-253.97 -2,134.08 -172.95",
26 | "QAngle": "0.00 131.13 0.00",
27 | "Team": 2,
28 | "Bombsite": 0,
29 | "CanBePlanter": true
30 | },
31 | {
32 | "Vector": "-583.17 -2,393.24 -167.97",
33 | "QAngle": "0.00 169.48 0.00",
34 | "Team": 2,
35 | "Bombsite": 0,
36 | "CanBePlanter": false
37 | },
38 | {
39 | "Vector": "87.97 -2,091.41 -167.97",
40 | "QAngle": "0.00 147.81 0.00",
41 | "Team": 2,
42 | "Bombsite": 0,
43 | "CanBePlanter": false
44 | },
45 | {
46 | "Vector": "111.53 -1,717.97 -167.97",
47 | "QAngle": "0.00 70.31 0.00",
48 | "Team": 2,
49 | "Bombsite": 0,
50 | "CanBePlanter": false
51 | },
52 | {
53 | "Vector": "-142.97 -1,418.03 -72.18",
54 | "QAngle": "0.00 -76.73 0.00",
55 | "Team": 2,
56 | "Bombsite": 0,
57 | "CanBePlanter": false
58 | },
59 | {
60 | "Vector": "95.97 -2,226.65 -39.97",
61 | "QAngle": "0.00 140.88 0.00",
62 | "Team": 2,
63 | "Bombsite": 0,
64 | "CanBePlanter": false
65 | },
66 | {
67 | "Vector": "-1,711.97 -761.03 -167.97",
68 | "QAngle": "0.00 -70.84 0.00",
69 | "Team": 3,
70 | "Bombsite": 0,
71 | "CanBePlanter": false
72 | },
73 | {
74 | "Vector": "-1,506.86 -876.06 -170.56",
75 | "QAngle": "0.00 -30.52 0.00",
76 | "Team": 3,
77 | "Bombsite": 0,
78 | "CanBePlanter": false
79 | },
80 | {
81 | "Vector": "-1,166.20 -519.36 -167.97",
82 | "QAngle": "0.00 -33.34 0.00",
83 | "Team": 3,
84 | "Bombsite": 0,
85 | "CanBePlanter": false
86 | },
87 | {
88 | "Vector": "-1,040.86 238.60 -171.73",
89 | "QAngle": "0.00 -35.02 0.00",
90 | "Team": 3,
91 | "Bombsite": 0,
92 | "CanBePlanter": false
93 | },
94 | {
95 | "Vector": "215.21 -323.35 -167.40",
96 | "QAngle": "0.00 -139.94 0.00",
97 | "Team": 3,
98 | "Bombsite": 0,
99 | "CanBePlanter": false
100 | },
101 | {
102 | "Vector": "-174.44 -479.53 -166.40",
103 | "QAngle": "0.00 165.44 0.00",
104 | "Team": 3,
105 | "Bombsite": 1,
106 | "CanBePlanter": false
107 | },
108 | {
109 | "Vector": "-84.52 -770.57 -209.42",
110 | "QAngle": "0.00 142.59 0.00",
111 | "Team": 3,
112 | "Bombsite": 1,
113 | "CanBePlanter": false
114 | },
115 | {
116 | "Vector": "-1,221.50 -1,399.10 -163.29",
117 | "QAngle": "0.00 87.20 0.00",
118 | "Team": 3,
119 | "Bombsite": 1,
120 | "CanBePlanter": false
121 | },
122 | {
123 | "Vector": "-1,513.05 -1,327.52 -259.97",
124 | "QAngle": "0.00 101.53 0.00",
125 | "Team": 3,
126 | "Bombsite": 1,
127 | "CanBePlanter": false
128 | },
129 | {
130 | "Vector": "-1,710.34 -1,675.10 -263.80",
131 | "QAngle": "0.00 85.10 0.00",
132 | "Team": 3,
133 | "Bombsite": 1,
134 | "CanBePlanter": false
135 | },
136 | {
137 | "Vector": "-1,969.03 450.03 -159.97",
138 | "QAngle": "0.00 -89.07 0.00",
139 | "Team": 2,
140 | "Bombsite": 1,
141 | "CanBePlanter": true
142 | },
143 | {
144 | "Vector": "-1,892.41 242.03 -159.97",
145 | "QAngle": "0.00 -7.66 0.00",
146 | "Team": 2,
147 | "Bombsite": 1,
148 | "CanBePlanter": true
149 | },
150 | {
151 | "Vector": "-2,198.97 89.03 -159.97",
152 | "QAngle": "0.00 67.56 0.00",
153 | "Team": 2,
154 | "Bombsite": 1,
155 | "CanBePlanter": true
156 | },
157 | {
158 | "Vector": "-2,494.53 312.34 -167.97",
159 | "QAngle": "0.00 -6.40 0.00",
160 | "Team": 2,
161 | "Bombsite": 1,
162 | "CanBePlanter": false
163 | },
164 | {
165 | "Vector": "-2,384.63 652.63 -78.97",
166 | "QAngle": "0.00 7.03 0.00",
167 | "Team": 2,
168 | "Bombsite": 1,
169 | "CanBePlanter": false
170 | },
171 | {
172 | "Vector": "-1,944.35 629.97 -167.97",
173 | "QAngle": "0.00 -25.35 0.00",
174 | "Team": 2,
175 | "Bombsite": 1,
176 | "CanBePlanter": false
177 | },
178 | {
179 | "Vector": "-1,553.30 -231.97 -157.80",
180 | "QAngle": "0.00 128.49 0.00",
181 | "Team": 2,
182 | "Bombsite": 1,
183 | "CanBePlanter": false
184 | },
185 | {
186 | "Vector": "-122.00 863.96 -135.93",
187 | "QAngle": "0.00 -133.14 0.00",
188 | "Team": 3,
189 | "Bombsite": 1,
190 | "CanBePlanter": false
191 | },
192 | {
193 | "Vector": "-476.44 358.03 -146.92",
194 | "QAngle": "0.00 167.67 0.00",
195 | "Team": 3,
196 | "Bombsite": 1,
197 | "CanBePlanter": false
198 | },
199 | {
200 | "Vector": "-1,187.47 -1,273.15 -167.97",
201 | "QAngle": "0.00 98.60 0.00",
202 | "Team": 3,
203 | "Bombsite": 1,
204 | "CanBePlanter": false
205 | },
206 | {
207 | "Vector": "1,223.29 -1,406.86 -167.97",
208 | "QAngle": "0.00 -163.94 0.00",
209 | "Team": 3,
210 | "Bombsite": 0,
211 | "CanBePlanter": false
212 | },
213 | {
214 | "Vector": "938.00 -1,435.66 -167.97",
215 | "QAngle": "0.00 -88.56 0.00",
216 | "Team": 3,
217 | "Bombsite": 0,
218 | "CanBePlanter": false
219 | },
220 | {
221 | "Vector": "1,319.59 -441.11 -167.97",
222 | "QAngle": "0.00 -109.80 0.00",
223 | "Team": 3,
224 | "Bombsite": 0,
225 | "CanBePlanter": false
226 | },
227 | {
228 | "Vector": "-1,045.11 -377.16 -347.39",
229 | "QAngle": "0.00 -66.76 0.00",
230 | "Team": 3,
231 | "Bombsite": 0,
232 | "CanBePlanter": false
233 | }
234 | ]
235 | }
--------------------------------------------------------------------------------
/RetakesPlugin/map_config/de_nuke.json:
--------------------------------------------------------------------------------
1 | {
2 | "Spawns": [
3 | {
4 | "Vector": "-407.09 -754.33 -415.47",
5 | "QAngle": "0.00 -63.47 0.00",
6 | "Team": 3,
7 | "Bombsite": 0,
8 | "CanBePlanter": false
9 | },
10 | {
11 | "Vector": "-422.84 -913.68 -415.97",
12 | "QAngle": "0.00 -34.62 0.00",
13 | "Team": 3,
14 | "Bombsite": 0,
15 | "CanBePlanter": false
16 | },
17 | {
18 | "Vector": "-126.31 -515.35 -415.97",
19 | "QAngle": "0.00 -79.92 0.00",
20 | "Team": 3,
21 | "Bombsite": 0,
22 | "CanBePlanter": false
23 | },
24 | {
25 | "Vector": "-116.09 -409.32 -415.97",
26 | "QAngle": "0.00 -91.43 0.00",
27 | "Team": 3,
28 | "Bombsite": 0,
29 | "CanBePlanter": false
30 | },
31 | {
32 | "Vector": "761.83 -2,085.21 -415.97",
33 | "QAngle": "0.00 65.94 0.00",
34 | "Team": 3,
35 | "Bombsite": 0,
36 | "CanBePlanter": false
37 | },
38 | {
39 | "Vector": "967.00 -2,141.28 -415.97",
40 | "QAngle": "0.00 94.51 0.00",
41 | "Team": 3,
42 | "Bombsite": 0,
43 | "CanBePlanter": false
44 | },
45 | {
46 | "Vector": "1,168.45 -2,188.91 -407.97",
47 | "QAngle": "0.00 119.02 0.00",
48 | "Team": 3,
49 | "Bombsite": 0,
50 | "CanBePlanter": false
51 | },
52 | {
53 | "Vector": "1,786.11 -1,872.03 -415.97",
54 | "QAngle": "0.00 171.20 0.00",
55 | "Team": 3,
56 | "Bombsite": 0,
57 | "CanBePlanter": false
58 | },
59 | {
60 | "Vector": "1,121.13 -1,162.69 -415.97",
61 | "QAngle": "0.00 67.97 0.00",
62 | "Team": 3,
63 | "Bombsite": 0,
64 | "CanBePlanter": false
65 | },
66 | {
67 | "Vector": "1,254.25 -1,162.96 -415.97",
68 | "QAngle": "0.00 84.31 0.00",
69 | "Team": 3,
70 | "Bombsite": 0,
71 | "CanBePlanter": false
72 | },
73 | {
74 | "Vector": "1,080.60 -711.78 -239.97",
75 | "QAngle": "0.00 89.03 0.00",
76 | "Team": 3,
77 | "Bombsite": 0,
78 | "CanBePlanter": false
79 | },
80 | {
81 | "Vector": "1,337.23 -393.82 -127.97",
82 | "QAngle": "0.00 -179.71 0.00",
83 | "Team": 3,
84 | "Bombsite": 0,
85 | "CanBePlanter": false
86 | },
87 | {
88 | "Vector": "1,369.60 -485.97 -415.97",
89 | "QAngle": "0.00 171.79 0.00",
90 | "Team": 3,
91 | "Bombsite": 0,
92 | "CanBePlanter": false
93 | },
94 | {
95 | "Vector": "1,086.34 -102.99 -415.97",
96 | "QAngle": "0.00 -82.09 0.00",
97 | "Team": 3,
98 | "Bombsite": 0,
99 | "CanBePlanter": false
100 | },
101 | {
102 | "Vector": "1,118.28 2.73 -415.97",
103 | "QAngle": "0.00 -88.78 0.00",
104 | "Team": 3,
105 | "Bombsite": 0,
106 | "CanBePlanter": false
107 | },
108 | {
109 | "Vector": "710.74 -769.41 -399.97",
110 | "QAngle": "0.00 -104.64 0.00",
111 | "Team": 2,
112 | "Bombsite": 0,
113 | "CanBePlanter": true
114 | },
115 | {
116 | "Vector": "700.41 -625.43 -399.97",
117 | "QAngle": "0.00 51.52 0.00",
118 | "Team": 2,
119 | "Bombsite": 0,
120 | "CanBePlanter": true
121 | },
122 | {
123 | "Vector": "888.60 -334.04 -413.97",
124 | "QAngle": "0.00 -98.62 0.00",
125 | "Team": 2,
126 | "Bombsite": 0,
127 | "CanBePlanter": false
128 | },
129 | {
130 | "Vector": "461.69 -336.03 -413.97",
131 | "QAngle": "0.00 -87.05 0.00",
132 | "Team": 2,
133 | "Bombsite": 0,
134 | "CanBePlanter": false
135 | },
136 | {
137 | "Vector": "398.36 -830.92 -391.97",
138 | "QAngle": "0.00 -94.38 0.00",
139 | "Team": 2,
140 | "Bombsite": 0,
141 | "CanBePlanter": false
142 | },
143 | {
144 | "Vector": "496.03 -1,104.97 -415.97",
145 | "QAngle": "0.00 53.17 0.00",
146 | "Team": 2,
147 | "Bombsite": 0,
148 | "CanBePlanter": false
149 | },
150 | {
151 | "Vector": "302.61 -1,275.99 -415.97",
152 | "QAngle": "0.00 -179.50 0.00",
153 | "Team": 2,
154 | "Bombsite": 0,
155 | "CanBePlanter": false
156 | },
157 | {
158 | "Vector": "514.24 -926.35 -408.74",
159 | "QAngle": "0.00 -69.52 0.00",
160 | "Team": 2,
161 | "Bombsite": 0,
162 | "CanBePlanter": true
163 | },
164 | {
165 | "Vector": "189.57 -91.70 -415.97",
166 | "QAngle": "0.00 61.93 0.00",
167 | "Team": 3,
168 | "Bombsite": 1,
169 | "CanBePlanter": false
170 | },
171 | {
172 | "Vector": "134.42 91.37 -415.97",
173 | "QAngle": "0.00 13.81 0.00",
174 | "Team": 3,
175 | "Bombsite": 1,
176 | "CanBePlanter": false
177 | },
178 | {
179 | "Vector": "1,127.96 43.59 -415.97",
180 | "QAngle": "0.00 148.74 0.00",
181 | "Team": 3,
182 | "Bombsite": 1,
183 | "CanBePlanter": false
184 | },
185 | {
186 | "Vector": "810.88 -43.95 -415.97",
187 | "QAngle": "0.00 109.53 0.00",
188 | "Team": 3,
189 | "Bombsite": 1,
190 | "CanBePlanter": false
191 | },
192 | {
193 | "Vector": "1,595.55 -1,904.47 -639.97",
194 | "QAngle": "0.00 178.97 0.00",
195 | "Team": 3,
196 | "Bombsite": 1,
197 | "CanBePlanter": false
198 | },
199 | {
200 | "Vector": "1,519.83 -2,124.91 -639.97",
201 | "QAngle": "0.00 92.68 0.00",
202 | "Team": 3,
203 | "Bombsite": 1,
204 | "CanBePlanter": false
205 | },
206 | {
207 | "Vector": "537.07 -1,297.97 -415.97",
208 | "QAngle": "0.00 -121.33 0.00",
209 | "Team": 3,
210 | "Bombsite": 1,
211 | "CanBePlanter": false
212 | },
213 | {
214 | "Vector": "332.87 -1,284.17 -415.97",
215 | "QAngle": "0.00 -51.04 0.00",
216 | "Team": 3,
217 | "Bombsite": 1,
218 | "CanBePlanter": false
219 | },
220 | {
221 | "Vector": "638.92 -1,256.03 -768.84",
222 | "QAngle": "0.00 178.40 0.00",
223 | "Team": 2,
224 | "Bombsite": 1,
225 | "CanBePlanter": true
226 | },
227 | {
228 | "Vector": "925.87 -1,330.99 -767.97",
229 | "QAngle": "0.00 178.18 0.00",
230 | "Team": 2,
231 | "Bombsite": 1,
232 | "CanBePlanter": false
233 | },
234 | {
235 | "Vector": "340.03 -712.90 -765.97",
236 | "QAngle": "0.00 -0.75 0.00",
237 | "Team": 2,
238 | "Bombsite": 1,
239 | "CanBePlanter": true
240 | },
241 | {
242 | "Vector": "866.35 -965.26 -765.97",
243 | "QAngle": "0.00 117.44 0.00",
244 | "Team": 2,
245 | "Bombsite": 1,
246 | "CanBePlanter": true
247 | },
248 | {
249 | "Vector": "896.28 -286.70 -767.97",
250 | "QAngle": "0.00 -94.11 0.00",
251 | "Team": 2,
252 | "Bombsite": 1,
253 | "CanBePlanter": false
254 | },
255 | {
256 | "Vector": "1,392.87 -962.18 -767.97",
257 | "QAngle": "0.00 -169.38 0.00",
258 | "Team": 2,
259 | "Bombsite": 1,
260 | "CanBePlanter": false
261 | },
262 | {
263 | "Vector": "1,313.84 -763.57 -679.68",
264 | "QAngle": "0.00 111.56 0.00",
265 | "Team": 2,
266 | "Bombsite": 1,
267 | "CanBePlanter": false
268 | }
269 | ]
270 | }
--------------------------------------------------------------------------------
/RetakesPlugin/map_config/de_overpass.json:
--------------------------------------------------------------------------------
1 | {
2 | "Spawns": [
3 | {
4 | "Vector": "-475.18 -1,949.86 148.52",
5 | "QAngle": "0.00 109.60 0.00",
6 | "Team": 3,
7 | "Bombsite": 1,
8 | "CanBePlanter": false
9 | },
10 | {
11 | "Vector": "-634.80 -1,962.89 148.26",
12 | "QAngle": "0.00 90.52 0.00",
13 | "Team": 3,
14 | "Bombsite": 1,
15 | "CanBePlanter": false
16 | },
17 | {
18 | "Vector": "-2,031.97 -1,090.09 132.03",
19 | "QAngle": "0.00 32.39 0.00",
20 | "Team": 3,
21 | "Bombsite": 1,
22 | "CanBePlanter": false
23 | },
24 | {
25 | "Vector": "-2,027.76 -940.15 132.03",
26 | "QAngle": "0.00 4.52 0.00",
27 | "Team": 3,
28 | "Bombsite": 1,
29 | "CanBePlanter": false
30 | },
31 | {
32 | "Vector": "-1,806.49 1,289.26 357.07",
33 | "QAngle": "0.00 -92.03 0.00",
34 | "Team": 3,
35 | "Bombsite": 1,
36 | "CanBePlanter": false
37 | },
38 | {
39 | "Vector": "-2,387.09 1,393.76 360.03",
40 | "QAngle": "0.00 -82.57 0.00",
41 | "Team": 3,
42 | "Bombsite": 1,
43 | "CanBePlanter": false
44 | },
45 | {
46 | "Vector": "-1,982.02 847.97 213.55",
47 | "QAngle": "0.00 -81.53 0.00",
48 | "Team": 3,
49 | "Bombsite": 1,
50 | "CanBePlanter": false
51 | },
52 | {
53 | "Vector": "-1,495.00 574.45 29.11",
54 | "QAngle": "0.00 -75.23 0.00",
55 | "Team": 2,
56 | "Bombsite": 1,
57 | "CanBePlanter": false
58 | },
59 | {
60 | "Vector": "-1,709.92 80.32 116.10",
61 | "QAngle": "0.00 25.36 0.00",
62 | "Team": 2,
63 | "Bombsite": 1,
64 | "CanBePlanter": false
65 | },
66 | {
67 | "Vector": "-1,337.48 -100.30 12.90",
68 | "QAngle": "0.00 124.71 0.00",
69 | "Team": 2,
70 | "Bombsite": 1,
71 | "CanBePlanter": false
72 | },
73 | {
74 | "Vector": "-964.36 200.23 100.03",
75 | "QAngle": "0.00 -161.05 0.00",
76 | "Team": 2,
77 | "Bombsite": 1,
78 | "CanBePlanter": true
79 | },
80 | {
81 | "Vector": "-1,251.97 5.03 103.53",
82 | "QAngle": "0.00 135.84 0.00",
83 | "Team": 2,
84 | "Bombsite": 1,
85 | "CanBePlanter": true
86 | },
87 | {
88 | "Vector": "-1,166.28 -77.88 103.81",
89 | "QAngle": "0.00 17.11 0.00",
90 | "Team": 2,
91 | "Bombsite": 1,
92 | "CanBePlanter": true
93 | },
94 | {
95 | "Vector": "-970.96 -82.35 100.03",
96 | "QAngle": "0.00 -87.08 0.00",
97 | "Team": 2,
98 | "Bombsite": 1,
99 | "CanBePlanter": true
100 | },
101 | {
102 | "Vector": "-822.62 461.98 100.03",
103 | "QAngle": "0.00 -132.62 0.00",
104 | "Team": 2,
105 | "Bombsite": 1,
106 | "CanBePlanter": false
107 | },
108 | {
109 | "Vector": "-1,896.92 -1,302.58 244.03",
110 | "QAngle": "0.00 -179.88 0.00",
111 | "Team": 3,
112 | "Bombsite": 0,
113 | "CanBePlanter": false
114 | },
115 | {
116 | "Vector": "-2,831.40 -1,527.39 439.45",
117 | "QAngle": "0.00 67.72 0.00",
118 | "Team": 3,
119 | "Bombsite": 0,
120 | "CanBePlanter": false
121 | },
122 | {
123 | "Vector": "-2,553.77 -1,454.92 438.88",
124 | "QAngle": "0.00 70.36 0.00",
125 | "Team": 3,
126 | "Bombsite": 0,
127 | "CanBePlanter": false
128 | },
129 | {
130 | "Vector": "-3,127.90 -933.39 492.03",
131 | "QAngle": "0.00 150.53 0.00",
132 | "Team": 3,
133 | "Bombsite": 0,
134 | "CanBePlanter": false
135 | },
136 | {
137 | "Vector": "-3,595.13 -1,235.10 491.91",
138 | "QAngle": "0.00 95.07 0.00",
139 | "Team": 3,
140 | "Bombsite": 0,
141 | "CanBePlanter": false
142 | },
143 | {
144 | "Vector": "-3,909.98 -1,134.18 522.11",
145 | "QAngle": "0.00 67.35 0.00",
146 | "Team": 3,
147 | "Bombsite": 0,
148 | "CanBePlanter": false
149 | },
150 | {
151 | "Vector": "-2,331.11 982.95 342.27",
152 | "QAngle": "0.00 102.71 0.00",
153 | "Team": 3,
154 | "Bombsite": 0,
155 | "CanBePlanter": false
156 | },
157 | {
158 | "Vector": "-1,782.69 814.76 132.03",
159 | "QAngle": "0.00 91.21 0.00",
160 | "Team": 3,
161 | "Bombsite": 0,
162 | "CanBePlanter": false
163 | },
164 | {
165 | "Vector": "-2,021.00 786.01 239.54",
166 | "QAngle": "0.00 140.07 0.00",
167 | "Team": 3,
168 | "Bombsite": 0,
169 | "CanBePlanter": false
170 | },
171 | {
172 | "Vector": "-1,805.03 632.04 476.97",
173 | "QAngle": "0.00 124.23 0.00",
174 | "Team": 2,
175 | "Bombsite": 0,
176 | "CanBePlanter": true
177 | },
178 | {
179 | "Vector": "-2,189.32 563.58 477.39",
180 | "QAngle": "0.00 115.43 0.00",
181 | "Team": 2,
182 | "Bombsite": 0,
183 | "CanBePlanter": true
184 | },
185 | {
186 | "Vector": "-2,518.97 657.72 484.03",
187 | "QAngle": "0.00 -78.05 0.00",
188 | "Team": 2,
189 | "Bombsite": 0,
190 | "CanBePlanter": true
191 | },
192 | {
193 | "Vector": "-2,471.04 781.19 476.53",
194 | "QAngle": "0.00 56.77 0.00",
195 | "Team": 2,
196 | "Bombsite": 0,
197 | "CanBePlanter": true
198 | },
199 | {
200 | "Vector": "-2,746.53 243.08 441.22",
201 | "QAngle": "0.00 -63.32 0.00",
202 | "Team": 2,
203 | "Bombsite": 0,
204 | "CanBePlanter": false
205 | },
206 | {
207 | "Vector": "-2,348.45 471.52 484.03",
208 | "QAngle": "0.00 -106.72 0.00",
209 | "Team": 2,
210 | "Bombsite": 0,
211 | "CanBePlanter": false
212 | }
213 | ]
214 | }
--------------------------------------------------------------------------------
/RetakesPlugin/map_config/de_train.json:
--------------------------------------------------------------------------------
1 | {
2 | "Spawns": [
3 | {
4 | "Vector": "299.80554 -131.18439 -141.96872",
5 | "QAngle": "0 119.90512 0",
6 | "Team": 2,
7 | "Bombsite": 0,
8 | "CanBePlanter": true
9 | },
10 | {
11 | "Vector": "400.76358 36.26184 -215.96875",
12 | "QAngle": "0 160.90784 0",
13 | "Team": 2,
14 | "Bombsite": 0,
15 | "CanBePlanter": false
16 | },
17 | {
18 | "Vector": "-20.978577 68.708275 -215.96875",
19 | "QAngle": "0 -40.293045 0",
20 | "Team": 2,
21 | "Bombsite": 0,
22 | "CanBePlanter": false
23 | },
24 | {
25 | "Vector": "-47.636795 367.8039 -215.96875",
26 | "QAngle": "0 -10.980499 0",
27 | "Team": 2,
28 | "Bombsite": 0,
29 | "CanBePlanter": false
30 | },
31 | {
32 | "Vector": "-358.19907 24.961477 -215.96872",
33 | "QAngle": "0 -10.980499 0",
34 | "Team": 2,
35 | "Bombsite": 0,
36 | "CanBePlanter": false
37 | },
38 | {
39 | "Vector": "564.9993 368.17227 -215.96875",
40 | "QAngle": "0 -23.258392 0",
41 | "Team": 2,
42 | "Bombsite": 0,
43 | "CanBePlanter": false
44 | },
45 | {
46 | "Vector": "-111.987305 637.3805 -215.96875",
47 | "QAngle": "0 -39.9281 0",
48 | "Team": 2,
49 | "Bombsite": 0,
50 | "CanBePlanter": false
51 | },
52 | {
53 | "Vector": "181.44771 -896.66095 -320.96875",
54 | "QAngle": "0 1.4038391 0",
55 | "Team": 3,
56 | "Bombsite": 0,
57 | "CanBePlanter": false
58 | },
59 | {
60 | "Vector": "-1032.6996 -940.31915 -153.96875",
61 | "QAngle": "0 77.14667 0",
62 | "Team": 3,
63 | "Bombsite": 0,
64 | "CanBePlanter": false
65 | },
66 | {
67 | "Vector": "-1145.9028 -931.85986 -101.96875",
68 | "QAngle": "0 56.454285 0",
69 | "Team": 3,
70 | "Bombsite": 0,
71 | "CanBePlanter": false
72 | },
73 | {
74 | "Vector": "1621.404 -1169.5692 -327.09747",
75 | "QAngle": "0 113.62677 0",
76 | "Team": 3,
77 | "Bombsite": 0,
78 | "CanBePlanter": false
79 | },
80 | {
81 | "Vector": "538.01776 -196.08826 -215.96875",
82 | "QAngle": "0 -176.79268 0",
83 | "Team": 2,
84 | "Bombsite": 0,
85 | "CanBePlanter": true
86 | },
87 | {
88 | "Vector": "641.9331 -114.88289 -159.31398",
89 | "QAngle": "0 91.798676 0",
90 | "Team": 2,
91 | "Bombsite": 0,
92 | "CanBePlanter": true
93 | },
94 | {
95 | "Vector": "-463.31555 -1764.1312 -166.96875",
96 | "QAngle": "0 35.669525 0",
97 | "Team": 2,
98 | "Bombsite": 1,
99 | "CanBePlanter": false
100 | },
101 | {
102 | "Vector": "-1039.5194 -1348.9492 -153.96875",
103 | "QAngle": "0 78.23297 0",
104 | "Team": 2,
105 | "Bombsite": 1,
106 | "CanBePlanter": false
107 | },
108 | {
109 | "Vector": "-56.79004 -1581.2207 -350.96875",
110 | "QAngle": "0 21.919434 0",
111 | "Team": 2,
112 | "Bombsite": 1,
113 | "CanBePlanter": false
114 | },
115 | {
116 | "Vector": "-648.4443 -919.01654 -299.01733",
117 | "QAngle": "0 16.768219 0",
118 | "Team": 2,
119 | "Bombsite": 1,
120 | "CanBePlanter": false
121 | },
122 | {
123 | "Vector": "-468.63867 -1163.8351 -350.96875",
124 | "QAngle": "0 15.166626 0",
125 | "Team": 2,
126 | "Bombsite": 1,
127 | "CanBePlanter": false
128 | },
129 | {
130 | "Vector": "-210.91364 -1378.0321 -350.96875",
131 | "QAngle": "0 -0.19981384 0",
132 | "Team": 2,
133 | "Bombsite": 1,
134 | "CanBePlanter": true
135 | },
136 | {
137 | "Vector": "-232.72836 -1275.144 -277.96872",
138 | "QAngle": "0 -1.3866882 0",
139 | "Team": 2,
140 | "Bombsite": 1,
141 | "CanBePlanter": true
142 | },
143 | {
144 | "Vector": "-211.15405 -1203.9713 -350.96875",
145 | "QAngle": "0 1.2586212 0",
146 | "Team": 2,
147 | "Bombsite": 1,
148 | "CanBePlanter": true
149 | },
150 | {
151 | "Vector": "175.32149 10.643065 -215.96872",
152 | "QAngle": "0 -90.558586 0",
153 | "Team": 3,
154 | "Bombsite": 1,
155 | "CanBePlanter": false
156 | },
157 | {
158 | "Vector": "1663.8279 -1019.1846 -327.78796",
159 | "QAngle": "0 -125.21599 0",
160 | "Team": 3,
161 | "Bombsite": 1,
162 | "CanBePlanter": false
163 | },
164 | {
165 | "Vector": "508.98727 -548.0317 -210.13342",
166 | "QAngle": "0 -29.438553 0",
167 | "Team": 3,
168 | "Bombsite": 1,
169 | "CanBePlanter": false
170 | },
171 | {
172 | "Vector": "446.56094 -661.15765 -215.96875",
173 | "QAngle": "0 -0.813324 0",
174 | "Team": 3,
175 | "Bombsite": 1,
176 | "CanBePlanter": false
177 | },
178 | {
179 | "Vector": "1713.3424 -1390.0256 -326.969",
180 | "QAngle": "0 -142.32101 0",
181 | "Team": 3,
182 | "Bombsite": 1,
183 | "CanBePlanter": false
184 | },
185 | {
186 | "Vector": "-331.3629 -152.44049 -215.96875",
187 | "QAngle": "0 -125.80891 0",
188 | "Team": 3,
189 | "Bombsite": 1,
190 | "CanBePlanter": false
191 | },
192 | {
193 | "Vector": "-578.0866 -347.74524 -213.46875",
194 | "QAngle": "0 179.88397 0",
195 | "Team": 3,
196 | "Bombsite": 1,
197 | "CanBePlanter": false
198 | },
199 | {
200 | "Vector": "310.09763 1625.2981 -215.96875",
201 | "QAngle": "0 -2.0197754 0",
202 | "Team": 3,
203 | "Bombsite": 0,
204 | "CanBePlanter": false
205 | },
206 | {
207 | "Vector": "1500.1053 407.40268 -211.96875",
208 | "QAngle": "0 159.29626 0",
209 | "Team": 2,
210 | "Bombsite": 0,
211 | "CanBePlanter": false
212 | },
213 | {
214 | "Vector": "1288.9019 -881.8668 -320.96875",
215 | "QAngle": "0 179.5087 0",
216 | "Team": 3,
217 | "Bombsite": 0,
218 | "CanBePlanter": false
219 | },
220 | {
221 | "Vector": "-183.92128 -891.17664 -318.96875",
222 | "QAngle": "0 7.8030396 0",
223 | "Team": 3,
224 | "Bombsite": 0,
225 | "CanBePlanter": false
226 | },
227 | {
228 | "Vector": "419.41 1695.32 -213.96753",
229 | "QAngle": "0 -159.70 0",
230 | "Team": 3,
231 | "Bombsite": 0,
232 | "CanBePlanter": false
233 | },
234 | {
235 | "Vector": "969.0126 54.807076 -211.73273",
236 | "QAngle": "0 146.66406 0",
237 | "Team": 2,
238 | "Bombsite": 0,
239 | "CanBePlanter": false
240 | },
241 | {
242 | "Vector": "853.34656 -207.24608 -211.96875",
243 | "QAngle": "0 27.79129 0",
244 | "Team": 2,
245 | "Bombsite": 0,
246 | "CanBePlanter": false
247 | },
248 | {
249 | "Vector": "697.96875 -394.9673 -207.96875",
250 | "QAngle": "0 132.99911 0",
251 | "Team": 2,
252 | "Bombsite": 0,
253 | "CanBePlanter": false
254 | },
255 | {
256 | "Vector": "-970.47906 736.40625 -0.013118744",
257 | "QAngle": "0 -90.05699 0",
258 | "Team": 3,
259 | "Bombsite": 1,
260 | "CanBePlanter": false
261 | },
262 | {
263 | "Vector": "-601.53766 467.045 16.03125",
264 | "QAngle": "0 -97.439804 0",
265 | "Team": 3,
266 | "Bombsite": 1,
267 | "CanBePlanter": false
268 | },
269 | {
270 | "Vector": "-189.8856 -1759.5509 -166.96875",
271 | "QAngle": "0 64.80731 0",
272 | "Team": 2,
273 | "Bombsite": 1,
274 | "CanBePlanter": false
275 | },
276 | {
277 | "Vector": "1738.521 -1655.278 -320.35007",
278 | "QAngle": "0 -177.37633 0",
279 | "Team": 3,
280 | "Bombsite": 1,
281 | "CanBePlanter": false
282 | },
283 | {
284 | "Vector": "1381.968 -946.7372 -319.09604",
285 | "QAngle": "0 168.67722 0",
286 | "Team": 3,
287 | "Bombsite": 1,
288 | "CanBePlanter": false
289 | },
290 | {
291 | "Vector": "-42.018127 -1442.7357 -350.96875",
292 | "QAngle": "0 2.7002258 0",
293 | "Team": 2,
294 | "Bombsite": 1,
295 | "CanBePlanter": false
296 | },
297 | {
298 | "Vector": "-876.7882 -1780.6874 -153.96875",
299 | "QAngle": "0 93.76489 0",
300 | "Team": 2,
301 | "Bombsite": 1,
302 | "CanBePlanter": false
303 | },
304 | {
305 | "Vector": "-483.96826 -1042.182 -350.96875",
306 | "QAngle": "0 -3.0037231 0",
307 | "Team": 2,
308 | "Bombsite": 1,
309 | "CanBePlanter": false
310 | }
311 | ]
312 | }
313 |
--------------------------------------------------------------------------------
/RetakesPlugin/map_config/de_vertigo.json:
--------------------------------------------------------------------------------
1 | {
2 | "Spawns": [
3 | {
4 | "Vector": "-1,538.83 -471.80 11,488.03",
5 | "QAngle": "0.00 53.54 0.00",
6 | "Team": 3,
7 | "Bombsite": 0,
8 | "CanBePlanter": false
9 | },
10 | {
11 | "Vector": "-1,359.65 -459.51 11,488.03",
12 | "QAngle": "0.00 80.63 0.00",
13 | "Team": 3,
14 | "Bombsite": 0,
15 | "CanBePlanter": false
16 | },
17 | {
18 | "Vector": "-1,470.26 -174.25 11,488.03",
19 | "QAngle": "0.00 12.38 0.00",
20 | "Team": 3,
21 | "Bombsite": 0,
22 | "CanBePlanter": false
23 | },
24 | {
25 | "Vector": "-1,688.27 212.41 11,776.03",
26 | "QAngle": "0.00 -10.99 0.00",
27 | "Team": 3,
28 | "Bombsite": 0,
29 | "CanBePlanter": false
30 | },
31 | {
32 | "Vector": "-1,616.42 412.66 11,776.03",
33 | "QAngle": "0.00 -28.74 0.00",
34 | "Team": 3,
35 | "Bombsite": 0,
36 | "CanBePlanter": false
37 | },
38 | {
39 | "Vector": "-850.09 772.83 11,776.03",
40 | "QAngle": "0.00 -68.73 0.00",
41 | "Team": 3,
42 | "Bombsite": 0,
43 | "CanBePlanter": false
44 | },
45 | {
46 | "Vector": "-721.03 593.25 11,776.03",
47 | "QAngle": "0.00 -80.79 0.00",
48 | "Team": 3,
49 | "Bombsite": 0,
50 | "CanBePlanter": false
51 | },
52 | {
53 | "Vector": "-114.03 -540.03 11,776.03",
54 | "QAngle": "0.00 -114.19 0.00",
55 | "Team": 2,
56 | "Bombsite": 0,
57 | "CanBePlanter": true
58 | },
59 | {
60 | "Vector": "-114.26 -708.79 11,776.03",
61 | "QAngle": "0.00 -174.81 0.00",
62 | "Team": 2,
63 | "Bombsite": 0,
64 | "CanBePlanter": true
65 | },
66 | {
67 | "Vector": "-347.03 -682.82 11,774.13",
68 | "QAngle": "0.00 -89.35 0.00",
69 | "Team": 2,
70 | "Bombsite": 0,
71 | "CanBePlanter": true
72 | },
73 | {
74 | "Vector": "-1,088.85 -605.93 11,776.03",
75 | "QAngle": "0.00 -90.96 0.00",
76 | "Team": 2,
77 | "Bombsite": 0,
78 | "CanBePlanter": false
79 | },
80 | {
81 | "Vector": "-132.62 -922.37 11,776.03",
82 | "QAngle": "0.00 -146.10 0.00",
83 | "Team": 2,
84 | "Bombsite": 0,
85 | "CanBePlanter": false
86 | },
87 | {
88 | "Vector": "-94.62 -1,454.97 11,776.03",
89 | "QAngle": "0.00 141.09 0.00",
90 | "Team": 2,
91 | "Bombsite": 0,
92 | "CanBePlanter": false
93 | },
94 | {
95 | "Vector": "-306.82 -1,245.42 11,764.43",
96 | "QAngle": "0.00 161.18 0.00",
97 | "Team": 2,
98 | "Bombsite": 0,
99 | "CanBePlanter": false
100 | },
101 | {
102 | "Vector": "-949.32 303.59 11,776.03",
103 | "QAngle": "0.00 -87.52 0.00",
104 | "Team": 3,
105 | "Bombsite": 0,
106 | "CanBePlanter": false
107 | },
108 | {
109 | "Vector": "-1,712.84 -557.14 11,552.03",
110 | "QAngle": "0.00 159.76 0.00",
111 | "Team": 3,
112 | "Bombsite": 1,
113 | "CanBePlanter": false
114 | },
115 | {
116 | "Vector": "-1,716.41 -358.35 11,521.61",
117 | "QAngle": "0.00 60.74 0.00",
118 | "Team": 3,
119 | "Bombsite": 0,
120 | "CanBePlanter": false
121 | },
122 | {
123 | "Vector": "-2,250.37 -494.91 11,488.03",
124 | "QAngle": "0.00 9.88 0.00",
125 | "Team": 3,
126 | "Bombsite": 1,
127 | "CanBePlanter": false
128 | },
129 | {
130 | "Vector": "-2,127.21 -568.09 11,512.43",
131 | "QAngle": "0.00 48.91 0.00",
132 | "Team": 3,
133 | "Bombsite": 1,
134 | "CanBePlanter": false
135 | },
136 | {
137 | "Vector": "-1,975.13 -536.34 11,552.03",
138 | "QAngle": "0.00 88.17 0.00",
139 | "Team": 3,
140 | "Bombsite": 1,
141 | "CanBePlanter": false
142 | },
143 | {
144 | "Vector": "-725.01 600.79 11,776.03",
145 | "QAngle": "0.00 152.23 0.00",
146 | "Team": 3,
147 | "Bombsite": 1,
148 | "CanBePlanter": false
149 | },
150 | {
151 | "Vector": "-1,006.14 490.74 11,776.03",
152 | "QAngle": "0.00 119.92 0.00",
153 | "Team": 3,
154 | "Bombsite": 1,
155 | "CanBePlanter": false
156 | },
157 | {
158 | "Vector": "-985.64 178.04 11,776.03",
159 | "QAngle": "0.00 154.84 0.00",
160 | "Team": 3,
161 | "Bombsite": 1,
162 | "CanBePlanter": false
163 | },
164 | {
165 | "Vector": "-980.33 14.97 11,776.03",
166 | "QAngle": "0.00 136.64 0.00",
167 | "Team": 3,
168 | "Bombsite": 1,
169 | "CanBePlanter": false
170 | },
171 | {
172 | "Vector": "-1,232.30 120.59 11,776.03",
173 | "QAngle": "0.00 122.42 0.00",
174 | "Team": 3,
175 | "Bombsite": 1,
176 | "CanBePlanter": false
177 | },
178 | {
179 | "Vector": "-1,885.15 216.14 11,776.03",
180 | "QAngle": "0.00 30.73 0.00",
181 | "Team": 3,
182 | "Bombsite": 1,
183 | "CanBePlanter": false
184 | },
185 | {
186 | "Vector": "-1,914.40 82.53 11,776.03",
187 | "QAngle": "0.00 42.34 0.00",
188 | "Team": 3,
189 | "Bombsite": 1,
190 | "CanBePlanter": false
191 | },
192 | {
193 | "Vector": "-2,341.19 -718.18 11,488.03",
194 | "QAngle": "0.00 41.27 0.00",
195 | "Team": 3,
196 | "Bombsite": 1,
197 | "CanBePlanter": false
198 | },
199 | {
200 | "Vector": "-2,610.02 509.38 11,752.14",
201 | "QAngle": "0.00 -64.29 0.00",
202 | "Team": 2,
203 | "Bombsite": 1,
204 | "CanBePlanter": false
205 | },
206 | {
207 | "Vector": "-2,146.03 669.57 11,744.03",
208 | "QAngle": "0.00 -13.23 0.00",
209 | "Team": 2,
210 | "Bombsite": 1,
211 | "CanBePlanter": true
212 | },
213 | {
214 | "Vector": "-2,216.03 865.05 11,744.03",
215 | "QAngle": "0.00 -4.80 0.00",
216 | "Team": 2,
217 | "Bombsite": 1,
218 | "CanBePlanter": true
219 | },
220 | {
221 | "Vector": "-2,353.50 927.36 11,744.03",
222 | "QAngle": "0.00 -95.54 0.00",
223 | "Team": 2,
224 | "Bombsite": 1,
225 | "CanBePlanter": true
226 | },
227 | {
228 | "Vector": "-2,516.83 1,036.03 11,747.60",
229 | "QAngle": "0.00 -1.15 0.00",
230 | "Team": 2,
231 | "Bombsite": 1,
232 | "CanBePlanter": false
233 | },
234 | {
235 | "Vector": "-2,506.29 814.71 11,744.03",
236 | "QAngle": "0.00 -77.92 0.00",
237 | "Team": 2,
238 | "Bombsite": 1,
239 | "CanBePlanter": false
240 | },
241 | {
242 | "Vector": "-2,153.03 407.41 11,776.03",
243 | "QAngle": "0.00 -128.05 0.00",
244 | "Team": 2,
245 | "Bombsite": 1,
246 | "CanBePlanter": false
247 | },
248 | {
249 | "Vector": "-2,608.66 891.99 11,751.97",
250 | "QAngle": "0.00 -11.97 0.00",
251 | "Team": 2,
252 | "Bombsite": 1,
253 | "CanBePlanter": false
254 | }
255 | ]
256 | }
--------------------------------------------------------------------------------
/RetakesPluginShared/.gitignore:
--------------------------------------------------------------------------------
1 | **/obj/
2 | **/obj/**/*.nuget.cache
3 | **/bin/
4 |
--------------------------------------------------------------------------------
/RetakesPluginShared/Enums/Bombsite.cs:
--------------------------------------------------------------------------------
1 | namespace RetakesPluginShared.Enums;
2 |
3 | public enum Bombsite
4 | {
5 | A = 0,
6 | B = 1
7 | }
8 |
--------------------------------------------------------------------------------
/RetakesPluginShared/Events/AllocateEvent.cs:
--------------------------------------------------------------------------------
1 | namespace RetakesPluginShared.Events;
2 |
3 | public record AllocateEvent() : IRetakesPluginEvent;
4 |
--------------------------------------------------------------------------------
/RetakesPluginShared/Events/AnnounceBombsiteEvent.cs:
--------------------------------------------------------------------------------
1 | using RetakesPluginShared.Enums;
2 |
3 | namespace RetakesPluginShared.Events;
4 |
5 | public record AnnounceBombsiteEvent(Bombsite Bombsite) : IRetakesPluginEvent;
6 |
--------------------------------------------------------------------------------
/RetakesPluginShared/Events/IRetakesPluginEvent.cs:
--------------------------------------------------------------------------------
1 | namespace RetakesPluginShared.Events;
2 |
3 | public interface IRetakesPluginEvent { }
4 |
--------------------------------------------------------------------------------
/RetakesPluginShared/IRetakesPluginEventSender.cs:
--------------------------------------------------------------------------------
1 | using RetakesPluginShared.Events;
2 |
3 | namespace RetakesPluginShared;
4 |
5 | public interface IRetakesPluginEventSender
6 | {
7 | public event EventHandler RetakesPluginEventHandlers;
8 | public void TriggerEvent(IRetakesPluginEvent @event);
9 | }
10 |
--------------------------------------------------------------------------------
/RetakesPluginShared/RetakesPluginShared.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | RetakesPluginShared
4 | RetakesPluginShared
5 |
6 | This library is made to run alongside https://github.com/b3none/cs2-retakes
7 |
8 | B3none
9 |
10 | 2.0.0
11 |
12 | net8.0
13 | enable
14 | enable
15 | RetakesPluginShared
16 | true
17 |
18 | README.md
19 | LICENSE
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------