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 | ||||
| //                            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: | ||||
| // ---------------------------------------------------------------------------- | ||||
| // the/path/to/the/model	;public/hidden/adminonly/etc | ||||
| // * ;public - The model will be treated as a model that any client has access to. | ||||
| // * ;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: | ||||
| // | ||||
| // SHORT DESCRIPTIONS | ||||
| // | ||||
| // Attribute:   Description: | ||||
| // ---------------------------------------------------------------------------- | ||||
| // 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/player/zh/zh_zombie003			;public | ||||
| models/player/zh/zh_corpse002			;public | ||||
| models/player/ics/hellknight_red/t_guerilla	;public | ||||
| // models/player/adminmodels/1337model		;adminonly   // None of these models will be randomly chosen. | ||||
| // models/player/donatormodels/donatormodel	;donator | ||||
| // models/player/hiddenmodels/myhiddenmodel	;non-public | ||||
| "models" | ||||
| { | ||||
|     "zh_charple001" | ||||
|     { | ||||
|         "name"      "zh_charple001" | ||||
|         "path"      "models/player/zh/" | ||||
|         "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 /> | ||||
| 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> | ||||
|  | ||||
| @@ -747,6 +747,9 @@ configuration files are optional.</p> | ||||
| certain maps. That could be scaling knockback, restricting certain weapons, changing class | ||||
| 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> | ||||
| <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 | ||||
| @@ -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> | ||||
|  | ||||
| <p><strong>Note:</strong> Work in progress. Model list data structure is about to be changed | ||||
| with support for human models and model restrictions. Currently, assume all models to be public | ||||
| 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 model configuration file is a list of models used on the server stored in Valve's key/value | ||||
| format.</p> | ||||
|  | ||||
| <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 | ||||
| listed in this file.</p> | ||||
|  | ||||
| <p>In addition certain flags can be added to mark the model as special, such as only for | ||||
| admins/donators, hidden from random selection or only for mother zombies.</p> | ||||
| <p>In addition models can be restricted to certain groups using the "access" attribute.</p> | ||||
|  | ||||
| <p>Each line is separated into two fields with ";". The last field is optional.</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> | ||||
| <p>List of available model attributes:</p> | ||||
|  | ||||
| <blockquote><table> | ||||
| 	<caption>Model Flags</caption> | ||||
| 	<caption>Model Attributes</caption> | ||||
| 	 | ||||
| 	 | ||||
| 	<tr> | ||||
| 		<td class="valueoption">public</td> | ||||
| 		<td>Everyone can use the model.</td> | ||||
| 		<th class="namewidth">Attribute:</th> | ||||
| 		<th class="mediumwidth">Value type:</th> | ||||
| 	</tr> | ||||
| 	 | ||||
| 	<tr> | ||||
| 		<td class="commandheader">name</td> | ||||
| 		<td class="commandheader">text</td> | ||||
| 	</tr> | ||||
| 	<tr> | ||||
| 		<td class="valueoption">adminonly</td> | ||||
| 		<td>(Incomplete) Can only be used by admins.</td> | ||||
| 		<td class="indent" colspan="2"> | ||||
| 			<p>File name of model file, without extension.</p> | ||||
| 		</td> | ||||
| 	</tr> | ||||
| 	 | ||||
| 	<tr> | ||||
| 		<td class="commandheader">path</td> | ||||
| 		<td class="commandheader">text</td> | ||||
| 	</tr> | ||||
| 	<tr> | ||||
| 		<td class="valueoption">donator</td> | ||||
| 		<td>(Incomplete) Can only be used by donators.</td> | ||||
| 		<td class="indent" colspan="2"> | ||||
| 			<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> | ||||
| 		<td class="valueoption">hidden</td> | ||||
| 		<td>(Incomplete) Is not included in random selections.</td> | ||||
| 		<td class="indent" colspan="2"> | ||||
| 			<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> | ||||
| 		<td class="valueoption">motherzombie</td> | ||||
| 		<td>(Incomplete) Can only be used on mother zombies.</td> | ||||
| 		<td class="indent" colspan="2"> | ||||
| 			<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> | ||||
| </table></blockquote> | ||||
|  | ||||
| <p>Example usages:</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>For example usages see examples in default model configuration.</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> | ||||
|  | ||||
| <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> | ||||
|  | ||||
| @@ -1011,8 +1053,8 @@ the admin-only flag in the <span class="code">flags</span> attribute.</p> | ||||
| 	</tr> | ||||
| 	<tr> | ||||
| 		<td class="indent" colspan="3"> | ||||
| 			<p>The model file to use on the player, path is relative to the "cstrike" folder. There | ||||
| 			are a few special values supported by this attribute:</p> | ||||
| 			<p>Model file path to use on the player, relative to the "cstrike" folder. There are a | ||||
| 			few presets supported by this attribute:</p> | ||||
| 			<blockquote><table> | ||||
| 				<tr> | ||||
| 					<td class="valueoption">default</td> | ||||
| @@ -1020,12 +1062,31 @@ the admin-only flag in the <span class="code">flags</span> attribute.</p> | ||||
| 				</tr> | ||||
| 				<tr> | ||||
| 					<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> | ||||
| 					<td class="valueoption">nochange</td> | ||||
| 					<td>Don't change model. To be used in combination with other plugins that | ||||
| 					change model on players.</td> | ||||
| 					<td class="valueoption">random_public</td> | ||||
| 					<td>Selects a random public model for the current team.</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> | ||||
| 			</table></blockquote>	 | ||||
| 		</td> | ||||
|   | ||||
| @@ -54,6 +54,7 @@ | ||||
|  | ||||
| // Header includes. | ||||
| #include "zr/log.h" | ||||
| #include "zr/models.h" | ||||
|  | ||||
| #if defined ADD_VERSION_INFO | ||||
| #include "zr/hgversion.h" | ||||
| @@ -79,10 +80,10 @@ | ||||
| #include "zr/paramtools" | ||||
| #include "zr/paramparser" | ||||
| #include "zr/shoppinglist" | ||||
| #include "zr/models" | ||||
| #include "zr/downloads" | ||||
| #include "zr/overlays" | ||||
| #include "zr/playerclasses/playerclasses" | ||||
| #include "zr/models" | ||||
| #include "zr/weapons/weapons" | ||||
| #include "zr/hitgroups" | ||||
| #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 | ||||
|  *  Type:          Core | ||||
|  *  Description:   Model validation. | ||||
|  *  Description:   Model manager. | ||||
|  * | ||||
|  *  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. | ||||
|  */ | ||||
| ModelsLoad() | ||||
| { | ||||
|     new Handle:kvModels = INVALID_HANDLE; | ||||
|      | ||||
|     // Register config file. | ||||
|     ConfigRegisterConfig(File_Models, Structure_List, CONFIG_FILE_ALIAS_MODELS); | ||||
|      | ||||
|     // Get models file path. | ||||
|     decl String:pathmodels[PLATFORM_MAX_PATH]; | ||||
|     new bool:exists = ConfigGetCvarFilePath(CVAR_CONFIG_PATH_MODELS, pathmodels); | ||||
|     decl String:modelPath[PLATFORM_MAX_PATH]; | ||||
|     new bool:exists = ConfigGetCvarFilePath(CVAR_CONFIG_PATH_MODELS, modelPath); | ||||
|      | ||||
|     // If file doesn't exist, then log and stop. | ||||
|     if (!exists) | ||||
|     { | ||||
|         // 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. | ||||
|     ConfigSetConfigPath(File_Models, pathmodels); | ||||
|     ConfigSetConfigPath(File_Models, modelPath); | ||||
|      | ||||
|     // Load config from file and create array structure. | ||||
|     new bool:success = ConfigLoadConfig(File_Models, arrayModels, PLATFORM_MAX_PATH); | ||||
|     // Prepare key/value structure. | ||||
|     kvModels = CreateKeyValues(CONFIG_FILE_ALIAS_MODELS); | ||||
|      | ||||
|     // Unexpected error, stop plugin. | ||||
|     if (!success) | ||||
|     // Log what models file that is loaded. | ||||
|     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; | ||||
|     new modelpublicvalidcount; | ||||
|     new modelnonpublicvalidcount; | ||||
|     new modelfilecount; | ||||
|     decl String:buffer[256]; | ||||
|     decl String:name[64]; | ||||
|     decl String:path[PLATFORM_MAX_PATH]; | ||||
|     decl String:team[64]; | ||||
|     decl String:access[64]; | ||||
|     decl String:group[64]; | ||||
|      | ||||
|     decl String:modelbase[PLATFORM_MAX_PATH]; | ||||
|     decl String:modelpath[PLATFORM_MAX_PATH]; | ||||
|     decl String:modelname[MODELS_PATH_DIR_MAX_LENGTH]; | ||||
|     decl String:modelfile[MODELS_PATH_DIR_MAX_LENGTH]; | ||||
|     decl String:modeldiskname[MODELS_PATH_DIR_MAX_LENGTH]; | ||||
|     decl String:modelfullpath[PLATFORM_MAX_PATH]; | ||||
|     ModelCount = 0; | ||||
|     new failedCount; | ||||
|     new publicCount; | ||||
|      | ||||
|     new String:baseexploded[MODELS_PATH_MAX_DEPTH][MODELS_PATH_DIR_MAX_LENGTH]; | ||||
|      | ||||
|     new FileType:type; | ||||
|      | ||||
|     new models = modelcount = GetArraySize(arrayModels); | ||||
|      | ||||
|     // x = model array index. | ||||
|     for (new x = 0; x < models; x++) | ||||
|     // Loop through all models and store attributes in ModelData array. | ||||
|     do | ||||
|     { | ||||
|         // Get base model path, excluding the public/non-public setting. | ||||
|         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) | ||||
|         if (ModelCount > MODELS_MAX) | ||||
|         { | ||||
|             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; | ||||
|         } | ||||
|          | ||||
|         // Reset model file count. | ||||
|         modelfilecount = 0; | ||||
|          | ||||
|         while (ReadDirEntry(modeldir, modelfile, sizeof(modelfile), type)) | ||||
|         // Validate team. | ||||
|         if (ModelData[ModelCount][Model_Team] == ModelTeam_Invalid) | ||||
|         { | ||||
|             // 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) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
|              | ||||
|             // Find break point index in the string to get model name. | ||||
|             // Add one because it seems to break on the character before. | ||||
|             new breakpoint = FindCharInString(modelfile, '.') + 1; | ||||
|             strcopy(modeldiskname, breakpoint, modelfile); | ||||
|             // Add one to make space for null terminator. | ||||
|             new breakpoint = FindCharInString(file, '.') + 1; | ||||
|             strcopy(fileShort, breakpoint, file); | ||||
|              | ||||
|             // If this file doesn't match, then stop. | ||||
|             if (!StrEqual(modelname, modeldiskname, false)) | ||||
|             // If this file doesn't match model name, then skip it. | ||||
|             if (!StrEqual(name, fileShort, false)) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
|              | ||||
|             // Format a full path string. | ||||
|             strcopy(modelfullpath, sizeof(modelfullpath), modelpath); | ||||
|             Format(modelfullpath, sizeof(modelfullpath), "%s/%s", modelfullpath, modelfile); | ||||
|             strcopy(buffer, sizeof(buffer), path); | ||||
|             Format(buffer, sizeof(buffer), "%s%s", buffer, file); | ||||
|              | ||||
|             // Precache model file and add to downloads table. | ||||
|             PrecacheModel(modelfullpath); | ||||
|             AddFileToDownloadsTable(modelfullpath); | ||||
|              | ||||
|             // Increment modelfilecount | ||||
|             modelfilecount++; | ||||
|             PrecacheModel(buffer); | ||||
|             AddFileToDownloadsTable(buffer); | ||||
|         } | ||||
|          | ||||
|         CloseHandle(modeldir); | ||||
|         CloseHandle(dir); | ||||
|          | ||||
|         // Increment variable if model files are valid. | ||||
|         if (modelfilecount) | ||||
|         ModelCount++; | ||||
|     } while (KvGotoNextKey(kvModels)); | ||||
|      | ||||
|     CloseHandle(kvModels); | ||||
|      | ||||
|     // Check if there are no public models. | ||||
|     if (!publicCount) | ||||
|     { | ||||
|             // Increment proper variable. | ||||
|             if (ModelIsPublic(x)) | ||||
|             { | ||||
|                 modelpublicvalidcount++; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 modelnonpublicvalidcount++; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Remove client from array. | ||||
|             RemoveFromArray(arrayModels, x); | ||||
|              | ||||
|             // Subtract one from count. | ||||
|             models--; | ||||
|              | ||||
|             // Backtrack one index, because we deleted it out from under the loop. | ||||
|             x--; | ||||
|              | ||||
|             // Log missing model files. | ||||
|             LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Missing model files on server (%s)", modelbase); | ||||
|         } | ||||
|         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); | ||||
|     } | ||||
|      | ||||
|     // 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)); | ||||
|      | ||||
|     // 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); | ||||
|     } | ||||
|     LogEvent(false, LogType_Normal, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Successful: %d | Unsuccessful: %d", ModelCount, failedCount); | ||||
|      | ||||
|     // Set config data. | ||||
|     ConfigSetConfigLoaded(File_Models, true); | ||||
|     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. | ||||
|  * @return              True if public, false if not. | ||||
|  * @param client                Optional. Client index used to check for | ||||
|  *                              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 String:modelsetting[PLATFORM_MAX_PATH + 16]; | ||||
|     GetArrayString(arrayModels, modelindex, modelsetting, sizeof(modelsetting)); | ||||
|     decl modelIndexes[MODELS_MAX]; | ||||
|     new listCount; | ||||
|      | ||||
|     // We define this to use as little memory as possible, because the value won't be used. | ||||
|     decl String:modelpath[1]; | ||||
|     decl String:strpublic[32]; | ||||
|      | ||||
|     if (StrContains(modelsetting, ";") > -1) | ||||
|     // Loop through all models. | ||||
|     for (new index = 0; index < ModelCount; index++) | ||||
|     { | ||||
|         // Get string index of where the public setting starts. | ||||
|         new strindex = SplitString(modelsetting, ";", modelpath, sizeof(modelpath)); | ||||
|          | ||||
|         // 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); | ||||
|         // Check team filtering. Skip if no match. | ||||
|         if (teamFilter != ModelTeam_Invalid && | ||||
|             ModelsGetTeam(index) != teamFilter) | ||||
|         { | ||||
|             continue; | ||||
|         } | ||||
|          | ||||
|     // If nothing is specified, assume public. | ||||
|     return true; | ||||
|         // 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; | ||||
|         } | ||||
|          | ||||
| /** | ||||
|  * 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) | ||||
|         // Do client group authentication if client is specified. | ||||
|         if (client > 0) | ||||
|         { | ||||
|     // Get the entire model string to parse for what we need. | ||||
|     decl String:modelsetting[PLATFORM_MAX_PATH + 16]; | ||||
|     GetArrayString(arrayModels, modelindex, modelsetting, sizeof(modelsetting)); | ||||
|             // Check if current model use group authentication. | ||||
|             if (access == ModelAccess_Group) | ||||
|             { | ||||
|                 decl String:group[64]; | ||||
|                 ModelsGetGroup(index, group, sizeof(group)); | ||||
|                  | ||||
|     // Copy to path before split just in case the string has no ";" | ||||
|     strcopy(modelpath, maxlen, modelsetting); | ||||
|     if (StrContains(modelsetting, ";") > -1) | ||||
|                 if (!ZRIsClientInGroup(client, group)) | ||||
|                 { | ||||
|         SplitString(modelsetting, ";", modelpath, maxlen); | ||||
|                     // Client not authorized to use this model. | ||||
|                     continue; | ||||
|                 } | ||||
|      | ||||
|     // 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++) | ||||
|                 // No group authentication. Do regular authentication if model | ||||
|                 // is a admin model. | ||||
|                 if (access == ModelAccess_Admins && | ||||
|                     !ZRIsClientAdmin(client)) | ||||
|                 { | ||||
|             if (publicmodels == ModelIsPublic(x)) | ||||
|                     // Client not authorized to use this model. | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         // Model passed filter tests. Add to list. | ||||
|         modelIndexes[listCount] = index; | ||||
|         listCount++; | ||||
|     } | ||||
|      | ||||
|     // Check if any models passed the filter. | ||||
|     if (listCount) | ||||
|     { | ||||
|                 // Transfer model to temp array. | ||||
|                 GetArrayString(arrayModels, x, modelsetting, sizeof(modelsetting)); | ||||
|                 PushArrayString(modelsarray, modelsetting); | ||||
|         return modelIndexes[GetRandomInt(0, listCount - 1)]; | ||||
|     } | ||||
|         } | ||||
|          | ||||
|         // y = Array index. | ||||
|         size = GetArraySize(modelsarray); | ||||
|          | ||||
|         // If there are no models then copy a blank string to the output. | ||||
|         if (size == 0) | ||||
|     else | ||||
|     { | ||||
|             strcopy(modelpath, maxlen, ""); | ||||
|              | ||||
|             // Destroy the handle. | ||||
|             CloseHandle(modelsarray); | ||||
|              | ||||
|             return; | ||||
|         return -1; | ||||
|     } | ||||
| } | ||||
|  | ||||
|         // Get random model index from the temp list, and return the string in it. | ||||
|         modelindex = GetRandomInt(0, GetArraySize(modelsarray) - 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); | ||||
|      | ||||
|         // Destroy the handle. | ||||
|         CloseHandle(modelsarray); | ||||
|     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); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     // Get the path to the selected model. | ||||
|     ModelReturnPath(modelindex, modelpath, maxlen); | ||||
| /** | ||||
|  * 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) | ||||
| { | ||||
|     new bool:isAttributePreset = false; | ||||
|     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) | ||||
|     { | ||||
|         ClassGetModelPath(client, modelpath, sizeof(modelpath), cachetype); | ||||
|         index = client; | ||||
|     } | ||||
|     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)) | ||||
|     { | ||||
|         // TODO: Make a function that gets a random model from the specified team. | ||||
|         ModelsGetRandomModelIndex(modelpath, sizeof(modelpath), false, true); | ||||
|         Format(modelpath, sizeof(modelpath), "%s.mdl", modelpath); | ||||
|         // Set access filter flags. | ||||
|         access = MODEL_ACCESS_PUBLIC | MODEL_ACCESS_ADMINS; | ||||
|          | ||||
|         // 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)) | ||||
|     { | ||||
| @@ -112,12 +152,33 @@ bool:ClassApplyModel(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER) | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     else if (StrEqual(modelpath, "nochange", false)) | ||||
|     else if (StrEqual(modelpath, "no_change", false)) | ||||
|     { | ||||
|         // Do nothing. | ||||
|         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); | ||||
|     return true; | ||||
| } | ||||
|   | ||||
| @@ -162,8 +162,12 @@ stock ClassValidateAttributes(classindex) | ||||
|     } | ||||
|     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) && | ||||
|             !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, "nochange", false)) | ||||
|         { | ||||
| @@ -705,8 +709,8 @@ stock bool:ClassFilterMatch(index, filter[ClassFilter], cachetype = ZR_CLASS_CAC | ||||
| stock bool:ClassFlagFilterMatch(index, require, deny, cachetype) | ||||
| { | ||||
|     new flags; | ||||
|     new bool:requirepassed; | ||||
|     new bool:denypassed; | ||||
|     new bool:requirepassed = false; | ||||
|     new bool:denypassed = false; | ||||
|      | ||||
|     // Do quick check for optimization reasons: Check if no flags are specified. | ||||
|     if (require == 0 && deny == 0) | ||||
| @@ -723,11 +727,6 @@ stock bool:ClassFlagFilterMatch(index, require, deny, cachetype) | ||||
|         // All required flags are set. | ||||
|         requirepassed = true; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // Not all required flags are set. | ||||
|         requirepassed = false; | ||||
|     } | ||||
|      | ||||
|     // Match deny filter. | ||||
|     if (deny == 0 || !(flags & deny)) | ||||
| @@ -735,11 +734,6 @@ stock bool:ClassFlagFilterMatch(index, require, deny, cachetype) | ||||
|         // No denied flags are set. | ||||
|         denypassed = true; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // It has denied flags set. | ||||
|         denypassed = false; | ||||
|     } | ||||
|      | ||||
|     // Check if required and denied flags passed the filter. | ||||
|     if (requirepassed && denypassed) | ||||
| @@ -747,12 +741,10 @@ stock bool:ClassFlagFilterMatch(index, require, deny, cachetype) | ||||
|         // The class pass the filter. | ||||
|         return true; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|      | ||||
|     // The class didn't pass the filter. | ||||
|     return false; | ||||
| } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Decides whether a class selection menu should be enabled. The decision is | ||||
|   | ||||
| @@ -376,11 +376,6 @@ new ClassNoFilter[ClassFilter]; | ||||
|  */ | ||||
| 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. | ||||
|  * ZR_CLASS_CACHE_ORIGINAL is the cache type to this array. | ||||
| @@ -413,7 +408,7 @@ new Float:ClassMultiplierCache[ZR_CLASS_TEAMCOUNT][ClassMultipliers]; | ||||
| 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. | ||||
|  */ | ||||
| new bool:ClassValidated; | ||||
| @@ -485,11 +480,9 @@ ClassLoad() | ||||
|     // Register config file. | ||||
|     ConfigRegisterConfig(File_Classes, Structure_Keyvalue, CONFIG_FILE_ALIAS_CLASSES); | ||||
|      | ||||
|     new Handle:kvClassData; | ||||
|      | ||||
|     // Make sure kvClassData is ready to use. | ||||
|     if (kvClassData != INVALID_HANDLE) | ||||
|     { | ||||
|         CloseHandle(kvClassData); | ||||
|     } | ||||
|     kvClassData = CreateKeyValues(CONFIG_FILE_ALIAS_CLASSES); | ||||
|      | ||||
|     // Get weapons config path. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user