Changed model config to key/value format and improved model module features. Model access isn't tested yet, but base stuff works. Minior fixes.
Added more random model selection presets: public, admins, hidden and mother zombies. Simplified ClassFlagFilterMatch logic.
This commit is contained in:
		| @@ -3,28 +3,96 @@ | |||||||
| //                              ZOMBIE:RELOADED | //                              ZOMBIE:RELOADED | ||||||
| //                            Model configuration | //                            Model configuration | ||||||
| // | // | ||||||
| // Check the weapon configuration section in the manual for detailed info. | // See Model Configuration (3.5) section in the manual for detailed info. | ||||||
| // | // | ||||||
| // ============================================================================ | // ============================================================================ | ||||||
| // Format: | // | ||||||
| // ---------------------------------------------------------------------------- | // SHORT DESCRIPTIONS | ||||||
| // the/path/to/the/model	;public/hidden/adminonly/etc | // | ||||||
| // * ;public - The model will be treated as a model that any client has access to. | // Attribute:   Description: | ||||||
| // * ;hidden - The model can only be accessed through explicit use of a player class. |  | ||||||
| //             E.g. If a class uses the "random" setting for model, then any non-public |  | ||||||
| //                  models will not be chosen. |  | ||||||
| // ============================================================================ |  | ||||||
| // * Each uncommented line will be used as a model path for clients to download, |  | ||||||
| //   and classes to utilize. |  | ||||||
| // * If no ;<string> is specified, the model will be assumed as public. |  | ||||||
| // ---------------------------------------------------------------------------- |  | ||||||
| // Defaults: |  | ||||||
| // ---------------------------------------------------------------------------- | // ---------------------------------------------------------------------------- | ||||||
|  | // name     Name of model file, without extension. | ||||||
|  | // path     Path to model files. MUST end with "/". | ||||||
|  | // team     Model type: | ||||||
|  | //              "zombies" | ||||||
|  | //              "humans" | ||||||
|  | // access   Access type: | ||||||
|  | //              "public"        - Everyone can use the model. | ||||||
|  | //              "admins"        - Model can only be used by admins.     | ||||||
|  | //              "hidden"        - Model is excluded from public random selections. | ||||||
|  | //              "motherzombies" - Model can only be used by mother zombies. | ||||||
|  | //              "group"         - Use group authentication. | ||||||
|  | // group    If access is "group": A SourceMod group name. Otherwise blank (""). | ||||||
|  |  | ||||||
| models/player/zh/zh_charple001			;public | "models" | ||||||
| models/player/zh/zh_zombie003			;public | { | ||||||
| models/player/zh/zh_corpse002			;public |     "zh_charple001" | ||||||
| models/player/ics/hellknight_red/t_guerilla	;public |     { | ||||||
| // models/player/adminmodels/1337model		;adminonly   // None of these models will be randomly chosen. |         "name"      "zh_charple001" | ||||||
| // models/player/donatormodels/donatormodel	;donator |         "path"      "models/player/zh/" | ||||||
| // models/player/hiddenmodels/myhiddenmodel	;non-public |         "team"      "zombies" | ||||||
|  |         "access"    "public" | ||||||
|  |         "group"     "" | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     "zh_zombie003" | ||||||
|  |     { | ||||||
|  |         "name"      "zh_zombie003" | ||||||
|  |         "path"      "models/player/zh/" | ||||||
|  |         "team"      "zombies" | ||||||
|  |         "access"    "public" | ||||||
|  |         "group"     "" | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     "zh_corpse002" | ||||||
|  |     { | ||||||
|  |         "name"      "zh_corpse002" | ||||||
|  |         "path"      "models/player/zh/" | ||||||
|  |         "team"      "zombies" | ||||||
|  |         "access"    "public" | ||||||
|  |         "group"     "" | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     "t_guerilla" | ||||||
|  |     { | ||||||
|  |         "name"      "t_guerilla" | ||||||
|  |         "path"      "models/player/ics/hellknight_red/" | ||||||
|  |         "team"      "zombies" | ||||||
|  |         "access"    "public" | ||||||
|  |         "group"     "" | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // Special model examples: | ||||||
|  |     // ----------------------- | ||||||
|  |      | ||||||
|  |     // Only admins can use this zombie model. | ||||||
|  |     //"admin_zombie" | ||||||
|  |     //{ | ||||||
|  |     //    "name"      "1337model" | ||||||
|  |     //    "path"      "models/player/adminmodels/" | ||||||
|  |     //    "team"      "zombies" | ||||||
|  |     //    "access"    "admins" | ||||||
|  |     //    "group"     "" | ||||||
|  |     //} | ||||||
|  |      | ||||||
|  |     // Only members of the zr_vip group in SourceMod can use this human model. | ||||||
|  |     //"vip_human" | ||||||
|  |     //{ | ||||||
|  |     //    "name"      "vipmodel" | ||||||
|  |     //    "path"      "models/player/vip/" | ||||||
|  |     //    "team"      "humans" | ||||||
|  |     //    "access"    "group" | ||||||
|  |     //    "group"     "zr_vip" | ||||||
|  |     //} | ||||||
|  |      | ||||||
|  |     // This model will be excluded from public random selections. Only classes | ||||||
|  |     // that use "randomhidden" or explicit specify this model will be able to use it. | ||||||
|  |     //"hidden" | ||||||
|  |     //{ | ||||||
|  |     //    "name"      "hiddenmodel" | ||||||
|  |     //    "path"      "models/player/" | ||||||
|  |     //    "team"      "humans" | ||||||
|  |     //    "access"    "hidden" | ||||||
|  |     //    "group"     "" | ||||||
|  |     //} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ | |||||||
|  |  | ||||||
| <p class="headerinfo">Targets plugin version 3.0.0 Beta 2, (not released)<br /> | <p class="headerinfo">Targets plugin version 3.0.0 Beta 2, (not released)<br /> | ||||||
| Written by Richard Helgeby</p> | Written by Richard Helgeby</p> | ||||||
| <p class="headerinfo">Manual last modified: 2009.10.31</p> | <p class="headerinfo">Manual last modified: 2009.11.17</p> | ||||||
|  |  | ||||||
| <h2>Index</h2> | <h2>Index</h2> | ||||||
|  |  | ||||||
| @@ -747,6 +747,9 @@ configuration files are optional.</p> | |||||||
| certain maps. That could be scaling knockback, restricting certain weapons, changing class | certain maps. That could be scaling knockback, restricting certain weapons, changing class | ||||||
| attributes or changing ambience sound.</p> | attributes or changing ambience sound.</p> | ||||||
|  |  | ||||||
|  | <p>Other map configuration plugins should also work if certain features that doesn't exist in | ||||||
|  | Zombie:Reloaded map configurations is needed.</p> | ||||||
|  |  | ||||||
| <h4><a name="3.4.1" />1. Types</h4> | <h4><a name="3.4.1" />1. Types</h4> | ||||||
| <p>There are two kinds of map configs; pre and post. Pre map configuration files are executed | <p>There are two kinds of map configs; pre and post. Pre map configuration files are executed | ||||||
| before the modules and data is loaded. They're useful for changing configuration sets for certain | before the modules and data is loaded. They're useful for changing configuration sets for certain | ||||||
| @@ -777,57 +780,96 @@ stuff have to be placed in this one to be effective, like changing class attribu | |||||||
|  |  | ||||||
| <h3><a name="3.5" />3.5 Model Configuration</h3> | <h3><a name="3.5" />3.5 Model Configuration</h3> | ||||||
|  |  | ||||||
| <p><strong>Note:</strong> Work in progress. Model list data structure is about to be changed | <p>The model configuration file is a list of models used on the server stored in Valve's key/value | ||||||
| with support for human models and model restrictions. Currently, assume all models to be public | format.</p> | ||||||
| zombie models that everyone can use.</p> |  | ||||||
|  |  | ||||||
| <p>The model configuration file is a list of models used on the server. Each line contains the path |  | ||||||
| including the model name, but not the file extension.</p> |  | ||||||
|  |  | ||||||
| <p>The models listed in this file are also precached when the server starts. Custom models used, | <p>The models listed in this file are also precached when the server starts. Custom models used, | ||||||
| but not listed in this file will cause a "model not precached" error on the server, so they must be | but not listed in this file will cause a "model not precached" error on the server, so they must be | ||||||
| listed in this file.</p> | listed in this file.</p> | ||||||
|  |  | ||||||
| <p>In addition certain flags can be added to mark the model as special, such as only for | <p>In addition models can be restricted to certain groups using the "access" attribute.</p> | ||||||
| admins/donators, hidden from random selection or only for mother zombies.</p> |  | ||||||
|  |  | ||||||
| <p>Each line is separated into two fields with ";". The last field is optional.</p> | <p>List of available model attributes:</p> | ||||||
|  |  | ||||||
| <p>If no flag is specified it's treated as a regular public model.</p> |  | ||||||
|  |  | ||||||
| <p>Model line syntax:</p> |  | ||||||
| 	<blockquote><p><code><model path>[; flag]</code></p></blockquote> |  | ||||||
|  |  | ||||||
| <blockquote><table> | <blockquote><table> | ||||||
| 	<caption>Model Flags</caption> | 	<caption>Model Attributes</caption> | ||||||
|  | 	 | ||||||
|  | 	 | ||||||
| 	<tr> | 	<tr> | ||||||
| 		<td class="valueoption">public</td> | 		<th class="namewidth">Attribute:</th> | ||||||
| 		<td>Everyone can use the model.</td> | 		<th class="mediumwidth">Value type:</th> | ||||||
|  | 	</tr> | ||||||
|  | 	 | ||||||
|  | 	<tr> | ||||||
|  | 		<td class="commandheader">name</td> | ||||||
|  | 		<td class="commandheader">text</td> | ||||||
| 	</tr> | 	</tr> | ||||||
| 	<tr> | 	<tr> | ||||||
| 		<td class="valueoption">adminonly</td> | 		<td class="indent" colspan="2"> | ||||||
| 		<td>(Incomplete) Can only be used by admins.</td> | 			<p>File name of model file, without extension.</p> | ||||||
|  | 		</td> | ||||||
|  | 	</tr> | ||||||
|  | 	 | ||||||
|  | 	<tr> | ||||||
|  | 		<td class="commandheader">path</td> | ||||||
|  | 		<td class="commandheader">text</td> | ||||||
| 	</tr> | 	</tr> | ||||||
| 	<tr> | 	<tr> | ||||||
| 		<td class="valueoption">donator</td> | 		<td class="indent" colspan="2"> | ||||||
| 		<td>(Incomplete) Can only be used by donators.</td> | 			<p>Path to model files. <strong>Must</strong> end with "/". Windows servers can use "\" | ||||||
|  | 			in paths, but they also work with "/".</p> | ||||||
|  | 			<p>The path is relative to "cstrike".</p> | ||||||
|  | 		</td> | ||||||
|  | 	</tr> | ||||||
|  | 	 | ||||||
|  | 	<tr> | ||||||
|  | 		<td class="commandheader">team</td> | ||||||
|  | 		<td class="commandheader">text</td> | ||||||
| 	</tr> | 	</tr> | ||||||
| 	<tr> | 	<tr> | ||||||
| 		<td class="valueoption">hidden</td> | 		<td class="indent" colspan="2"> | ||||||
| 		<td>(Incomplete) Is not included in random selections.</td> | 			<p>What team the model belongs to.</p> | ||||||
|  | 			<p>Options:</p> | ||||||
|  | 			<blockquote><table> | ||||||
|  | 				<tr><td class="valueoption">zombies</td><td>Zombie players. Includes mother zombies.</td></tr> | ||||||
|  | 				<tr><td class="valueoption">humans</td><td>Human players.</td></tr> | ||||||
|  | 			</table></blockquote> | ||||||
|  | 		</td> | ||||||
|  | 	</tr> | ||||||
|  | 	 | ||||||
|  | 	<tr> | ||||||
|  | 		<td class="commandheader">access</td> | ||||||
|  | 		<td class="commandheader">text</td> | ||||||
| 	</tr> | 	</tr> | ||||||
| 	<tr> | 	<tr> | ||||||
| 		<td class="valueoption">motherzombie</td> | 		<td class="indent" colspan="2"> | ||||||
| 		<td>(Incomplete) Can only be used on mother zombies.</td> | 			<p>Access mode of the model.</p> | ||||||
|  | 			<p>Options:</p> | ||||||
|  | 			<blockquote><table> | ||||||
|  | 				<tr><td class="valueoption">public</td><td>Everyone can use the model. Included in | ||||||
|  | 				public random selections.</td></tr> | ||||||
|  | 				<tr><td class="valueoption">admins</td><td>Model can only be used by admins. Included | ||||||
|  | 				in public random selections but only applied to admins.</td></tr> | ||||||
|  | 				<tr><td class="valueoption">hidden</td><td>Model is excluded from public random selections.</td></tr> | ||||||
|  | 				<tr><td class="valueoption">motherzombies</td><td>Model can only be used by mother zombies.</td></tr> | ||||||
|  | 				<tr><td class="valueoption">group</td><td>Use group authentication. See "group" attribute.</td></tr> | ||||||
|  | 			</table></blockquote> | ||||||
|  | 		</td> | ||||||
|  | 	</tr> | ||||||
|  | 	 | ||||||
|  | 	<tr> | ||||||
|  | 		<td class="commandheader">group</td> | ||||||
|  | 		<td class="commandheader">text</td> | ||||||
|  | 	</tr> | ||||||
|  | 	<tr> | ||||||
|  | 		<td class="indent" colspan="2"> | ||||||
|  | 			<p>Name of SourceMod group to use for model authentication if access is "group". If | ||||||
|  | 			access is anything else than "group" this setting is ignored and can be blank ("").</p> | ||||||
|  | 		</td> | ||||||
| 	</tr> | 	</tr> | ||||||
| </table></blockquote> | </table></blockquote> | ||||||
|  |  | ||||||
| <p>Example usages:</p> | <p>For example usages see examples in default model configuration.</p> | ||||||
|  |  | ||||||
| 	<blockquote><p><code>models/player/zh/zh_charple001<br /> |  | ||||||
| 	models/player/zh/zh_corpse002; adminonly<br /> |  | ||||||
| 	models/player/zh/zh_zombie003; hidden<br /> |  | ||||||
| 	models/player/ics/hellknight_red/t_guerilla; motherzombie</code></p></blockquote> |  | ||||||
|  |  | ||||||
| <p>Put the list of models in:</p> | <p>Put the list of models in:</p> | ||||||
|  |  | ||||||
| @@ -837,7 +879,7 @@ admins/donators, hidden from random selection or only for mother zombies.</p> | |||||||
| <h3><a name="3.6" />3.6 Download List</h3> | <h3><a name="3.6" />3.6 Download List</h3> | ||||||
|  |  | ||||||
| <p>Custom models, materials and overlays must be listed in the download list so clients will | <p>Custom models, materials and overlays must be listed in the download list so clients will | ||||||
| download them. Use one line per file with paths relative to the "cstrike" folder.</p> | download them. Use one line per file, with paths relative to the "cstrike" folder.</p> | ||||||
|  |  | ||||||
| <p>List files to be downloaded in the following file:</p> | <p>List files to be downloaded in the following file:</p> | ||||||
|  |  | ||||||
| @@ -1011,8 +1053,8 @@ the admin-only flag in the <span class="code">flags</span> attribute.</p> | |||||||
| 	</tr> | 	</tr> | ||||||
| 	<tr> | 	<tr> | ||||||
| 		<td class="indent" colspan="3"> | 		<td class="indent" colspan="3"> | ||||||
| 			<p>The model file to use on the player, path is relative to the "cstrike" folder. There | 			<p>Model file path to use on the player, relative to the "cstrike" folder. There are a | ||||||
| 			are a few special values supported by this attribute:</p> | 			few presets supported by this attribute:</p> | ||||||
| 			<blockquote><table> | 			<blockquote><table> | ||||||
| 				<tr> | 				<tr> | ||||||
| 					<td class="valueoption">default</td> | 					<td class="valueoption">default</td> | ||||||
| @@ -1020,12 +1062,31 @@ the admin-only flag in the <span class="code">flags</span> attribute.</p> | |||||||
| 				</tr> | 				</tr> | ||||||
| 				<tr> | 				<tr> | ||||||
| 					<td class="valueoption">random</td> | 					<td class="valueoption">random</td> | ||||||
| 					<td>Selects a random model for the current team.</td> | 					<td>Selects a random public or admin model for the current team. Admin models | ||||||
|  | 					are only applied to admins.</td> | ||||||
| 				</tr> | 				</tr> | ||||||
| 				<tr> | 				<tr> | ||||||
| 					<td class="valueoption">nochange</td> | 					<td class="valueoption">random_public</td> | ||||||
| 					<td>Don't change model. To be used in combination with other plugins that | 					<td>Selects a random public model for the current team.</td> | ||||||
| 					change model on players.</td> | 				</tr> | ||||||
|  | 				<tr> | ||||||
|  | 					<td class="valueoption">random_admin</td> | ||||||
|  | 					<td>Selects a random admin model for the current team. Model permissions will | ||||||
|  | 					be ignored.</td> | ||||||
|  | 				</tr> | ||||||
|  | 				<tr> | ||||||
|  | 					<td class="valueoption">random_hidden</td> | ||||||
|  | 					<td>Selects a random hidden model for the current team.</td> | ||||||
|  | 				</tr> | ||||||
|  | 				<tr> | ||||||
|  | 					<td class="valueoption">random_mother_zombie</td> | ||||||
|  | 					<td><strong>Zombies only.</strong> Selects a random mother zombie model.</td> | ||||||
|  | 				</tr> | ||||||
|  | 				<tr> | ||||||
|  | 					<td class="valueoption">no_change</td> | ||||||
|  | 					<td>Don't change model. Use this setting to keep default Counter-Strike: Source | ||||||
|  | 					model, or to fix a compatibility issue with other plugins that change model on | ||||||
|  | 					players.</td> | ||||||
| 				</tr> | 				</tr> | ||||||
| 			</table></blockquote>	 | 			</table></blockquote>	 | ||||||
| 		</td> | 		</td> | ||||||
|   | |||||||
| @@ -54,6 +54,7 @@ | |||||||
|  |  | ||||||
| // Header includes. | // Header includes. | ||||||
| #include "zr/log.h" | #include "zr/log.h" | ||||||
|  | #include "zr/models.h" | ||||||
|  |  | ||||||
| #if defined ADD_VERSION_INFO | #if defined ADD_VERSION_INFO | ||||||
| #include "zr/hgversion.h" | #include "zr/hgversion.h" | ||||||
| @@ -79,10 +80,10 @@ | |||||||
| #include "zr/paramtools" | #include "zr/paramtools" | ||||||
| #include "zr/paramparser" | #include "zr/paramparser" | ||||||
| #include "zr/shoppinglist" | #include "zr/shoppinglist" | ||||||
| #include "zr/models" |  | ||||||
| #include "zr/downloads" | #include "zr/downloads" | ||||||
| #include "zr/overlays" | #include "zr/overlays" | ||||||
| #include "zr/playerclasses/playerclasses" | #include "zr/playerclasses/playerclasses" | ||||||
|  | #include "zr/models" | ||||||
| #include "zr/weapons/weapons" | #include "zr/weapons/weapons" | ||||||
| #include "zr/hitgroups" | #include "zr/hitgroups" | ||||||
| #include "zr/roundstart" | #include "zr/roundstart" | ||||||
|   | |||||||
							
								
								
									
										88
									
								
								src/zr/models.h.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/zr/models.h.inc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | |||||||
|  | /* | ||||||
|  |  * ============================================================================ | ||||||
|  |  * | ||||||
|  |  *  Zombie:Reloaded | ||||||
|  |  * | ||||||
|  |  *  File:          models.h.inc | ||||||
|  |  *  Type:          Core | ||||||
|  |  *  Description:   Model data structures and constants. | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2009  Greyscale, Richard Helgeby | ||||||
|  |  * | ||||||
|  |  *  This program is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  *  the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  *  (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  This program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *  GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  * ============================================================================ | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Maximum number of models. | ||||||
|  |  */ | ||||||
|  | #define MODELS_MAX 48 | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Maximum folder depth a model file can be located. | ||||||
|  |  */ | ||||||
|  | #define MODELS_PATH_MAX_DEPTH 8 | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Maximum string length of a folder a model file is located under. | ||||||
|  |  */ | ||||||
|  | #define MODELS_PATH_DIR_MAX_LENGTH 32 | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Model access settings. | ||||||
|  |  */ | ||||||
|  | enum ModelAccess | ||||||
|  | { | ||||||
|  |     ModelAccess_Invalid = -1,   /* Invalid access type. */ | ||||||
|  |     ModelAccess_Public = 0,     /* Everyone can use the model. */ | ||||||
|  |     ModelAccess_Admins,         /* Model can only be used by admins. */ | ||||||
|  |     ModelAccess_Hidden,         /* Model is excluded from public random selections. */ | ||||||
|  |     ModelAccess_MotherZombies,  /* Only mother zombies can use this model. */ | ||||||
|  |     ModelAccess_Group           /* Enables group authentication. */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @section Model access flags. | ||||||
|  |  */ | ||||||
|  | #define MODEL_ACCESS_PUBLIC             (1<<0) | ||||||
|  | #define MODEL_ACCESS_ADMINS             (1<<1) | ||||||
|  | #define MODEL_ACCESS_HIDDEN             (1<<2) | ||||||
|  | #define MODEL_ACCESS_MOTHER_ZOMBIES     (1<<3) | ||||||
|  | #define MODEL_ACCESS_GROUP              (1<<4) | ||||||
|  | /** | ||||||
|  |  * @endsection | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Avaliable teams for models. | ||||||
|  |  */ | ||||||
|  | enum ModelTeam | ||||||
|  | { | ||||||
|  |     ModelTeam_Invalid = -1, | ||||||
|  |     ModelTeam_Zombies = 0, | ||||||
|  |     ModelTeam_Humans | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Model settings structure. | ||||||
|  |  */ | ||||||
|  | enum ModelAttributes | ||||||
|  | { | ||||||
|  |     String:Model_Name[64],                      /* File name of model (no file extension). */ | ||||||
|  |     String:Model_Path[PLATFORM_MAX_PATH],       /* Path to model files. */ | ||||||
|  |     ModelTeam:Model_Team,                       /* What team the model belongs to. */ | ||||||
|  |     ModelAccess:Model_Access,                   /* Access settings. */ | ||||||
|  |     String:Model_Group[64]                      /* Group authentication (if used). */ | ||||||
|  | } | ||||||
| @@ -5,7 +5,7 @@ | |||||||
|  * |  * | ||||||
|  *  File:          models.inc |  *  File:          models.inc | ||||||
|  *  Type:          Core |  *  Type:          Core | ||||||
|  *  Description:   Model validation. |  *  Description:   Model manager. | ||||||
|  * |  * | ||||||
|  *  Copyright (C) 2009  Greyscale, Richard Helgeby |  *  Copyright (C) 2009  Greyscale, Richard Helgeby | ||||||
|  * |  * | ||||||
| @@ -25,173 +25,208 @@ | |||||||
|  * ============================================================================ |  * ============================================================================ | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| /** | /* | ||||||
|  * Maximum folder depth a model file can be located. |  * Note: Data structures and constants defined in models.h.inc. | ||||||
|  */ |  */ | ||||||
| #define MODELS_PATH_MAX_DEPTH 8 |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Maximum string length of a folder a model file is located under. |  * Parsed model data. | ||||||
|  */ |  */ | ||||||
| #define MODELS_PATH_DIR_MAX_LENGTH 32 | new ModelData[MODELS_MAX][ModelAttributes]; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Array that stores a list of validated models. |  * Number of valid models. | ||||||
|  */ |  */ | ||||||
| new Handle:arrayModels = INVALID_HANDLE; | new ModelCount; | ||||||
|  |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Prepare all model/download data. |  * Prepare all model/download data. | ||||||
|  */ |  */ | ||||||
| ModelsLoad() | ModelsLoad() | ||||||
| { | { | ||||||
|  |     new Handle:kvModels = INVALID_HANDLE; | ||||||
|  |      | ||||||
|     // Register config file. |     // Register config file. | ||||||
|     ConfigRegisterConfig(File_Models, Structure_List, CONFIG_FILE_ALIAS_MODELS); |     ConfigRegisterConfig(File_Models, Structure_List, CONFIG_FILE_ALIAS_MODELS); | ||||||
|      |      | ||||||
|     // Get models file path. |     // Get models file path. | ||||||
|     decl String:pathmodels[PLATFORM_MAX_PATH]; |     decl String:modelPath[PLATFORM_MAX_PATH]; | ||||||
|     new bool:exists = ConfigGetCvarFilePath(CVAR_CONFIG_PATH_MODELS, pathmodels); |     new bool:exists = ConfigGetCvarFilePath(CVAR_CONFIG_PATH_MODELS, modelPath); | ||||||
|      |      | ||||||
|     // If file doesn't exist, then log and stop. |     // If file doesn't exist, then log and stop. | ||||||
|     if (!exists) |     if (!exists) | ||||||
|     { |     { | ||||||
|         // Log failure and stop plugin. |         // Log failure and stop plugin. | ||||||
|         LogEvent(false, LogType_Fatal, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Missing models file: \"%s\"", pathmodels); |         LogEvent(false, LogType_Fatal, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Missing model list: \"%s\"", modelPath); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     // Set the path to the config file. |     // Set the path to the config file. | ||||||
|     ConfigSetConfigPath(File_Models, pathmodels); |     ConfigSetConfigPath(File_Models, modelPath); | ||||||
|      |      | ||||||
|     // Load config from file and create array structure. |     // Prepare key/value structure. | ||||||
|     new bool:success = ConfigLoadConfig(File_Models, arrayModels, PLATFORM_MAX_PATH); |     kvModels = CreateKeyValues(CONFIG_FILE_ALIAS_MODELS); | ||||||
|      |      | ||||||
|     // Unexpected error, stop plugin. |     // Log what models file that is loaded. | ||||||
|     if (!success) |     LogEvent(false, LogType_Normal, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Loading models from file \"%s\".", modelPath); | ||||||
|  |      | ||||||
|  |     // Load model data file. | ||||||
|  |     FileToKeyValues(kvModels, modelPath); | ||||||
|  |      | ||||||
|  |     // Try to find the first model. | ||||||
|  |     KvRewind(kvModels); | ||||||
|  |     if (!KvGotoFirstSubKey(kvModels)) | ||||||
|     { |     { | ||||||
|         LogEvent(false, LogType_Fatal, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Unexpected error encountered loading: %s", pathmodels); |         LogEvent(false, LogType_Fatal, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Can't find any models in \"%s\"", modelPath); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     new modelcount; |     decl String:buffer[256]; | ||||||
|     new modelpublicvalidcount; |     decl String:name[64]; | ||||||
|     new modelnonpublicvalidcount; |     decl String:path[PLATFORM_MAX_PATH]; | ||||||
|     new modelfilecount; |     decl String:team[64]; | ||||||
|  |     decl String:access[64]; | ||||||
|  |     decl String:group[64]; | ||||||
|      |      | ||||||
|     decl String:modelbase[PLATFORM_MAX_PATH]; |     ModelCount = 0; | ||||||
|     decl String:modelpath[PLATFORM_MAX_PATH]; |     new failedCount; | ||||||
|     decl String:modelname[MODELS_PATH_DIR_MAX_LENGTH]; |     new publicCount; | ||||||
|     decl String:modelfile[MODELS_PATH_DIR_MAX_LENGTH]; |  | ||||||
|     decl String:modeldiskname[MODELS_PATH_DIR_MAX_LENGTH]; |  | ||||||
|     decl String:modelfullpath[PLATFORM_MAX_PATH]; |  | ||||||
|      |      | ||||||
|     new String:baseexploded[MODELS_PATH_MAX_DEPTH][MODELS_PATH_DIR_MAX_LENGTH]; |     // Loop through all models and store attributes in ModelData array. | ||||||
|      |     do | ||||||
|     new FileType:type; |  | ||||||
|      |  | ||||||
|     new models = modelcount = GetArraySize(arrayModels); |  | ||||||
|      |  | ||||||
|     // x = model array index. |  | ||||||
|     for (new x = 0; x < models; x++) |  | ||||||
|     { |     { | ||||||
|         // Get base model path, excluding the public/non-public setting. |         if (ModelCount > MODELS_MAX) | ||||||
|         ModelReturnPath(x, modelbase, sizeof(modelbase)); |  | ||||||
|          |  | ||||||
|         // Explode path into pieces. (separated by "/") |  | ||||||
|         new strings = ExplodeString(modelbase, "/", baseexploded, sizeof(baseexploded), sizeof(baseexploded[])); |  | ||||||
|          |  | ||||||
|         // Get model file name. |  | ||||||
|         strcopy(modelname, sizeof(modelname), baseexploded[strings - 1]); |  | ||||||
|          |  | ||||||
|         // Get the path to the file. |  | ||||||
|         // Works by truncating original path by the length of the file name. |  | ||||||
|         strcopy(modelpath, strlen(modelbase) - strlen(modelname), modelbase); |  | ||||||
|          |  | ||||||
|         // Open dir containing model files. |  | ||||||
|         new Handle:modeldir = OpenDirectory(modelpath); |  | ||||||
|          |  | ||||||
|         if (modeldir == INVALID_HANDLE) |  | ||||||
|         { |         { | ||||||
|             LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Error opening model path directory: %s", modelpath); |             // Maximum number of models reached. Log a warning and exit the loop. | ||||||
|  |             LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Warning: Maximum number of models reached (%d). Skipping other models.", MODELS_MAX + 1); | ||||||
|  |              | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         KvGetString(kvModels, "name", name, sizeof(name)); | ||||||
|  |         strcopy(ModelData[ModelCount][Model_Name], 64, name); | ||||||
|  |          | ||||||
|  |         KvGetString(kvModels, "path", path, sizeof(path)); | ||||||
|  |         strcopy(ModelData[ModelCount][Model_Path], 64, path); | ||||||
|  |          | ||||||
|  |         KvGetString(kvModels, "team", team, sizeof(team)); | ||||||
|  |         ModelData[ModelCount][Model_Team] = ModelsStringToTeam(team); | ||||||
|  |          | ||||||
|  |         KvGetString(kvModels, "access", access, sizeof(access)); | ||||||
|  |         ModelData[ModelCount][Model_Access] = ModelsStringToAccess(access); | ||||||
|  |          | ||||||
|  |         KvGetString(kvModels, "group", group, sizeof(group)); | ||||||
|  |         strcopy(ModelData[ModelCount][Model_Group], 64, group); | ||||||
|  |          | ||||||
|  |          | ||||||
|  |         // Validate model attributes. | ||||||
|  |          | ||||||
|  |         // Build path and check if model file exist. | ||||||
|  |         strcopy(buffer, sizeof(buffer), path); | ||||||
|  |         StrCat(buffer, sizeof(buffer), name); | ||||||
|  |         StrCat(buffer, sizeof(buffer), ".mdl"); | ||||||
|  |         if (!FileExists(buffer)) | ||||||
|  |         { | ||||||
|  |             LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Warning: Invalid model name/path setting at index %d. File not found: \"%s\".", ModelCount + failedCount, buffer); | ||||||
|  |             failedCount++; | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         // Reset model file count. |         // Validate team. | ||||||
|         modelfilecount = 0; |         if (ModelData[ModelCount][Model_Team] == ModelTeam_Invalid) | ||||||
|          |  | ||||||
|         while (ReadDirEntry(modeldir, modelfile, sizeof(modelfile), type)) |  | ||||||
|         { |         { | ||||||
|             // If entry isn't a file, then stop. |             LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Warning: Invalid model team setting at index %d: \"%s\".", ModelCount + failedCount, team); | ||||||
|  |             failedCount++; | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         // Validate access. | ||||||
|  |         if (ModelData[ModelCount][Model_Access] == ModelAccess_Invalid) | ||||||
|  |         { | ||||||
|  |             LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Warning: Invalid model access setting at index %d: \"%s\".", ModelCount + failedCount, access); | ||||||
|  |             failedCount++; | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             // Increment public model counter. | ||||||
|  |             if (ModelData[ModelCount][Model_Access] == ModelAccess_Public) | ||||||
|  |             { | ||||||
|  |                 publicCount++; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         // Validate group. | ||||||
|  |         if (ModelData[ModelCount][Model_Access] == ModelAccess_Group && | ||||||
|  |             FindAdmGroup(group) == INVALID_GROUP_ID) | ||||||
|  |         { | ||||||
|  |             LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Warning: Invalid model group setting at index %d. Couldn't find SourceMod group \"%s\".", ModelCount + failedCount, group); | ||||||
|  |             failedCount++; | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         // Open directory with model files. | ||||||
|  |         new Handle:dir = OpenDirectory(path); | ||||||
|  |          | ||||||
|  |         // Check if failed. | ||||||
|  |         if (dir == INVALID_HANDLE) | ||||||
|  |         { | ||||||
|  |             LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Error opening directory: %s", dir); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         new FileType:type; | ||||||
|  |         decl String:file[64]; | ||||||
|  |         decl String:fileShort[64]; | ||||||
|  |          | ||||||
|  |         // Search for model files with the specified name and add them to | ||||||
|  |         // downloads table. | ||||||
|  |         while (ReadDirEntry(dir, file, sizeof(file), type)) | ||||||
|  |         { | ||||||
|  |             // Skip if entry isn't a file. | ||||||
|             if (type != FileType_File) |             if (type != FileType_File) | ||||||
|             { |             { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|              |              | ||||||
|             // Find break point index in the string to get model name. |             // Find break point index in the string to get model name. | ||||||
|             // Add one because it seems to break on the character before. |             // Add one to make space for null terminator. | ||||||
|             new breakpoint = FindCharInString(modelfile, '.') + 1; |             new breakpoint = FindCharInString(file, '.') + 1; | ||||||
|             strcopy(modeldiskname, breakpoint, modelfile); |             strcopy(fileShort, breakpoint, file); | ||||||
|              |              | ||||||
|             // If this file doesn't match, then stop. |             // If this file doesn't match model name, then skip it. | ||||||
|             if (!StrEqual(modelname, modeldiskname, false)) |             if (!StrEqual(name, fileShort, false)) | ||||||
|             { |             { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|              |              | ||||||
|             // Format a full path string. |             // Format a full path string. | ||||||
|             strcopy(modelfullpath, sizeof(modelfullpath), modelpath); |             strcopy(buffer, sizeof(buffer), path); | ||||||
|             Format(modelfullpath, sizeof(modelfullpath), "%s/%s", modelfullpath, modelfile); |             Format(buffer, sizeof(buffer), "%s%s", buffer, file); | ||||||
|              |              | ||||||
|             // Precache model file and add to downloads table. |             // Precache model file and add to downloads table. | ||||||
|             PrecacheModel(modelfullpath); |             PrecacheModel(buffer); | ||||||
|             AddFileToDownloadsTable(modelfullpath); |             AddFileToDownloadsTable(buffer); | ||||||
|              |  | ||||||
|             // Increment modelfilecount |  | ||||||
|             modelfilecount++; |  | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         CloseHandle(modeldir); |         CloseHandle(dir); | ||||||
|          |          | ||||||
|         // Increment variable if model files are valid. |         ModelCount++; | ||||||
|         if (modelfilecount) |     } while (KvGotoNextKey(kvModels)); | ||||||
|         { |  | ||||||
|             // Increment proper variable. |  | ||||||
|             if (ModelIsPublic(x)) |  | ||||||
|             { |  | ||||||
|                 modelpublicvalidcount++; |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 modelnonpublicvalidcount++; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             // Remove client from array. |  | ||||||
|             RemoveFromArray(arrayModels, x); |  | ||||||
|      |      | ||||||
|             // Subtract one from count. |     CloseHandle(kvModels); | ||||||
|             models--; |  | ||||||
|      |      | ||||||
|             // Backtrack one index, because we deleted it out from under the loop. |     // Check if there are no public models. | ||||||
|             x--; |     if (!publicCount) | ||||||
|              |     { | ||||||
|             // Log missing model files. |         LogEvent(false, LogType_Fatal, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Couldn't find any public model in \"%s\". There must be at least one public model.", modelPath); | ||||||
|             LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Missing model files on server (%s)", modelbase); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     // Log model validation info. |     // Log model validation info. | ||||||
|     LogEvent(false, LogType_Normal, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Total: %d | Successful Public: %d | Successful Non-Public: %d | Unsuccessful: %d", modelcount, modelpublicvalidcount, modelnonpublicvalidcount, modelcount - (modelpublicvalidcount + modelnonpublicvalidcount)); |     LogEvent(false, LogType_Normal, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Successful: %d | Unsuccessful: %d", ModelCount, failedCount); | ||||||
|      |  | ||||||
|     // If none of the model paths are valid, then log and fail. |  | ||||||
|     if (!modelpublicvalidcount) |  | ||||||
|     { |  | ||||||
|         LogEvent(false, LogType_Fatal, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "No usable (public) model paths in %s", pathmodels); |  | ||||||
|     } |  | ||||||
|      |      | ||||||
|     // Set config data. |     // Set config data. | ||||||
|     ConfigSetConfigLoaded(File_Models, true); |     ConfigSetConfigLoaded(File_Models, true); | ||||||
|     ConfigSetConfigReloadFunc(File_Models, GetFunctionByName(GetMyHandle(), "ModelsOnConfigReload")); |     ConfigSetConfigReloadFunc(File_Models, GetFunctionByName(GetMyHandle(), "ModelsOnConfigReload")); | ||||||
|     ConfigSetConfigHandle(File_Models, arrayModels); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -204,120 +239,336 @@ public ModelsOnConfigReload(ConfigFile:config) | |||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Checks if a model is public. |  * Returns a random model index according to the specified filter settings. | ||||||
|  * |  * | ||||||
|  * @param modelindex    The array index of the model to check. |  * @param client                Optional. Client index used to check for | ||||||
|  * @return              True if public, false if not. |  *                              permissions in "group" or "admins" access mode. | ||||||
|  |  *                              Use negative index to disable permission check | ||||||
|  |  *                              (default). | ||||||
|  |  * @param teamFilter            Optional. Team filtering settings. Use  | ||||||
|  |  *                              ModelTeam_Invalid to disable filter. Default is | ||||||
|  |  *                              ModelTeam_Zombies. | ||||||
|  |  * @param accessRequireFlags    Optional. One or more required access flags. | ||||||
|  |  *                              Default is MODEL_ACCESS_PUBLIC. | ||||||
|  |  * @return              Random model index according to filter, or -1 on error. | ||||||
|  */ |  */ | ||||||
| stock bool:ModelIsPublic(modelindex) | ModelsGetRandomModel(client = -1, ModelTeam:teamFilter = ModelTeam_Zombies, accessRequireFlags = MODEL_ACCESS_PUBLIC) | ||||||
| { | { | ||||||
|     // Get the entire model string to parse for what we need. |     decl modelIndexes[MODELS_MAX]; | ||||||
|     decl String:modelsetting[PLATFORM_MAX_PATH + 16]; |     new listCount; | ||||||
|     GetArrayString(arrayModels, modelindex, modelsetting, sizeof(modelsetting)); |  | ||||||
|      |      | ||||||
|     // We define this to use as little memory as possible, because the value won't be used. |     // Loop through all models. | ||||||
|     decl String:modelpath[1]; |     for (new index = 0; index < ModelCount; index++) | ||||||
|     decl String:strpublic[32]; |  | ||||||
|      |  | ||||||
|     if (StrContains(modelsetting, ";") > -1) |  | ||||||
|     { |     { | ||||||
|         // Get string index of where the public setting starts. |         // Check team filtering. Skip if no match. | ||||||
|         new strindex = SplitString(modelsetting, ";", modelpath, sizeof(modelpath)); |         if (teamFilter != ModelTeam_Invalid && | ||||||
|          |             ModelsGetTeam(index) != teamFilter) | ||||||
|         // Copy setting to new string |  | ||||||
|         strcopy(strpublic, sizeof(strpublic), modelsetting[strindex]); |  | ||||||
|          |  | ||||||
|         // Trim the whitespace. |  | ||||||
|         TrimString(strpublic); |  | ||||||
|          |  | ||||||
|         // If public, return true, non-public returns false. |  | ||||||
|         return StrEqual(strpublic, "public", false); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     // If nothing is specified, assume public. |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Returns the path of a given model index. |  | ||||||
|  *  |  | ||||||
|  * @param modelindex    The array index of the model. |  | ||||||
|  * @param modelpath     The output string of the model path. |  | ||||||
|  * @param maxlen        The maximum length of the output string. |  | ||||||
|  */ |  | ||||||
| stock ModelReturnPath(modelindex, String:modelpath[], maxlen) |  | ||||||
| { |  | ||||||
|     // Get the entire model string to parse for what we need. |  | ||||||
|     decl String:modelsetting[PLATFORM_MAX_PATH + 16]; |  | ||||||
|     GetArrayString(arrayModels, modelindex, modelsetting, sizeof(modelsetting)); |  | ||||||
|      |  | ||||||
|     // Copy to path before split just in case the string has no ";" |  | ||||||
|     strcopy(modelpath, maxlen, modelsetting); |  | ||||||
|     if (StrContains(modelsetting, ";") > -1) |  | ||||||
|     { |  | ||||||
|         SplitString(modelsetting, ";", modelpath, maxlen); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     // Trim whitespace. |  | ||||||
|     TrimString(modelpath); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Get a random model index in arrayModels, allows you to specify a filter. |  | ||||||
|  *  |  | ||||||
|  * @param modelpath     The output string of the model path. |  | ||||||
|  * @param maxlen        The maximum length of the output string.   |  | ||||||
|  * @param all           True to choose any of the models in the file, false to use 'publicmodels' param. |  | ||||||
|  * @param publicmodels  True to find a random public model, false to find non-public. |  | ||||||
|  */ |  | ||||||
| stock ModelsGetRandomModelIndex(String:modelpath[], maxlen, bool:all = true, bool:publicmodels = true) |  | ||||||
| { |  | ||||||
|     new modelindex = -1; |  | ||||||
|      |  | ||||||
|     // Return any random model. |  | ||||||
|     if (all) |  | ||||||
|     { |  | ||||||
|         // Get random model index and return the string in it. |  | ||||||
|         modelindex = GetRandomInt(0, GetArraySize(arrayModels) - 1); |  | ||||||
|     } |  | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         new Handle:modelsarray = CreateArray(PLATFORM_MAX_PATH); |  | ||||||
|         decl String:modelsetting[PLATFORM_MAX_PATH]; |  | ||||||
|          |  | ||||||
|         // x = Array index. |  | ||||||
|         new size = GetArraySize(arrayModels); |  | ||||||
|         for (new x = 0; x < size; x++) |  | ||||||
|         { |         { | ||||||
|             if (publicmodels == ModelIsPublic(x)) |             continue; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         // Cache current model access flag. | ||||||
|  |         new ModelAccess:access = ModelsGetAccess(index); | ||||||
|  |         new accessFlag = ModelsGetAccessFlag(access); | ||||||
|  |          | ||||||
|  |         // Check access filtering. Skip if no match. | ||||||
|  |         if (accessRequireFlags > 0 && | ||||||
|  |             !(accessRequireFlags & accessFlag)) | ||||||
|  |         { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         // Do client group authentication if client is specified. | ||||||
|  |         if (client > 0) | ||||||
|  |         { | ||||||
|  |             // Check if current model use group authentication. | ||||||
|  |             if (access == ModelAccess_Group) | ||||||
|             { |             { | ||||||
|                 // Transfer model to temp array. |                 decl String:group[64]; | ||||||
|                 GetArrayString(arrayModels, x, modelsetting, sizeof(modelsetting)); |                 ModelsGetGroup(index, group, sizeof(group)); | ||||||
|                 PushArrayString(modelsarray, modelsetting); |                  | ||||||
|  |                 if (!ZRIsClientInGroup(client, group)) | ||||||
|  |                 { | ||||||
|  |                     // Client not authorized to use this model. | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 // No group authentication. Do regular authentication if model | ||||||
|  |                 // is a admin model. | ||||||
|  |                 if (access == ModelAccess_Admins && | ||||||
|  |                     !ZRIsClientAdmin(client)) | ||||||
|  |                 { | ||||||
|  |                     // Client not authorized to use this model. | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         // y = Array index. |         // Model passed filter tests. Add to list. | ||||||
|         size = GetArraySize(modelsarray); |         modelIndexes[listCount] = index; | ||||||
|          |         listCount++; | ||||||
|         // If there are no models then copy a blank string to the output. |  | ||||||
|         if (size == 0) |  | ||||||
|         { |  | ||||||
|             strcopy(modelpath, maxlen, ""); |  | ||||||
|              |  | ||||||
|             // Destroy the handle. |  | ||||||
|             CloseHandle(modelsarray); |  | ||||||
|              |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         // Get random model index from the temp list, and return the string in it. |  | ||||||
|         modelindex = GetRandomInt(0, GetArraySize(modelsarray) - 1); |  | ||||||
|          |  | ||||||
|         // Destroy the handle. |  | ||||||
|         CloseHandle(modelsarray); |  | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     // Get the path to the selected model. |     // Check if any models passed the filter. | ||||||
|     ModelReturnPath(modelindex, modelpath, maxlen); |     if (listCount) | ||||||
|  |     { | ||||||
|  |         return modelIndexes[GetRandomInt(0, listCount - 1)]; | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Validates the specified index according to maximum number of models, and | ||||||
|  |  * number of models in use. Unused indexes will fail validation by default. | ||||||
|  |  * | ||||||
|  |  * @param index         Model index to validate. | ||||||
|  |  * @param rangeOnly     Optional. Do not check if the index is in use. Default | ||||||
|  |  *                      is false, check if in use. | ||||||
|  |  * @return              True if valid, false otherwise. | ||||||
|  |  */ | ||||||
|  | bool:ModelsIsValidIndex(index, bool:rangeOnly = false) | ||||||
|  | { | ||||||
|  |     new bool:rangeValid = (index >= 0 && index < MODELS_MAX); | ||||||
|  |      | ||||||
|  |     if (rangeOnly) | ||||||
|  |     { | ||||||
|  |         // Only check if the index is valid. | ||||||
|  |         return rangeValid; | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         // Check if the index is valid, and if it's in use. | ||||||
|  |         return rangeValid && (index < ModelCount); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Gets the name for the specified model. | ||||||
|  |  * | ||||||
|  |  * @param index     Model index. | ||||||
|  |  * @param buffer    Destination string buffer. | ||||||
|  |  * @param maxlen    Size of buffer. | ||||||
|  |  * @return          Number of cells written, or -1 on error. | ||||||
|  |  */ | ||||||
|  | ModelsGetName(index, String:buffer[], maxlen) | ||||||
|  | { | ||||||
|  |     // Validate index. | ||||||
|  |     if (!ModelsIsValidIndex(index)) | ||||||
|  |     { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return strcopy(buffer, maxlen, ModelData[index][Model_Name]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Gets the path for the specified model. | ||||||
|  |  * | ||||||
|  |  * @param index     Model index. | ||||||
|  |  * @param buffer    Destination string buffer. | ||||||
|  |  * @param maxlen    Size of buffer. | ||||||
|  |  * @return          Number of cells written, or -1 on error. | ||||||
|  |  */ | ||||||
|  | ModelsGetPath(index, String:buffer[], maxlen) | ||||||
|  | { | ||||||
|  |     // Validate index. | ||||||
|  |     if (!ModelsIsValidIndex(index)) | ||||||
|  |     { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return strcopy(buffer, maxlen, ModelData[index][Model_Path]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Gets the team for the specified model. | ||||||
|  |  * | ||||||
|  |  * @param index     Model index. | ||||||
|  |  * @return          Team for the specified model, ModelTeam_Invalid on error. | ||||||
|  |  */ | ||||||
|  | ModelTeam:ModelsGetTeam(index) | ||||||
|  | { | ||||||
|  |     // Validate index. | ||||||
|  |     if (!ModelsIsValidIndex(index)) | ||||||
|  |     { | ||||||
|  |         return ModelTeam_Invalid; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return ModelData[index][Model_Team]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Gets the access setting for the specified model. | ||||||
|  |  * | ||||||
|  |  * @param index     Model index. | ||||||
|  |  * @return          Access setting for the specified model, ModelAccess_Invalid | ||||||
|  |  *                  on error. | ||||||
|  |  */ | ||||||
|  | ModelAccess:ModelsGetAccess(index) | ||||||
|  | { | ||||||
|  |     // Validate index. | ||||||
|  |     if (!ModelsIsValidIndex(index)) | ||||||
|  |     { | ||||||
|  |         return ModelAccess_Invalid; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return ModelData[index][Model_Access]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Gets the access flag for the specified access setting. | ||||||
|  |  * | ||||||
|  |  * @param access    Access setting to convert. | ||||||
|  |  * @return          Access flag, or 0 on error. | ||||||
|  |  */ | ||||||
|  | ModelsGetAccessFlag(ModelAccess:access) | ||||||
|  | { | ||||||
|  |     switch (access) | ||||||
|  |     { | ||||||
|  |         case ModelAccess_Public: | ||||||
|  |         { | ||||||
|  |             return MODEL_ACCESS_PUBLIC; | ||||||
|  |         } | ||||||
|  |         case ModelAccess_Admins: | ||||||
|  |         { | ||||||
|  |             return MODEL_ACCESS_ADMINS; | ||||||
|  |         } | ||||||
|  |         case ModelAccess_Hidden: | ||||||
|  |         { | ||||||
|  |             return MODEL_ACCESS_HIDDEN; | ||||||
|  |         } | ||||||
|  |         case ModelAccess_MotherZombies: | ||||||
|  |         { | ||||||
|  |             return MODEL_ACCESS_MOTHER_ZOMBIES; | ||||||
|  |         } | ||||||
|  |         case ModelAccess_Group: | ||||||
|  |         { | ||||||
|  |             return MODEL_ACCESS_GROUP; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // Invalid access flag. | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Gets the group for the specified model. | ||||||
|  |  * | ||||||
|  |  * @param index     Model index. | ||||||
|  |  * @param buffer    Destination string buffer. | ||||||
|  |  * @param maxlen    Size of buffer. | ||||||
|  |  * @return          Number of cells written, or -1 on error. | ||||||
|  |  */ | ||||||
|  | ModelsGetGroup(index, String:buffer[], maxlen) | ||||||
|  | { | ||||||
|  |     // Validate index. | ||||||
|  |     if (!ModelsIsValidIndex(index)) | ||||||
|  |     { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return strcopy(buffer, maxlen, ModelData[index][Model_Group]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Gets the full model file path for the specified model. | ||||||
|  |  * | ||||||
|  |  * @param index     Model index. | ||||||
|  |  * @param buffer    Destination string buffer. | ||||||
|  |  * @param maxlen    Size of buffer. | ||||||
|  |  * @return          Number of cells written, or -1 on error. | ||||||
|  |  */ | ||||||
|  | ModelsGetFullPath(index, String:buffer[], maxlen) | ||||||
|  | { | ||||||
|  |     decl String:path[PLATFORM_MAX_PATH]; | ||||||
|  |     decl String:name[64]; | ||||||
|  |      | ||||||
|  |     ModelsGetPath(index, path, sizeof(path)); | ||||||
|  |     ModelsGetName(index, name, sizeof(name)); | ||||||
|  |      | ||||||
|  |     buffer[0] = 0; | ||||||
|  |      | ||||||
|  |     StrCat(buffer, maxlen, path); | ||||||
|  |     StrCat(buffer, maxlen, name); | ||||||
|  |     StrCat(buffer, maxlen, ".mdl"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Converts the specified string to a team setting. | ||||||
|  |  * | ||||||
|  |  * @param team  String to convert. | ||||||
|  |  * @return      Team setting, or ModelTeam_Invalid on error. | ||||||
|  |  */ | ||||||
|  | ModelTeam:ModelsStringToTeam(const String:team[]) | ||||||
|  | { | ||||||
|  |     if (StrEqual(team, "zombies", false)) | ||||||
|  |     { | ||||||
|  |         return ModelTeam_Zombies; | ||||||
|  |     } | ||||||
|  |     else if (StrEqual(team, "humans", false)) | ||||||
|  |     { | ||||||
|  |         return ModelTeam_Humans; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return ModelTeam_Invalid; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Converts the specified class team ID to a team setting. | ||||||
|  |  * | ||||||
|  |  * @param teamid    Class team ID. | ||||||
|  |  * @return          Team setting, or ModelTeam_Invalid on error. | ||||||
|  |  */ | ||||||
|  | ModelTeam:ModelsTeamIdToTeam(teamid) | ||||||
|  | { | ||||||
|  |     switch (teamid) | ||||||
|  |     { | ||||||
|  |         case ZR_CLASS_TEAM_ZOMBIES: | ||||||
|  |         { | ||||||
|  |             return ModelTeam_Zombies; | ||||||
|  |         } | ||||||
|  |         case ZR_CLASS_TEAM_HUMANS: | ||||||
|  |         { | ||||||
|  |             return ModelTeam_Humans; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return ModelTeam_Invalid; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Converts the specified string to a access setting. | ||||||
|  |  * | ||||||
|  |  * @param access    String to convert. | ||||||
|  |  * @return          Access setting, or ModelAccess_Invalid on error. | ||||||
|  |  */ | ||||||
|  | ModelAccess:ModelsStringToAccess(const String:access[]) | ||||||
|  | { | ||||||
|  |     if (StrEqual(access, "public", false)) | ||||||
|  |     { | ||||||
|  |         return ModelAccess_Public; | ||||||
|  |     } | ||||||
|  |     else if (StrEqual(access, "admins", false)) | ||||||
|  |     { | ||||||
|  |         return ModelAccess_Admins; | ||||||
|  |     } | ||||||
|  |     else if (StrEqual(access, "hidden", false)) | ||||||
|  |     { | ||||||
|  |         return ModelAccess_Hidden; | ||||||
|  |     } | ||||||
|  |     else if (StrEqual(access, "motherzombies", false)) | ||||||
|  |     { | ||||||
|  |         return ModelAccess_MotherZombies; | ||||||
|  |     } | ||||||
|  |     else if (StrEqual(access, "group", false)) | ||||||
|  |     { | ||||||
|  |         return ModelAccess_Group; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return ModelAccess_Invalid; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -77,24 +77,64 @@ bool:ClassApplyAttributes(client, bool:improved = false) | |||||||
|  */ |  */ | ||||||
| bool:ClassApplyModel(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER) | bool:ClassApplyModel(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER) | ||||||
| { | { | ||||||
|  |     new bool:isAttributePreset = false; | ||||||
|     decl String:modelpath[PLATFORM_MAX_PATH]; |     decl String:modelpath[PLATFORM_MAX_PATH]; | ||||||
|  |     new index; | ||||||
|  |     new ModelTeam:team; | ||||||
|  |     new access; | ||||||
|  |     new model; | ||||||
|      |      | ||||||
|     // Get the model path from the specified cache. |     // Get correct index according to cache type. | ||||||
|     if (cachetype == ZR_CLASS_CACHE_PLAYER) |     if (cachetype == ZR_CLASS_CACHE_PLAYER) | ||||||
|     { |     { | ||||||
|         ClassGetModelPath(client, modelpath, sizeof(modelpath), cachetype); |         index = client; | ||||||
|     } |     } | ||||||
|     else |     else | ||||||
|     { |     { | ||||||
|         ClassGetModelPath(classindex, modelpath, sizeof(modelpath), cachetype); |         index = classindex; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     // Check if the user specified a pre-defined model setting. |     // Get the model path from the specified cache. | ||||||
|  |     ClassGetModelPath(index, modelpath, sizeof(modelpath), cachetype); | ||||||
|  |      | ||||||
|  |     // Get model team setting from the specified cache. | ||||||
|  |     team = ModelsTeamIdToTeam(ClassGetTeamID(index, cachetype)); | ||||||
|  |      | ||||||
|  |     // Check if the user specified a pre-defined model setting. If so, setup | ||||||
|  |     // model filter settings. | ||||||
|     if (StrEqual(modelpath, "random", false)) |     if (StrEqual(modelpath, "random", false)) | ||||||
|     { |     { | ||||||
|         // TODO: Make a function that gets a random model from the specified team. |         // Set access filter flags. | ||||||
|         ModelsGetRandomModelIndex(modelpath, sizeof(modelpath), false, true); |         access = MODEL_ACCESS_PUBLIC | MODEL_ACCESS_ADMINS; | ||||||
|         Format(modelpath, sizeof(modelpath), "%s.mdl", modelpath); |          | ||||||
|  |         // Specify client for including admin models if client is admin. | ||||||
|  |         index = client; | ||||||
|  |          | ||||||
|  |         isAttributePreset = true; | ||||||
|  |     } | ||||||
|  |     else if (StrEqual(modelpath, "random_public", false)) | ||||||
|  |     { | ||||||
|  |         access = MODEL_ACCESS_PUBLIC; | ||||||
|  |         index = -1; | ||||||
|  |         isAttributePreset = true; | ||||||
|  |     } | ||||||
|  |     else if (StrEqual(modelpath, "random_hidden", false)) | ||||||
|  |     { | ||||||
|  |         access = MODEL_ACCESS_HIDDEN; | ||||||
|  |         index = -1; | ||||||
|  |         isAttributePreset = true; | ||||||
|  |     } | ||||||
|  |     else if (StrEqual(modelpath, "random_admin", false)) | ||||||
|  |     { | ||||||
|  |         access = MODEL_ACCESS_ADMINS; | ||||||
|  |         index = -1; | ||||||
|  |         isAttributePreset = true; | ||||||
|  |     } | ||||||
|  |     else if (StrEqual(modelpath, "random_mother_zombie", false)) | ||||||
|  |     { | ||||||
|  |         access = MODEL_ACCESS_MOTHER_ZOMBIES; | ||||||
|  |         index = -1; | ||||||
|  |         isAttributePreset = true; | ||||||
|     } |     } | ||||||
|     else if (StrEqual(modelpath, "default", false)) |     else if (StrEqual(modelpath, "default", false)) | ||||||
|     { |     { | ||||||
| @@ -112,12 +152,33 @@ bool:ClassApplyModel(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER) | |||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     else if (StrEqual(modelpath, "nochange", false)) |     else if (StrEqual(modelpath, "no_change", false)) | ||||||
|     { |     { | ||||||
|         // Do nothing. |         // Do nothing. | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |     // Check if model setting is a attribute preset. | ||||||
|  |     if (isAttributePreset) | ||||||
|  |     { | ||||||
|  |         // Get model based on filter settings set up earlier. | ||||||
|  |         model = ModelsGetRandomModel(index, team, access); | ||||||
|  |          | ||||||
|  |         // Check if found. | ||||||
|  |         if (model >= 0) | ||||||
|  |         { | ||||||
|  |             // Get model path. | ||||||
|  |             ModelsGetFullPath(model, modelpath, sizeof(modelpath)); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             // Couldn't find any models based on filter. Fall back to a random | ||||||
|  |             // public model. Then get its path. | ||||||
|  |             model = ModelsGetRandomModel(-1, team, MODEL_ACCESS_PUBLIC); | ||||||
|  |             ModelsGetFullPath(model, modelpath, sizeof(modelpath)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|     SetEntityModel(client, modelpath); |     SetEntityModel(client, modelpath); | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -162,8 +162,12 @@ stock ClassValidateAttributes(classindex) | |||||||
|     } |     } | ||||||
|     else |     else | ||||||
|     { |     { | ||||||
|         // Check if a model different from a pre-defined setting. |         // Validate only if not a pre-defined setting. | ||||||
|         if (!StrEqual(model_path, "random", false) && |         if (!StrEqual(model_path, "random", false) && | ||||||
|  |             !StrEqual(model_path, "random_public", false) && | ||||||
|  |             !StrEqual(model_path, "random_hidden", false) && | ||||||
|  |             !StrEqual(model_path, "random_admins", false) && | ||||||
|  |             !StrEqual(model_path, "random_mother_zombies", false) && | ||||||
|             !StrEqual(model_path, "default", false) && |             !StrEqual(model_path, "default", false) && | ||||||
|             !StrEqual(model_path, "nochange", false)) |             !StrEqual(model_path, "nochange", false)) | ||||||
|         { |         { | ||||||
| @@ -705,8 +709,8 @@ stock bool:ClassFilterMatch(index, filter[ClassFilter], cachetype = ZR_CLASS_CAC | |||||||
| stock bool:ClassFlagFilterMatch(index, require, deny, cachetype) | stock bool:ClassFlagFilterMatch(index, require, deny, cachetype) | ||||||
| { | { | ||||||
|     new flags; |     new flags; | ||||||
|     new bool:requirepassed; |     new bool:requirepassed = false; | ||||||
|     new bool:denypassed; |     new bool:denypassed = false; | ||||||
|      |      | ||||||
|     // Do quick check for optimization reasons: Check if no flags are specified. |     // Do quick check for optimization reasons: Check if no flags are specified. | ||||||
|     if (require == 0 && deny == 0) |     if (require == 0 && deny == 0) | ||||||
| @@ -723,11 +727,6 @@ stock bool:ClassFlagFilterMatch(index, require, deny, cachetype) | |||||||
|         // All required flags are set. |         // All required flags are set. | ||||||
|         requirepassed = true; |         requirepassed = true; | ||||||
|     } |     } | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         // Not all required flags are set. |  | ||||||
|         requirepassed = false; |  | ||||||
|     } |  | ||||||
|      |      | ||||||
|     // Match deny filter. |     // Match deny filter. | ||||||
|     if (deny == 0 || !(flags & deny)) |     if (deny == 0 || !(flags & deny)) | ||||||
| @@ -735,11 +734,6 @@ stock bool:ClassFlagFilterMatch(index, require, deny, cachetype) | |||||||
|         // No denied flags are set. |         // No denied flags are set. | ||||||
|         denypassed = true; |         denypassed = true; | ||||||
|     } |     } | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         // It has denied flags set. |  | ||||||
|         denypassed = false; |  | ||||||
|     } |  | ||||||
|      |      | ||||||
|     // Check if required and denied flags passed the filter. |     // Check if required and denied flags passed the filter. | ||||||
|     if (requirepassed && denypassed) |     if (requirepassed && denypassed) | ||||||
| @@ -747,11 +741,9 @@ stock bool:ClassFlagFilterMatch(index, require, deny, cachetype) | |||||||
|         // The class pass the filter. |         // The class pass the filter. | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|     else |      | ||||||
|     { |     // The class didn't pass the filter. | ||||||
|         // The class didn't pass the filter. |     return false; | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|   | |||||||
| @@ -376,11 +376,6 @@ new ClassNoFilter[ClassFilter]; | |||||||
|  */ |  */ | ||||||
| new ClassNoSpecialClasses[ClassFilter] = {false, 0, ZR_CLASS_SPECIALFLAGS, -1}; | new ClassNoSpecialClasses[ClassFilter] = {false, 0, ZR_CLASS_SPECIALFLAGS, -1}; | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Keyvalue handle to store class data. |  | ||||||
|  */ |  | ||||||
| new Handle:kvClassData = INVALID_HANDLE; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * The original class data. This array only changed when class data is loaded. |  * The original class data. This array only changed when class data is loaded. | ||||||
|  * ZR_CLASS_CACHE_ORIGINAL is the cache type to this array. |  * ZR_CLASS_CACHE_ORIGINAL is the cache type to this array. | ||||||
| @@ -413,7 +408,7 @@ new Float:ClassMultiplierCache[ZR_CLASS_TEAMCOUNT][ClassMultipliers]; | |||||||
| new ClassCount; | new ClassCount; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Specifies wether the class team requirement and attributes are valid or not. |  * Specifies whether the class team requirements and attributes are valid or not. | ||||||
|  * Used to block events that happend before the module is done loading. |  * Used to block events that happend before the module is done loading. | ||||||
|  */ |  */ | ||||||
| new bool:ClassValidated; | new bool:ClassValidated; | ||||||
| @@ -485,11 +480,9 @@ ClassLoad() | |||||||
|     // Register config file. |     // Register config file. | ||||||
|     ConfigRegisterConfig(File_Classes, Structure_Keyvalue, CONFIG_FILE_ALIAS_CLASSES); |     ConfigRegisterConfig(File_Classes, Structure_Keyvalue, CONFIG_FILE_ALIAS_CLASSES); | ||||||
|      |      | ||||||
|  |     new Handle:kvClassData; | ||||||
|  |      | ||||||
|     // Make sure kvClassData is ready to use. |     // Make sure kvClassData is ready to use. | ||||||
|     if (kvClassData != INVALID_HANDLE) |  | ||||||
|     { |  | ||||||
|         CloseHandle(kvClassData); |  | ||||||
|     } |  | ||||||
|     kvClassData = CreateKeyValues(CONFIG_FILE_ALIAS_CLASSES); |     kvClassData = CreateKeyValues(CONFIG_FILE_ALIAS_CLASSES); | ||||||
|      |      | ||||||
|     // Get weapons config path. |     // Get weapons config path. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user