├── LICENSE
├── README.md
└── src
├── maps
└── mp
│ └── gametypes
│ ├── _class.gsc
│ ├── _globallogic.gsc
│ ├── _menus.gsc
│ ├── _rank.gsc
│ └── war.gsc
└── settings
└── level55.gsc
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
341 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cod4-level-hack-mod
2 | Level Hack Server Mod for Call of Duty 4
3 | - You can see the mod in action: https://www.youtube.com/watch?v=JR1TWOGiYuE
4 |
5 | # Installation
6 | You need a CoD4X server to run this mod:
7 | http://www.cod4x.me/
8 |
9 | - Set up your server to run without mod
10 | - Add these additional gsc (in src/ folder) files to override the server-side scripts
11 | - Set up your server to run on TDM (g_gametype "war") and only on Crash (map mp_crash)
12 | - Enjoy your own level hack server!
13 |
14 | Optional:
15 | - You can modify the parameter of these functions: 'self tickprint("TEXT");' in "settings/level55.gsc" to place your own messages/ads/etc.
16 | - You **should NOT** remove parts where original author name is present! e.g. 'self iprintln("^3Level 55 mod by Shuffni!");'!
17 | - You **CAN** add yourself to authors if you want!
18 |
--------------------------------------------------------------------------------
/src/maps/mp/gametypes/_class.gsc:
--------------------------------------------------------------------------------
1 | // _____ _ __ __ _
2 | // / ____| | / _|/ _| (_)
3 | // | (___ | |__ _ _| |_| |_ _ __ _
4 | // \___ \| '_ \| | | | _| _| '_ \| |
5 | // ____) | | | | |_| | | | | | | | | |
6 | // |_____/|_| |_|\__,_|_| |_| |_| |_|_|
7 | // Level Hack Server Mod
8 | //
9 | // This file is part of Shuffni's level hack server mod.
10 | //
11 | // This is free software: you can redistribute it and/or modify
12 | // it under the terms of the GNU General Public License as published by
13 | // the Free Software Foundation, either version 3 of the License, or
14 | // (at your option) any later version.
15 | //
16 | // This is distributed in the hope that it will be useful,
17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | // GNU General Public License for more details.
20 | //
21 | // You should have received a copy of the GNU General Public License
22 | // along with this software. If not, see .
23 |
24 |
25 | #include common_scripts\utility;
26 | // check if below includes are removable
27 | #include maps\mp\_utility;
28 | #include maps\mp\gametypes\_hud_util;
29 |
30 | init()
31 | {
32 | level.classMap["assault_mp"] = "CLASS_ASSAULT";
33 | level.classMap["specops_mp"] = "CLASS_SPECOPS";
34 | level.classMap["heavygunner_mp"] = "CLASS_HEAVYGUNNER";
35 | level.classMap["demolitions_mp"] = "CLASS_DEMOLITIONS";
36 | level.classMap["sniper_mp"] = "CLASS_SNIPER";
37 |
38 | level.classMap["offline_class1_mp"] = "OFFLINE_CLASS1";
39 | level.classMap["offline_class2_mp"] = "OFFLINE_CLASS2";
40 | level.classMap["offline_class3_mp"] = "OFFLINE_CLASS3";
41 | level.classMap["offline_class4_mp"] = "OFFLINE_CLASS4";
42 | level.classMap["offline_class5_mp"] = "OFFLINE_CLASS5";
43 | level.classMap["offline_class6_mp"] = "OFFLINE_CLASS6";
44 | level.classMap["offline_class7_mp"] = "OFFLINE_CLASS7";
45 | level.classMap["offline_class8_mp"] = "OFFLINE_CLASS8";
46 | level.classMap["offline_class9_mp"] = "OFFLINE_CLASS9";
47 | level.classMap["offline_class10_mp"] = "OFFLINE_CLASS10";
48 |
49 | level.classMap["custom1"] = "CLASS_CUSTOM1";
50 | level.classMap["custom2"] = "CLASS_CUSTOM2";
51 | level.classMap["custom3"] = "CLASS_CUSTOM3";
52 | level.classMap["custom4"] = "CLASS_CUSTOM4";
53 | level.classMap["custom5"] = "CLASS_CUSTOM5";
54 |
55 | if ( level.onlineGame )
56 | level.defaultClass = "CLASS_ASSAULT";
57 | else
58 | level.defaultClass = "OFFLINE_CLASS1";
59 |
60 | level.weapons["frag"] = "frag_grenade_mp";
61 | level.weapons["smoke"] = "smoke_grenade_mp";
62 | level.weapons["flash"] = "flash_grenade_mp";
63 | level.weapons["concussion"] = "concussion_grenade_mp";
64 | level.weapons["c4"] = "c4_mp";
65 | level.weapons["claymore"] = "claymore_mp";
66 | level.weapons["rpg"] = "rpg_mp";
67 |
68 | // initializes create a class settings
69 | cac_init();
70 |
71 | // default class weapon loadout for offline mode
72 | // param( team, class, stat number, inventory string, inventory count )
73 |
74 | offline_class_datatable = "mp/offline_classTable.csv";
75 |
76 | load_default_loadout( offline_class_datatable, "both", "OFFLINE_CLASS1", 200 );
77 | load_default_loadout( offline_class_datatable, "both", "OFFLINE_CLASS2", 210 );
78 | load_default_loadout( offline_class_datatable, "both", "OFFLINE_CLASS3", 220 );
79 | load_default_loadout( offline_class_datatable, "both", "OFFLINE_CLASS4", 230 );
80 | load_default_loadout( offline_class_datatable, "both", "OFFLINE_CLASS5", 240 );
81 | load_default_loadout( offline_class_datatable, "both", "OFFLINE_CLASS6", 250 );
82 | load_default_loadout( offline_class_datatable, "both", "OFFLINE_CLASS7", 260 );
83 | load_default_loadout( offline_class_datatable, "both", "OFFLINE_CLASS8", 270 );
84 | load_default_loadout( offline_class_datatable, "both", "OFFLINE_CLASS9", 280 );
85 | load_default_loadout( offline_class_datatable, "both", "OFFLINE_CLASS10", 290 );
86 |
87 | online_class_datatable = "mp/classTable.csv";
88 |
89 | load_default_loadout( online_class_datatable, "both", "CLASS_ASSAULT", 200 ); // assault
90 | load_default_loadout( online_class_datatable, "both", "CLASS_SPECOPS", 210 ); // spec ops
91 | load_default_loadout( online_class_datatable, "both", "CLASS_HEAVYGUNNER", 220 ); // heavy gunner
92 | load_default_loadout( online_class_datatable, "both", "CLASS_DEMOLITIONS", 230 ); // demolitions
93 | load_default_loadout( online_class_datatable, "both", "CLASS_SNIPER", 240 ); // sniper
94 |
95 | // generating weapon type arrays which classifies the weapon as primary (back stow), pistol, or inventory (side pack stow)
96 | // using mp/statstable.csv's weapon grouping data ( numbering 0 - 149 )
97 | level.primary_weapon_array = [];
98 | level.side_arm_array = [];
99 | level.grenade_array = [];
100 | level.inventory_array = [];
101 | max_weapon_num = 149;
102 | for( i = 0; i < max_weapon_num; i++ )
103 | {
104 | if( !isdefined( level.tbl_weaponIDs[i] ) || level.tbl_weaponIDs[i]["group"] == "" )
105 | continue;
106 | if( !isdefined( level.tbl_weaponIDs[i] ) || level.tbl_weaponIDs[i]["reference"] == "" )
107 | continue;
108 |
109 | //statstablelookup( get_col, with_col, with_data )
110 | weapon_type = level.tbl_weaponIDs[i]["group"]; //statstablelookup( level.cac_cgroup, level.cac_cstat, i );
111 | weapon = level.tbl_weaponIDs[i]["reference"]; //statstablelookup( level.cac_creference, level.cac_cstat, i );
112 | attachment = level.tbl_weaponIDs[i]["attachment"]; //statstablelookup( level.cac_cstring, level.cac_cstat, i );
113 |
114 | weapon_class_register( weapon+"_mp", weapon_type );
115 |
116 | if( isdefined( attachment ) && attachment != "" )
117 | {
118 | attachment_tokens = strtok( attachment, " " );
119 | if( isdefined( attachment_tokens ) )
120 | {
121 | if( attachment_tokens.size == 0 )
122 | weapon_class_register( weapon+"_"+attachment+"_mp", weapon_type );
123 | else
124 | {
125 | // multiple attachment options
126 | for( k = 0; k < attachment_tokens.size; k++ )
127 | weapon_class_register( weapon+"_"+attachment_tokens[k]+"_mp", weapon_type );
128 | }
129 | }
130 | }
131 | }
132 |
133 | precacheShader( "waypoint_bombsquad" );
134 |
135 | level thread onPlayerConnecting();
136 | }
137 |
138 | // assigns default class loadout to team from datatable
139 | load_default_loadout( datatable, team, class, stat_num )
140 | {
141 | if( team == "both" )
142 | {
143 | // do not thread, tablelookup is demanding
144 | load_default_loadout_raw( datatable, "allies", class, stat_num );
145 | load_default_loadout_raw( datatable, "axis", class, stat_num );
146 | }
147 | else
148 | load_default_loadout_raw( datatable, team, class, stat_num );
149 | }
150 |
151 | load_default_loadout_raw( class_dataTable, team, class, stat_num )
152 | {
153 | // give primary weapon and attachment
154 | primary_attachment = tablelookup( class_dataTable, 1, stat_num + 2, 4 );
155 | if( primary_attachment != "" && primary_attachment != "none" )
156 | level.classWeapons[team][class][0] = tablelookup( class_dataTable, 1, stat_num + 1, 4 ) + "_" + primary_attachment + "_mp";
157 | else
158 | level.classWeapons[team][class][0] = tablelookup( class_dataTable, 1, stat_num + 1, 4 ) + "_mp";
159 |
160 | // give secondary weapon and attachment
161 | secondary_attachment = tablelookup( class_dataTable, 1, stat_num + 4, 4 );
162 | if( secondary_attachment != "" && secondary_attachment != "none" )
163 | level.classSidearm[team][class] = tablelookup( class_dataTable, 1, stat_num + 3, 4 ) + "_" + secondary_attachment + "_mp";
164 | else
165 | level.classSidearm[team][class] = tablelookup( class_dataTable, 1, stat_num + 3, 4 ) + "_mp";
166 |
167 | // give frag and special grenades
168 | level.classGrenades[class]["primary"]["type"] = tablelookup( class_dataTable, 1, stat_num, 4 ) + "_mp";
169 | level.classGrenades[class]["primary"]["count"] = int( tablelookup( class_dataTable, 1, stat_num, 6 ) );
170 | level.classGrenades[class]["secondary"]["type"] = tablelookup( class_dataTable, 1, stat_num + 8, 4 ) + "_mp";
171 | level.classGrenades[class]["secondary"]["count"] = int( tablelookup( class_dataTable, 1, stat_num + 8, 6 ) );
172 |
173 | // give default class perks
174 | level.default_perk[class] = [];
175 | level.default_perk[class][0] = tablelookup( class_dataTable, 1, stat_num + 5, 4 );
176 | level.default_perk[class][1] = tablelookup( class_dataTable, 1, stat_num + 6, 4 );
177 | level.default_perk[class][2] = tablelookup( class_dataTable, 1, stat_num + 7, 4 );
178 |
179 | // give all inventory
180 | inventory_ref = tablelookup( class_dataTable, 1, stat_num + 5, 4 );
181 | if( isdefined( inventory_ref ) && tablelookup( "mp/statsTable.csv", 6, inventory_ref, 2 ) == "inventory" )
182 | {
183 | inventory_count = int( tablelookup( "mp/statsTable.csv", 6, inventory_ref, 5 ) );
184 | inventory_item_ref = tablelookup( "mp/statsTable.csv", 6, inventory_ref, 4 );
185 | assertex( isdefined( inventory_count ) && inventory_count != 0 && isdefined( inventory_item_ref ) && inventory_item_ref != "" , "Inventory in statsTable.csv not specified correctly" );
186 |
187 | level.classItem[team][class]["type"] = inventory_item_ref;
188 | level.classItem[team][class]["count"] = inventory_count;
189 | }
190 | else
191 | {
192 | level.classItem[team][class]["type"] = "";
193 | level.classItem[team][class]["count"] = 0;
194 | }
195 | // give all inventory
196 | //level.classItem[team][class]["type"] = inventory;
197 | //level.classItem[team][class]["count"] = inv_count;
198 | }
199 |
200 | weapon_class_register( weapon, weapon_type )
201 | {
202 | if( isSubstr( "weapon_smg weapon_assault weapon_projectile weapon_sniper weapon_shotgun weapon_lmg", weapon_type ) )
203 | level.primary_weapon_array[weapon] = 1;
204 | else if( weapon_type == "weapon_pistol" )
205 | level.side_arm_array[weapon] = 1;
206 | else if( weapon_type == "weapon_grenade" )
207 | level.grenade_array[weapon] = 1;
208 | else if( weapon_type == "weapon_explosive" )
209 | level.inventory_array[weapon] = 1;
210 | else
211 | assertex( false, "Weapon group info is missing from statsTable for: " + weapon_type );
212 | }
213 |
214 | // create a class init
215 | cac_init()
216 | {
217 | // max create a class "class" allowed
218 | level.cac_size = 5;
219 |
220 | // init cac data table column definitions
221 | level.cac_numbering = 0; // unique unsigned int - general numbering of all items
222 | level.cac_cstat = 1; // unique unsigned int - stat number assigned
223 | level.cac_cgroup = 2; // string - item group name, "primary" "secondary" "inventory" "specialty" "grenades" "special grenades" "stow back" "stow side" "attachment"
224 | level.cac_cname = 3; // string - name of the item, "Extreme Conditioning"
225 | level.cac_creference = 4; // string - reference string of the item, "m203" "m16" "bulletdamage" "c4"
226 | level.cac_ccount = 5; // signed int - item count, if exists, -1 = has no count
227 | level.cac_cimage = 6; // string - item's image file name
228 | level.cac_cdesc = 7; // long string - item's description
229 | level.cac_cstring = 8; // long string - item's other string data, reserved
230 | level.cac_cint = 9; // signed int - item's other number data, used for attachment number representations
231 | level.cac_cunlock = 10; // unsigned int - represents if item is unlocked by default
232 | level.cac_cint2 = 11; // signed int - item's other number data, used for primary weapon camo skin number representations
233 |
234 | // generating camo/attachment data vars collected from attachmentTable.csv
235 | level.tbl_CamoSkin = [];
236 | for( i=0; i<8; i++ )
237 | {
238 | // this for-loop is shared because there are equal number of attachments and camo skins.
239 | level.tbl_CamoSkin[i]["bitmask"] = int( tableLookup( "mp/attachmentTable.csv", 11, i, 10 ) );
240 |
241 | level.tbl_WeaponAttachment[i]["bitmask"] = int( tableLookup( "mp/attachmentTable.csv", 9, i, 10 ) );
242 | level.tbl_WeaponAttachment[i]["reference"] = tableLookup( "mp/attachmentTable.csv", 9, i, 4 );
243 | }
244 |
245 | level.tbl_weaponIDs = [];
246 | for( i=0; i<150; i++ )
247 | {
248 | reference_s = tableLookup( "mp/statsTable.csv", 0, i, 4 );
249 | if( reference_s != "" )
250 | {
251 | level.tbl_weaponIDs[i]["reference"] = reference_s;
252 | level.tbl_weaponIDs[i]["group"] = tablelookup( "mp/statstable.csv", 0, i, 2 );
253 | level.tbl_weaponIDs[i]["count"] = int( tablelookup( "mp/statstable.csv", 0, i, 5 ) );
254 | level.tbl_weaponIDs[i]["attachment"] = tablelookup( "mp/statstable.csv", 0, i, 8 );
255 | }
256 | else
257 | continue;
258 | }
259 |
260 | perkReferenceToIndex = [];
261 |
262 | level.perkNames = [];
263 | level.perkIcons = [];
264 | level.PerkData = [];
265 | // generating perk data vars collected form statsTable.csv
266 | for( i=150; i<194; i++ )
267 | {
268 | reference_s = tableLookup( "mp/statsTable.csv", 0, i, 4 );
269 | if( reference_s != "" )
270 | {
271 | level.tbl_PerkData[i]["reference"] = reference_s;
272 | level.tbl_PerkData[i]["reference_full"] = tableLookup( "mp/statsTable.csv", 0, i, 6 );
273 | level.tbl_PerkData[i]["count"] = int( tableLookup( "mp/statsTable.csv", 0, i, 5 ) );
274 | level.tbl_PerkData[i]["group"] = tableLookup( "mp/statsTable.csv", 0, i, 2 );
275 | level.tbl_PerkData[i]["name"] = tableLookupIString( "mp/statsTable.csv", 0, i, 3 );
276 | precacheString( level.tbl_PerkData[i]["name"] );
277 | level.tbl_PerkData[i]["perk_num"] = tableLookup( "mp/statsTable.csv", 0, i, 8 );
278 |
279 | perkReferenceToIndex[ level.tbl_PerkData[i]["reference_full"] ] = i;
280 |
281 | level.perkNames[level.tbl_PerkData[i]["reference_full"]] = level.tbl_PerkData[i]["name"];
282 | level.perkIcons[level.tbl_PerkData[i]["reference_full"]] = level.tbl_PerkData[i]["reference_full"];
283 | precacheShader( level.perkIcons[level.tbl_PerkData[i]["reference_full"]] );
284 | }
285 | else
286 | continue;
287 | }
288 |
289 | // allowed perks in each slot, for validation.
290 | level.allowedPerks[0] = [];
291 | level.allowedPerks[1] = [];
292 | level.allowedPerks[2] = [];
293 |
294 | level.allowedPerks[0][ 0] = 190; // 190 through 193 are attachments and "none"
295 | level.allowedPerks[0][ 1] = 191;
296 | level.allowedPerks[0][ 2] = 192;
297 | level.allowedPerks[0][ 3] = 193;
298 | level.allowedPerks[0][ 4] = perkReferenceToIndex[ "specialty_weapon_c4" ];
299 | level.allowedPerks[0][ 5] = perkReferenceToIndex[ "specialty_specialgrenade" ];
300 | level.allowedPerks[0][ 6] = perkReferenceToIndex[ "specialty_weapon_rpg" ];
301 | level.allowedPerks[0][ 7] = perkReferenceToIndex[ "specialty_weapon_claymore" ];
302 | level.allowedPerks[0][ 8] = perkReferenceToIndex[ "specialty_fraggrenade" ];
303 | level.allowedPerks[0][ 9] = perkReferenceToIndex[ "specialty_extraammo" ];
304 | level.allowedPerks[0][10] = perkReferenceToIndex[ "specialty_detectexplosive" ];
305 |
306 | level.allowedPerks[1][ 0] = 190;
307 | level.allowedPerks[1][ 1] = perkReferenceToIndex[ "specialty_bulletdamage" ];
308 | level.allowedPerks[1][ 2] = perkReferenceToIndex[ "specialty_armorvest" ];
309 | level.allowedPerks[1][ 3] = perkReferenceToIndex[ "specialty_fastreload" ];
310 | level.allowedPerks[1][ 4] = perkReferenceToIndex[ "specialty_rof" ];
311 | level.allowedPerks[1][ 5] = perkReferenceToIndex[ "specialty_twoprimaries" ];
312 | level.allowedPerks[1][ 6] = perkReferenceToIndex[ "specialty_gpsjammer" ];
313 | level.allowedPerks[1][ 7] = perkReferenceToIndex[ "specialty_explosivedamage" ];
314 |
315 | level.allowedPerks[2][ 0] = 190;
316 | level.allowedPerks[2][ 1] = perkReferenceToIndex[ "specialty_longersprint" ];
317 | level.allowedPerks[2][ 2] = perkReferenceToIndex[ "specialty_bulletaccuracy" ];
318 | level.allowedPerks[2][ 3] = perkReferenceToIndex[ "specialty_pistoldeath" ];
319 | level.allowedPerks[2][ 4] = perkReferenceToIndex[ "specialty_grenadepulldeath" ];
320 | level.allowedPerks[2][ 5] = perkReferenceToIndex[ "specialty_bulletpenetration" ];
321 | level.allowedPerks[2][ 6] = perkReferenceToIndex[ "specialty_holdbreath" ];
322 | level.allowedPerks[2][ 7] = perkReferenceToIndex[ "specialty_quieter" ];
323 | level.allowedPerks[2][ 8] = perkReferenceToIndex[ "specialty_parabolic" ];
324 | }
325 |
326 | getClassChoice( response )
327 | {
328 | tokens = strtok( response, "," );
329 |
330 | assert( isDefined( level.classMap[tokens[0]] ) );
331 |
332 | return ( level.classMap[tokens[0]] );
333 | }
334 |
335 | getWeaponChoice( response )
336 | {
337 | tokens = strtok( response, "," );
338 | if ( tokens.size > 1 )
339 | return int(tokens[1]);
340 | else
341 | return 0;
342 | }
343 |
344 | // ============================================================================
345 | // obtains custom class setup from stat values
346 | cac_getdata()
347 | {
348 | if ( isDefined( self.cac_initialized ) )
349 | return;
350 |
351 | /* custom class stat allocation order, example of custom class slot 1
352 | 201 weapon_primary
353 | 202 weapon_primary attachment
354 | 203 weapon_secondary
355 | 204 weapon_secondary attachment
356 | 205 weapon_specialty1
357 | 206 weapon_specialty2
358 | 207 weapon_specialty3
359 | 208 weapon_special_grenade_type
360 | 209 weapon_primary_camo_style
361 | */
362 |
363 | for( i = 0; i < 5; i ++ )
364 | {
365 | //assertex( self getstat ( i*10+200 ) == 1, "Custom class not initialized!" );
366 |
367 | // do not change the allocation and assignment of 0-299 stat bytes, or data will be misinterpreted by this function!
368 | primary_num = self getstat ( 200+(i*10)+1 ); // returns weapon number (also the unlock stat number from data table)
369 | primary_attachment_flag = self getstat ( 200+(i*10)+2 ); // returns attachment number (from data table)
370 | if ( !isDefined( level.tbl_WeaponAttachment[primary_attachment_flag] ) ) // handle bad attachment stat
371 | primary_attachment_flag = 0;
372 | primary_attachment_mask = level.tbl_WeaponAttachment[primary_attachment_flag]["bitmask"];
373 | secondary_num = self getstat ( 200+(i*10)+3 ); // returns weapon number (also the unlock stat number from data table)
374 | secondary_attachment_flag = self getstat ( 200+(i*10)+4 ); // returns attachment number (from data table)
375 | if ( !isDefined( level.tbl_WeaponAttachment[secondary_attachment_flag] ) ) // handle bad attachment stat
376 | secondary_attachment_flag = 0;
377 | secondary_attachment_mask = level.tbl_WeaponAttachment[secondary_attachment_flag]["bitmask"];
378 | specialty1 = self getstat ( 200+(i*10)+5 ); // returns specialty number (from data table)
379 | specialty2 = self getstat ( 200+(i*10)+6 ); // returns specialty number (from data table)
380 | specialty3 = self getstat ( 200+(i*10)+7 ); // returns specialty number (from data table)
381 | special_grenade = self getstat ( 200+(i*10)+8 ); // returns special grenade type as single special grenade items (from data table)
382 | camo_num = self getstat ( 200+(i*10)+9 ); // returns camo number (from data table)
383 |
384 | if ( camo_num < 0 || camo_num >= level.tbl_CamoSkin.size )
385 | {
386 | println( "^1Warning: (" + self.name + ") camo " + camo_num + " is invalid. Setting to none." );
387 | camo_num = 0;
388 | }
389 |
390 | camo_mask = level.tbl_CamoSkin[camo_num]["bitmask"];
391 |
392 | m16WeaponIndex = 25;
393 | assert( level.tbl_weaponIDs[m16WeaponIndex]["reference"] == "m16" );
394 | if ( primary_num < 0 || !isDefined( level.tbl_weaponIDs[ primary_num ] ) )
395 | {
396 | primary_num = m16WeaponIndex;
397 | primary_attachment_flag = 0;
398 | }
399 | if ( secondary_num < 0 || !isDefined( level.tbl_weaponIDs[ secondary_num ] ) )
400 | {
401 | secondary_num = 0;
402 | secondary_attachment_flag = 0;
403 | }
404 |
405 | specialty1 = validatePerk( specialty1, 0 );
406 | specialty2 = validatePerk( specialty2, 1 );
407 | specialty3 = validatePerk( specialty3, 2 );
408 |
409 | // if specialty2 is not Overkill, disallow anything besides pistols for secondary weapon
410 | if ( level.tbl_PerkData[specialty2]["reference_full"] != "specialty_twoprimaries" )
411 | {
412 | if ( level.tbl_weaponIDs[secondary_num]["group"] != "weapon_pistol" )
413 | {
414 | println( "^1Warning: (" + self.name + ") secondary weapon is not a pistol but perk 2 is not Overkill. Setting secondary weapon to pistol." );
415 | secondary_num = 0;
416 | secondary_attachment_flag = 0;
417 | }
418 | }
419 | // if certain attachments are used, make sure specialty1 is set right
420 | primary_attachment_ref = level.tbl_WeaponAttachment[primary_attachment_flag]["reference"];
421 | secondary_attachment_ref = level.tbl_WeaponAttachment[secondary_attachment_flag]["reference"];
422 | if ( primary_attachment_ref == "grip" || primary_attachment_ref == "gl" || secondary_attachment_ref == "grip" || secondary_attachment_ref == "gl" )
423 | {
424 | if ( specialty1 != 190 && specialty1 != 191 && specialty1 != 192 && specialty1 != 193 )
425 | {
426 | println( "^1Warning: (" + self.name + ") grip or grenade launcher is used but perk 1 was index " + specialty1 + ". Setting perk 1 to none." );
427 | specialty1 = 193; // 193 = there's an attachment, so no perk
428 | }
429 | }
430 |
431 | // validate weapon attachments, if faulty attachement found, reset to no attachments
432 | primary_ref = level.tbl_WeaponIDs[primary_num]["reference"];
433 | primary_attachment_set = level.tbl_weaponIDs[primary_num]["attachment"];
434 | secondary_ref = level.tbl_WeaponIDs[secondary_num]["reference"];
435 | secondary_attachment_set = level.tbl_weaponIDs[secondary_num]["attachment"];
436 | if ( !issubstr( primary_attachment_set, primary_attachment_ref ) )
437 | {
438 | println( "^1Warning: (" + self.name + ") attachment [" + primary_attachment_ref + "] is not valid for [" + primary_ref + "]. Removing attachment." );
439 | primary_attachment_flag = 0;
440 | }
441 | if ( !issubstr( secondary_attachment_set, secondary_attachment_ref ) )
442 | {
443 | println( "^1Warning: (" + self.name + ") attachment [" + secondary_attachment_ref + "] is not valid for [" + secondary_ref + "]. Removing attachment." );
444 | secondary_attachment_flag = 0;
445 | }
446 |
447 | // validate special grenade type
448 | flashGrenadeIndex = 101;
449 | assert( level.tbl_weaponIDs[flashGrenadeIndex]["reference"] == "flash_grenade" ); // if this fails we need to change flashGrenadeIndex
450 | if ( !isDefined( level.tbl_weaponIDs[special_grenade] ) )
451 | special_grenade = flashGrenadeIndex;
452 | specialGrenadeType = level.tbl_weaponIDs[special_grenade]["reference"];
453 | if ( specialGrenadeType != "flash_grenade" && specialGrenadeType != "smoke_grenade" && specialGrenadeType != "concussion_grenade" )
454 | {
455 | println( "^1Warning: (" + self.name + ") special grenade " + special_grenade + " is invalid. Setting to flash grenade." );
456 | special_grenade = flashGrenadeIndex;
457 | }
458 |
459 | if ( specialGrenadeType == "smoke_grenade" && level.tbl_PerkData[specialty1]["reference_full"] == "specialty_specialgrenade" )
460 | {
461 | println( "^1Warning: (" + self.name + ") smoke grenade may not be used with extra special grenades. Setting to flash grenade." );
462 | special_grenade = flashGrenadeIndex;
463 | }
464 |
465 | // apply attachment to primary weapon, getting weapon reference strings
466 | attachment_string = level.tbl_WeaponAttachment[primary_attachment_flag]["reference"];
467 | if( primary_attachment_flag != 0 && attachment_string != "" )
468 | self.custom_class[i]["primary"] = level.tbl_weaponIDs[primary_num]["reference"]+"_"+attachment_string+"_mp";
469 | else
470 | self.custom_class[i]["primary"] = level.tbl_weaponIDs[primary_num]["reference"]+"_mp";
471 |
472 | // apply attachment to secondary weapon, getting weapon reference strings
473 | attachment_string = level.tbl_WeaponAttachment[secondary_attachment_flag]["reference"];
474 | if( secondary_attachment_flag != 0 && attachment_string != "" )
475 | self.custom_class[i]["secondary"] = level.tbl_weaponIDs[secondary_num]["reference"]+"_"+attachment_string+"_mp";
476 | else
477 | self.custom_class[i]["secondary"] = level.tbl_weaponIDs[secondary_num]["reference"]+"_mp";
478 |
479 | // obtaining specialties, getting specialty reference strings
480 | assertex( isdefined( level.tbl_PerkData[specialty1] ), "Specialty #:"+specialty1+"'s data is undefined" );
481 | self.custom_class[i]["specialty1"] = level.tbl_PerkData[specialty1]["reference_full"]; //tablelookup( "mp/statstable.csv", level.cac_cstat, specialty1, level.cac_cimage );
482 | self.custom_class[i]["specialty1_weaponref"] = level.tbl_PerkData[specialty1]["reference"]; //tablelookup( "mp/statstable.csv", level.cac_cstat, specialty1, level.cac_creference );
483 | self.custom_class[i]["specialty1_count"] = level.tbl_PerkData[specialty1]["count"]; //int( tablelookup( "mp/statstable.csv", level.cac_cstat, specialty1, level.cac_ccount ) );
484 | self.custom_class[i]["specialty1_group"] = level.tbl_PerkData[specialty1]["group"]; //tablelookup( "mp/statstable.csv", level.cac_cstat, specialty1, level.cac_cgroup );
485 |
486 | self.custom_class[i]["specialty2"] = level.tbl_PerkData[specialty2]["reference"]; //tablelookup( "mp/statstable.csv", level.cac_cstat, specialty2, level.cac_creference );
487 | self.custom_class[i]["specialty2_weaponref"] = self.custom_class[i]["specialty2"];
488 | self.custom_class[i]["specialty2_count"] = level.tbl_PerkData[specialty2]["count"]; //int( tablelookup( "mp/statstable.csv", level.cac_cstat, specialty2, level.cac_ccount ) );
489 | self.custom_class[i]["specialty2_group"] = level.tbl_PerkData[specialty2]["group"]; //tablelookup( "mp/statstable.csv", level.cac_cstat, specialty2, level.cac_cgroup );
490 |
491 | self.custom_class[i]["specialty3"] = level.tbl_PerkData[specialty3]["reference"]; //tablelookup( "mp/statstable.csv", level.cac_cstat, specialty3, level.cac_creference );
492 | self.custom_class[i]["specialty3_weaponref"] = self.custom_class[i]["specialty3"];
493 | self.custom_class[i]["specialty3_count"] = level.tbl_PerkData[specialty3]["count"]; //int( tablelookup( "mp/statstable.csv", level.cac_cstat, specialty3, level.cac_ccount ) );
494 | self.custom_class[i]["specialty3_group"] = level.tbl_PerkData[specialty3]["group"]; //tablelookup( "mp/statstable.csv", level.cac_cstat, specialty3, level.cac_cgroup );
495 |
496 | // builds the full special grenade reference string
497 | self.custom_class[i]["special_grenade"] = level.tbl_weaponIDs[special_grenade]["reference"]+"_mp"; //tablelookup( "mp/statstable.csv", level.cac_numbering, special_grenade, level.cac_creference ) + "_mp";
498 | self.custom_class[i]["special_grenade_count"] = level.tbl_weaponIDs[special_grenade]["count"]; //int( tablelookup( "mp/statstable.csv", level.cac_numbering, special_grenade, level.cac_ccount ) );
499 |
500 | // camo selection, default 0 = no camo skin
501 | self.custom_class[i]["camo_num"] = camo_num;
502 | self.cac_initialized = true;
503 |
504 | /* debug
505 | println( "\n ========== CLASS DEBUG INFO ========== \n" );
506 | println( "Primary: "+self.custom_class[i]["primary"] );
507 | println( "Secondary: "+self.custom_class[i]["secondary"] );
508 | println( "Specialty1: "+self.custom_class[i]["specialty1"]+" - Group: "+self.custom_class[i]["specialty1_group"]+" - Count: "+self.custom_class[i]["specialty1_count"] );
509 | println( "Specialty2: "+self.custom_class[i]["specialty2"] );
510 | println( "Specialty3: "+self.custom_class[i]["specialty3"] );
511 | println( "Special Grenade: "+self.custom_class[i]["special_grenade"]+" - Count: "+self.custom_class[i]["special_grenade_count"] );
512 | println( "Primary Camo: "+attachmenttablelookup( level.cac_cname, level.cac_cint2, camo_num ) );
513 | */
514 | }
515 | }
516 |
517 | validatePerk( perkIndex, perkSlotIndex )
518 | {
519 | for ( i = 0; i < level.allowedPerks[ perkSlotIndex ].size; i++ )
520 | {
521 | if ( perkIndex == level.allowedPerks[ perkSlotIndex ][i] )
522 | return perkIndex;
523 | }
524 | println( "^1Warning: (" + self.name + ") Perk " + level.tbl_PerkData[perkIndex]["reference_full"] + " is not allowed for perk slot index " + perkSlotIndex + "; replacing with no perk" );
525 | return 190;
526 | }
527 |
528 |
529 | logClassChoice( class, primaryWeapon, specialType, perks )
530 | {
531 | if ( class == self.lastClass )
532 | return;
533 |
534 | self logstring( "choseclass: " + class + " weapon: " + primaryWeapon + " special: " + specialType );
535 | for( i=0; i 0, "Default class: " + self.pers["class"] + " is missing specialties " );
786 |
787 | // re-registering perks to code since perks are cleared after respawn in case if players switch classes
788 | self register_perks();
789 |
790 | // weapon override for round based gametypes
791 | // TODO: if they switched to a sidearm, we shouldn't give them that as their primary!
792 | if ( isDefined( self.pers["weapon"] ) && self.pers["weapon"] != "none" )
793 | weapon = self.pers["weapon"];
794 | else
795 | weapon = level.classWeapons[team][class][primaryIndex];
796 |
797 | sidearm = level.classSidearm[team][class];
798 |
799 | self GiveWeapon( sidearm );
800 | if ( self cac_hasSpecialty( "specialty_extraammo" ) )
801 | self giveMaxAmmo( sidearm );
802 |
803 | // give primary weapon
804 | primaryWeapon = weapon;
805 |
806 | primaryTokens = strtok( primaryWeapon, "_" );
807 | self.pers["primaryWeapon"] = primaryTokens[0];
808 |
809 | if ( self.pers["primaryWeapon"] == "m14" )
810 | self.pers["primaryWeapon"] = "m21";
811 |
812 | self maps\mp\gametypes\_teams::playerModelForWeapon( self.pers["primaryWeapon"] );
813 |
814 | self GiveWeapon( weapon );
815 | if( self cac_hasSpecialty( "specialty_extraammo" ) )
816 | self giveMaxAmmo( weapon );
817 | self setSpawnWeapon( weapon );
818 |
819 | // give secondary weapon
820 | self SetActionSlot( 1, "nightvision" );
821 |
822 | secondaryWeapon = level.classItem[team][class]["type"];
823 | if ( secondaryWeapon != "" )
824 | {
825 | self GiveWeapon( secondaryWeapon );
826 |
827 | self setWeaponAmmoOverall( secondaryWeapon, level.classItem[team][class]["count"] );
828 |
829 | self SetActionSlot( 3, "weapon", secondaryWeapon );
830 | self SetActionSlot( 4, "" );
831 | }
832 | else
833 | {
834 | self SetActionSlot( 3, "altMode" );
835 | self SetActionSlot( 4, "" );
836 | }
837 |
838 | grenadeTypePrimary = level.classGrenades[class]["primary"]["type"];
839 | if ( grenadeTypePrimary != "" )
840 | {
841 | grenadeCount = level.classGrenades[class]["primary"]["count"];
842 |
843 | self GiveWeapon( grenadeTypePrimary );
844 | self SetWeaponAmmoClip( grenadeTypePrimary, grenadeCount );
845 | self SwitchToOffhand( grenadeTypePrimary );
846 | }
847 |
848 | grenadeTypeSecondary = level.classGrenades[class]["secondary"]["type"];
849 | if ( grenadeTypeSecondary != "" )
850 | {
851 | grenadeCount = level.classGrenades[class]["secondary"]["count"];
852 |
853 | if ( grenadeTypeSecondary == level.weapons["flash"])
854 | self setOffhandSecondaryClass("flash");
855 | else
856 | self setOffhandSecondaryClass("smoke");
857 |
858 | self giveWeapon( grenadeTypeSecondary );
859 | self SetWeaponAmmoClip( grenadeTypeSecondary, grenadeCount );
860 | }
861 |
862 | self thread logClassChoice( class, primaryWeapon, grenadeTypeSecondary, self.specialty );
863 | }
864 |
865 | switch ( weaponClass( primaryWeapon ) )
866 | {
867 | case "rifle":
868 | self setMoveSpeedScale( 0.95 );
869 | break;
870 | case "pistol":
871 | self setMoveSpeedScale( 1.0 );
872 | break;
873 | case "mg":
874 | self setMoveSpeedScale( 0.875 );
875 | break;
876 | case "smg":
877 | self setMoveSpeedScale( 1.0 );
878 | break;
879 | case "spread":
880 | self setMoveSpeedScale( 1.0 );
881 | break;
882 | default:
883 | self setMoveSpeedScale( 1.0 );
884 | break;
885 | }
886 |
887 | // cac specialties that require loop threads
888 | self cac_selector();
889 | }
890 |
891 | // sets the amount of ammo in the gun.
892 | // if the clip maxs out, the rest goes into the stock.
893 | setWeaponAmmoOverall( weaponname, amount )
894 | {
895 | if ( isWeaponClipOnly( weaponname ) )
896 | {
897 | self setWeaponAmmoClip( weaponname, amount );
898 | }
899 | else
900 | {
901 | self setWeaponAmmoClip( weaponname, amount );
902 | diff = amount - self getWeaponAmmoClip( weaponname );
903 | assert( diff >= 0 );
904 | self setWeaponAmmoStock( weaponname, diff );
905 | }
906 | }
907 |
908 | replenishLoadout() // used by ammo hardpoint.
909 | {
910 | team = self.pers["team"];
911 | class = self.pers["class"];
912 |
913 | weaponsList = self GetWeaponsList();
914 | for( idx = 0; idx < weaponsList.size; idx++ )
915 | {
916 | weapon = weaponsList[idx];
917 |
918 | self giveMaxAmmo( weapon );
919 | self SetWeaponAmmoClip( weapon, 9999 );
920 |
921 | if ( weapon == "claymore_mp" || weapon == "claymore_detonator_mp" )
922 | self setWeaponAmmoStock( weapon, 2 );
923 | }
924 |
925 | if ( self getAmmoCount( level.classGrenades[class]["primary"]["type"] ) < level.classGrenades[class]["primary"]["count"] )
926 | self SetWeaponAmmoClip( level.classGrenades[class]["primary"]["type"], level.classGrenades[class]["primary"]["count"] );
927 |
928 | if ( self getAmmoCount( level.classGrenades[class]["secondary"]["type"] ) < level.classGrenades[class]["secondary"]["count"] )
929 | self SetWeaponAmmoClip( level.classGrenades[class]["secondary"]["type"], level.classGrenades[class]["secondary"]["count"] );
930 | }
931 |
932 | onPlayerConnecting()
933 | {
934 | for(;;)
935 | {
936 | level waittill( "connecting", player );
937 |
938 | if ( !level.oldschool )
939 | {
940 | if ( !isDefined( player.pers["class"] ) )
941 | {
942 | player.pers["class"] = "";
943 | }
944 | player.class = player.pers["class"];
945 | player.lastClass = "";
946 | }
947 | player.detectExplosives = false;
948 | player.bombSquadIcons = [];
949 | player.bombSquadIds = [];
950 | }
951 | }
952 |
953 |
954 | fadeAway( waitDelay, fadeDelay )
955 | {
956 | wait waitDelay;
957 |
958 | self fadeOverTime( fadeDelay );
959 | self.alpha = 0;
960 | }
961 |
962 |
963 | setClass( newClass )
964 | {
965 | self.curClass = newClass;
966 | }
967 |
968 |
969 | // ============================================================================================
970 | // ======= =======
971 | // ======= Create a Class Specialties =======
972 | // ======= =======
973 | // ============================================================================================
974 |
975 | initPerkDvars()
976 | {
977 | level.cac_bulletdamage_data = cac_get_dvar_int( "perk_bulletDamage", "40" ); // increased bullet damage by this %
978 | level.cac_armorvest_data = cac_get_dvar_int( "perk_armorVest", "75" ); // increased health by this %
979 | level.cac_explosivedamage_data = cac_get_dvar_int( "perk_explosiveDamage", "25" ); // increased explosive damage by this %
980 | }
981 |
982 | // CAC: Selector function, calls the individual cac features according to player's class settings
983 | // Info: Called every time player spawns during loadout stage
984 | cac_selector()
985 | {
986 | perks = self.specialty;
987 |
988 | self.detectExplosives = false;
989 | for( i=0; i " + victim.name + "'s armor countered " + attacker.name + "'s increased bullet damage" );
1097 | #/
1098 | }
1099 | else
1100 | {
1101 | final_damage = damage*(100+level.cac_bulletdamage_data)/100;
1102 | /#
1103 | if ( getdvarint("scr_perkdebug") )
1104 | println( "Perk/> " + attacker.name + "'s bullet damage did extra damage to " + victim.name );
1105 | #/
1106 | }
1107 | }
1108 | else if( attacker cac_hasSpecialty( "specialty_explosivedamage" ) && isExplosiveDamage( meansofdeath ) )
1109 | {
1110 | // if victim has armor then do not change damage, it is cancelled out, else damage is increased
1111 |
1112 | if( isdefined( victim ) && isPlayer( victim ) && victim cac_hasSpecialty( "specialty_armorvest" ) )
1113 | {
1114 | final_damage = old_damage;
1115 | /#
1116 | if ( getdvarint("scr_perkdebug") )
1117 | println( "Perk/> " + victim.name + "'s armor countered " + attacker.name + "'s increased explosive damage" );
1118 | #/
1119 | }
1120 | else
1121 | {
1122 | final_damage = damage*(100+level.cac_explosivedamage_data)/100;
1123 | /#
1124 | if ( getdvarint("scr_perkdebug") )
1125 | println( "Perk/> " + attacker.name + "'s explosive damage did extra damage to " + victim.name );
1126 | #/
1127 | }
1128 | }
1129 | else
1130 | {
1131 | // if attacker has no bullet damage then check if victim has armor
1132 | // if victim has armor then less damage is taken, else damage unchanged
1133 |
1134 | if( isdefined( victim ) && isPlayer( victim ) && victim cac_hasSpecialty( "specialty_armorvest" ) )
1135 | {
1136 | final_damage = old_damage*(level.cac_armorvest_data/100);
1137 | /#
1138 | if ( getdvarint("scr_perkdebug") )
1139 | println( "Perk/> " + victim.name + "'s armor decreased " + attacker.name + "'s damage" );
1140 | #/
1141 | }
1142 | else
1143 | {
1144 | final_damage = old_damage;
1145 | }
1146 | }
1147 |
1148 | // debug
1149 | /#
1150 | if ( getdvarint("scr_perkdebug") )
1151 | println( "Perk/> Damage Factor: " + final_damage/old_damage + " - Pre Damage: " + old_damage + " - Post Damage: " + final_damage );
1152 | #/
1153 |
1154 | // return unchanged damage
1155 | return int( final_damage );
1156 | }
1157 |
1158 | // including grenade launcher, grenade, RPG, C4, claymore
1159 | isExplosiveDamage( meansofdeath )
1160 | {
1161 | explosivedamage = "MOD_GRENADE MOD_GRENADE_SPLASH MOD_PROJECTILE MOD_PROJECTILE_SPLASH MOD_EXPLOSIVE";
1162 | if( isSubstr( explosivedamage, meansofdeath ) )
1163 | return true;
1164 | return false;
1165 | }
1166 |
1167 | // if primary weapon damage
1168 | isPrimaryDamage( meansofdeath )
1169 | {
1170 | // including pistols as well since sometimes they share ammo
1171 | if( meansofdeath == "MOD_RIFLE_BULLET" || meansofdeath == "MOD_PISTOL_BULLET" )
1172 | return true;
1173 | return false;
1174 | }
1175 |
--------------------------------------------------------------------------------
/src/maps/mp/gametypes/_menus.gsc:
--------------------------------------------------------------------------------
1 | // _____ _ __ __ _
2 | // / ____| | / _|/ _| (_)
3 | // | (___ | |__ _ _| |_| |_ _ __ _
4 | // \___ \| '_ \| | | | _| _| '_ \| |
5 | // ____) | | | | |_| | | | | | | | | |
6 | // |_____/|_| |_|\__,_|_| |_| |_| |_|_|
7 | // Level Hack Server Mod
8 | //
9 | // This file is part of Shuffni's level hack server mod.
10 | //
11 | // This is free software: you can redistribute it and/or modify
12 | // it under the terms of the GNU General Public License as published by
13 | // the Free Software Foundation, either version 3 of the License, or
14 | // (at your option) any later version.
15 | //
16 | // This is distributed in the hope that it will be useful,
17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | // GNU General Public License for more details.
20 | //
21 | // You should have received a copy of the GNU General Public License
22 | // along with this software. If not, see .
23 |
24 | init()
25 | {
26 | game["menu_team"] = "team_marinesopfor";
27 | game["menu_class_allies"] = "class_marines";
28 | game["menu_changeclass_allies"] = "changeclass_marines";
29 | game["menu_initteam_allies"] = "initteam_marines";
30 | game["menu_class_axis"] = "class_opfor";
31 | game["menu_changeclass_axis"] = "changeclass_opfor";
32 | game["menu_initteam_axis"] = "initteam_opfor";
33 | game["menu_class"] = "class";
34 | game["menu_changeclass"] = "changeclass";
35 | game["menu_changeclass_offline"] = "changeclass_offline";
36 |
37 | if ( !level.console )
38 | {
39 | game["menu_callvote"] = "callvote";
40 | game["menu_muteplayer"] = "muteplayer";
41 | precacheMenu(game["menu_callvote"]);
42 | precacheMenu(game["menu_muteplayer"]);
43 |
44 | // ---- back up one folder to access game_summary.menu ----
45 | // game summary menu file precache
46 | game["menu_eog_main"] = "endofgame";
47 |
48 | // menu names (do not precache since they are in game_summary_ingame which should be precached
49 | game["menu_eog_unlock"] = "popup_unlock";
50 | game["menu_eog_summary"] = "popup_summary";
51 | game["menu_eog_unlock_page1"] = "popup_unlock_page1";
52 | game["menu_eog_unlock_page2"] = "popup_unlock_page2";
53 |
54 | precacheMenu(game["menu_eog_main"]);
55 | precacheMenu(game["menu_eog_unlock"]);
56 | precacheMenu(game["menu_eog_summary"]);
57 | precacheMenu(game["menu_eog_unlock_page1"]);
58 | precacheMenu(game["menu_eog_unlock_page2"]);
59 |
60 | }
61 | else
62 | {
63 | game["menu_controls"] = "ingame_controls";
64 | game["menu_options"] = "ingame_options";
65 | game["menu_leavegame"] = "popup_leavegame";
66 |
67 | if(level.splitscreen)
68 | {
69 | game["menu_team"] += "_splitscreen";
70 | game["menu_class_allies"] += "_splitscreen";
71 | game["menu_changeclass_allies"] += "_splitscreen";
72 | game["menu_class_axis"] += "_splitscreen";
73 | game["menu_changeclass_axis"] += "_splitscreen";
74 | game["menu_class"] += "_splitscreen";
75 | game["menu_changeclass"] += "_splitscreen";
76 | game["menu_controls"] += "_splitscreen";
77 | game["menu_options"] += "_splitscreen";
78 | game["menu_leavegame"] += "_splitscreen";
79 | }
80 |
81 | precacheMenu(game["menu_controls"]);
82 | precacheMenu(game["menu_options"]);
83 | precacheMenu(game["menu_leavegame"]);
84 | }
85 |
86 | precacheMenu("scoreboard");
87 | precacheMenu(game["menu_team"]);
88 | precacheMenu(game["menu_class_allies"]);
89 | precacheMenu(game["menu_changeclass_allies"]);
90 | precacheMenu(game["menu_initteam_allies"]);
91 | precacheMenu(game["menu_class_axis"]);
92 | precacheMenu(game["menu_changeclass_axis"]);
93 | precacheMenu(game["menu_class"]);
94 | precacheMenu(game["menu_changeclass"]);
95 | precacheMenu(game["menu_initteam_axis"]);
96 | precacheMenu(game["menu_changeclass_offline"]);
97 | precacheString( &"MP_HOST_ENDED_GAME" );
98 | precacheString( &"MP_HOST_ENDGAME_RESPONSE" );
99 |
100 | level thread onPlayerConnect();
101 | }
102 |
103 | onPlayerConnect()
104 | {
105 | for(;;)
106 | {
107 | level waittill("connecting", player);
108 |
109 | player setClientDvar("ui_3dwaypointtext", "1");
110 | player.enable3DWaypoints = true;
111 | player setClientDvar("ui_deathicontext", "1");
112 | player.enableDeathIcons = true;
113 |
114 | player thread onMenuResponse();
115 | }
116 | }
117 |
118 | onMenuResponse()
119 | {
120 | self endon("disconnect");
121 |
122 | for(;;)
123 | {
124 | self waittill("menuresponse", menu, response);
125 |
126 | //println( self getEntityNumber() + " menuresponse: " + menu + " " + response );
127 |
128 | //iprintln("^6", response);
129 | /*if(response == "open_changeclass_menu" )
130 | {
131 | if( getDvarInt( "onlinegame" ) )
132 | self openMenu( "changeclass" );
133 | else
134 | self openMenu( "changeclass_offline" );
135 | }
136 | */
137 |
138 | if ( response == "back" )
139 | {
140 | self closeMenu();
141 | self closeInGameMenu();
142 |
143 | if ( level.console )
144 | {
145 | if( menu == game["menu_changeclass"] || menu == game["menu_changeclass_offline"] || menu == game["menu_team"] || menu == game["menu_controls"] )
146 | {
147 | // assert(self.pers["team"] == "allies" || self.pers["team"] == "axis");
148 |
149 | if( self.pers["team"] == "allies" )
150 | self openMenu( game["menu_class_allies"] );
151 | if( self.pers["team"] == "axis" )
152 | self openMenu( game["menu_class_axis"] );
153 | }
154 | }
155 | continue;
156 | }
157 |
158 | if(response == "changeteam")
159 | {
160 | self closeMenu();
161 | self closeInGameMenu();
162 | self openMenu(game["menu_team"]);
163 | }
164 |
165 | if(response == "changeclass_marines" )
166 | {
167 | self closeMenu();
168 | self closeInGameMenu();
169 | self openMenu( game["menu_changeclass_allies"] );
170 | continue;
171 | }
172 |
173 | if(response == "changeclass_opfor" )
174 | {
175 | self closeMenu();
176 | self closeInGameMenu();
177 | self openMenu( game["menu_changeclass_axis"] );
178 | continue;
179 | }
180 |
181 | if(response == "changeclass_marines_splitscreen" )
182 | self openMenu( "changeclass_marines_splitscreen" );
183 |
184 | if(response == "changeclass_opfor_splitscreen" )
185 | self openMenu( "changeclass_opfor_splitscreen" );
186 |
187 | // rank update text options
188 | if(response == "xpTextToggle")
189 | {
190 | self.enableText = !self.enableText;
191 | if (self.enableText)
192 | self setClientDvar( "ui_xpText", "1" );
193 | else
194 | self setClientDvar( "ui_xpText", "0" );
195 | continue;
196 | }
197 |
198 | // 3D Waypoint options
199 | if(response == "waypointToggle")
200 | {
201 | self.enable3DWaypoints = !self.enable3DWaypoints;
202 | if (self.enable3DWaypoints)
203 | self setClientDvar( "ui_3dwaypointtext", "1" );
204 | else
205 | self setClientDvar( "ui_3dwaypointtext", "0" );
206 | // self maps\mp\gametypes\_objpoints::updatePlayerObjpoints();
207 | continue;
208 | }
209 |
210 | // 3D death icon options
211 | if(response == "deathIconToggle")
212 | {
213 | self.enableDeathIcons = !self.enableDeathIcons;
214 | if (self.enableDeathIcons)
215 | self setClientDvar( "ui_deathicontext", "1" );
216 | else
217 | self setClientDvar( "ui_deathicontext", "0" );
218 | self maps\mp\gametypes\_deathicons::updateDeathIconsEnabled();
219 | continue;
220 | }
221 |
222 | if(response == "endgame")
223 | {
224 | // TODO: replace with onSomethingEvent call
225 | if(level.splitscreen)
226 | {
227 | if ( level.console )
228 | endparty();
229 | level.skipVote = true;
230 |
231 | if ( !level.gameEnded )
232 | {
233 | level thread maps\mp\gametypes\_globallogic::forceEnd();
234 | }
235 | }
236 |
237 | continue;
238 | }
239 |
240 | if ( response == "endround" && level.console )
241 | {
242 | if ( !level.gameEnded )
243 | {
244 | level thread maps\mp\gametypes\_globallogic::forceEnd();
245 | }
246 | else
247 | {
248 | self closeMenu();
249 | self closeInGameMenu();
250 | self iprintln( &"MP_HOST_ENDGAME_RESPONSE" );
251 | }
252 | continue;
253 | }
254 |
255 | if(menu == game["menu_team"])
256 | {
257 | switch(response)
258 | {
259 | case "allies":
260 | //self closeMenu();
261 | //self closeInGameMenu();
262 | self [[level.allies]]();
263 | break;
264 |
265 | case "axis":
266 | //self closeMenu();
267 | //self closeInGameMenu();
268 | self [[level.axis]]();
269 | break;
270 |
271 | case "autoassign":
272 | //self closeMenu();
273 | //self closeInGameMenu();
274 | self [[level.autoassign]]();
275 | break;
276 |
277 | case "spectator":
278 | //self closeMenu();
279 | //self closeInGameMenu();
280 | self [[level.spectator]]();
281 | break;
282 | }
283 | } // the only responses remain are change class events
284 | else if( menu == game["menu_changeclass"] || menu == game["menu_changeclass_offline"] )
285 | {
286 | self closeMenu();
287 | self closeInGameMenu();
288 |
289 | self.selectedClass = true;
290 | self [[level.class]](response);
291 | }
292 | else if ( !level.console )
293 | {
294 | if(menu == game["menu_quickcommands"])
295 | maps\mp\gametypes\_quickmessages::quickcommands(response);
296 | else if(menu == game["menu_quickstatements"])
297 | maps\mp\gametypes\_quickmessages::quickstatements(response);
298 | else if(menu == game["menu_quickresponses"])
299 | maps\mp\gametypes\_quickmessages::quickresponses(response);
300 | }
301 |
302 | // ======== catching response for create-a-class events ========
303 | /*
304 | responseTok = strTok( response, "," );
305 |
306 | if( isdefined( responseTok ) && responseTok.size > 1 )
307 | {
308 | if( responseTok[0] == "primary" )
309 | {
310 | // primary weapon selection
311 | assertex( responseTok.size != 2, "Primary weapon selection in create-a-class-ingame is sending bad response:" + response );
312 |
313 | stat_offset = cacMenuStatOffset( menu, response );
314 | self setstat( stat_offset+201, ( int( tableLookup( "mp/statsTable.csv", 4, responseTok[1], 1 ) ) - 3000 ) );
315 | }
316 | else if( responseTok[0] == "attachment" )
317 | {
318 | // primary or secondary weapon attachment selection
319 | assertex( responseTok.size != 3, "Weapon attachment selection in create-a-class-ingame is sending bad response:" + response );
320 |
321 | stat_offset = cacMenuStatOffset( menu, response );
322 | if( responseTok[1] == "primary" )
323 | self setstat( stat_offset+202, int( tableLookup( "mp/attachmentTable.csv", 4, responseTok[2], 9 ) ) );
324 | else if( responseTok[1] == "secondary" )
325 | self setstat( stat_offset+204, int( tableLookup( "mp/attachmentTable.csv", 4, responseTok[2], 9 ) ) );
326 | }
327 | else if( responseTok[0] == "secondary" )
328 | {
329 | // secondary weapon selection
330 | assertex( responseTok.size != 2, "Secondary weapon selection in create-a-class-ingame is sending bad response:" + response );
331 |
332 | stat_offset = cacMenuStatOffset( menu, response );
333 | self setstat( stat_offset+203, ( int( tableLookup( "mp/statsTable.csv", 4, responseTok[1], 1 ) ) - 3000 ) );
334 | }
335 | else if( responseTok[0] == "perk" )
336 | {
337 | // all 3 perks selection
338 | assertex( responseTok.size != 3, "Perks selection in create-a-class-ingame is sending bad response:" + response );
339 |
340 | stat_offset = cacMenuStatOffset( menu, response );
341 | self setstat( stat_offset+200+int(responseTok[1]), int( tableLookup( "mp/statsTable.csv", 4, responseTok[2], 1 ) ) );
342 | }
343 | else if( responseTok[0] == "sgrenade" )
344 | {
345 | assertex( responseTok.size != 2, "Special grenade selection in create-a-class-ingame is sending bad response:" + response );
346 |
347 | stat_offset = cacMenuStatOffset( menu, response );
348 | self setstat( stat_offset+208, ( int( tableLookup( "mp/statsTable.csv", 4, responseTok[1], 1 ) ) - 3000 ) );
349 | }
350 | else if( responseTok[0] == "camo" )
351 | {
352 | assertex( responseTok.size != 2, "Primary weapon camo skin selection in create-a-class-ingame is sending bad response:" + response );
353 |
354 | stat_offset = cacMenuStatOffset( menu, response );
355 | self setstat( stat_offset+209, int( tableLookup( "mp/attachmentTable.csv", 4, responseTok[2], 11 ) ) );
356 | }
357 | }
358 | */
359 | }
360 | }
361 |
362 | /*
363 | // sort response message from CAC menu
364 | cacMenuStatOffset( menu, response )
365 | {
366 | stat_offset = -1;
367 |
368 | if( menu == "menu_cac_assault" )
369 | stat_offset = 0;
370 | else if( menu == "menu_cac_specops" )
371 | stat_offset = 10;
372 | else if( menu == "menu_cac_heavygunner" )
373 | stat_offset = 20;
374 | else if( menu == "menu_cac_demolitions" )
375 | stat_offset = 30;
376 | else if( menu == "menu_cac_sniper" )
377 | stat_offset = 40;
378 |
379 | assertex( stat_offset >= 0, "The response: " + response + " came from non-CAC menu" );
380 |
381 | return stat_offset;
382 | }
383 | */
--------------------------------------------------------------------------------
/src/maps/mp/gametypes/_rank.gsc:
--------------------------------------------------------------------------------
1 | // _____ _ __ __ _
2 | // / ____| | / _|/ _| (_)
3 | // | (___ | |__ _ _| |_| |_ _ __ _
4 | // \___ \| '_ \| | | | _| _| '_ \| |
5 | // ____) | | | | |_| | | | | | | | | |
6 | // |_____/|_| |_|\__,_|_| |_| |_| |_|_|
7 | // Level Hack Server Mod
8 | //
9 | // This file is part of Shuffni's level hack server mod.
10 | //
11 | // This is free software: you can redistribute it and/or modify
12 | // it under the terms of the GNU General Public License as published by
13 | // the Free Software Foundation, either version 3 of the License, or
14 | // (at your option) any later version.
15 | //
16 | // This is distributed in the hope that it will be useful,
17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | // GNU General Public License for more details.
20 | //
21 | // You should have received a copy of the GNU General Public License
22 | // along with this software. If not, see .
23 |
24 | #include common_scripts\utility;
25 | #include maps\mp\gametypes\_hud_util;
26 |
27 |
28 | init()
29 | {
30 | level.scoreInfo = [];
31 | level.rankTable = [];
32 |
33 | precacheShader("white");
34 |
35 | precacheString( &"RANK_PLAYER_WAS_PROMOTED_N" );
36 | precacheString( &"RANK_PLAYER_WAS_PROMOTED" );
37 | precacheString( &"RANK_PROMOTED" );
38 | precacheString( &"MP_PLUS" );
39 | precacheString( &"RANK_ROMANI" );
40 | precacheString( &"RANK_ROMANII" );
41 |
42 | if ( level.teamBased )
43 | {
44 | registerScoreInfo( "kill", 10 );
45 | registerScoreInfo( "headshot", 10 );
46 | registerScoreInfo( "assist", 2 );
47 | registerScoreInfo( "suicide", 0 );
48 | registerScoreInfo( "teamkill", 0 );
49 | }
50 | else
51 | {
52 | registerScoreInfo( "kill", 5 );
53 | registerScoreInfo( "headshot", 5 );
54 | registerScoreInfo( "assist", 0 );
55 | registerScoreInfo( "suicide", 0 );
56 | registerScoreInfo( "teamkill", 0 );
57 | }
58 |
59 | registerScoreInfo( "win", 1 );
60 | registerScoreInfo( "loss", 0.5 );
61 | registerScoreInfo( "tie", 0.75 );
62 | registerScoreInfo( "capture", 30 );
63 | registerScoreInfo( "defend", 30 );
64 |
65 | registerScoreInfo( "challenge", 250 );
66 |
67 | level.maxRank = int(tableLookup( "mp/rankTable.csv", 0, "maxrank", 1 ));
68 | level.maxPrestige = int(tableLookup( "mp/rankIconTable.csv", 0, "maxprestige", 1 ));
69 |
70 | pId = 0;
71 | rId = 0;
72 | for ( pId = 0; pId <= level.maxPrestige; pId++ )
73 | {
74 | for ( rId = 0; rId <= level.maxRank; rId++ )
75 | precacheShader( tableLookup( "mp/rankIconTable.csv", 0, rId, pId+1 ) );
76 | }
77 |
78 | rankId = 0;
79 | rankName = tableLookup( "mp/ranktable.csv", 0, rankId, 1 );
80 | assert( isDefined( rankName ) && rankName != "" );
81 |
82 | while ( isDefined( rankName ) && rankName != "" )
83 | {
84 | level.rankTable[rankId][1] = tableLookup( "mp/ranktable.csv", 0, rankId, 1 );
85 | level.rankTable[rankId][2] = tableLookup( "mp/ranktable.csv", 0, rankId, 2 );
86 | level.rankTable[rankId][3] = tableLookup( "mp/ranktable.csv", 0, rankId, 3 );
87 | level.rankTable[rankId][7] = tableLookup( "mp/ranktable.csv", 0, rankId, 7 );
88 |
89 | precacheString( tableLookupIString( "mp/ranktable.csv", 0, rankId, 16 ) );
90 |
91 | rankId++;
92 | rankName = tableLookup( "mp/ranktable.csv", 0, rankId, 1 );
93 | }
94 |
95 | level.statOffsets = [];
96 | level.statOffsets["weapon_assault"] = 290;
97 | level.statOffsets["weapon_lmg"] = 291;
98 | level.statOffsets["weapon_smg"] = 292;
99 | level.statOffsets["weapon_shotgun"] = 293;
100 | level.statOffsets["weapon_sniper"] = 294;
101 | level.statOffsets["weapon_pistol"] = 295;
102 | level.statOffsets["perk1"] = 296;
103 | level.statOffsets["perk2"] = 297;
104 | level.statOffsets["perk3"] = 298;
105 |
106 | level.numChallengeTiers = 10;
107 |
108 | buildChallegeInfo();
109 |
110 | level thread onPlayerConnect();
111 | }
112 |
113 |
114 | isRegisteredEvent( type )
115 | {
116 | if ( isDefined( level.scoreInfo[type] ) )
117 | return true;
118 | else
119 | return false;
120 | }
121 |
122 | registerScoreInfo( type, value )
123 | {
124 | level.scoreInfo[type]["value"] = value;
125 | }
126 |
127 | getScoreInfoValue( type )
128 | {
129 | return ( level.scoreInfo[type]["value"] );
130 | }
131 |
132 | getScoreInfoLabel( type )
133 | {
134 | return ( level.scoreInfo[type]["label"] );
135 | }
136 |
137 | getRankInfoMinXP( rankId )
138 | {
139 | return int(level.rankTable[rankId][2]);
140 | }
141 |
142 | getRankInfoXPAmt( rankId )
143 | {
144 | return int(level.rankTable[rankId][3]);
145 | }
146 |
147 | getRankInfoMaxXp( rankId )
148 | {
149 | return int(level.rankTable[rankId][7]);
150 | }
151 |
152 | getRankInfoFull( rankId )
153 | {
154 | return tableLookupIString( "mp/ranktable.csv", 0, rankId, 16 );
155 | }
156 |
157 | getRankInfoIcon( rankId, prestigeId )
158 | {
159 | return tableLookup( "mp/rankIconTable.csv", 0, rankId, prestigeId+1 );
160 | }
161 |
162 | getRankInfoUnlockWeapon( rankId )
163 | {
164 | return tableLookup( "mp/ranktable.csv", 0, rankId, 8 );
165 | }
166 |
167 | getRankInfoUnlockPerk( rankId )
168 | {
169 | return tableLookup( "mp/ranktable.csv", 0, rankId, 9 );
170 | }
171 |
172 | getRankInfoUnlockChallenge( rankId )
173 | {
174 | return tableLookup( "mp/ranktable.csv", 0, rankId, 10 );
175 | }
176 |
177 | getRankInfoUnlockFeature( rankId )
178 | {
179 | return tableLookup( "mp/ranktable.csv", 0, rankId, 15 );
180 | }
181 |
182 | getRankInfoUnlockCamo( rankId )
183 | {
184 | return tableLookup( "mp/ranktable.csv", 0, rankId, 11 );
185 | }
186 |
187 | getRankInfoUnlockAttachment( rankId )
188 | {
189 | return tableLookup( "mp/ranktable.csv", 0, rankId, 12 );
190 | }
191 |
192 | getRankInfoLevel( rankId )
193 | {
194 | return int( tableLookup( "mp/ranktable.csv", 0, rankId, 13 ) );
195 | }
196 |
197 |
198 | onPlayerConnect()
199 | {
200 | for(;;)
201 | {
202 | level waittill( "connected", player );
203 |
204 | player.pers["rankxp"] = player maps\mp\gametypes\_persistence::statGet( "rankxp" );
205 | rankId = player getRankForXp( player getRankXP() );
206 | player.pers["rank"] = rankId;
207 | player.pers["participation"] = 0;
208 |
209 | player maps\mp\gametypes\_persistence::statSet( "rank", rankId );
210 | player maps\mp\gametypes\_persistence::statSet( "minxp", getRankInfoMinXp( rankId ) );
211 | player maps\mp\gametypes\_persistence::statSet( "maxxp", getRankInfoMaxXp( rankId ) );
212 | player maps\mp\gametypes\_persistence::statSet( "lastxp", player.pers["rankxp"] );
213 |
214 | player.rankUpdateTotal = 0;
215 |
216 | // for keeping track of rank through stat#251 used by menu script
217 | // attempt to move logic out of menus as much as possible
218 | player.cur_rankNum = rankId;
219 | assertex( isdefined(player.cur_rankNum), "rank: "+ rankId + " does not have an index, check mp/ranktable.csv" );
220 | player setStat( 251, player.cur_rankNum );
221 |
222 | prestige = 0;
223 | player setRank( rankId, prestige );
224 | player.pers["prestige"] = prestige;
225 |
226 | // resetting unlockable vars
227 | if ( !isDefined( player.pers["unlocks"] ) )
228 | {
229 | player.pers["unlocks"] = [];
230 | player.pers["unlocks"]["weapon"] = 0;
231 | player.pers["unlocks"]["perk"] = 0;
232 | player.pers["unlocks"]["challenge"] = 0;
233 | player.pers["unlocks"]["camo"] = 0;
234 | player.pers["unlocks"]["attachment"] = 0;
235 | player.pers["unlocks"]["feature"] = 0;
236 | player.pers["unlocks"]["page"] = 0;
237 |
238 | // resetting unlockable dvars
239 | player setClientDvar( "player_unlockweapon0", "" );
240 | player setClientDvar( "player_unlockweapon1", "" );
241 | player setClientDvar( "player_unlockweapon2", "" );
242 | player setClientDvar( "player_unlockweapons", "0" );
243 |
244 | player setClientDvar( "player_unlockcamo0a", "" );
245 | player setClientDvar( "player_unlockcamo0b", "" );
246 | player setClientDvar( "player_unlockcamo1a", "" );
247 | player setClientDvar( "player_unlockcamo1b", "" );
248 | player setClientDvar( "player_unlockcamo2a", "" );
249 | player setClientDvar( "player_unlockcamo2b", "" );
250 | player setClientDvar( "player_unlockcamos", "0" );
251 |
252 | player setClientDvar( "player_unlockattachment0a", "" );
253 | player setClientDvar( "player_unlockattachment0b", "" );
254 | player setClientDvar( "player_unlockattachment1a", "" );
255 | player setClientDvar( "player_unlockattachment1b", "" );
256 | player setClientDvar( "player_unlockattachment2a", "" );
257 | player setClientDvar( "player_unlockattachment2b", "" );
258 | player setClientDvar( "player_unlockattachments", "0" );
259 |
260 | player setClientDvar( "player_unlockperk0", "" );
261 | player setClientDvar( "player_unlockperk1", "" );
262 | player setClientDvar( "player_unlockperk2", "" );
263 | player setClientDvar( "player_unlockperks", "0" );
264 |
265 | player setClientDvar( "player_unlockfeature0", "" );
266 | player setClientDvar( "player_unlockfeature1", "" );
267 | player setClientDvar( "player_unlockfeature2", "" );
268 | player setClientDvar( "player_unlockfeatures", "0" );
269 |
270 | player setClientDvar( "player_unlockchallenge0", "" );
271 | player setClientDvar( "player_unlockchallenge1", "" );
272 | player setClientDvar( "player_unlockchallenge2", "" );
273 | player setClientDvar( "player_unlockchallenges", "0" );
274 |
275 | player setClientDvar( "player_unlock_page", "0" );
276 | }
277 |
278 | if ( !isDefined( player.pers["summary"] ) )
279 | {
280 | player.pers["summary"] = [];
281 | player.pers["summary"]["xp"] = 0;
282 | player.pers["summary"]["score"] = 0;
283 | player.pers["summary"]["challenge"] = 0;
284 | player.pers["summary"]["match"] = 0;
285 | player.pers["summary"]["misc"] = 0;
286 |
287 | // resetting game summary dvars
288 | player setClientDvar( "player_summary_xp", "0" );
289 | player setClientDvar( "player_summary_score", "0" );
290 | player setClientDvar( "player_summary_challenge", "0" );
291 | player setClientDvar( "player_summary_match", "0" );
292 | player setClientDvar( "player_summary_misc", "0" );
293 | }
294 |
295 |
296 | // resetting summary vars
297 |
298 | // set default popup in lobby after a game finishes to game "summary"
299 | // if player got promoted during the game, we set it to "promotion"
300 | player setclientdvar( "ui_lobbypopup", "" );
301 |
302 | player updateChallenges();
303 | player.explosiveKills[0] = 0;
304 | player.xpGains = [];
305 |
306 | player thread onPlayerSpawned();
307 | player thread onJoinedTeam();
308 | player thread onJoinedSpectators();
309 | }
310 | }
311 |
312 |
313 | onJoinedTeam()
314 | {
315 | self endon("disconnect");
316 |
317 | for(;;)
318 | {
319 | self waittill("joined_team");
320 | self thread removeRankHUD();
321 | }
322 | }
323 |
324 |
325 | onJoinedSpectators()
326 | {
327 | self endon("disconnect");
328 |
329 | for(;;)
330 | {
331 | self waittill("joined_spectators");
332 | self thread removeRankHUD();
333 | }
334 | }
335 |
336 |
337 | onPlayerSpawned()
338 | {
339 | self endon("disconnect");
340 |
341 | for(;;)
342 | {
343 | self waittill("spawned_player");
344 |
345 | if(!isdefined(self.hud_rankscroreupdate))
346 | {
347 | self.hud_rankscroreupdate = newClientHudElem(self);
348 | self.hud_rankscroreupdate.horzAlign = "center";
349 | self.hud_rankscroreupdate.vertAlign = "middle";
350 | self.hud_rankscroreupdate.alignX = "center";
351 | self.hud_rankscroreupdate.alignY = "middle";
352 | self.hud_rankscroreupdate.x = 0;
353 | self.hud_rankscroreupdate.y = -60;
354 | self.hud_rankscroreupdate.font = "default";
355 | self.hud_rankscroreupdate.fontscale = 2.0;
356 | self.hud_rankscroreupdate.archived = false;
357 | self.hud_rankscroreupdate.color = (0.5,0.5,0.5);
358 | self.hud_rankscroreupdate maps\mp\gametypes\_hud::fontPulseInit();
359 | }
360 | }
361 | }
362 |
363 | roundUp( floatVal )
364 | {
365 | if ( int( floatVal ) != floatVal )
366 | return int( floatVal+1 );
367 | else
368 | return int( floatVal );
369 | }
370 |
371 | giveRankXP( type, value )
372 | {
373 | self endon("disconnect");
374 |
375 | if ( level.teamBased && (!level.playerCount["allies"] || !level.playerCount["axis"]) )
376 | return;
377 | else if ( !level.teamBased && (level.playerCount["allies"] + level.playerCount["axis"] < 2) )
378 | return;
379 |
380 | if ( !isDefined( value ) )
381 | value = getScoreInfoValue( type );
382 |
383 | if ( !isDefined( self.xpGains[type] ) )
384 | self.xpGains[type] = 0;
385 |
386 | switch( type )
387 | {
388 | case "kill":
389 | case "headshot":
390 | case "suicide":
391 | case "teamkill":
392 | case "assist":
393 | case "capture":
394 | case "defend":
395 | case "return":
396 | case "pickup":
397 | case "assault":
398 | case "plant":
399 | case "defuse":
400 | if ( level.numLives >= 1 )
401 | {
402 | multiplier = max(1,int( 10/level.numLives ));
403 | value = int(value * multiplier);
404 | }
405 | break;
406 | }
407 |
408 | self.xpGains[type] += value;
409 |
410 | self incRankXP( value );
411 |
412 | if ( level.rankedMatch && updateRank() )
413 | self thread updateRankAnnounceHUD();
414 |
415 | if ( isDefined( self.enableText ) && self.enableText && !level.hardcoreMode )
416 | {
417 | if ( type == "teamkill" )
418 | self thread updateRankScoreHUD( 0 - getScoreInfoValue( "kill" ) );
419 | else
420 | self thread updateRankScoreHUD( value );
421 | }
422 |
423 | switch( type )
424 | {
425 | case "kill":
426 | case "headshot":
427 | case "suicide":
428 | case "teamkill":
429 | case "assist":
430 | case "capture":
431 | case "defend":
432 | case "return":
433 | case "pickup":
434 | case "assault":
435 | case "plant":
436 | case "defuse":
437 | self.pers["summary"]["score"] += value;
438 | self.pers["summary"]["xp"] += value;
439 | break;
440 |
441 | case "win":
442 | case "loss":
443 | case "tie":
444 | self.pers["summary"]["match"] += value;
445 | self.pers["summary"]["xp"] += value;
446 | break;
447 |
448 | case "challenge":
449 | self.pers["summary"]["challenge"] += value;
450 | self.pers["summary"]["xp"] += value;
451 | break;
452 |
453 | default:
454 | self.pers["summary"]["misc"] += value; //keeps track of ungrouped match xp reward
455 | self.pers["summary"]["match"] += value;
456 | self.pers["summary"]["xp"] += value;
457 | break;
458 | }
459 |
460 | self setClientDvars(
461 | "player_summary_xp", self.pers["summary"]["xp"],
462 | "player_summary_score", self.pers["summary"]["score"],
463 | "player_summary_challenge", self.pers["summary"]["challenge"],
464 | "player_summary_match", self.pers["summary"]["match"],
465 | "player_summary_misc", self.pers["summary"]["misc"]
466 | );
467 | }
468 |
469 | updateRank()
470 | {
471 | newRankId = self getRank();
472 | if ( newRankId == self.pers["rank"] )
473 | return false;
474 |
475 | oldRank = self.pers["rank"];
476 | rankId = self.pers["rank"];
477 | self.pers["rank"] = newRankId;
478 |
479 | while ( rankId <= newRankId )
480 | {
481 | self maps\mp\gametypes\_persistence::statSet( "rank", rankId );
482 | self maps\mp\gametypes\_persistence::statSet( "minxp", int(level.rankTable[rankId][2]) );
483 | self maps\mp\gametypes\_persistence::statSet( "maxxp", int(level.rankTable[rankId][7]) );
484 |
485 | // set current new rank index to stat#252
486 | self setStat( 252, rankId );
487 |
488 | // tell lobby to popup promotion window instead
489 | self.setPromotion = true;
490 | if ( level.rankedMatch && level.gameEnded )
491 | self setClientDvar( "ui_lobbypopup", "promotion" );
492 |
493 | // unlocks weapon =======
494 | unlockedWeapon = self getRankInfoUnlockWeapon( rankId ); // unlockedweapon is weapon reference string
495 | if ( isDefined( unlockedWeapon ) && unlockedWeapon != "" )
496 | unlockWeapon( unlockedWeapon );
497 |
498 | // unlock perk ==========
499 | unlockedPerk = self getRankInfoUnlockPerk( rankId ); // unlockedweapon is weapon reference string
500 | if ( isDefined( unlockedPerk ) && unlockedPerk != "" )
501 | unlockPerk( unlockedPerk );
502 |
503 | // unlock challenge =====
504 | unlockedChallenge = self getRankInfoUnlockChallenge( rankId );
505 | if ( isDefined( unlockedChallenge ) && unlockedChallenge != "" )
506 | unlockChallenge( unlockedChallenge );
507 |
508 | // unlock attachment ====
509 | unlockedAttachment = self getRankInfoUnlockAttachment( rankId ); // ex: ak47 gl
510 | if ( isDefined( unlockedAttachment ) && unlockedAttachment != "" )
511 | unlockAttachment( unlockedAttachment );
512 |
513 | unlockedCamo = self getRankInfoUnlockCamo( rankId ); // ex: ak47 camo_brockhaurd
514 | if ( isDefined( unlockedCamo ) && unlockedCamo != "" )
515 | unlockCamo( unlockedCamo );
516 |
517 | unlockedFeature = self getRankInfoUnlockFeature( rankId ); // ex: feature_cac
518 | if ( isDefined( unlockedFeature ) && unlockedFeature != "" )
519 | unlockFeature( unlockedFeature );
520 |
521 | rankId++;
522 | }
523 | self logString( "promoted from " + oldRank + " to " + newRankId + " timeplayed: " + self maps\mp\gametypes\_persistence::statGet( "time_played_total" ) );
524 |
525 | self setRank( newRankId );
526 | return true;
527 | }
528 |
529 | updateRankAnnounceHUD()
530 | {
531 | self endon("disconnect");
532 |
533 | self notify("update_rank");
534 | self endon("update_rank");
535 |
536 | team = self.pers["team"];
537 | if ( !isdefined( team ) )
538 | return;
539 |
540 | self notify("reset_outcome");
541 | newRankName = self getRankInfoFull( self.pers["rank"] );
542 |
543 | notifyData = spawnStruct();
544 |
545 | notifyData.titleText = &"RANK_PROMOTED";
546 | notifyData.iconName = self getRankInfoIcon( self.pers["rank"], self.pers["prestige"] );
547 | notifyData.sound = "mp_level_up";
548 | notifyData.duration = 4.0;
549 |
550 | /* //flawed
551 | if ( isSubStr( level.rankTable[self.pers["rank"]][1], "2" ) )
552 | subRank = 2;
553 | else if ( isSubStr( level.rankTable[self.pers["rank"]][1], "3" ) )
554 | subRank = 3;
555 | else
556 | subRank = 1;
557 | */
558 |
559 | rank_char = level.rankTable[self.pers["rank"]][1];
560 | subRank = int(rank_char[rank_char.size-1]);
561 |
562 | if ( subRank == 2 )
563 | {
564 | notifyData.textLabel = newRankName;
565 | notifyData.notifyText = &"RANK_ROMANI";
566 | notifyData.textIsString = true;
567 | }
568 | else if ( subRank == 3 )
569 | {
570 | notifyData.textLabel = newRankName;
571 | notifyData.notifyText = &"RANK_ROMANII";
572 | notifyData.textIsString = true;
573 | }
574 | else
575 | {
576 | notifyData.notifyText = newRankName;
577 | }
578 |
579 | thread maps\mp\gametypes\_hud_message::notifyMessage( notifyData );
580 |
581 | if ( subRank > 1 )
582 | return;
583 |
584 | for ( i = 0; i < level.players.size; i++ )
585 | {
586 | player = level.players[i];
587 | playerteam = player.pers["team"];
588 | if ( isdefined( playerteam ) && player != self )
589 | {
590 | if ( playerteam == team )
591 | player iprintln( &"RANK_PLAYER_WAS_PROMOTED", self, newRankName );
592 | }
593 | }
594 | }
595 |
596 | // End of game summary/unlock menu page setup
597 | // 0 = no unlocks, 1 = only page one, 2 = only page two, 3 = both pages
598 | unlockPage( in_page )
599 | {
600 | if( in_page == 1 )
601 | {
602 | if( self.pers["unlocks"]["page"] == 0 )
603 | {
604 | self setClientDvar( "player_unlock_page", "1" );
605 | self.pers["unlocks"]["page"] = 1;
606 | }
607 | if( self.pers["unlocks"]["page"] == 2 )
608 | self setClientDvar( "player_unlock_page", "3" );
609 | }
610 | else if( in_page == 2 )
611 | {
612 | if( self.pers["unlocks"]["page"] == 0 )
613 | {
614 | self setClientDvar( "player_unlock_page", "2" );
615 | self.pers["unlocks"]["page"] = 2;
616 | }
617 | if( self.pers["unlocks"]["page"] == 1 )
618 | self setClientDvar( "player_unlock_page", "3" );
619 | }
620 | }
621 |
622 | // unlocks weapon
623 | unlockWeapon( refString )
624 | {
625 | assert( isDefined( refString ) && refString != "" );
626 |
627 | stat = int( tableLookup( "mp/statstable.csv", 4, refString, 1 ) );
628 |
629 | assertEx( stat > 0, "statsTable refstring " + refString + " has invalid stat number: " + stat );
630 |
631 | if( self getStat( stat ) > 0 )
632 | return;
633 |
634 | self setStat( stat, 65537 ); // 65537 is binary mask for newly unlocked weapon
635 | self setClientDvar( "player_unlockWeapon" + self.pers["unlocks"]["weapon"], refString );
636 | self.pers["unlocks"]["weapon"]++;
637 | self setClientDvar( "player_unlockWeapons", self.pers["unlocks"]["weapon"] );
638 |
639 | self unlockPage( 1 );
640 | }
641 |
642 | // unlocks perk
643 | unlockPerk( refString )
644 | {
645 | assert( isDefined( refString ) && refString != "" );
646 |
647 | stat = int( tableLookup( "mp/statstable.csv", 4, refString, 1 ) );
648 |
649 | if( self getStat( stat ) > 0 )
650 | return;
651 |
652 | self setStat( stat, 2 ); // 2 is binary mask for newly unlocked perk
653 | self setClientDvar( "player_unlockPerk" + self.pers["unlocks"]["perk"], refString );
654 | self.pers["unlocks"]["perk"]++;
655 | self setClientDvar( "player_unlockPerks", self.pers["unlocks"]["perk"] );
656 |
657 | self unlockPage( 2 );
658 | }
659 |
660 | // unlocks camo - multiple
661 | unlockCamo( refString )
662 | {
663 | assert( isDefined( refString ) && refString != "" );
664 |
665 | // tokenize reference string, accepting multiple camo unlocks in one call
666 | Ref_Tok = strTok( refString, ";" );
667 | assertex( Ref_Tok.size > 0, "Camo unlock specified in datatable ["+refString+"] is incomplete or empty" );
668 |
669 | for( i=0; i 0, "Attachment unlock specified in datatable ["+refString+"] is incomplete or empty" );
709 |
710 | for( i=0; i32 : 536805376 for new status
782 | for( i=0; i<149; i++ )
783 | {
784 | if( !isdefined( weaponIDs[i] ) )
785 | continue;
786 | if( self getStat( i+3000 ) & 536805376 )
787 | setBaseNewStatus( i );
788 | }
789 |
790 | perkIDs = level.tbl_PerkData;
791 | // update for perks
792 | for( i=150; i<199; i++ )
793 | {
794 | if( !isdefined( perkIDs[i] ) )
795 | continue;
796 | if( self getStat( i ) > 1 )
797 | setBaseNewStatus( i );
798 | }
799 | }
800 | */
801 |
802 | unlockChallenge( refString )
803 | {
804 | assert( isDefined( refString ) && refString != "" );
805 |
806 | // tokenize reference string, accepting multiple camo unlocks in one call
807 | Ref_Tok = strTok( refString, ";" );
808 | assertex( Ref_Tok.size > 0, "Camo unlock specified in datatable ["+refString+"] is incomplete or empty" );
809 |
810 | for( i=0; i 0, "Challenge unlock specified in datatable ["+refString+"] is incomplete or empty" );
844 |
845 | assert( tokens[0] == "tier" );
846 |
847 | tierId = int( tokens[1] );
848 | assertEx( tierId > 0 && tierId <= level.numChallengeTiers, "invalid tier ID " + tierId );
849 |
850 | groupId = "";
851 | if ( tokens.size > 2 )
852 | groupId = tokens[2];
853 |
854 | challengeArray = getArrayKeys( level.challengeInfo );
855 |
856 | for ( index = 0; index < challengeArray.size; index++ )
857 | {
858 | challenge = level.challengeInfo[challengeArray[index]];
859 |
860 | if ( challenge["tier"] != tierId )
861 | continue;
862 |
863 | if ( challenge["group"] != groupId )
864 | continue;
865 |
866 | if ( self getStat( challenge["stateid"] ) )
867 | continue;
868 |
869 | self setStat( challenge["stateid"], 1 );
870 |
871 | // set tier as new
872 | self setStat( 269 + challenge["tier"], 2 );// 2: new, 1: old
873 |
874 | }
875 |
876 | //desc = tableLookup( "mp/challengeTable.csv", 0, tierId, 1 );
877 |
878 | //self setClientDvar( "player_unlockchallenge" + self.pers["unlocks"]["challenge"], desc );
879 | self.pers["unlocks"]["challenge"]++;
880 | self setClientDvar( "player_unlockchallenges", self.pers["unlocks"]["challenge"] );
881 | self unlockPage( 2 );
882 | }
883 |
884 |
885 | unlockFeature( refString )
886 | {
887 | assert( isDefined( refString ) && refString != "" );
888 |
889 | stat = int( tableLookup( "mp/statstable.csv", 4, refString, 1 ) );
890 |
891 | if( self getStat( stat ) > 0 )
892 | return;
893 |
894 | if ( refString == "feature_cac" )
895 | self setStat( 200, 1 );
896 |
897 | self setStat( stat, 2 ); // 2 is binary mask for newly unlocked
898 |
899 | if ( refString == "feature_challenges" )
900 | {
901 | self unlockPage( 2 );
902 | return;
903 | }
904 |
905 | self setClientDvar( "player_unlockfeature"+self.pers["unlocks"]["feature"], tableLookup( "mp/statstable.csv", 4, refString, 3 ) );
906 | self.pers["unlocks"]["feature"]++;
907 | self setClientDvar( "player_unlockfeatures", self.pers["unlocks"]["feature"] );
908 |
909 | self unlockPage( 2 );
910 | }
911 |
912 |
913 | // update copy of a challenges to be progressed this game, only at the start of the game
914 | // challenges unlocked during the game will not be progressed on during that game session
915 | updateChallenges()
916 | {
917 | self.challengeData = [];
918 | for ( i = 1; i <= level.numChallengeTiers; i++ )
919 | {
920 | tableName = "mp/challengetable_tier"+i+".csv";
921 |
922 | idx = 1;
923 | // unlocks all the challenges in this tier
924 | for( idx = 1; isdefined( tableLookup( tableName, 0, idx, 0 ) ) && tableLookup( tableName, 0, idx, 0 ) != ""; idx++ )
925 | {
926 | stat_num = tableLookup( tableName, 0, idx, 2 );
927 | if( isdefined( stat_num ) && stat_num != "" )
928 | {
929 | statVal = self getStat( int( stat_num ) );
930 |
931 | refString = tableLookup( tableName, 0, idx, 7 );
932 | if ( statVal )
933 | self.challengeData[refString] = statVal;
934 | }
935 | }
936 | }
937 | }
938 |
939 |
940 | buildChallegeInfo()
941 | {
942 | level.challengeInfo = [];
943 |
944 | for ( i = 1; i <= level.numChallengeTiers; i++ )
945 | {
946 | tableName = "mp/challengetable_tier"+i+".csv";
947 |
948 | baseRef = "";
949 | // unlocks all the challenges in this tier
950 | for( idx = 1; isdefined( tableLookup( tableName, 0, idx, 0 ) ) && tableLookup( tableName, 0, idx, 0 ) != ""; idx++ )
951 | {
952 | stat_num = tableLookup( tableName, 0, idx, 2 );
953 | refString = tableLookup( tableName, 0, idx, 7 );
954 |
955 | level.challengeInfo[refString] = [];
956 | level.challengeInfo[refString]["tier"] = i;
957 | level.challengeInfo[refString]["stateid"] = int( tableLookup( tableName, 0, idx, 2 ) );
958 | level.challengeInfo[refString]["statid"] = int( tableLookup( tableName, 0, idx, 3 ) );
959 | level.challengeInfo[refString]["maxval"] = int( tableLookup( tableName, 0, idx, 4 ) );
960 | level.challengeInfo[refString]["minval"] = int( tableLookup( tableName, 0, idx, 5 ) );
961 | level.challengeInfo[refString]["name"] = tableLookupIString( tableName, 0, idx, 8 );
962 | level.challengeInfo[refString]["desc"] = tableLookupIString( tableName, 0, idx, 9 );
963 | level.challengeInfo[refString]["reward"] = int( tableLookup( tableName, 0, idx, 10 ) );
964 | level.challengeInfo[refString]["camo"] = tableLookup( tableName, 0, idx, 12 );
965 | level.challengeInfo[refString]["attachment"] = tableLookup( tableName, 0, idx, 13 );
966 | level.challengeInfo[refString]["group"] = tableLookup( tableName, 0, idx, 14 );
967 |
968 | precacheString( level.challengeInfo[refString]["name"] );
969 |
970 | if ( !int( level.challengeInfo[refString]["stateid"] ) )
971 | {
972 | level.challengeInfo[baseRef]["levels"]++;
973 | level.challengeInfo[refString]["stateid"] = level.challengeInfo[baseRef]["stateid"];
974 | level.challengeInfo[refString]["level"] = level.challengeInfo[baseRef]["levels"];
975 | }
976 | else
977 | {
978 | level.challengeInfo[refString]["levels"] = 1;
979 | level.challengeInfo[refString]["level"] = 1;
980 | baseRef = refString;
981 | }
982 | }
983 | }
984 | }
985 |
986 |
987 | endGameUpdate()
988 | {
989 | player = self;
990 | }
991 |
992 | updateRankScoreHUD( amount )
993 | {
994 | self endon( "disconnect" );
995 | self endon( "joined_team" );
996 | self endon( "joined_spectators" );
997 |
998 | if ( amount == 0 )
999 | return;
1000 |
1001 | self notify( "update_score" );
1002 | self endon( "update_score" );
1003 |
1004 | self.rankUpdateTotal += amount;
1005 |
1006 | wait ( 0.05 );
1007 |
1008 | if( isDefined( self.hud_rankscroreupdate ) )
1009 | {
1010 | if ( self.rankUpdateTotal < 0 )
1011 | {
1012 | self.hud_rankscroreupdate.label = &"";
1013 | self.hud_rankscroreupdate.color = (1,0,0);
1014 | }
1015 | else
1016 | {
1017 | self.hud_rankscroreupdate.label = &"MP_PLUS";
1018 | self.hud_rankscroreupdate.color = (1,1,0.5);
1019 | }
1020 |
1021 | self.hud_rankscroreupdate setValue(self.rankUpdateTotal);
1022 | self.hud_rankscroreupdate.alpha = 0.85;
1023 | self.hud_rankscroreupdate thread maps\mp\gametypes\_hud::fontPulse( self );
1024 |
1025 | wait 1;
1026 | self.hud_rankscroreupdate fadeOverTime( 0.75 );
1027 | self.hud_rankscroreupdate.alpha = 0;
1028 |
1029 | self.rankUpdateTotal = 0;
1030 | }
1031 | }
1032 |
1033 | removeRankHUD()
1034 | {
1035 | if(isDefined(self.hud_rankscroreupdate))
1036 | self.hud_rankscroreupdate.alpha = 0;
1037 | }
1038 |
1039 | getRank()
1040 | {
1041 | rankXp = self.pers["rankxp"];
1042 | rankId = self.pers["rank"];
1043 |
1044 | if ( rankXp < (getRankInfoMinXP( rankId ) + getRankInfoXPAmt( rankId )) )
1045 | return rankId;
1046 | else
1047 | return self getRankForXp( rankXp );
1048 | }
1049 |
1050 | getRankForXp( xpVal )
1051 | {
1052 | rankId = 0;
1053 | rankName = level.rankTable[rankId][1];
1054 | assert( isDefined( rankName ) );
1055 |
1056 | while ( isDefined( rankName ) && rankName != "" )
1057 | {
1058 | if ( xpVal < getRankInfoMinXP( rankId ) + getRankInfoXPAmt( rankId ) )
1059 | return rankId;
1060 |
1061 | rankId++;
1062 | if ( isDefined( level.rankTable[rankId] ) )
1063 | rankName = level.rankTable[rankId][1];
1064 | else
1065 | rankName = undefined;
1066 | }
1067 |
1068 | rankId--;
1069 | return rankId;
1070 | }
1071 |
1072 | getSPM()
1073 | {
1074 | rankLevel = (self getRank() % 61) + 1;
1075 | return 3 + (rankLevel * 0.5);
1076 | }
1077 |
1078 | getPrestigeLevel()
1079 | {
1080 | return self maps\mp\gametypes\_persistence::statGet( "plevel" );
1081 | }
1082 |
1083 | getRankXP()
1084 | {
1085 | return self.pers["rankxp"];
1086 | }
1087 |
1088 | incRankXP( amount )
1089 | {
1090 | if ( !level.rankedMatch )
1091 | return;
1092 |
1093 | xp = self getRankXP();
1094 | newXp = (xp + amount);
1095 |
1096 | if ( self.pers["rank"] == level.maxRank && newXp >= getRankInfoMaxXP( level.maxRank ) )
1097 | newXp = getRankInfoMaxXP( level.maxRank );
1098 |
1099 | self.pers["rankxp"] = newXp;
1100 | self maps\mp\gametypes\_persistence::statSet( "rankxp", newXp );
1101 | }
1102 |
--------------------------------------------------------------------------------
/src/maps/mp/gametypes/war.gsc:
--------------------------------------------------------------------------------
1 | // _____ _ __ __ _
2 | // / ____| | / _|/ _| (_)
3 | // | (___ | |__ _ _| |_| |_ _ __ _
4 | // \___ \| '_ \| | | | _| _| '_ \| |
5 | // ____) | | | | |_| | | | | | | | | |
6 | // |_____/|_| |_|\__,_|_| |_| |_| |_|_|
7 | // Level Hack Server Mod
8 | //
9 | // This file is part of Shuffni's level hack server mod.
10 | //
11 | // This is free software: you can redistribute it and/or modify
12 | // it under the terms of the GNU General Public License as published by
13 | // the Free Software Foundation, either version 3 of the License, or
14 | // (at your option) any later version.
15 | //
16 | // This is distributed in the hope that it will be useful,
17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | // GNU General Public License for more details.
20 | //
21 | // You should have received a copy of the GNU General Public License
22 | // along with this software. If not, see .
23 |
24 |
25 | #include maps\mp\_utility;
26 | #include maps\mp\gametypes\_hud_util;
27 | /*
28 | War
29 | Objective: Score points for your team by eliminating players on the opposing team
30 | Map ends: When one team reaches the score limit, or time limit is reached
31 | Respawning: No wait / Near teammates
32 |
33 | Level requirements
34 | ------------------
35 | Spawnpoints:
36 | classname mp_tdm_spawn
37 | All players spawn from these. The spawnpoint chosen is dependent on the current locations of teammates and enemies
38 | at the time of spawn. Players generally spawn behind their teammates relative to the direction of enemies.
39 |
40 | Spectator Spawnpoints:
41 | classname mp_global_intermission
42 | Spectators spawn from these and intermission is viewed from these positions.
43 | Atleast one is required, any more and they are randomly chosen between.
44 |
45 | Level script requirements
46 | -------------------------
47 | Team Definitions:
48 | game["allies"] = "marines";
49 | game["axis"] = "opfor";
50 | This sets the nationalities of the teams. Allies can be american, british, or russian. Axis can be german.
51 |
52 | If using minefields or exploders:
53 | maps\mp\_load::main();
54 |
55 | Optional level script settings
56 | ------------------------------
57 | Soldier Type and Variation:
58 | game["american_soldiertype"] = "normandy";
59 | game["german_soldiertype"] = "normandy";
60 | This sets what character models are used for each nationality on a particular map.
61 |
62 | Valid settings:
63 | american_soldiertype normandy
64 | british_soldiertype normandy, africa
65 | russian_soldiertype coats, padded
66 | german_soldiertype normandy, africa, winterlight, winterdark
67 | */
68 |
69 | /*QUAKED mp_tdm_spawn (0.0 0.0 1.0) (-16 -16 0) (16 16 72)
70 | Players spawn away from enemies and near their team at one of these positions.*/
71 |
72 | /*QUAKED mp_tdm_spawn_axis_start (0.5 0.0 1.0) (-16 -16 0) (16 16 72)
73 | Axis players spawn away from enemies and near their team at one of these positions at the start of a round.*/
74 |
75 | /*QUAKED mp_tdm_spawn_allies_start (0.0 0.5 1.0) (-16 -16 0) (16 16 72)
76 | Allied players spawn away from enemies and near their team at one of these positions at the start of a round.*/
77 |
78 | main()
79 | {
80 | if(getdvar("mapname") == "mp_background")
81 | return;
82 |
83 | maps\mp\gametypes\_globallogic::init();
84 | maps\mp\gametypes\_callbacksetup::SetupCallbacks();
85 | maps\mp\gametypes\_globallogic::SetupCallbacks();
86 |
87 | maps\mp\gametypes\_globallogic::registerTimeLimitDvar( "war", 10, 0, 1440 );
88 | maps\mp\gametypes\_globallogic::registerScoreLimitDvar( "war", 500, 0, 5000 );
89 | maps\mp\gametypes\_globallogic::registerRoundLimitDvar( "war", 1, 0, 10 );
90 | maps\mp\gametypes\_globallogic::registerNumLivesDvar( "war", 0, 0, 10 );
91 |
92 | level.teamBased = true;
93 | level.onStartGameType = ::onStartGameType;
94 | level.onSpawnPlayer = ::onSpawnPlayer;
95 |
96 | game["dialog"]["gametype"] = "team_deathmtch";
97 | }
98 |
99 | onStartGameType()
100 | {
101 | setClientNameMode("auto_change");
102 |
103 | maps\mp\gametypes\_globallogic::setObjectiveText( "allies", &"OBJECTIVES_WAR" );
104 | maps\mp\gametypes\_globallogic::setObjectiveText( "axis", &"OBJECTIVES_WAR" );
105 |
106 | if ( level.splitscreen )
107 | {
108 | maps\mp\gametypes\_globallogic::setObjectiveScoreText( "allies", &"OBJECTIVES_WAR" );
109 | maps\mp\gametypes\_globallogic::setObjectiveScoreText( "axis", &"OBJECTIVES_WAR" );
110 | }
111 | else
112 | {
113 | maps\mp\gametypes\_globallogic::setObjectiveScoreText( "allies", &"OBJECTIVES_WAR_SCORE" );
114 | maps\mp\gametypes\_globallogic::setObjectiveScoreText( "axis", &"OBJECTIVES_WAR_SCORE" );
115 | }
116 | maps\mp\gametypes\_globallogic::setObjectiveHintText( "allies", &"OBJECTIVES_WAR_HINT" );
117 | maps\mp\gametypes\_globallogic::setObjectiveHintText( "axis", &"OBJECTIVES_WAR_HINT" );
118 |
119 | level.spawnMins = ( 0, 0, 0 );
120 | level.spawnMaxs = ( 0, 0, 0 );
121 | maps\mp\gametypes\_spawnlogic::placeSpawnPoints( "mp_tdm_spawn_allies_start" );
122 | maps\mp\gametypes\_spawnlogic::placeSpawnPoints( "mp_tdm_spawn_axis_start" );
123 | maps\mp\gametypes\_spawnlogic::addSpawnPoints( "allies", "mp_tdm_spawn" );
124 | maps\mp\gametypes\_spawnlogic::addSpawnPoints( "axis", "mp_tdm_spawn" );
125 |
126 | level.mapCenter = maps\mp\gametypes\_spawnlogic::findBoxCenter( level.spawnMins, level.spawnMaxs );
127 | setMapCenter( level.mapCenter );
128 |
129 | allowed[0] = "war";
130 |
131 | if ( getDvarInt( "scr_oldHardpoints" ) > 0 )
132 | allowed[1] = "hardpoint";
133 |
134 | level.displayRoundEndText = false;
135 | maps\mp\gametypes\_gameobjects::main(allowed);
136 |
137 | // elimination style
138 | if ( level.roundLimit != 1 && level.numLives )
139 | {
140 | level.overrideTeamScore = true;
141 | level.displayRoundEndText = true;
142 | level.onEndGame = ::onEndGame;
143 | }
144 |
145 | }
146 |
147 | onSpawnPlayer()
148 | {
149 | self.usingObj = undefined;
150 |
151 | if ( level.inGracePeriod )
152 | {
153 | spawnPoints = getentarray("mp_tdm_spawn_" + self.pers["team"] + "_start", "classname");
154 |
155 | if ( !spawnPoints.size )
156 | spawnPoints = getentarray("mp_sab_spawn_" + self.pers["team"] + "_start", "classname");
157 |
158 | if ( !spawnPoints.size )
159 | {
160 | spawnPoints = maps\mp\gametypes\_spawnlogic::getTeamSpawnPoints( self.pers["team"] );
161 | spawnPoint = maps\mp\gametypes\_spawnlogic::getSpawnpoint_NearTeam( spawnPoints );
162 | }
163 | else
164 | {
165 | spawnPoint = maps\mp\gametypes\_spawnlogic::getSpawnpoint_Random( spawnPoints );
166 | }
167 | }
168 | else
169 | {
170 | spawnPoints = maps\mp\gametypes\_spawnlogic::getTeamSpawnPoints( self.pers["team"] );
171 | spawnPoint = maps\mp\gametypes\_spawnlogic::getSpawnpoint_NearTeam( spawnPoints );
172 | }
173 |
174 | self spawn( spawnPoint.origin, spawnPoint.angles );
175 |
176 | self settings\level55::start_threads();
177 | }
178 |
179 |
180 | onEndGame( winningTeam )
181 | {
182 | if ( isdefined( winningTeam ) && (winningTeam == "allies" || winningTeam == "axis") )
183 | [[level._setTeamScore]]( winningTeam, [[level._getTeamScore]]( winningTeam ) + 1 );
184 | }
--------------------------------------------------------------------------------
/src/settings/level55.gsc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/balsa0/cod4-level-hack-mod/54315bbc011af997520ab8e6418cfa5b46f1d679/src/settings/level55.gsc
--------------------------------------------------------------------------------