diff --git a/cstrike/addons/sourcemod/configs/zr/playerclasses.txt b/cstrike/addons/sourcemod/configs/zr/playerclasses.txt index 045badd..1640aad 100644 --- a/cstrike/addons/sourcemod/configs/zr/playerclasses.txt +++ b/cstrike/addons/sourcemod/configs/zr/playerclasses.txt @@ -10,16 +10,17 @@ // // Attribute: Values: Description: // ---------------------------------------------------------------------------- -// enabled 0/1 Enables or disables a class. +// enabled yes/no Enables or disables a class. // team number Specifies what team the class belongs to: // 0 - Zombies // 1 - Humans // 2 - Admin mode classes (incomplete feautre!) -// team_default 0/1 Marks the class as the default class in the team. +// team_default yes/no Marks the class as the default class in the team. // flags number Special class flags (bit field). To combine multiple flags // use a sum of the flag values. Available flags: // 1 - Admins only // 2 - Mother zombies only +// group text Restrict class to member of this SourceMod group. Leave blank for no restriction. // name text The class name used in class menu. // description text The class description used in class menu. // model_path text Path to model to use. Relative to cstrike folder. @@ -27,13 +28,13 @@ // alpha_damaged number Transparency when damaged. // alpha_damage number How much damage to do before switching alpha. // overlay_path text Overlay displayed at the player. -// nvgs 0/1 Give night vision. +// nvgs yes/no Give night vision. // fov number Field of view value. 90 is default. -// has_napalm 0/1 Allows player to throw napalm grenades. Humans only. +// has_napalm yes/no Allows player to throw napalm grenades. Humans only. // napalm_time decimal Napalm burn duration. Zombies only. // immunity_mode number Sets the immunity mode. // immunity_amount decimal Sets the immunity value. -// no_fall_damage 0/1 Disables fall damage. +// no_fall_damage on/off Disables fall damage. // health number How many health points to give. // health_regen_interval decimal Sets the regeneration interval. 0 to disable. // health_regen_amount number How much HP to give per interval. @@ -54,10 +55,11 @@ "zombie_classic" { // General - "enabled" "1" + "enabled" "yes" "team" "0" - "team_default" "1" + "team_default" "yes" "flags" "0" + "group" "" "name" "Classic" "description" "Need brains!!! Arrrrggghh!" @@ -70,17 +72,17 @@ // Hud "overlay_path" "overlays/zr/zvision" - "nvgs" "0" + "nvgs" "no" "fov" "90" // Effects - "has_napalm" "0" + "has_napalm" "no" "napalm_time" "10.0" // Player behaviour "immunity_mode" "0" "immunity_amount" "0.0" - "no_fall_damage" "1" + "no_fall_damage" "yes" "health" "2500" "health_regen_interval" "0.0" @@ -97,10 +99,11 @@ "zombie_fast" { // General - "enabled" "1" + "enabled" "yes" "team" "0" - "team_default" "0" + "team_default" "no" "flags" "0" + "group" "" "name" "Fast" "description" "-HP | +Speed | +Jump | +Knockback" @@ -113,17 +116,17 @@ // Hud "overlay_path" "overlays/zr/zvision" - "nvgs" "0" + "nvgs" "no" "fov" "90" // Effects - "has_napalm" "0" + "has_napalm" "no" "napalm_time" "5.0" // Player behaviour "immunity_mode" "0" "immunity_amount" "0.0" - "no_fall_damage" "1" + "no_fall_damage" "yes" "health" "2000" "health_regen_interval" "0.0" @@ -140,10 +143,11 @@ "zombie_mutated" { // General - "enabled" "1" + "enabled" "yes" "team" "0" - "team_default" "0" + "team_default" "no" "flags" "0" + "group" "" "name" "Mutated" "description" "+HP | -Speed | +Jump | +Knockback" @@ -156,17 +160,17 @@ // Hud "overlay_path" "overlays/zr/zvision" - "nvgs" "0" + "nvgs" "no" "fov" "90" // Effects - "has_napalm" "0" + "has_napalm" "no" "napalm_time" "15.0" // Player behaviour "immunity_mode" "0" "immunity_amount" "0.0" - "no_fall_damage" "1" + "no_fall_damage" "yes" "health" "3500" "health_regen_interval" "0.0" @@ -183,10 +187,11 @@ "zombie_heavy" { // General - "enabled" "1" + "enabled" "yes" "team" "0" - "team_default" "0" + "team_default" "no" "flags" "0" + "group" "" "name" "Heavy" "description" "+HP | -Speed | -Jump | -Knockback" @@ -199,17 +204,17 @@ // Hud "overlay_path" "overlays/zr/zvision" - "nvgs" "0" + "nvgs" "no" "fov" "90" // Effects - "has_napalm" "0" + "has_napalm" "no" "napalm_time" "20.0" // Player behaviour "immunity_mode" "0" "immunity_amount" "0.0" - "no_fall_damage" "1" + "no_fall_damage" "yes" "health" "4000" "health_regen_interval" "0.0" @@ -226,10 +231,11 @@ "mother_zombie" { // General - "enabled" "1" + "enabled" "yes" "team" "0" - "team_default" "0" + "team_default" "no" "flags" "2" + "group" "" "name" "Mother zombie" "description" "+HP regen | +Speed | +Jump | -Knockback" @@ -242,7 +248,7 @@ // Hud "overlay_path" "overlays/zr/zvision" - "nvgs" "0" + "nvgs" "no" "fov" "110" // Effects @@ -252,7 +258,7 @@ // Player behaviour "immunity_mode" "0" "immunity_amount" "0.0" - "no_fall_damage" "1" + "no_fall_damage" "yes" "health" "2500" "health_regen_interval" "0.25" @@ -269,10 +275,11 @@ "mother_zombie_admin" { // General - "enabled" "1" + "enabled" "yes" "team" "0" - "team_default" "0" + "team_default" "no" "flags" "3" + "group" "" "name" "Admin mother zombie" "description" "+HP regen | +Speed | +Jump | -Knockback" @@ -285,7 +292,7 @@ // Hud "overlay_path" "overlays/zr/zvision" - "nvgs" "0" + "nvgs" "no" "fov" "110" // Effects @@ -295,7 +302,7 @@ // Player behaviour "immunity_mode" "0" "immunity_amount" "0.0" - "no_fall_damage" "1" + "no_fall_damage" "yes" "health" "3500" "health_regen_interval" "0.25" @@ -318,10 +325,11 @@ "human_normal" { // General - "enabled" "1" + "enabled" "yes" "team" "1" - "team_default" "1" + "team_default" "yes" "flags" "0" + "group" "" "name" "Normal Human" "description" "Default Counter-Strike settings" @@ -334,17 +342,17 @@ // Hud "overlay_path" "" - "nvgs" "0" + "nvgs" "no" "fov" "90" // Effects - "has_napalm" "1" + "has_napalm" "yes" "napalm_time" "0.0" // Player behaviour "immunity_mode" "0" "immunity_amount" "0.0" - "no_fall_damage" "0" + "no_fall_damage" "no" "health" "100" "health_regen_interval" "0.0" @@ -361,10 +369,11 @@ "human_vip" { // General - "enabled" "1" + "enabled" "yes" "team" "1" - "team_default" "0" + "team_default" "yes" "flags" "0" + "group" "" "name" "VIP Human" "description" "Human class for important players" @@ -377,7 +386,7 @@ // Hud "overlay_path" "" - "nvgs" "0" + "nvgs" "no" "fov" "90" // Effects @@ -387,7 +396,7 @@ // Player behaviour "immunity_mode" "0" "immunity_amount" "0.0" - "no_fall_damage" "0" + "no_fall_damage" "no" "health" "200" "health_regen_interval" "1.0" @@ -404,10 +413,11 @@ "human_admin" { // General - "enabled" "1" + "enabled" "yes" "team" "1" - "team_default" "0" + "team_default" "no" "flags" "1" + "group" "" "name" "Admin Human" "description" "Human class for admins" @@ -420,7 +430,7 @@ // Hud "overlay_path" "" - "nvgs" "0" + "nvgs" "no" "fov" "90" // Effects @@ -430,7 +440,7 @@ // Player behaviour "immunity_mode" "0" "immunity_amount" "0.0" - "no_fall_damage" "1" + "no_fall_damage" "yes" "health" "200" "health_regen_interval" "1.0" @@ -447,10 +457,11 @@ "human_speedy" { // General - "enabled" "1" + "enabled" "yes" "team" "1" - "team_default" "0" + "team_default" "no" "flags" "0" + "group" "" "name" "Speedy" "description" "Fast human" @@ -463,11 +474,11 @@ // Hud "overlay_path" "" - "nvgs" "0" + "nvgs" "no" "fov" "90" // Effects - "has_napalm" "0" + "has_napalm" "no" "napalm_time" "0.0" // Player behaviour @@ -490,10 +501,11 @@ "human_light" { // General - "enabled" "1" + "enabled" "yes" "team" "1" - "team_default" "0" + "team_default" "no" "flags" "0" + "group" "" "name" "Light" "description" "Regular human with improved jump skills" @@ -506,17 +518,17 @@ // Hud "overlay_path" "" - "nvgs" "0" + "nvgs" "no" "fov" "90" // Effects - "has_napalm" "1" + "has_napalm" "yes" "napalm_time" "0.0" // Player behaviour "immunity_mode" "0" "immunity_amount" "0.0" - "no_fall_damage" "0" + "no_fall_damage" "no" "health" "100" "health_regen_interval" "0.0" diff --git a/cstrike/addons/sourcemod/translations/no/zombiereloaded.phrases.txt b/cstrike/addons/sourcemod/translations/no/zombiereloaded.phrases.txt index 4f59fed..1e5eb3b 100644 --- a/cstrike/addons/sourcemod/translations/no/zombiereloaded.phrases.txt +++ b/cstrike/addons/sourcemod/translations/no/zombiereloaded.phrases.txt @@ -216,49 +216,29 @@ "no" "Klassevalg:" } - "Classes menu zombie" + "Classes menu active" { - "no" "Velg zombieklasse:" + "no" "Aktiv: {1}" } - "Classes menu zombie current" + "Classes menu next" { - "no" "Velg zombieklasse\n {1}" + "no" "Påfølgende: {1}" } - "Classes menu zombie next" + "Classes menu select zombie" { - "no" "Velg zombieklasse\n Aktiv: {1}\n Påfølgende: {2}" + "no" "Velg zombieklasse" } - "Classes menu human" + "Classes menu select human" { - "no" "Velg menneskeklasse:" + "no" "Velg menneskeklasse" } - "Classes menu human current" + "Classes menu select admin" { - "no" "Velg menneskeklasse\n {1}" - } - - "Classes menu human next" - { - "no" "Velg menneskeklasse\n Aktiv: {1}\n Påfølgende: {2}" - } - - "Classes menu admin" - { - "no" "Velg administratormodusklasse:" - } - - "Classes menu admin current" - { - "no" "Velg administratormodusklasse\n {1}" - } - - "Classes menu admin next" - { - "no" "Velg administratormodusklasse\n Active: {1}\n Next spawn: {2}" + "no" "Velg administratormodusklasse" } "Classes menu admin mode toggle" @@ -268,7 +248,7 @@ "Classes Menu Team Select Title" { - "no" "velg lag:" + "no" "Velg lag:" } "Classes Menu Zombies" @@ -338,6 +318,11 @@ "no" "Hoppelengde" } + "Classes Selection Not Allowed" + { + "no" "Endring av klasse er ikke tillatt." + } + // =========================== // Overlays (core) // =========================== @@ -372,49 +357,44 @@ "Infect menu clients title" { - "en" "Zombie Management\nToggle Infection:\n[] = Infected" + "no" "Spillerbehandling\nVeksle mellom zombie og menneske:\n[] = Infisert" } // Commands "Infect command infect syntax" { - "en" "Infect a client. Usage: zr_infect [respawn - 1/0]" + "no" "Infiserer en spiller. Syntaks: zr_infect [teleporter - 1/0]" } "Infect command human syntax" { - "en" "Turn a client into a human. Usage: zr_human [respawn - 1/0] [protect - 1/0]" + "no" "Gjør en spiller om til et menneske. Syntaks: zr_human [teleporter - 1/0] [beskyttelse - 1/0]" } "Infect command infect successful" { - "#format" "{1:s}" - "en" "Player @green{1} @defaulthas been successfully infected." + "no" "@green{1}@default ble vellykket infisert." } "Infect command infect mother successful" { - "#format" "{1:s}" - "en" "Player @green{1} @defaulthas been successfully infected as the mother zombie." + "no" "@green{1}@default ble vellykket infisert som moderzombie." } "Infect command infect unsuccessful" { - "#format" "{1:s}" - "en" "Player @green{1} @defaultis already a zombie." + "no" "@green{1}@default er allerede en zombie." } "Infect command human successful" { - "#format" "{1:s}" - "en" "Player @green{1} @defaulthas been successfully brought back as a human." + "no" "@green{1}@default ble vellykket gjort om til et menneske." } "Infect command human unsuccessful" { - "#format" "{1:s}" - "en" "Player @green{1} @defaultis already a human." + "no" "@green{1}@default er allerede et menneske." } // =========================== @@ -423,7 +403,7 @@ "Damage suicide intercept" { - "en" "Suicide attempt intercepted." + "no" "Selvmordsforsøk hindret." } // =========================== @@ -434,50 +414,42 @@ "Menu main title" { - "#format" "{1:s},{2:s}" - "en" "ZR Commands:\nPrefix command with \"{1}\" or \"{2}\" (quiet) when typing in chat." + "no" "ZR-kommandoer:\nStart kommandoen med \"{1}\" or \"{2}\" (skjules) i samtaleområdet." } "Menu main zadmin" { - "#format" "{1:s}" - "en" "ZAdmin - Open admin menu. (Command: {1})" + "no" "ZAdmin - Åpne administrasjonsmeny" } "Menu main zclass" { - "#format" "{1:s}" - "en" "ZClass - Configure class settings. (Command: {1})" + "no" "ZClass - Endre klasseinstillinger" } "Menu main zcookies" { - "#format" "{1:s}" - "en" "ZCookies - Toggle personal ZR settings here. (Command: {1})" + "no" "ZCookies - Endre lagrede instillinger" } "Menu main zspawn" { - "#format" "{1:s}" - "en" "ZSpawn - Join late? Spawn with this. (Command: {1})" + "no" "ZSpawn - Bli med i spillet øyeblikkelig" } "Menu main ztele" { - "#format" "{1:s}" - "en" "ZTele - Stuck? Teleport back to spawn. (Command: {1})" + "no" "ZTele - Teleporter til startområde" } "Menu main zhp" { - "#format" "{1:s}" - "en" "ZHP - Shows real HP as zombie. (Command: {1})" + "no" "ZHP - Vis nåværende helsenivå" } "Menu main zmarket" { - "#format" "{1:s}" - "en" "ZMarket - Customize loadouts here. (Command: {1})" + "no" "ZMarket - Endre våpeninstillinger" } // =========================== @@ -488,280 +460,252 @@ "Zombie cant use weapon" { - "en" "Zombies can't use weapons!" - "ru" "Зомби не могут использовать оружие!" + "no" "Zombier kan ikke bruke våpen." } // Restrict "Restrict weapon" { - "#format" "{1:s}" - "en" "Weapon @green\"{1}\" @defaulthas been restricted." + "no" "Våpenet @green\"{1}\"@default ble sperret." } "Unrestrict weapon" { - "#format" "{1:s}" - "en" "Weapon @green\"{1}\" @defaulthas been unrestricted." + "no" "Våpenet @green\"{1}\"@default er ikke lenger sperret." } "Restrict weapon stopped" { - "#format" "{1:s}" - "en" "Weapon @green\"{1}\" @defaultis already restricted." + "no" "Våpenet @green\"{1}\"@default er allerede sperret." } "Unrestrict weapon stopped" { - "#format" "{1:s}" - "en" "Weapon @green\"{1}\" @default has no restrictions set." + "no" "Våpenet @green\"{1}\"@default er ikke sperret." } "Restrict weapon type" { - "#format" "{1:s}" - "en" "Weapons of type @green\"{1}\" @defaulthave been restricted." + "no" "Våpentypen @green\"{1}\"@default er sperret." } "Unrestrict weapon type" { - "#format" "{1:s}" - "en" "Weapons of type @green\"{1}\" @defaulthave been unrestricted." + "no" "Våpentypen @green\"{1}\"@default er ikke lenger sperret." } "Restrict weapon type stopped" { - "#format" "{1:s}" - "en" "Weapons of type @green\"{1}\" @defaultare all already restricted." + "no" "Våpentypen @green\"{1}\"@default er allerede sperret." } "Unrestrict weapon type stopped" { - "#format" "{1:s}" - "en" "Weapons of type @green\"{1}\" @defaulthave no restrictions set." + "no" "Våpentypen @green\"{1}\"@default er ikke sperret." } "Restrict weapon untoggleable" { - "#format" "{1:s}" - "en" "Weapon @green\"{1}\" @defaultmay not have its restrictions toggled." + "no" "Våpenet @green\"{1}\"@default kan ikke sperres." } "Weapon invalid" { - "#format" "{1:s}" - "en" "Weapon @green\"{1}\" @defaultis an invalid weapon (type) name." + "no" "Våpenet eller våpentypen @green\"{1}\"@default er ugyldig." } "Weapon is restricted" { - "#format" "{1:s}" - "en" "Weapon @green{1} @defaultis restricted." - "ru" "Оружие @green{1} @default запрещено." + "no" "Våpenet @green{1}@default er sperret." } // ZMarket "Weapons zmarket buyzone" { - "en" "You must be in a buyzone to use ZMarket." + "no" "Du må være i en kjøpesone for å kunne bruke ZMarket." } "Weapons zmarket purchase" { - "#format" "{1:s}" - "en" "You have purchased weapon @green{1}. @defaultSelect item again to buy ammo if you are low." + "no" "Du har kjøpt våpenet @green{1}@default. Velg gjenstanden igjen for å kjøpe mer ammunisjon." } "Weapons zmarket purchase max" { - "#format" "{1:s},{2:d}" - "en" "Weapon @green{1} @defaulthas a purchase limit of @green{2}@default. Wait until you respawn to try again." + "no" "Våpenet @green{1}@default har en kjøpegrense på @green{2}@default enheter. Vent til neste runde eller gjenoppliving." } "Weapons zmarket auto-rebuy toggle on" { - "en" "Auto-rebuy has been enabled, your loadout will be automatically purchased for you each time you spawn." + "no" "Autokjøp er aktivert, dine valg vil automatisk bli kjøpt hver runde eller hver gjenoppliving." } "Weapons zmarket auto-rebuy toggle off" { - "en" "Auto-rebuy has been disabled." + "no" "Autokjøp er deaktivert." } "Weapons zmarket updated loadout" { - "en" "You are not able to purchase a weapon at this time, but your loadout has been updated with this weapon." + "no" "Du kan ikke kjøpe våpen for øyeblikket, men våpenvalget er blitt lagret." } "Weapons zmarket get current loadout" { - "en" "Updated current loadout, use rebuy option to purchase these weapons again." + "no" "Våpenvalg oppdatert. Bruk gjenkjøpvalget for å kjøpe disse våpnene igjen." } // Commands "Weapons command restrict syntax" { - "en" "Restricts a weapon or a weapon type. Usage: zr_restrict [weapon2|weapontype2] ..." + "no" "Sperrer et våpen eller en våpentype. Syntaks: zr_restrict [våpen|våpentype] ..." } "Weapons command unrestrict syntax" { - "en" "Unrestricts a weapon or a weapon type. Usage: zr_unrestrict [weapon2|weapontype2] ..." + "no" "Fjerner sperre av et våpen eller en våpentype. Syntaks: zr_unrestrict [våpen|våpentype] ..." } // Menu (Restrict) "Weapons menu restrict main title" { - "en" "Weapons Management" + "no" "Våpenbehandling" } "Weapons menu restrict main restrict" { - "en" "Weapon Restrictions" + "no" "Våpensperringer" } "Weapons menu restrict main market" { - "en" "ZMarket" + "no" "ZMarket" } "Weapons menu restrict types title" { - "en" "Weapon Restrictions\nSelect Weapon Type:" + "no" "Våpensperringer\nVelg våpentype:" } "Weapons menu restrict types weapon type title" { - "#format" "{1:s}" - "en" "Weapon Restrictions\nWeapon Type: {1}\n[] = Restricted" + "no" "Våpensperringer\nVåpentype: {1}\n[] = Sperret" } "Weapons menu restrict types restrict all" { - "#format" "{1:s}" - "en" "Restrict weapon type {1}" + "no" "Sperr våpentypen \"{1}\"" } "Weapons menu restrict types unrestrict all" { - "#format" "{1:s}" - "en" "Unrestrict weapon type {1}" + "no" "Fjern sperre for våpentypen \"{1}\"" } "Weapons menu restrict zmarket title" { - "en" "ZMarket\nSelect Setting:" + "no" "ZMarket\nVelg instilling:" } "Weapons menu restrict zmarket buyzone" { - "#format" "{1:s}" - "en" "Buyzone Only - {1}" + "no" "Kun i kjøpesoner - {1}" } // Menu (ZMarket) "Weapons menu zmarket main title" { - "en" "ZMarket\nSelect an Option:" + "no" "ZMarket" } "Weapons menu zmarket main get loadout" { - "en" "Get Current Loadout" + "no" "Lagre gjeldende våpenvalg" } "Weapons menu zmarket main view loadout" { - "en" "View My Loadout" + "no" "Vis våpenvalg" } "Weapons menu zmarket main rebuy" { - "en" "Rebuy My Loadout" + "no" "Kjøp gjeldende våpenvalg" } "Weapons menu zmarket main auto-rebuy" { - "#format" "{1:s}" - "en" "Auto-Rebuy: {1}" + "no" "Autokjøp: {1}" } "Weapons menu zmarket main weapons list" { - "en" "View Weapons List" + "no" "Vis våpenliste" } "Weapons menu zmarket loadout title" { - "en" "ZMarket\nMy Current Loadout:\nSelect weapon slot to clear.\nNote: Rebuy refers to these weapons." + "no" "ZMarket\nGjeldende våpenvalg:\nFjern våpen ved å velge det." } "Weapons menu zmarket loadout primary" { - "#format" "{1:s}" - "en" "Primary: {1}" + "no" "Primærvåpen: {1}" } "Weapons menu zmarket loadout secondary" { - "#format" "{1:s}" - "en" "Secondary: {1}" + "no" "sekundærvåpen: {1}" } "Weapons menu zmarket loadout melee" { - "#format" "{1:s}" - "en" "Melee: {1}" + "no" "Nærkamp: {1}" } "Weapons menu zmarket loadout projectile" { - "#format" "{1:s}" - "en" "Projectile: {1}" + "no" "Prosjektiler: {1}" } "Weapons menu zmarket loadout explosive" { - "#format" "{1:s}" - "en" "Explosive: {1}" + "no" "Granater: {1}" } "Weapons menu zmarket loadout nvgs" { - "#format" "{1:s}" - "en" "NVGs: {1}" + "no" "Nattsyn: {1}" } "Weapons menu zmarket loadout empty" { - "en" "(None)" + "no" "(Ingen)" } "Weapons menu zmarket types title" { - "en" "ZMarket\nSelect Weapon Type:" + "no" "ZMarket\nVelg våpentype:" } "Weapons menu zmarket types weapon type title" { - "#format" "{1:s}" - "en" "ZMarket\nWeapon Type: {1}\n[] = Restricted" + "no" "ZMarket\nVåpentype: {1}\n[] = Sperret" } "Weapons menu zmarket types weapon info" { - "#format" "{1:s},{2:d},{3:d}" - "en" "{1} (${2})\nPurchases Left: {3}" + "no" "{1} (${2})\nAntall kjøp igjen: {3}" } "Weapons menu zmarket types weapon ammo" { - "en" "Ammo" + "no" "Ammunisjon" } // =========================== @@ -772,62 +716,59 @@ "Hitgroups menu hitgroups title" { - "en" "Hitgroup Management\nCommands: zr_hitgroup(_enable_all/_headshots_only)\nSelect a Hitgroup to Toggle:" + "no" "Treffgruppebehandling\nKommandoer: zr_hitgroup(_enable_all/_headshots_only)\nVelg en treffgruppe for å aktivere/deaktivere:" } "Hitgroups menu hitgroups enable all" { - "en" "Enable All Hitgroups" + "no" "Aktiver alle treffgrupper" } "Hitgroups menu hitgroups headshots only" { - "en" "Headshots Only" + "no" "Kun hodeskudd" } // Commands "Hitgroups command syntax" { - "en" "Toggles or sets if a zombie's hitgroup can be damaged. Usage: zr_hitgroup [1/0]" + "no" "Veksle, aktiver eller deaktiver en treffgruppe til zombiene. Usage: zr_hitgroup [aktiv - 1/0]" } "Hitgroups command related commands" { - "en" "Related command(s): zr_hitgroup_enable_all, zr_hitgroup_headshots_only" + "no" "Relaterte kommandoer: zr_hitgroup_enable_all, zr_hitgroup_headshots_only" } "Hitgroups command syntax names" { - "en" "Hitgroup Names:" + "no" "Treffgruppenavn:" } "Hitgroups command successful on" { - "#format" "{1:s}" - "en" "Damage for hitgroup \"{1}\" has been toggled on." + "no" "Skade for treffgruppen \"{1}\" er aktivert." } "Hitgroups command successful off" { - "#format" "{1:s}" - "en" "Damage for hitgroup \"{1}\" has been toggled off." + "no" "Skade for treffgruppen \"{1}\" er deaktivert." } "Hitgroups command invalid hitgroup" { - "#format" "{1:s}" - "en" "\"{1}\" is not a valid hitgroup alias. Type zr_hitgroup to view all aliases." + "no" "\"{1}\" er ikke en gyldig treffgruppe. Bruk zr_hitgroup for å se alle treffgruppenavnene." } "Hitgroups command enable all successful" { - "en" "All zombie hitgroups have been enabled for damage." + "no" "Alle treffgruppene er aktivert." } "Hitgroups command headshots only successful" { - "en" "Zombies may now only be damaged by headshots." + "no" "Zombiene kan nå kun bli skadet av hodeskudd." } // =========================== @@ -836,37 +777,37 @@ "ZAdmin main title" { - "en" "ZAdmin\nSelect Category:" + "no" "ZAdmin\nVelg kategori:" } "ZAdmin main class multipliers" { - "en" "Class Multipliers" + "no" "Klassemultiplikatorer" } "ZAdmin main weapons" { - "en" "Weapon Management" + "no" "Våpenbehandling" } "ZAdmin main hitgroups" { - "en" "Hitgroup Management" + "no" "Treffgruppebehandling" } "ZAdmin main zombie" { - "en" "Zombie Management" + "no" "Spillerbehandling" } "ZAdmin main force zspawn" { - "en" "Force ZSpawn" + "no" "Tving gjenoppliving" } "ZAdmin main force ztele" { - "en" "Force ZTele" + "no" "Tving teleport" } // =========================== @@ -874,39 +815,37 @@ // =========================== // Commands + + // Not translating antistick phrases because hull width settings had no effect. To be removed. "AntiStick command set width syntax" { - "en" "Sets the width of a model's hull. (See zr_antistick_list_models) Usage: zr_antistick_set_width " + "no" "Sets the width of a model's hull. (See zr_antistick_list_models) Usage: zr_antistick_set_width " } "AntiStick command list models list" { - "en" "Player name: Model: Hull width:\n-------------------------------------------------------------------------------------------------------------" + "no" "Player name: Model: Hull width:\n-------------------------------------------------------------------------------------------------------------" } "AntiStick command list models name" { - "#format" "{1:-35s},{2:-51s},{3:-.2f}" - "en" "{1} {2} {3}" + "no" "{1} {2} {3}" } "AntiStick command set width successful" { - "#format" "{1:s},{2:f}" - "en" "Model hull width for model \"{1}\" has been changed to \"{2}.\"" + "no" "Model hull width for model \"{1}\" has been changed to \"{2}.\"" } "AntiStick command set width invalid model" { - "#format" "{1:s}" - "en" "Invalid model/player specified: \"{1}\"" + "no" "Invalid model/player specified: \"{1}\"" } "AntiStick command set width invalid width" { - "#format" "{1:f}" - "en" "Invalid model hull width specified: \"{1}\"" + "no" "Invalid model hull width specified: \"{1}\"" } // =========================== @@ -917,23 +856,19 @@ "Spawn protection begin" { - "#format" "{1:d}" - "en" "Zombies are present, you have {1} seconds to find a safe place before you lose immunity." - "ru" "Зомби на свободе, у вас {1} секунд, чтобы найти безопасное место, пока не потеряли иммунитет." + "no" "Zombier tilstede, du er beskyttet og har {1} sekunder på å finne et trygt sted." } "Spawn protection end" { - "en" "You are now susceptible to zombie infection." - "ru" "Теперь вы восприимчивы к зомби инфекции." + "no" "Du er nå utsatt for å bli smittet av en zombie." } // HUD "Spawn Protect" { - "#format" "{1:d}" - "en" "Zombie protection ends in: {1} seconds." + "no" "Zombiebeskyttelsen stopper om {1} sekunder." } // =========================== @@ -942,30 +877,27 @@ "ZCookies Menu main title" { - "en" "ZCookies\nSelect a Cookie:" + "no" "ZCookies\nVelg informasjonskapsel:" } "ZCookies menu main auto-rebuy" { - "#format" "{1:s}" - "en" "ZMarket Auto-Rebuy: {1}" + "no" "Autokjøp (ZMarket): {1}" } "ZCookies menu main zhp" { - "#format" "{1:s}" - "en" "ZHP Display: {1}" + "no" "ZHP-skjerm: {1}" } "ZCookies menu main overlay" { - "#format" "{1:s}" - "en" "Human/Zombie Class Overlay: {1}" + "no" "Vis klasseoverlegg: {1}" } "ZCookies zmarket loadout" { - "en" "ZMarket Loadout" + "no" "Våpenvalg (ZMarket)" } // =========================== @@ -976,40 +908,36 @@ "ZSpawn double spawn" { - "en" "ZSpawn can only be used if you joined late during a round in progress." + "no" "ZSpawn kan kun bli brukt hvis du koblet til servern midt i en runde." } "ZSpawn timelimit" { - "#format" "{1:d}" - "en" "The timelimit ({1} seconds), to use ZSpawn, has already expired." + "no" "Tidsfristen ({1} sekunder) for å bruke ZSpawn har gått ut." } // Menu "ZSpawn clients title" { - "en" "Force ZSpawn (zr_zspawn_force)\nSelect a Player:" + "no" "Tving gjenoppliving (zr_zspawn_force)\nVelg en spiller:" } // Commands "ZSpawn command force syntax" { - "en" "Force ZSpawn on a client. Usage: zr_zspawn_force ['0' = Spawn as human | '1' = Spawn as zombie]" - + "no" "Tving gjenoppliving på en spiller. Syntaks: zr_zspawn_force ['0' = Menneske | '1' = Zombie]" } "ZSpawn command force successful" { - "#format" "{1:s}" - "en" "Player {1} was successfully spawned." + "no" "{1} ble vellykket gjenopplivet." } "ZSpawn command force unsuccessful" { - "#format" "{1:s}" - "en" "Player {1} couldn't be spawned." + "no" "{1} kunne ikke bli gjenopplivet." } // =========================== @@ -1020,70 +948,63 @@ "ZTele restricted human" { - "en" "ZTele is restricted for humans at this time." + "no" "Mennesker kan ikke teleporteres for øyeblikket." } "ZTele max" { - "#format" "{1:d}" - "en" "You have reached your max of {1} teleport(s) per round." + "no" "Du har brukt opp alle teleportene ({1}) for denne runden." } "ZTele in progress" { - "en" "You are currently pending teleportation." + "no" "Du venter på allerede på en teleport." } "ZTele autocancel text" { - "#format" "{1:d}" - "en" "Teleport has been cancelled. (Restrictive boundary is {1} ft.)" + "no" "Teleport avbrutt. Du har forflyttet deg lengre enn tillatt da du ventet på en teleport. Maks lengde er {1} fot." } // Center Text "ZTele countdown" { - "#format" "{1:d}" - "en" "Teleporting back to spawn in: {1} seconds." + "no" "Teleporterer til startområdet om {1} sekunder." } "ZTele autocancel centertext" { - "en" "Teleport has been cancelled." + "no" "Teleport avbrutt." } "ZTele countdown end" { - "#format" "{1:d},{2:d}" - "en" "Teleported back to spawn. (Count: {1}/{2})" + "no" "Teleportert til startområdet. (Antall: {1}/{2})" } // Menu "ZTele clients title" { - "en" "Force ZTele (zr_ztele_force)\nSelect a Player:" + "no" "Tving teleport (zr_ztele_force)\nVelg en spiller:" } // Commands "ZTele command force syntax" { - "en" "Force ZTele on a client. Usage: zr_ztele_force " - + "no" "Tving teleport på en spiller. Syntaks: zr_ztele_force " } "ZTele command force successful" { - "#format" "{1:s}" - "en" "Player {1} was successfully teleported." + "no" "{1} ble vellykket teleportert." } "ZTele command force unsuccessful" { - "#format" "{1:s}" - "en" "Player {1} couldn't be teleported." + "no" "{1} kunne ikke bli teleportert." } // =========================== @@ -1094,22 +1015,19 @@ "ZHP enable" { - "en" "HP display enabled, your real HP will be displayed after infection." + "no" "HP-skjerm aktivert. Du vil se din ekte helseverdi som zombie." } "ZHP disable" { - "en" "HP display disabled." - "ru" "Отображение здоровья включено." + "no" "HP-skjerm deaktivert." } // HUD "Display HP" { - "#format" "{1:d}" - "en" "HP: {1}" - "ru" "Здоровье: {1}" + "no" "HP: {1}" } // =========================== @@ -1118,18 +1036,16 @@ "Vol Anticamp Message" { - "en" "This area is restricted, please move along." + "no" "Dette området er sperret, vennligst gå videre." } "Vol Slay" { - "#format" "{1:s},{2:d}" - "en" "Slayed player \"{1}\" for camping in a restricted area (ID: {2})." + "no" "Drepte \"{1}\" for opphold i sperret område (ID: {2})." } "Vol Ignite" { - "#format" "{1:s},{2:d}" - "en" "Ignited player \"{1}\" for camping in a restricted area (ID: {2})." + "no" "Satt fyr på \"{1}\" for opphold i sperret område (ID: {2})." } } diff --git a/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt b/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt index 6b34eca..c09ed0b 100644 --- a/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt +++ b/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt @@ -65,7 +65,7 @@ "Must be zombie" { - "en" "This feature requires that you are a zombie. + "en" "This feature requires that you are a zombie." } "Must be human" @@ -228,55 +228,31 @@ "en" "Class Selection:" } - "Classes menu zombie" - { - "en" "Select Zombie Class:" - } - - "Classes menu zombie current" + "Classes menu active" { "#format" "{1:s}" - "en" "Select Zombie Class\n {1}" + "en" "Active: {1}" } - "Classes menu zombie next" - { - "#format" "{1:s},{2:s}" - "en" "Select Zombie Class\n Active: {1}\n Next spawn: {2}" - } - - "Classes menu human" - { - "en" "Select Human Class:" - } - - "Classes menu human current" + "Classes menu next" { "#format" "{1:s}" - "en" "Select Human Class\n {1}" + "en" "Next spawn: {1}" } - "Classes menu human next" + "Classes menu select zombie" { - "#format" "{1:s},{2:s}" - "en" "Select Human Class\n Active: {1}\n Next spawn: {2}" + "en" "Select Zombie Class" } - "Classes menu admin" + "Classes menu select human" { - "en" "Select Admin Mode Class:" - } - - "Classes menu admin current" - { - "#format" "{1:s}" - "en" "Select Admin Mode Class\n {1}" + "en" "Select Human Class" } - "Classes menu admin next" + "Classes menu select admin" { - "#format" "{1:s},{2:s}" - "en" "Select Admin Mode Class\n Active: {1}\n Next spawn: {2}" + "en" "Select Admin Mode Class" } "Classes menu admin mode toggle" @@ -356,6 +332,11 @@ "en" "Jump Distance" } + "Classes Selection Not Allowed" + { + "en" "Changing classes is not allowed." + } + // =========================== // Overlays (core) // =========================== @@ -461,44 +442,37 @@ "Menu main zadmin" { - "#format" "{1:s}" - "en" "ZAdmin - Open admin menu. (Command: {1})" + "en" "ZAdmin - Open admin menu" } "Menu main zclass" { - "#format" "{1:s}" - "en" "ZClass - Configure class settings. (Command: {1})" + "en" "ZClass - Configure class settings" } "Menu main zcookies" { - "#format" "{1:s}" - "en" "ZCookies - Toggle personal ZR settings here. (Command: {1})" + "en" "ZCookies - Toggle personal ZR settings here" } "Menu main zspawn" { - "#format" "{1:s}" - "en" "ZSpawn - Join late? Spawn with this. (Command: {1})" + "en" "ZSpawn - Join late? Spawn with this" } "Menu main ztele" { - "#format" "{1:s}" - "en" "ZTele - Stuck? Teleport back to spawn. (Command: {1})" + "en" "ZTele - Stuck? Teleport back to spawn" } "Menu main zhp" { - "#format" "{1:s}" - "en" "ZHP - Shows real HP as zombie. (Command: {1})" + "en" "ZHP - Shows real HP as zombie" } "Menu main zmarket" { - "#format" "{1:s}" - "en" "ZMarket - Customize loadouts here. (Command: {1})" + "en" "ZMarket - Customize loadouts here" } // =========================== diff --git a/cstrike/cfg/sourcemod/zombiereloaded/zombiereloaded.cfg b/cstrike/cfg/sourcemod/zombiereloaded/zombiereloaded.cfg index 9bec016..7151e00 100644 --- a/cstrike/cfg/sourcemod/zombiereloaded/zombiereloaded.cfg +++ b/cstrike/cfg/sourcemod/zombiereloaded/zombiereloaded.cfg @@ -91,7 +91,7 @@ zr_log "1" // Default: "3" zr_log_flags "3" -// Enable module filtering. Only log events from listed modules will be logged. +// Enable module filtering. Only events from listed modules will be logged. // Default: "0" zr_log_module_filter "0" @@ -133,6 +133,15 @@ zr_config_path_weapons "configs/zr/weapons.txt" zr_config_path_hitgroups "configs/zr/hitgroups.txt" +// ---------------------------------------------------------------------------- +// Permission settings +// ---------------------------------------------------------------------------- + +// Use group authentication instead of flags to access admin features. Generic admin flag is still required on some features. +// - +// Default: "0" +zr_permissions_use_groups "0" + // ---------------------------------------------------------------------------- // Classes (core) // ---------------------------------------------------------------------------- @@ -143,10 +152,18 @@ zr_config_path_hitgroups "configs/zr/hitgroups.txt" // Default: "0" zr_classes_spawn "0" -// Player is assigned a random class every spawn. [Override: zr_classes_spawn & zr_classes_default_*] +// Player is assigned a random class every spawn. [Override: zr_classes_default_*] // Default: "0" zr_classes_random "0" +// Time limit to change human class with instant change after spawning. Time is in seconds. Use 0 or negative to disable. +// Default: "20" +zr_classes_change_timelimit "20" + +// Save players' class selections in server cookies and restore when connecting. [Override: zr_classes_default_*] +// Default: "1" +zr_classes_save "1" + // Admin class assigned to admins on connect. ["random" = Random admin class | "" = Class config default] // Default: "random" zr_classes_default_admin "random" @@ -163,6 +180,18 @@ zr_classes_default_zombie "random" // Default: "motherzombies" zr_classes_default_mother_zombie "motherzombies" +// Allow players to select zombie classes. +// Default: "1" +zr_classes_zombie_select "1" + +// Allow players to select human classes. +// Default: "1" +zr_classes_human_select "1" + +// Allow admins to select admin mode classes. (Not to be confused by admin-only classes!) +// Default: "1" +zr_classes_admin_select "1" + // Menu // Automatically close class selection menu. diff --git a/docs/zr_manual.txt b/docs/zr_manual.txt index 51c928a..8de2262 100644 --- a/docs/zr_manual.txt +++ b/docs/zr_manual.txt @@ -604,7 +604,7 @@ The list below explains all available class attributes in detail: Attribute: Value type: Limits/Requirements: =========================================================================== - enabled boolean 0 or 1 + enabled text "yes" or "no" --------------------------------------------------------------------------- Enables or disables the class. Disabled classes won't show up in the class selection menus. Also, if some attributes failed to validate the @@ -627,7 +627,7 @@ The list below explains all available class attributes in detail: See the "flags" attribute for admin-only classes. - team_default boolean 0 or 1 + team_default text "yes" or "no" --------------------------------------------------------------------------- Marks the class as the default class for its specified team. This class will be automatically selected on new players when they join the @@ -651,8 +651,13 @@ The list below explains all available class attributes in detail: setting must be set to "motherzombies" for this flag to take effect. - Remember that these flags can be used in a combination (1 + 2), so - it's a mother zombie class for admins only. + These flags can be used in a combination (1 + 2), so it could be a + mother zombie class for admins only. + + group text Name of a valid SourceMod group. + --------------------------------------------------------------------------- + Rescrict class to members of this SourceMod group. Useful for making + VIP-only classes. Leave blank to allow everyone to use this class. name text Unique, not empty, max 64 charact. --------------------------------------------------------------------------- @@ -668,10 +673,13 @@ The list below explains all available class attributes in detail: model_path text Max 256 characters, not empty --------------------------------------------------------------------------- The model file to use on the player, path is relative to the "cstrike" - folder. There are two special values supported by this attribute: + folder. There are a few special values supported by this attribute: - "default" - Don't change model, use default CS models. - "random" - Selects a random model for the current team. + "default" - Use default CS models. The one players select when + selecting team. + "random" - Selects a random model for the current team. + "nochange" - Don't change model. To be used in combination with + other plugins that change model on players. alpha_spawn number 0 - 255 --------------------------------------------------------------------------- @@ -694,7 +702,7 @@ The list below explains all available class attributes in detail: controlled by the night vision key ("n" by default). The path is relative to "cstrike/materials". Leave blank to disable. - nvgs boolean 0 or 1 + nvgs text "yes" or "no" --------------------------------------------------------------------------- Gives night vision to the player. @@ -703,7 +711,7 @@ The list below explains all available class attributes in detail: The field of view on the player. Note that the weapon hud disappear if FOV is anything else than 90. - has_napalm boolean 0 or 1 + has_napalm text "yes" or "no" --------------------------------------------------------------------------- Human classes only. Specifies whether the human can trow napalm grenades or not. @@ -725,7 +733,7 @@ The list below explains all available class attributes in detail: The value for the specified immunity mode. This feature is currently incomplete and values are ignored. - no_fall_damage boolean 0 or 1 + no_fall_damage text "yes" or "no" --------------------------------------------------------------------------- Blocks fall damage on the player. @@ -750,8 +758,8 @@ The list below explains all available class attributes in detail: kill_bonus number 0 - 16 --------------------------------------------------------------------------- - Zombie classes only. How many EXTRA points to give the killer of a - zombie. If this is 0, the default value of 1 point will be given. + Zombie classes only. How many extra points to give the killer of this + zombie. If this is 0, the default value of 1 point will be given. speed decimal 10.0 - 2000.0 --------------------------------------------------------------------------- @@ -762,13 +770,15 @@ The list below explains all available class attributes in detail: Zombie classes only. Force to apply on the zombie when shot at. The zombie is pushed in the same direction as the bullet. - jump_height decimal -500.0 - 500.0 + jump_height decimal 0.0 - 5.0 --------------------------------------------------------------------------- - Extra upward boost when jumping. + Jump height multiplier. Extra upward boost when jumping. If 1.0 or 0.0 + no boost will be applied. - jump_distance decimal -500.0 - 500.0 + jump_distance decimal 0.0 - 5.0 --------------------------------------------------------------------------- - Extra forward boost when jumping. + Jump distance multiplier. Extra forward boost when jumping. If 1.0 or + 0.0 no boost will be applied. 3.7.3 CLASS REQUIREMENTS @@ -798,29 +808,30 @@ Attribute flags: team 2 2 team_default 3 4 flags 4 8 - name 5 16 - description 6 32 - model_path 7 64 - alpha_initial 8 128 - alpha_damaged 9 256 - alpha_damage 10 512 - overlay_path 11 1024 - nvgs 12 2048 - fov 13 4096 - has_napalm 14 8192 - napalm_time 15 16384 - immunity_mode 16 32768 - immunity_amount 17 65536 - no_fall_damage 18 131072 - health 19 262144 - health_regen_interval 20 524288 - health_regen_amount 21 1048576 - infect_gain 22 2097152 - kill_bonus 23 4194304 - speed 24 8388608 - knockback 25 16777216 - jump_height 26 33554432 - jump_distance 27 67108864 + group 5 16 + name 6 32 + description 7 64 + model_path 8 128 + alpha_initial 9 256 + alpha_damaged 10 512 + alpha_damage 11 1024 + overlay_path 12 2048 + nvgs 13 4096 + fov 14 8192 + has_napalm 15 16384 + napalm_time 16 32768 + immunity_mode 17 65536 + immunity_amount 18 131072 + no_fall_damage 19 262144 + health 20 524288 + health_regen_interval 21 1048576 + health_regen_amount 22 2097152 + infect_gain 23 4194304 + kill_bonus 24 8388608 + speed 25 16777216 + knockback 26 33554432 + jump_height 27 67108864 + jump_distance 28 134217728 The error flags are stored in a bit field (explained in the log configuration in section 3.2). @@ -873,6 +884,25 @@ Class console variables: --------------------------------------------------------------------------- Assign random classes to all players each round. + This setting overrides zr_classes_save. + + Options: + 0 or 1 + + zr_classes_change_timelimit 20 + --------------------------------------------------------------------------- + The time limit to change human classes with instant change after + spawning. So humans don't have to set class before spawning. Time is in + seconds. + + zr_classes_save 1 + --------------------------------------------------------------------------- + Save players' class selections in server cookies. Class selections are + restored next time players connect. + + This setting overrides zr_classes_default_*, but on first-time + connecting players the default classes are assigned. + Options: 0 or 1 diff --git a/src/zombiereloaded.sp b/src/zombiereloaded.sp index 22d9806..59dc729 100644 --- a/src/zombiereloaded.sp +++ b/src/zombiereloaded.sp @@ -41,6 +41,7 @@ #include "zr/zombiereloaded" #include "zr/translation" #include "zr/cvars" +#include "zr/admintools" #include "zr/log" #include "zr/config" #include "zr/steamidcache" @@ -49,6 +50,7 @@ #include "zr/menu" #include "zr/cookies" #include "zr/paramtools" +#include "zr/paramparser" #include "zr/models" #include "zr/downloads" #include "zr/overlays" diff --git a/src/zr/admintools.inc b/src/zr/admintools.inc new file mode 100644 index 0000000..e767679 --- /dev/null +++ b/src/zr/admintools.inc @@ -0,0 +1,168 @@ +/* + * ============================================================================ + * + * Zombie:Reloaded + * + * File: admintools.inc + * Type: Core + * Description: Functions for checking extended admin privileges. + * + * 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 . + * + * ============================================================================ + */ + +/** + * @section Pre-defined group names for authenticating players. + */ +#define ZR_GROUP_ADMINS "zr_admins" +#define ZR_GROUP_MODERATORS "zr_moderators" +#define ZR_GROUP_CONFIGURATORS "zr_configurators" +/** + * @endsection + */ + +/** + * List of operation types to specify the category of a admin operation. + */ +enum OperationTypes +{ + OperationType_Invalid = -1, /** Invalid operation type. */ + OperationType_Generic, /** Generic events like infecting or teleporting players. */ + OperationType_Configuration, /** Changing settings. */ +} + + +/** + * Returns whether a player is allowed to do a certain operation or not. + * + * @param client The client index. + * @param operationType The operation category. + * @return True if allowed, false otherwise. + */ +stock bool:ZRIsClientPrivileged(client, OperationTypes:operationType = OperationType_Generic) +{ + // Validate client index. + if (!ZRIsClientValid(client)) + { + return false; + } + + // Check if group permissions is enabled. + new bool:groupauth = GetConVarBool(g_hCvarsList[CVAR_PERMISSIONS_USE_GROUPS]); + if (groupauth) + { + /********************************** + * * + * GROUP BASED AUTHENTICATION * + * * + **********************************/ + + // Check if client is full admin. + if (ZRIsClientInGroup(client, ZR_GROUP_ADMINS)) + { + return true; + } + + // Check operation type. + switch (operationType) + { + case OperationType_Generic: + { + return ZRIsClientInGroup(client, ZR_GROUP_MODERATORS); + } + case OperationType_Configuration: + { + return ZRIsClientInGroup(client, ZR_GROUP_CONFIGURATORS); + } + } + + // Invalid operation type. + return false; + } + else + { + /********************************* + * * + * FLAG BASED AUTHENTICATION * + * * + *********************************/ + + new AdminFlag:flag; + + // Check operation type. + switch (operationType) + { + case OperationType_Generic: + { + flag = Admin_Generic; + } + case OperationType_Configuration: + { + flag = Admin_Config; + } + default: + { + // Invalid operation type. + return false; + } + } + + return GetAdminFlag(GetUserAdmin(client), flag); + } +} + +/** + * Returns whether a player is in a spesific group or not. + * + * @param client The client index. + * @param groupName SourceMod group name to check. + * @return True if in the group, false otherwise. + */ +stock bool:ZRIsClientInGroup(client, const String:groupName[]) +{ + new AdminId:id = GetUserAdmin(client); + + // Validate id. + if (id == INVALID_ADMIN_ID) + { + return false; + } + + // Get number of groups. + new groupnum = GetAdminGroupCount(id); + decl String:groupname[64]; + + // Validate number of groups. + if (groupnum > 0) + { + // Loop through each group. + for (new group = 0; group < groupnum; group++) + { + // Get group name. + GetAdminGroup(id, group, groupname, sizeof(groupname)); + + // Compare names. + if (StrEqual(groupName, groupname, false)) + { + return true; + } + } + } + + // No groups or no match. + return false; +} diff --git a/src/zr/cookies.inc b/src/zr/cookies.inc index 8c3c316..37069da 100644 --- a/src/zr/cookies.inc +++ b/src/zr/cookies.inc @@ -67,4 +67,36 @@ bool:CookiesGetClientCookieBool(client, Handle:cookie) // Return string casted into an int, then to bool. return bool:StringToInt(cookievalue); -} \ No newline at end of file +} + +/** + * Sets a integer value on a cookie. + * + * @param client The client index. + * @param cookie The handle to the cookie. + * @param value The value to set. + */ +CookiesSetInt(client, Handle:cookie, value) +{ + // Convert value to string. + decl String:strValue[16]; + IntToString(value, strValue, sizeof(strValue)); + + // Set string value. + SetClientCookie(client, cookie, strValue); +} + +/** + * Gets a integer value from a cookie. + * + * @param client The client index. + * @param cookie The handle to the cookie. + */ +CookiesGetInt(client, Handle:cookie) +{ + decl String:strValue[16]; + strValue[0] = 0; + GetClientCookie(client, cookie, strValue, sizeof(strValue)); + + return StringToInt(strValue); +} diff --git a/src/zr/cvars.inc b/src/zr/cvars.inc index 48fb863..faadd94 100644 --- a/src/zr/cvars.inc +++ b/src/zr/cvars.inc @@ -52,8 +52,11 @@ enum CvarsList Handle:CVAR_CONFIG_PATH_CLASSES, Handle:CVAR_CONFIG_PATH_WEAPONS, Handle:CVAR_CONFIG_PATH_HITGROUPS, + Handle:CVAR_PERMISSIONS_USE_GROUPS, Handle:CVAR_CLASSES_SPAWN, Handle:CVAR_CLASSES_RANDOM, + Handle:CVAR_CLASSES_CHANGE_TIMELIMIT, + Handle:CVAR_CLASSES_SAVE, Handle:CVAR_CLASSES_DEFAULT_ZOMBIE, Handle:CVAR_CLASSES_DEFAULT_M_ZOMB, Handle:CVAR_CLASSES_DEFAULT_HUMAN, @@ -62,6 +65,9 @@ enum CvarsList Handle:CVAR_CLASSES_OVERLAY_TOGGLE, Handle:CVAR_CLASSES_OVERLAY_TOGGLECMDS, Handle:CVAR_CLASSES_OVERLAY_DEFAULT, + Handle:CVAR_CLASSES_ZOMBIE_SELECT, + Handle:CVAR_CLASSES_HUMAN_SELECT, + Handle:CVAR_CLASSES_ADMIN_SELECT, Handle:CVAR_WEAPONS, Handle:CVAR_WEAPONS_RESTRICT, Handle:CVAR_WEAPONS_ZMARKET, @@ -216,7 +222,7 @@ CvarsCreate() // =========================== g_hCvarsList[CVAR_LOG] = CreateConVar("zr_log", "1", "Enable logging of events in the plugin. Fatal errors are logged independent on this setting."); g_hCvarsList[CVAR_LOG_FLAGS] = CreateConVar("zr_log_flags", "3", "A bit field that specify what event types to log. See logging section (3.3) in manual for details."); - g_hCvarsList[CVAR_LOG_MODULE_FILTER] = CreateConVar("zr_log_module_filter", "0", "Enable module filtering. Only log events from listed modules will be logged."); + g_hCvarsList[CVAR_LOG_MODULE_FILTER] = CreateConVar("zr_log_module_filter", "0", "Enable module filtering. Only events from listed modules will be logged."); g_hCvarsList[CVAR_LOG_IGNORE_CONSOLE] = CreateConVar("zr_log_ignore_console", "1", "Don't log events triggered by console commands that are executed by the console itself, like commands in configs. Enable this command to avoid spamming logs with events like weapon restrictions."); g_hCvarsList[CVAR_LOG_ERROR_OVERRIDE] = CreateConVar("zr_log_error_override", "1", "Always log error messages. Overrides module filter and logging flags."); g_hCvarsList[CVAR_LOG_PRINT_ADMINS] = CreateConVar("zr_log_print_admins", "0", "Print log events to admin chat in addition to the log file."); @@ -233,17 +239,28 @@ CvarsCreate() g_hCvarsList[CVAR_CONFIG_PATH_HITGROUPS] = CreateConVar("zr_config_path_hitgroups", "configs/zr/hitgroups.txt", "Path, relative to root sourcemod directory, to hitgroups config file."); + // =========================== + // Permission Settings + // =========================== + g_hCvarsList[CVAR_PERMISSIONS_USE_GROUPS] = CreateConVar("zr_permissions_use_groups", "0", "Use group authentication instead of flags to access admin features. Generic admin flag is still required on some features."); + + // =========================== // Classes (core) // =========================== // General g_hCvarsList[CVAR_CLASSES_SPAWN] = CreateConVar("zr_classes_spawn", "0", "Re-display class selection menu every spawn."); - g_hCvarsList[CVAR_CLASSES_RANDOM] = CreateConVar("zr_classes_random", "0", "Player is assigned a random class every spawn. [Override: zr_classes_spawn and zr_classes_default_*]"); + g_hCvarsList[CVAR_CLASSES_RANDOM] = CreateConVar("zr_classes_random", "0", "Player is assigned a random class every spawn. [Override: zr_classes_default_*]"); + g_hCvarsList[CVAR_CLASSES_CHANGE_TIMELIMIT] = CreateConVar("zr_classes_change_timelimit", "20", "Time limit to change human class with instant change after spawning. Time is in seconds. Use 0 or negative to disable."); + g_hCvarsList[CVAR_CLASSES_SAVE] = CreateConVar("zr_classes_save", "1", "Save players' class selections in server cookies and restore when connecting. [Override: zr_classes_default_*]"); g_hCvarsList[CVAR_CLASSES_DEFAULT_ZOMBIE] = CreateConVar("zr_classes_default_zombie", "random", "Zombie class assigned to players on connect. [\"random\" = Random zombie class | \"\" = Class config default]"); g_hCvarsList[CVAR_CLASSES_DEFAULT_M_ZOMB] = CreateConVar("zr_classes_default_mother_zombie", "motherzombies","Zombie class assigned to mother zombies. [\"motherzombies\" = Random mother zombie class | \"random\" = Random regular zombie class | \"disabled\" = Don't change class on mother zombies]"); g_hCvarsList[CVAR_CLASSES_DEFAULT_HUMAN] = CreateConVar("zr_classes_default_human", "random", "Human class assigned to players on connect. [\"random\" = Random human class | \"\" = Class config default]"); g_hCvarsList[CVAR_CLASSES_DEFAULT_ADMIN] = CreateConVar("zr_classes_default_admin", "random", "Admin class assigned to admins on connect. [\"random\" = Random admin class | \"\" = Class config default]"); + g_hCvarsList[CVAR_CLASSES_ZOMBIE_SELECT] = CreateConVar("zr_classes_zombie_select", "1", "Allow players to select zombie classes."); + g_hCvarsList[CVAR_CLASSES_HUMAN_SELECT] = CreateConVar("zr_classes_human_select", "1", "Allow players to select human classes."); + g_hCvarsList[CVAR_CLASSES_ADMIN_SELECT] = CreateConVar("zr_classes_admin_select", "1", "Allow admins to select admin mode classes. (Not to be confused by admin-only classes!)"); // Menu g_hCvarsList[CVAR_CLASSES_MENU_AUTOCLOSE] = CreateConVar("zr_classes_menu_autoclose", "0", "Automatically close class selection menu after selecting a class."); diff --git a/src/zr/damage.inc b/src/zr/damage.inc index 008a218..1919db5 100644 --- a/src/zr/damage.inc +++ b/src/zr/damage.inc @@ -307,12 +307,6 @@ public ZRTools_Action:DamageOnTakeDamage(client, inflictor, attacker, Float:dama // Client was damaged by falling. else if (damagetype & DMG_CSS_FALL) { - // If client isn't a zombie, then allow damage. - if (!InfectIsClientInfected(client)) - { - return ZRTools_Continue; - } - // If class has "nofalldamage" disabled, then allow damage. new bool:blockfalldamage = ClassGetNoFallDamage(client); if (!blockfalldamage) diff --git a/src/zr/log.inc b/src/zr/log.inc index 0682a3c..99e9e5c 100644 --- a/src/zr/log.inc +++ b/src/zr/log.inc @@ -624,7 +624,7 @@ public Action:Command_LogRemoveModule(client, argc) if (argc < 1) { // Display syntax info. - StrCat(buffer, sizeof(buffer), "Add one or more modules to the module filter. Usage: zr_log_add_module [module] ...\n"); + StrCat(buffer, sizeof(buffer), "Remove one or more modules to the module filter. Usage: zr_log_remove_module [module] ...\n"); StrCat(buffer, sizeof(buffer), "See zr_log_list to list available module names (short names)."); ReplyToCommand(client, buffer); } diff --git a/src/zr/menu.inc b/src/zr/menu.inc index 4585af7..47514aa 100644 --- a/src/zr/menu.inc +++ b/src/zr/menu.inc @@ -101,13 +101,13 @@ ZMenuMain(client) // Translate each line into client's language. Format(title, sizeof(title), "%t\n ", "Menu main title", publictrigger, silenttrigger); - Format(zadmin, sizeof(zadmin), "%t", "Menu main zadmin", SAYHOOKS_KEYWORD_ZADMIN); - Format(zclass, sizeof(zclass), "%t", "Menu main zclass", SAYHOOKS_KEYWORD_ZCLASS); - Format(zcookies, sizeof(zcookies), "%t", "Menu main zcookies", SAYHOOKS_KEYWORD_ZCOOKIES); - Format(zspawn, sizeof(zspawn), "%t", "Menu main zspawn", SAYHOOKS_KEYWORD_ZSPAWN); - Format(ztele, sizeof(ztele), "%t", "Menu main ztele", SAYHOOKS_KEYWORD_ZTELE); - Format(zhp, sizeof(zhp), "%t", "Menu main zhp", SAYHOOKS_KEYWORD_ZHP); - Format(zmarket, sizeof(zmarket), "%t", "Menu main zmarket", SAYHOOKS_KEYWORD_ZMARKET); + Format(zadmin, sizeof(zadmin), "%t", "Menu main zadmin"); + Format(zclass, sizeof(zclass), "%t", "Menu main zclass"); + Format(zcookies, sizeof(zcookies), "%t", "Menu main zcookies"); + Format(zspawn, sizeof(zspawn), "%t", "Menu main zspawn"); + Format(ztele, sizeof(ztele), "%t", "Menu main ztele"); + Format(zhp, sizeof(zhp), "%t", "Menu main zhp"); + Format(zmarket, sizeof(zmarket), "%t", "Menu main zmarket"); // Add items to menu. @@ -117,7 +117,10 @@ ZMenuMain(client) new bool:admin = ZRIsClientAdmin(client); AddMenuItem(menu_main, "zadmin", zadmin, MenuGetItemDraw(admin)); - AddMenuItem(menu_main, "zclass", zclass); + // Decide whether the client can use zclass. + new zclassdraw = ClassAllowSelection(client) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED; + + AddMenuItem(menu_main, "zclass", zclass, zclassdraw); AddMenuItem(menu_main, "zcookies", zcookies); AddMenuItem(menu_main, "zspawn", zspawn); AddMenuItem(menu_main, "ztele", ztele); diff --git a/src/zr/paramparser.inc b/src/zr/paramparser.inc new file mode 100644 index 0000000..ea3a5cc --- /dev/null +++ b/src/zr/paramparser.inc @@ -0,0 +1,531 @@ +/* + * ============================================================================ + * + * Zombie:Reloaded + * + * File: paramparser.inc + * Type: Core + * Description: Provides functions for parsing single line strings with + * flags, and parameters in key=value format. + * + * Supports quoted strings and escaped characters like "\n" + * and "\t". + * + * Examle raw string: + * "type=interval -disabled msg="Title:\n\"Example\"." + * + * 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 . + * + * ============================================================================ + */ + +/** + * @section Limit settings. + */ +#define PARAM_NAME_MAXLEN 64 /** Maximum length of key name or flag name. */ +#define PARAM_VALUE_MAXLEN 256 /** Maximum length of value string. */ +/** + * @endsection + */ + +/** + * @section Parsing error codes. + */ +#define PARAM_ERROR_EMPTY 1 /** Source string is empty. */ +#define PARAM_ERROR_FULL 2 /** Destination array is full. */ +#define PARAM_ERROR_UNEXPECTED_KEY 3 /** Unexpected key name. Could not find a equation sign (=) after previous key name. */ +#define PARAM_ERROR_UNEXPECTED_END 4 /** Unexpected end of source string. */ +#define PARAM_ERROR_MISSING_QUOTE 5 /** Unexpected end of source string. Missing end quote character. */ +#define PARAM_ERROR_UNKNOWN 6 /** Unknown error. The parser got a invalid result from a search function it couldn't handle. */ +/** + * @endsection + */ + +/** + * Modes for what to do and expect when parsing. White space characters between + * modes are ignored. + */ +enum ParamModes +{ + ParamMode_TypeCheck, /** Check if it's a flag or a key. */ + ParamMode_Flag, /** Expect a flag name (starts with "-"). */ + ParamMode_Key, /** Expect a key name. */ + ParamMode_Equal, /** Expect a equation sign. */ + ParamMode_Value /** Expect a value string. */ +} + +/** + * Structure for storing a key/value pair. + */ +enum ParamParseResult +{ + bool:Param_IsFlag, /** Specifies whether it's a flag or not. */ + String:Param_Name[PARAM_NAME_MAXLEN], /** Key or flag name. */ + String:Param_Value[PARAM_VALUE_MAXLEN] /** Value. Only used if a key. */ +} + + + +/************************************** + * * + * PARAMETER FUNCTIONS * + * * + **************************************/ + +/** + * Parses a parameter string in "key=value" format and store the result in a + * ParamParseResult array. + * + * @param buffer A ParamParseResult array to store results. + * @param maxlen Maximum number of keys that can be stored (first + * dimension of buffer). + * @param paramString The source string to parse. String is trimmed before + * parsing. + * @param err Opional output: Error code if parsing error. + * @param errPos Opional output: Position in paramString where the error + * occoured. + * @return Number of keys parsed. + */ +stock ParamParseString(buffer[][ParamParseResult], maxlen, String:paramString[], &err = 0, &errPos = -1) +{ + /* + * VALIDATION OF INPUT AND BUFFERS + */ + + // Trim raw string. + TrimString(paramString); + + // Check if raw string is empty. + if (strlen(paramString) == 0) + { + err = PARAM_ERROR_EMPTY; + errPos = 0; + return 0; + } + + // Validate specified length of destination buffer. + if (maxlen == 0) + { + err = PARAM_ERROR_FULL; + errPos = 0; + return 0; + } + + + /* + * PARSE LOOP + */ + + // Get raw string length. + new rawlen = sizeof(paramString); + + // Initialize. Expect the start to be a key or a flag. + new ParamModes:mode = ParamMode_TypeCheck; + + // Counter for number of parameters parsed. + new paramcount; + + // Buffers for temp values. + new startpos; + new endpos; + new bool:quoteon; + decl String:value[PARAM_VALUE_MAXLEN]; + + // Loop through all characters in the string. Exclude null terminator. + for (new strpos = 0; strpos < rawlen - 1; strpos++) + { + // Check if there's space left in the destination buffer. + if (paramcount > maxlen) + { + // Exit loop. No more parameters can be parsed. + err = PARAM_ERROR_FULL; + errPos = strpos; + break; + } + + + /* + * MODE CHECK + */ + + // Check mode for deciding what to do. + switch (mode) + { + case ParamMode_TypeCheck: + { + // Find start position of first non white space character. + startpos = ParamFindStartPos(paramString, strpos); + + // Check if it's a flag type. + if (paramString[startpos] == '-') + { + // It's a flag, change mode. + mode = ParamMode_Flag; + + // Update current position. + strpos = startpos; + } + else + { + // Expect a key name. + mode = ParamMode_Key; + + // Update current position. Substract by one to include + // the current character in next mode. + strpos = startpos - 1; + } + } + case ParamMode_Flag: + { + // Find stop position (last non white space character). + endpos = ParamFindEndPos(paramString, strpos); + + // Extract key name. + StrExtract(value, sizeof(value), paramString, strpos, endpos); + + // Copy flag to destination buffer. + strcopy(buffer[paramcount][Param_Name], PARAM_NAME_MAXLEN, value); + + // Set flag type. + buffer[paramcount][Param_IsFlag] = true; + + // Increment parameter counter. + paramcount++; + + // Set next parse mode. + mode = ParamMode_TypeCheck; + } + case ParamMode_Key: + { + // Find stop position. + endpos = ParamFindEndPos(paramString, strpos); + + // Extract key name. + StrExtract(value, sizeof(value), paramString, strpos, endpos); + + // Copy key name to destination buffer. + strcopy(buffer[paramcount][Param_Name], PARAM_NAME_MAXLEN, value); + + // Make sure flag type is not set. + buffer[paramcount][Param_IsFlag] = false; + + // Note: Do not increment parameter counter until the + // entire key/value pair is parsed. + + // Set next parse mode. Expect a equation sign. + mode = ParamMode_Equal; + } + case ParamMode_Equal: + { + // Find start position of first non white space character. + startpos = ParamFindStartPos(paramString, strpos); + + // Validate position. + if (startpos >= 0) + { + // Check if it's a equation sign. + if (paramString[startpos] == '=') + { + // Change mode to expect a value at next position. + mode = ParamMode_Value; + + // Update current position. + strpos = startpos; + } + else + { + // Parse error. + err = PARAM_ERROR_UNEXPECTED_KEY; + errPos = startpos; + break; + } + } + else + { + // Parse error. + err = PARAM_ERROR_UNEXPECTED_END; + errPos = strpos; + break; + } + } + case ParamMode_Value: + { + // Find start position of first non white space character. + startpos = ParamFindStartPos(paramString, strpos); + + // Validate start position. + if (startpos >= 0) + { + // Reset quote and escape settings. + quoteon = false; + + // Loop through all characters starting from the current + // position. Exclude null terminator. + for (strpos = startpos; strpos < rawlen - 1; strpos++) + { + // Check if the current character is a special character. + if (paramString[startpos] == '"') + { + // Toggle quote. + quoteon = !quoteon; + + // Check quote state. + if (quoteon) + { + // Quote started, update start position. + startpos = strpos + 1; + } + else + { + // Quote end, set end position. + endpos = strpos - 1; + } + } + + // Check if it's a white space character or end of the string. + else if (!quoteon && (IsCharSpace(paramString[strpos]) || strpos == rawlen - 1)) + { + // End of value reached. Save positions. + endpos = strpos - 1; + + // Exit loop. + break; + } + } + + // Check if quote still haven't ended. + if (quoteon) + { + // Parse error. + err = PARAM_ERROR_MISSING_QUOTE; + errPos = strpos; + break; + } + + // Extract value string. + StrExtract(value, sizeof(value), paramString, startpos, endpos); + + // Unescape string (converting "\n" to newline, etc.). + StrUnescape(value); + + // Copy value string to destination buffer. + strcopy(buffer[paramcount][Param_Value], PARAM_VALUE_MAXLEN, value); + + // Make sure flag type is not set. + buffer[paramcount][Param_IsFlag] = false; + + // Increment parameter counter. + paramcount++; + + // Set next parse mode. Expect a key or a flag. + mode = ParamMode_TypeCheck; + } + else + { + // Parse error. + err = PARAM_ERROR_UNEXPECTED_END; + errPos = strpos; + break; + } + } + } + } + + // Return number of parameters parsed. + return paramcount; +} + +/** + * Finds the first key index in a parameter array matching the specified key. + * + * @param params A ParamParseResult array to search through. + * @param maxlen Size of parameter array (first dimension). + * @param key Key to find. + * @param caseSensitive Specifies whether the search is case sensitive or + * not (default). + * @return Index of the key if found, -1 otherwise. + */ +stock ParamFindKey(const params[][ParamParseResult], maxlen, const String:key[], bool:caseSensitive = false) +{ + // Loop through all parameters. + for (new index = 0; index < maxlen; index++) + { + // Check parameter type. + if (params[index][Param_IsFlag]) + { + // It's a flag type, skip index. + continue; + } + + // Match key name. + if (StrEqual(params[index][Param_Name], key, caseSensitive)) + { + // Key found, return the key index. + return index; + } + } + + return -1; +} + +/** + * Checks if the specified flag is set in a parameter array. + * + * @param params A ParamParseResult array to search through. + * @param maxlen Size of parameter array (first dimension). + * @param flag Flag to check. + * @param caseSensitive Specifies whether the search is case sensitive or + * not (default). + * @return True flag is found, false otherwise. + */ +stock bool:ParamHasFlag(const params[][ParamParseResult], maxlen, const String:flag[], bool:caseSensitive = false) +{ + // Loop through all parameters. + for (new index = 0; index < maxlen; index++) + { + // Check parameter type. + if (!params[index][Param_IsFlag]) + { + // It's a key type, skip index. + continue; + } + + // Match flag name. + if (StrEqual(params[index][Param_Name], flag, caseSensitive)) + { + // Flag found. + return true; + } + } + + return false; +} + + + +/************************************** + * * + * HELPER FUNCTIONS * + * * + **************************************/ + +/** + * Finds the position of the last non white space character from a specified start position. + * + * @param paramString Raw string search in. + * @param startPos Optional. Position to start searching from. + * @return Position of the last non white space character, or -1 + * if failed. + */ +stock ParamFindEndPos(const String:paramString[], startPos = 0) +{ + new rawlen = sizeof(paramString); + + // Validate string length. + if (rawlen == 0) + { + return -1; + } + + // Loop through all characters from the specified start position. + for (new strpos = startPos; strpos < rawlen; strpos++) + { + // Check if white space or if current position is the last + // character before the null terminator. + if (IsCharSpace(paramString[strpos]) || strpos == rawlen - 1) + { + return strpos - 1; + } + } + + // It should never reach this place. Added to satisfy compiler. + return -1; +} + +/** + * Finds the first non white space character in a string, starting from the + * specified position. + * + * @param paramString Raw string to search in. + * @param startPos Optional. Position to start searching from. + * @return Position of first character or -1 if failed. + */ +stock ParamFindStartPos(const String:paramString[], startPos = 0) +{ + new rawlen = sizeof(paramString); + + // Validate string length. + if (rawlen == 0) + { + return -1; + } + + // Loop through all characters from the specified start position. + for (new strpos = startPos; strpos < rawlen; strpos++) + { + // Check if not white space. + if (!IsCharSpace(paramString[strpos])) + { + return strpos; + } + } + + // No character found. + return -1; +} + +/** + * Extracts a area in a string between two positions. + * + * @param buffer Destination string buffer. + * @param maxlen Size of destination buffer. + * @param source Source string to extract from. + * @param startpos Start position of string to extract. + * @param endpos End position of string to extract. + * @return Number of cells written. + */ +stock StrExtract(String:buffer[], maxlen, const String:source[], startpos, endpos) +{ + new len; + + // Calculate string length. Also add space for null terminator. + len = endpos - startpos + 1; + + // Validate length. + if (len < 0) + { + return 0; + } + + // Extract string and store it in the buffer. + return strcopy(buffer, len, source[startpos]); +} + +/** + * Unescapes a string (replaces "\n" with newlines, etc.). + * + * @param str String to unescape. + */ +stock StrUnescape(String:str[]) +{ + new len = sizeof(str); + + ReplaceString(str, len, "\\n", "\n"); + ReplaceString(str, len, "\\r", "\r"); + ReplaceString(str, len, "\\t", "\t"); + ReplaceString(str, len, "\\\"", "\""); + ReplaceString(str, len, "\\\\", "\\"); +} diff --git a/src/zr/playerclasses/apply.inc b/src/zr/playerclasses/apply.inc index 45fd0bd..5c622f8 100644 --- a/src/zr/playerclasses/apply.inc +++ b/src/zr/playerclasses/apply.inc @@ -89,16 +89,14 @@ bool:ClassApplyModel(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER) ClassGetModelPath(classindex, modelpath, sizeof(modelpath), cachetype); } - // Check if the user specified a random model. - if (strcmp(modelpath, "random", false) == 0) + // Check if the user specified a pre-defined model setting. + 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); } - - // Check if the user specified no change. - else if (strcmp(modelpath, "default", false) == 0) + else if (StrEqual(modelpath, "default", false)) { // Get current model. GetClientModel(client, modelpath, sizeof(modelpath)); @@ -114,6 +112,11 @@ bool:ClassApplyModel(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER) return true; } } + else if (StrEqual(modelpath, "nochange", false)) + { + // Do nothing. + return true; + } SetEntityModel(client, modelpath); return true; diff --git a/src/zr/playerclasses/attributes.inc b/src/zr/playerclasses/attributes.inc index 960c60b..274e263 100644 --- a/src/zr/playerclasses/attributes.inc +++ b/src/zr/playerclasses/attributes.inc @@ -52,15 +52,15 @@ stock bool:ClassIsEnabled(index, cachetype = ZR_CLASS_CACHE_MODIFIED) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_enabled]; + return ClassData[index][Class_Enabled]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_enabled]; + return ClassDataCache[index][Class_Enabled]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_enabled]; + return ClassPlayerCache[index][Class_Enabled]; } } return false; @@ -84,15 +84,15 @@ stock ClassGetTeamID(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_team]; + return ClassData[index][Class_Team]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_team]; + return ClassDataCache[index][Class_Team]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_team]; + return ClassPlayerCache[index][Class_Team]; } } return -1; @@ -118,15 +118,15 @@ stock bool:ClassGetTeamDefault(index, cachetype = ZR_CLASS_CACHE_MODIFIED) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_team_default]; + return ClassData[index][Class_TeamDefault]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_team_default]; + return ClassDataCache[index][Class_TeamDefault]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_team_default]; + return ClassPlayerCache[index][Class_TeamDefault]; } } return false; @@ -151,15 +151,15 @@ stock ClassGetFlags(index, cachetype = ZR_CLASS_CACHE_MODIFIED) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_flags]; + return ClassData[index][Class_Flags]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_flags]; + return ClassDataCache[index][Class_Flags]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_flags]; + return ClassPlayerCache[index][Class_Flags]; } } return -1; @@ -185,20 +185,55 @@ stock bool:ClassHasFlags(index, flags, cachetype = ZR_CLASS_CACHE_MODIFIED) { case ZR_CLASS_CACHE_ORIGINAL: { - return bool:(ClassData[index][class_flags] & flags); + return bool:(ClassData[index][Class_Flags] & flags); } case ZR_CLASS_CACHE_MODIFIED: { - return bool:(ClassDataCache[index][class_flags] & flags); + return bool:(ClassDataCache[index][Class_Flags] & flags); } case ZR_CLASS_CACHE_PLAYER: { - return bool:(ClassPlayerCache[index][class_flags] & flags); + return bool:(ClassPlayerCache[index][Class_Flags] & flags); } } return false; } +/** + * Gets the class group required to be a member of to use the class. + * + * @param index Index of the class in a class cache or a client index, + * depending on the cache type specified. + * @param buffer The destination string buffer. + * @param maxlen The length of the destination string buffer. + * @param cachetype Optional. Specifies what class cache to read from. Options: + * ZR_CLASS_CACHE_ORIGINAL - Unchanced class data. + * ZR_CLASS_CACHE_MODIFIED - Changed/newest class data. + * ZR_CLASS_CACHE_PLAYER (default) - Player cache. If this one + * is used, index will be used as a client index. + * @return Number of cells written. -1 on error. + */ +stock ClassGetGroup(index, String:buffer[], maxlen, cachetype = ZR_CLASS_CACHE_PLAYER) +{ + switch (cachetype) + { + case ZR_CLASS_CACHE_ORIGINAL: + { + return strcopy(buffer, maxlen, ClassData[index][Class_Group]); + } + case ZR_CLASS_CACHE_MODIFIED: + { + return strcopy(buffer, maxlen, ClassDataCache[index][Class_Group]); + } + case ZR_CLASS_CACHE_PLAYER: + { + return strcopy(buffer, maxlen, ClassPlayerCache[index][Class_Group]); + } + } + + return -1; +} + /** * Gets the class name to be displayed in the class menu. * @@ -219,15 +254,15 @@ stock ClassGetName(index, String:buffer[], maxlen, cachetype = ZR_CLASS_CACHE_PL { case ZR_CLASS_CACHE_ORIGINAL: { - return strcopy(buffer, maxlen, ClassData[index][class_name]); + return strcopy(buffer, maxlen, ClassData[index][Class_Name]); } case ZR_CLASS_CACHE_MODIFIED: { - return strcopy(buffer, maxlen, ClassDataCache[index][class_name]); + return strcopy(buffer, maxlen, ClassDataCache[index][Class_Name]); } case ZR_CLASS_CACHE_PLAYER: { - return strcopy(buffer, maxlen, ClassPlayerCache[index][class_name]); + return strcopy(buffer, maxlen, ClassPlayerCache[index][Class_Name]); } } @@ -254,15 +289,15 @@ stock ClassGetDescription(index, String:buffer[], maxlen, cachetype = ZR_CLASS_C { case ZR_CLASS_CACHE_ORIGINAL: { - return strcopy(buffer, maxlen, ClassData[index][class_description]); + return strcopy(buffer, maxlen, ClassData[index][Class_Description]); } case ZR_CLASS_CACHE_MODIFIED: { - return strcopy(buffer, maxlen, ClassDataCache[index][class_description]); + return strcopy(buffer, maxlen, ClassDataCache[index][Class_Description]); } case ZR_CLASS_CACHE_PLAYER: { - return strcopy(buffer, maxlen, ClassPlayerCache[index][class_description]); + return strcopy(buffer, maxlen, ClassPlayerCache[index][Class_Description]); } } return -1; @@ -298,15 +333,15 @@ stock ClassGetModelPath(index, String:buffer[], maxlen, cachetype = ZR_CLASS_CAC { case ZR_CLASS_CACHE_ORIGINAL: { - return strcopy(buffer, maxlen, ClassData[index][class_model_path]); + return strcopy(buffer, maxlen, ClassData[index][Class_ModelPath]); } case ZR_CLASS_CACHE_MODIFIED: { - return strcopy(buffer, maxlen, ClassDataCache[index][class_model_path]); + return strcopy(buffer, maxlen, ClassDataCache[index][Class_ModelPath]); } case ZR_CLASS_CACHE_PLAYER: { - return strcopy(buffer, maxlen, ClassPlayerCache[index][class_model_path]); + return strcopy(buffer, maxlen, ClassPlayerCache[index][Class_ModelPath]); } } return -1; @@ -330,15 +365,15 @@ stock ClassGetAlphaInitial(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_alpha_initial]; + return ClassData[index][Class_AlphaInitial]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_alpha_initial]; + return ClassDataCache[index][Class_AlphaInitial]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_alpha_initial]; + return ClassPlayerCache[index][Class_AlphaInitial]; } } return -1; @@ -363,15 +398,15 @@ stock ClassGetAlphaDamaged(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_alpha_damaged]; + return ClassData[index][Class_AlphaDamaged]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_alpha_damaged]; + return ClassDataCache[index][Class_AlphaDamaged]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_alpha_damaged]; + return ClassPlayerCache[index][Class_AlphaDamaged]; } } return -1; @@ -396,15 +431,15 @@ stock ClassGetAlphaDamage(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_alpha_damage]; + return ClassData[index][Class_AlphaDamage]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_alpha_damage]; + return ClassDataCache[index][Class_AlphaDamage]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_alpha_damage]; + return ClassPlayerCache[index][Class_AlphaDamage]; } } return -1; @@ -440,15 +475,15 @@ stock ClassGetOverlayPath(index, String:buffer[], maxlen, cachetype = ZR_CLASS_C { case ZR_CLASS_CACHE_ORIGINAL: { - return strcopy(buffer, maxlen, ClassData[index][class_overlay_path]); + return strcopy(buffer, maxlen, ClassData[index][Class_OverlayPath]); } case ZR_CLASS_CACHE_MODIFIED: { - return strcopy(buffer, maxlen, ClassDataCache[index][class_overlay_path]); + return strcopy(buffer, maxlen, ClassDataCache[index][Class_OverlayPath]); } case ZR_CLASS_CACHE_PLAYER: { - return strcopy(buffer, maxlen, ClassPlayerCache[index][class_overlay_path]); + return strcopy(buffer, maxlen, ClassPlayerCache[index][Class_OverlayPath]); } } return -1; @@ -472,15 +507,15 @@ stock bool:ClassGetNvgs(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_nvgs]; + return ClassData[index][Class_Nvgs]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_nvgs]; + return ClassDataCache[index][Class_Nvgs]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_nvgs]; + return ClassPlayerCache[index][Class_Nvgs]; } } return false; @@ -504,15 +539,15 @@ stock ClassGetFOV(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_fov]; + return ClassData[index][Class_Fov]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_fov]; + return ClassDataCache[index][Class_Fov]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_fov]; + return ClassPlayerCache[index][Class_Fov]; } } return -1; @@ -546,15 +581,15 @@ stock bool:ClassGetHasNapalm(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_has_napalm]; + return ClassData[index][Class_HasNapalm]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_has_napalm]; + return ClassDataCache[index][Class_HasNapalm]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_has_napalm]; + return ClassPlayerCache[index][Class_HasNapalm]; } } return false; @@ -580,15 +615,15 @@ stock Float:ClassGetNapalmTime(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_napalm_time]; + return ClassData[index][Class_NapalmTime]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_napalm_time]; + return ClassDataCache[index][Class_NapalmTime]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_napalm_time] * ClassGetAttributeMultiplier(index, ClassM_NapalmTime); + return ClassPlayerCache[index][Class_NapalmTime] * ClassGetAttributeMultiplier(index, ClassM_NapalmTime); } } return -1.0; @@ -622,15 +657,15 @@ stock ClassGetImmunityMode(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_immunity_mode]; + return ClassData[index][Class_ImmunityMode]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_immunity_mode]; + return ClassDataCache[index][Class_ImmunityMode]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_immunity_mode]; + return ClassPlayerCache[index][Class_ImmunityMode]; } } return -1; @@ -654,15 +689,15 @@ stock Float:ClassGetImmunityAmount(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_immunity_amount]; + return ClassData[index][Class_ImmunityAmount]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_immunity_amount]; + return ClassDataCache[index][Class_ImmunityAmount]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_immunity_amount]; + return ClassPlayerCache[index][Class_ImmunityAmount]; } } return -1.0; @@ -687,15 +722,15 @@ stock bool:ClassGetNoFallDamage(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_no_fall_damage]; + return ClassData[index][Class_NoFallDamage]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_no_fall_damage]; + return ClassDataCache[index][Class_NoFallDamage]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_no_fall_damage]; + return ClassPlayerCache[index][Class_NoFallDamage]; } } return false; @@ -721,15 +756,15 @@ stock ClassGetHealth(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_health]; + return ClassData[index][Class_Health]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_health]; + return ClassDataCache[index][Class_Health]; } case ZR_CLASS_CACHE_PLAYER: { - return RoundToCeil(ClassPlayerCache[index][class_health] * ClassGetAttributeMultiplier(index, ClassM_Health)); + return RoundToCeil(ClassPlayerCache[index][Class_Health] * ClassGetAttributeMultiplier(index, ClassM_Health)); } } return -1; @@ -756,15 +791,15 @@ stock Float:ClassGetHealthRegenInterval(index, cachetype = ZR_CLASS_CACHE_PLAYER { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_health_regen_interval]; + return ClassData[index][Class_HealthRegenInterval]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_health_regen_interval]; + return ClassDataCache[index][Class_HealthRegenInterval]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_health_regen_interval] * ClassGetAttributeMultiplier(index, ClassM_HealthRegenInterval); + return ClassPlayerCache[index][Class_HealthRegenInterval] * ClassGetAttributeMultiplier(index, ClassM_HealthRegenInterval); } } return -1.0; @@ -791,15 +826,15 @@ stock ClassGetHealthRegenAmount(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_health_regen_amount]; + return ClassData[index][Class_HealthRegenAmount]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_health_regen_amount]; + return ClassDataCache[index][Class_HealthRegenAmount]; } case ZR_CLASS_CACHE_PLAYER: { - return RoundToCeil(ClassPlayerCache[index][class_health_regen_amount] * ClassGetAttributeMultiplier(index, ClassM_HealthRegenAmount)); + return RoundToCeil(ClassPlayerCache[index][Class_HealthRegenAmount] * ClassGetAttributeMultiplier(index, ClassM_HealthRegenAmount)); } } return -1; @@ -826,15 +861,15 @@ stock ClassGetHealthInfectGain(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_health_infect_gain]; + return ClassData[index][Class_HealthInfectGain]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_health_infect_gain]; + return ClassDataCache[index][Class_HealthInfectGain]; } case ZR_CLASS_CACHE_PLAYER: { - return RoundToCeil(ClassPlayerCache[index][class_health_infect_gain] * ClassGetAttributeMultiplier(index, ClassM_HealthInfectGain)); + return RoundToCeil(ClassPlayerCache[index][Class_HealthInfectGain] * ClassGetAttributeMultiplier(index, ClassM_HealthInfectGain)); } } return -1; @@ -858,15 +893,15 @@ stock ClassGetKillBonus(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_kill_bonus]; + return ClassData[index][Class_KillBonus]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_kill_bonus]; + return ClassDataCache[index][Class_KillBonus]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_kill_bonus]; + return ClassPlayerCache[index][Class_KillBonus]; } } return -1; @@ -892,15 +927,15 @@ stock Float:ClassGetSpeed(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_speed]; + return ClassData[index][Class_Speed]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_speed]; + return ClassDataCache[index][Class_Speed]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_speed] * ClassGetAttributeMultiplier(index, ClassM_Speed); + return ClassPlayerCache[index][Class_Speed] * ClassGetAttributeMultiplier(index, ClassM_Speed); } } return -1.0; @@ -926,15 +961,15 @@ stock Float:ClassGetKnockback(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_knockback]; + return ClassData[index][Class_KnockBack]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_knockback]; + return ClassDataCache[index][Class_KnockBack]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_knockback] * ClassGetAttributeMultiplier(index, ClassM_Knockback); + return ClassPlayerCache[index][Class_KnockBack] * ClassGetAttributeMultiplier(index, ClassM_Knockback); } } return 0.0; @@ -960,15 +995,15 @@ stock Float:ClassGetJumpHeight(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_jump_height]; + return ClassData[index][Class_JumpHeight]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_jump_height]; + return ClassDataCache[index][Class_JumpHeight]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_jump_height] * ClassGetAttributeMultiplier(index, ClassM_JumpHeight); + return ClassPlayerCache[index][Class_JumpHeight] * ClassGetAttributeMultiplier(index, ClassM_JumpHeight); } } return -1.0; @@ -994,15 +1029,15 @@ stock Float:ClassGetJumpDistance(index, cachetype = ZR_CLASS_CACHE_PLAYER) { case ZR_CLASS_CACHE_ORIGINAL: { - return ClassData[index][class_jump_distance]; + return ClassData[index][Class_JumpDistance]; } case ZR_CLASS_CACHE_MODIFIED: { - return ClassDataCache[index][class_jump_distance]; + return ClassDataCache[index][Class_JumpDistance]; } case ZR_CLASS_CACHE_PLAYER: { - return ClassPlayerCache[index][class_jump_distance] * ClassGetAttributeMultiplier(index, ClassM_JumpDistance); + return ClassPlayerCache[index][Class_JumpDistance] * ClassGetAttributeMultiplier(index, ClassM_JumpDistance); } } return -1.0; @@ -1034,6 +1069,10 @@ stock ClassAttributeNameToFlag(const String:attributename[]) { return ZR_CLASS_FLAGS; } + else if (StrEqual(attributename, "group", false)) + { + return ZR_CLASS_GROUP; + } else if (StrEqual(attributename, "name", false)) { return ZR_CLASS_NAME; @@ -1230,7 +1269,8 @@ stock ClassDataTypes:ClassGetAttributeType(attributeflag) } // String. - case ZR_CLASS_NAME, + case ZR_CLASS_GROUP, + ZR_CLASS_NAME, ZR_CLASS_DESCRIPTION, ZR_CLASS_MODEL_PATH, ZR_CLASS_OVERLAY_PATH: diff --git a/src/zr/playerclasses/classcommands.inc b/src/zr/playerclasses/classcommands.inc index ae6b859..0405bce 100644 --- a/src/zr/playerclasses/classcommands.inc +++ b/src/zr/playerclasses/classcommands.inc @@ -59,7 +59,14 @@ public Action:ZClassCommand(client, argc) // If client is console, then stop and tell them this feature is for players only. if (ZRIsConsole(client)) { - TranslationPrintToServer("Must be player"); + TranslationReplyToCommand(client, "Must be player"); + return Plugin_Handled; + } + + // Check if class selection is allowed. + if (!ClassAllowSelection(client)) + { + TranslationReplyToCommand(client, "Classes Selection Not Allowed"); return Plugin_Handled; } @@ -612,22 +619,22 @@ stock bool:ClassModifyBoolean(classindex, attributeflag, bool:value) { case ZR_CLASS_ENABLED: { - ClassDataCache[classindex][class_enabled] = bool:value; + ClassDataCache[classindex][Class_Enabled] = bool:value; return true; } case ZR_CLASS_NVGS: { - ClassDataCache[classindex][class_nvgs] = bool:value; + ClassDataCache[classindex][Class_Nvgs] = bool:value; return true; } case ZR_CLASS_NO_FALL_DAMAGE: { - ClassDataCache[classindex][class_no_fall_damage] = bool:value; + ClassDataCache[classindex][Class_NoFallDamage] = bool:value; return true; } case ZR_CLASS_HAS_NAPALM: { - ClassDataCache[classindex][class_has_napalm] = bool:value; + ClassDataCache[classindex][Class_HasNapalm] = bool:value; return true; } } @@ -663,80 +670,80 @@ stock ClassModifyInteger(classindex, attributeflag, value, Float:multiplier = 0. { case ZR_CLASS_FLAGS: { - ClassDataCache[classindex][class_flags] = value; + ClassDataCache[classindex][Class_Flags] = value; return true; } case ZR_CLASS_ALPHA_INITIAL: { if (ismultiplier) { - value = RoundToNearest(float(ClassData[classindex][class_alpha_initial]) * multiplier); + value = RoundToNearest(float(ClassData[classindex][Class_AlphaInitial]) * multiplier); } - ClassDataCache[classindex][class_alpha_initial] = value; + ClassDataCache[classindex][Class_AlphaInitial] = value; return true; } case ZR_CLASS_ALPHA_DAMAGED: { if (ismultiplier) { - value = RoundToNearest(float(ClassData[classindex][class_alpha_damaged]) * multiplier); + value = RoundToNearest(float(ClassData[classindex][Class_AlphaDamaged]) * multiplier); } - ClassDataCache[classindex][class_alpha_damaged] = value; + ClassDataCache[classindex][Class_AlphaDamaged] = value; return true; } case ZR_CLASS_ALPHA_DAMAGE: { if (ismultiplier) { - value = RoundToNearest(float(ClassData[classindex][class_alpha_damage]) * multiplier); + value = RoundToNearest(float(ClassData[classindex][Class_AlphaDamage]) * multiplier); } - ClassDataCache[classindex][class_alpha_damage] = value; + ClassDataCache[classindex][Class_AlphaDamage] = value; return true; } case ZR_CLASS_FOV: { - ClassDataCache[classindex][class_fov] = value; + ClassDataCache[classindex][Class_Fov] = value; return true; } case ZR_CLASS_IMMUNITY_MODE: { - ClassDataCache[classindex][class_fov] = value; + ClassDataCache[classindex][Class_ImmunityMode] = value; return true; } case ZR_CLASS_HEALTH: { if (ismultiplier) { - value = RoundToNearest(float(ClassData[classindex][class_health]) * multiplier); + value = RoundToNearest(float(ClassData[classindex][Class_Health]) * multiplier); } - ClassDataCache[classindex][class_health] = value; + ClassDataCache[classindex][Class_Health] = value; return true; } case ZR_CLASS_HEALTH_REGEN_AMOUNT: { if (ismultiplier) { - value = RoundToNearest(float(ClassData[classindex][class_health_regen_amount]) * multiplier); + value = RoundToNearest(float(ClassData[classindex][Class_HealthRegenAmount]) * multiplier); } - ClassDataCache[classindex][class_health_regen_amount] = value; + ClassDataCache[classindex][Class_HealthRegenAmount] = value; return true; } case ZR_CLASS_HEALTH_INFECT_GAIN: { if (ismultiplier) { - value = RoundToNearest(float(ClassData[classindex][class_health_infect_gain]) * multiplier); + value = RoundToNearest(float(ClassData[classindex][Class_HealthInfectGain]) * multiplier); } - ClassDataCache[classindex][class_health_infect_gain] = value; + ClassDataCache[classindex][Class_HealthInfectGain] = value; return true; } case ZR_CLASS_KILL_BONUS: { if (ismultiplier) { - value = RoundToNearest(float(ClassData[classindex][class_kill_bonus]) * multiplier); + value = RoundToNearest(float(ClassData[classindex][Class_KillBonus]) * multiplier); } - ClassDataCache[classindex][class_kill_bonus] = value; + ClassDataCache[classindex][Class_KillBonus] = value; return true; } } @@ -770,63 +777,63 @@ stock ClassModifyFloat(classindex, attributeflag, Float:value, bool:ismultiplier { if (ismultiplier) { - value = ClassData[classindex][class_napalm_time] * value; + value = ClassData[classindex][Class_NapalmTime] * value; } - ClassDataCache[classindex][class_napalm_time] = value; + ClassDataCache[classindex][Class_NapalmTime] = value; return true; } case ZR_CLASS_IMMUNITY_AMOUNT: { if (ismultiplier) { - value = ClassData[classindex][class_immunity_amount] * value; + value = ClassData[classindex][Class_ImmunityAmount] * value; } - ClassDataCache[classindex][class_immunity_amount] = value; + ClassDataCache[classindex][Class_ImmunityAmount] = value; return true; } case ZR_CLASS_HEALTH_REGEN_INTERVAL: { if (ismultiplier) { - value = ClassData[classindex][class_health_regen_interval] * value; + value = ClassData[classindex][Class_HealthRegenInterval] * value; } - ClassDataCache[classindex][class_health_regen_interval] = value; + ClassDataCache[classindex][Class_HealthRegenInterval] = value; return true; } case ZR_CLASS_SPEED: { if (ismultiplier) { - value = ClassData[classindex][class_speed] * value; + value = ClassData[classindex][Class_Speed] * value; } - ClassDataCache[classindex][class_speed] = value; + ClassDataCache[classindex][Class_Speed] = value; return true; } case ZR_CLASS_KNOCKBACK: { if (ismultiplier) { - value = ClassData[classindex][class_knockback] * value; + value = ClassData[classindex][Class_KnockBack] * value; } - ClassDataCache[classindex][class_knockback] = value; + ClassDataCache[classindex][Class_KnockBack] = value; return true; } case ZR_CLASS_JUMP_HEIGHT: { if (ismultiplier) { - value = ClassData[classindex][class_jump_height] * value; + value = ClassData[classindex][Class_JumpHeight] * value; } - ClassDataCache[classindex][class_jump_height] = value; + ClassDataCache[classindex][Class_JumpHeight] = value; return true; } case ZR_CLASS_JUMP_DISTANCE: { if (ismultiplier) { - value = ClassData[classindex][class_jump_distance] * value; + value = ClassData[classindex][Class_JumpDistance] * value; } - ClassDataCache[classindex][class_jump_distance] = value; + ClassDataCache[classindex][Class_JumpDistance] = value; return true; } } @@ -853,24 +860,29 @@ stock ClassModifyString(classindex, attributeflag, const String:value[]) switch (attributeflag) { + case ZR_CLASS_GROUP: + { + strcopy(ClassDataCache[classindex][Class_Group], 64, value); + return true; + } case ZR_CLASS_NAME: { - strcopy(ClassDataCache[classindex][class_name], 64, value); + strcopy(ClassDataCache[classindex][Class_Name], 64, value); return true; } case ZR_CLASS_DESCRIPTION: { - strcopy(ClassDataCache[classindex][class_description], 256, value); + strcopy(ClassDataCache[classindex][Class_Description], 256, value); return true; } case ZR_CLASS_MODEL_PATH: { - strcopy(ClassDataCache[classindex][class_model_path], PLATFORM_MAX_PATH, value); + strcopy(ClassDataCache[classindex][Class_ModelPath], PLATFORM_MAX_PATH, value); return true; } case ZR_CLASS_OVERLAY_PATH: { - strcopy(ClassDataCache[classindex][class_overlay_path], PLATFORM_MAX_PATH, value); + strcopy(ClassDataCache[classindex][Class_OverlayPath], PLATFORM_MAX_PATH, value); return true; } } diff --git a/src/zr/playerclasses/classevents.inc b/src/zr/playerclasses/classevents.inc index b217081..9788419 100644 --- a/src/zr/playerclasses/classevents.inc +++ b/src/zr/playerclasses/classevents.inc @@ -24,6 +24,11 @@ ClassOnCookiesCreate() { // Forward event to sub-modules. ClassOverlayOnCookiesCreate(); + + // Create class index cookies. + g_hClassCookieClassSelected[ZR_CLASS_TEAM_HUMANS] = RegClientCookie("zr_humanclass", "The last human class selected.", CookieAccess_Protected); + g_hClassCookieClassSelected[ZR_CLASS_TEAM_ZOMBIES] = RegClientCookie("zr_zombieclass", "The last zombie class selected.", CookieAccess_Protected); + g_hClassCookieClassSelected[ZR_CLASS_TEAM_ADMINS] = RegClientCookie("zr_adminclass", "The last admin mode class selected.", CookieAccess_Protected); } /** @@ -75,6 +80,7 @@ ClassOnClientSpawn(client) decl String:originalmodel[PLATFORM_MAX_PATH]; decl String:steamid[16]; decl String:classname[64]; + new filter[ClassFilter]; // Check if the player is dead. Spawning into the game is also a event in // the connection process. @@ -121,15 +127,24 @@ ClassOnClientSpawn(client) // Assign random classes if enabled. Always do it for bots. if (randomclass || StrEqual(steamid, "BOT")) { - // Exclude special class flags like mother zombies and admin classes. - new denyflags = ZR_CLASS_SPECIALFLAGS; + // Setup filtering + // --------------- + // Exclude special class flags like mother zombies and admin classes. + filter[ClassFilter_DenyFlags] = ZR_CLASS_SPECIALFLAGS; + // Allow admin classes if admin. - denyflags -= ZRIsClientAdmin(client) ? ZR_CLASS_FLAG_ADMIN_ONLY : 0; + filter[ClassFilter_DenyFlags] -= ZRIsClientAdmin(client) ? ZR_CLASS_FLAG_ADMIN_ONLY : 0; + + // Specify client for checking group permissions. + filter[ClassFilter_Client] = client; + + // Get classes + // ----------- // Get random classes for each type. - new randomzombie = ClassGetRandomClass(ZR_CLASS_TEAM_ZOMBIES, _, _, denyflags); - new randomhuman = ClassGetRandomClass(ZR_CLASS_TEAM_HUMANS, _, _, denyflags); + new randomzombie = ClassGetRandomClass(ZR_CLASS_TEAM_ZOMBIES, filter); + new randomhuman = ClassGetRandomClass(ZR_CLASS_TEAM_HUMANS, filter); // Set selected zombie class index. ClassSelected[client][ZR_CLASS_TEAM_ZOMBIES] = randomzombie; @@ -141,11 +156,34 @@ ClassOnClientSpawn(client) ClassGetName(randomhuman, classname, sizeof(classname), ZR_CLASS_TEAM_HUMANS); TranslationPrintToChat(client, "Classes random assignment", classname); } + + // Display class menu if enabled. + new bool:classmenu = GetConVarBool(g_hCvarsList[CVAR_CLASSES_SPAWN]); + if (classmenu) + { + ClassMenuMain(client); + } } // Apply class attributes for the active class. ClassReloadPlayerCache(client, ClassGetActiveIndex(client)); ClassApplyAttributes(client); + + // Check if instant class change cvar is set. + new Float:instantspawn = GetConVarFloat(g_hCvarsList[CVAR_CLASSES_CHANGE_TIMELIMIT]); + if (instantspawn > 0) + { + // Allow instant class change. + ClassAllowInstantChange[client] = true; + + // Create timer to disable instant change. + CreateTimer(instantspawn, Event_ClassDisableInstantSpawn, client, TIMER_FLAG_NO_MAPCHANGE); + } + else + { + // Make sure instant change is not allowed. + ClassAllowInstantChange[client] = false; + } } /** @@ -176,12 +214,16 @@ ClassOnClientInfected(client, bool:motherzombie = false) new classindex = ClassGetActiveIndex(client); new isadmin; new motherindex; + new filter[ClassFilter]; decl String:motherzombiesetting[64]; // Disable class attributes with timers. ClassHealthRegenStop(client); + // Make sure the player is not allowed to instantly change class. + ClassAllowInstantChange[client] = false; + // Check if it's a mother zombie. if (motherzombie) { @@ -198,8 +240,23 @@ ClassOnClientInfected(client, bool:motherzombie = false) } else if (StrEqual(motherzombiesetting, "random", false)) { + // Setup filtering + // --------------- + + // Exclude special class flags. + filter[ClassFilter_DenyFlags] = ZR_CLASS_SPECIALFLAGS; + + // Allow admin classes if admin. + filter[ClassFilter_DenyFlags] -= isadmin; + + // Specify client for checking group permissions. + filter[ClassFilter_Client] = client; + + // Get class + // --------- + // Get random regular zombie class. Remove admin flag if admin. - motherindex = ClassGetRandomClass(ZR_CLASS_TEAM_ZOMBIES, _, _, ZR_CLASS_SPECIALFLAGS - isadmin); + motherindex = ClassGetRandomClass(ZR_CLASS_TEAM_ZOMBIES, filter); // Validate index. Do not change class if it's invalid. if (ClassValidateIndex(motherindex)) @@ -213,8 +270,26 @@ ClassOnClientInfected(client, bool:motherzombie = false) } else if (StrEqual(motherzombiesetting, "motherzombies", false)) { + // Setup filtering + // --------------- + + // Exclude special class flags except mother zombies. + filter[ClassFilter_DenyFlags] = ZR_CLASS_SPECIALFLAGS - ZR_CLASS_FLAG_MOTHER_ZOMBIE; + + // Require mother zombie class flag. + filter[ClassFilter_RequireFlags] = ZR_CLASS_FLAG_MOTHER_ZOMBIE; + + // Allow admin classes if admin. + filter[ClassFilter_DenyFlags] -= isadmin; + + // Specify client for checking group permissions. + filter[ClassFilter_Client] = client; + + // Get class + // --------- + // Get random mother zombie class. Include admin classes if admin. - motherindex = ClassGetRandomClass(ZR_CLASS_TEAM_ZOMBIES, _, ZR_CLASS_FLAG_MOTHER_ZOMBIE + isadmin, ZR_CLASS_FLAG_ADMIN_ONLY - isadmin); + motherindex = ClassGetRandomClass(ZR_CLASS_TEAM_ZOMBIES, filter); // Validate index. Do not change class if it's invalid. if (ClassValidateIndex(motherindex)) @@ -259,3 +334,12 @@ ClassOnClientInfected(client, bool:motherzombie = false) // Apply the new attributes. ClassApplyAttributes(client, motherzombie); } + +/** + * Timer callback for disabling instant class change setting on a client. + */ +public Action:Event_ClassDisableInstantSpawn(Handle:timer, any:client) +{ + // Disable instant class change. + ClassAllowInstantChange[client] = false; +} diff --git a/src/zr/playerclasses/classmenus.inc b/src/zr/playerclasses/classmenus.inc index 33deaca..981032e 100644 --- a/src/zr/playerclasses/classmenus.inc +++ b/src/zr/playerclasses/classmenus.inc @@ -65,18 +65,19 @@ ClassMenuMain(client) // Get number of enabled classes per team. new zombiecount = ClassCountTeam(ZR_CLASS_TEAM_ZOMBIES); new humancount = ClassCountTeam(ZR_CLASS_TEAM_ZOMBIES); - new admincount = ClassCountTeam(ZR_CLASS_TEAM_ZOMBIES); + new admincount = ClassCountTeam(ZR_CLASS_TEAM_ADMINS); - // Get previously selected class indexes, if set. + // Get next class indexes, if set. new nextzombie = ClassSelectedNext[client][ZR_CLASS_TEAM_ZOMBIES]; new nexthuman = ClassSelectedNext[client][ZR_CLASS_TEAM_HUMANS]; new nextadmin = ClassSelectedNext[client][ZR_CLASS_TEAM_ADMINS]; - // Set draw style on class options depending on number of enabled classes. - // Disable class selection if there's only one class. - new zombie_itemdraw = (zombiecount > 1) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED; - new human_itemdraw = (humancount > 1) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED; - new admin_itemdraw = (admincount > 1) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED; + // Set draw style on class options depending on number of enabled classes + // and selection permissions. Disable class selection if there's only one + // class. + new zombie_itemdraw = (zombiecount > 1 && ClassAllowSelection(client, ZR_CLASS_TEAM_ZOMBIES)) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED; + new human_itemdraw = (humancount > 1 && ClassAllowSelection(client, ZR_CLASS_TEAM_HUMANS)) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED; + new admin_itemdraw = (admincount > 1 && ClassAllowSelection(client, ZR_CLASS_TEAM_ADMINS)) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED; // Check if the player is in admin mode. if (ClassPlayerInAdminMode[client]) @@ -92,18 +93,18 @@ ClassMenuMain(client) // Get current class name. ClassGetName(ClassSelected[client][ZR_CLASS_TEAM_ZOMBIES], zombieclass, sizeof(zombieclass), ZR_CLASS_CACHE_MODIFIED); - // Check if previous index is set. + // Check if next index is set. if (ClassValidateIndex(nextzombie)) { // Get name of previous class index and format item text. ClassGetName(nextzombie, nextzombiename, sizeof(nextzombiename), ZR_CLASS_CACHE_MODIFIED); - Format(zombieselect, sizeof(zombieselect), "%t", "Classes menu zombie next", zombieclass, nextzombiename); + Format(zombieselect, sizeof(zombieselect), "%t\n %t\n %t", "Classes menu select zombie", "Classes menu active", zombieclass, "Classes menu next", nextzombiename); } else { // Use current class name and format item text. - Format(zombieselect, sizeof(zombieselect), "%t", "Classes menu zombie current", zombieclass); + Format(zombieselect, sizeof(zombieselect), "%t\n %s", "Classes menu select zombie", zombieclass); } // Add item to list. @@ -116,18 +117,18 @@ ClassMenuMain(client) // Get current class name. ClassGetName(ClassSelected[client][ZR_CLASS_TEAM_HUMANS], humanclass, sizeof(humanclass), ZR_CLASS_CACHE_MODIFIED); - // Check if previous index is set. + // Check if next index is set. if (ClassValidateIndex(nexthuman)) { // Get name of previous class index and format item text. ClassGetName(nexthuman, nexthumanname, sizeof(nexthumanname), ZR_CLASS_CACHE_MODIFIED); - Format(humanselect, sizeof(humanselect), "%t", "Classes menu human next", humanclass, nexthumanname); + Format(humanselect, sizeof(humanselect), "%t\n %t\n %t", "Classes menu select human", "Classes menu active", humanclass, "Classes menu next", nexthumanname); } else { // Use current class name and format item text. - Format(humanselect, sizeof(humanselect), "%t", "Classes menu human current", humanclass); + Format(humanselect, sizeof(humanselect), "%t\n %s", "Classes menu select human", humanclass); } // Add item to list. @@ -143,17 +144,17 @@ ClassMenuMain(client) // Get current class name. ClassGetName(ClassSelected[client][ZR_CLASS_TEAM_ADMINS], adminclass, sizeof(adminclass), ZR_CLASS_CACHE_MODIFIED); - // Check if previous index is set. + // Check if next index is set. if (ClassValidateIndex(nextadmin)) { // Get name of previous class index and format item text. ClassGetName(nextadmin, nextadminname, sizeof(nextadminname), ZR_CLASS_CACHE_MODIFIED); - Format(adminselect, sizeof(adminselect), "%t", "Classes menu admin next", adminclass, nextadminname); + Format(adminselect, sizeof(adminselect), "%t\n %t\n %t", "Classes menu select admin", "Classes menu active", adminclass, "Classes menu next", nextadminname); } else { // Use current class name and format item text. - Format(adminselect, sizeof(adminselect), "%t", "Classes menu admin current", adminclass); + Format(adminselect, sizeof(adminselect), "%t\n %s", "Classes menu select admin", adminclass); } // Add item to list. @@ -246,7 +247,6 @@ ClassMenuSelect(client, teamid) new Handle:menu = CreateMenu(ClassMenuSelectHandle); new arraycount; new classindex; - new denyflags; decl String:title[MENU_LINE_TITLE_LENGTH]; decl String:classname[MENU_LINE_REG_LENGTH]; @@ -260,15 +260,15 @@ ClassMenuSelect(client, teamid) { case ZR_CLASS_TEAM_ZOMBIES: { - Format(title, sizeof(title), "%t\n", "Classes menu zombie"); + Format(title, sizeof(title), "%t:\n", "Classes menu select zombie"); } case ZR_CLASS_TEAM_HUMANS: { - Format(title, sizeof(title), "%t\n", "Classes menu human"); + Format(title, sizeof(title), "%t:\n", "Classes menu select human"); } case ZR_CLASS_TEAM_ADMINS: { - Format(title, sizeof(title), "%t\n", "Classes menu admin"); + Format(title, sizeof(title), "%t:\n", "Classes menu select admin"); } } SetMenuTitle(menu, title); @@ -276,12 +276,24 @@ ClassMenuSelect(client, teamid) // Create buffer array. new Handle:classarray = CreateArray(); - // Set up filtering. - denyflags = ZR_CLASS_FLAG_MOTHER_ZOMBIE; // Hide mother zombie classes. - denyflags += !ZRIsClientAdmin(client) ? ZR_CLASS_FLAG_ADMIN_ONLY : 0; // Hide admin-only classes if not admin. + // Set up filtering + // ---------------- + new filter[ClassFilter]; + + // Hide mother zombie classes. + filter[ClassFilter_DenyFlags] = ZR_CLASS_FLAG_MOTHER_ZOMBIE; + + // Hide admin-only classes if not admin. + filter[ClassFilter_DenyFlags] += !ZRIsClientAdmin(client) ? ZR_CLASS_FLAG_ADMIN_ONLY : 0; + + // Specify client for checking class group permissions. + filter[ClassFilter_Client] = client; + + // Get classes + // ----------- // Copy all class indexes into the array, with the specified filter settings. - if (ClassAddToArray(classarray, teamid, _, _, denyflags)) + if (ClassAddToArray(classarray, teamid, filter)) { // Get number of classes. arraycount = GetArraySize(classarray); @@ -331,28 +343,57 @@ public ClassMenuSelectHandle(Handle:menu, MenuAction:action, client, slot) // Solve teamid from the class index. teamid = ClassGetTeamID(classindex, ZR_CLASS_CACHE_MODIFIED); - // Check if the player is alive. - if (IsPlayerAlive(client)) + // Allow instant class change if enabled and both class and player is human. + if (ClassAllowInstantChange[client] && !iszombie && teamid == ZR_CLASS_TEAM_HUMANS) { - // Set next spawn index if the player is changing the class on - // his active team. - if ((iszombie && teamid == ZR_CLASS_TEAM_ZOMBIES) || - (!iszombie && teamid == ZR_CLASS_TEAM_HUMANS) || - (ClassPlayerInAdminMode[client] && teamid == ZR_CLASS_TEAM_ADMINS)) - { - // Set class to be used on next spawn. - ClassSelectedNext[client][teamid] = classindex; - } - else - { - // Directly change the selected class index. - ClassSelected[client][teamid] = classindex; - } + // Directly change the selected class index. + ClassSelected[client][teamid] = classindex; + + // Update cache and apply attributes. + ClassReloadPlayerCache(client, classindex); + ClassApplyAttributes(client); } else { - // Player isn't alive. The class can be directly changed. - ClassSelected[client][teamid] = classindex; + // Check if the player is alive. + if (IsPlayerAlive(client)) + { + // Set next spawn index if the player is changing the class on + // his active team. + if ((iszombie && teamid == ZR_CLASS_TEAM_ZOMBIES) || + (!iszombie && teamid == ZR_CLASS_TEAM_HUMANS) || + (ClassPlayerInAdminMode[client] && teamid == ZR_CLASS_TEAM_ADMINS)) + { + // Check if player selected the same class that he already is. + if (ClassSelected[client][teamid] == classindex) + { + // Player is already the specified class. Disable + // next class for the specified team. + ClassSelectedNext[client][teamid] = -1; + } + else + { + // Set class to be used on next spawn. + ClassSelectedNext[client][teamid] = classindex; + } + } + else + { + // Directly change the selected class index. + ClassSelected[client][teamid] = classindex; + } + } + else + { + // Player isn't alive. The class can be directly changed. + ClassSelected[client][teamid] = classindex; + } + + // Save selected class index in cookie if enabled. + if (GetConVarBool(g_hCvarsList[CVAR_CLASSES_SAVE])) + { + CookiesSetInt(client, g_hClassCookieClassSelected[teamid], classindex + 1); + } } } case MenuAction_Cancel: @@ -395,15 +436,9 @@ public ClassMenuSelectHandle(Handle:menu, MenuAction:action, client, slot) * @return True if displayed, false otherwise. */ bool:ClassTeamSelect(client) -{ - // Validate client. - if (!ZRIsClientValid(client, false)) - { - return false; - } - +{ // Validate permissions. - if (!ZRIsClientAdmin(client, Admin_Config)) + if (!ZRIsClientPrivileged(client, OperationType_Configuration)) { return false; } diff --git a/src/zr/playerclasses/filtertools.inc b/src/zr/playerclasses/filtertools.inc index e74c0b6..79f8656 100644 --- a/src/zr/playerclasses/filtertools.inc +++ b/src/zr/playerclasses/filtertools.inc @@ -47,8 +47,8 @@ stock bool:ClassValidateTeamRequirements(cachetype = ZR_CLASS_CACHE_ORIGINAL) } // Test if a zombie and human class was found. - zombieindex = ClassGetFirstClass(ZR_CLASS_TEAM_ZOMBIES, _, _, ZR_CLASS_SPECIALFLAGS, cachetype); - humanindex = ClassGetFirstClass(ZR_CLASS_TEAM_HUMANS, _, _, ZR_CLASS_SPECIALFLAGS, cachetype); + zombieindex = ClassGetFirstClass(ZR_CLASS_TEAM_ZOMBIES, _, cachetype); + humanindex = ClassGetFirstClass(ZR_CLASS_TEAM_HUMANS, _, cachetype); // Validate indexes. if (ClassValidateIndex(zombieindex) && ClassValidateIndex(humanindex)) @@ -79,8 +79,8 @@ stock bool:ClassValidateTeamDefaults(cachetype = ZR_CLASS_CACHE_ORIGINAL) } // Test if a default zombie and human class was found. - zombieindex = ClassGetDefaultClass(ZR_CLASS_TEAM_ZOMBIES, _, _, _, cachetype); - humanindex = ClassGetDefaultClass(ZR_CLASS_TEAM_HUMANS, _, _, _, cachetype); + zombieindex = ClassGetDefaultClass(ZR_CLASS_TEAM_ZOMBIES, _, cachetype); + humanindex = ClassGetDefaultClass(ZR_CLASS_TEAM_HUMANS, _, cachetype); // Validate indexes. if (ClassValidateIndex(zombieindex) && ClassValidateIndex(humanindex)) @@ -107,26 +107,36 @@ stock ClassValidateAttributes(classindex) new flags; // Team. - if (ClassData[classindex][class_team] < ZR_CLASS_TEAM_MIN || ClassData[classindex][class_team] > ZR_CLASS_TEAM_MAX) + if (ClassData[classindex][Class_Team] < ZR_CLASS_TEAM_MIN || ClassData[classindex][Class_Team] > ZR_CLASS_TEAM_MAX) { flags += ZR_CLASS_TEAM; } // Class flags. - if (ClassData[classindex][class_flags] < ZR_CLASS_FLAGS_MIN || ClassData[classindex][class_flags] > ZR_CLASS_FLAGS_MAX) + if (ClassData[classindex][Class_Flags] < ZR_CLASS_FLAGS_MIN || ClassData[classindex][Class_Flags] > ZR_CLASS_FLAGS_MAX) { flags += ZR_CLASS_FLAGS; } + // Group. + if (strlen(ClassData[classindex][Class_Group])) + { + // Check if the group exist. + if (FindAdmGroup(ClassData[classindex][Class_Group]) == INVALID_GROUP_ID) + { + flags += ZR_CLASS_GROUP; + } + } + // Name. - if (strlen(ClassData[classindex][class_name]) < ZR_CLASS_NAME_MIN) + if (strlen(ClassData[classindex][Class_Name]) < ZR_CLASS_NAME_MIN) { flags += ZR_CLASS_NAME; } else { decl String:name[64]; - strcopy(name, sizeof(name), ClassData[classindex][class_name]); + strcopy(name, sizeof(name), ClassData[classindex][Class_Name]); // Check for reserved name keyworks. These aren't allowed as names. if (StrEqual(name, "all", false) || @@ -139,21 +149,23 @@ stock ClassValidateAttributes(classindex) } // Description. - if (strlen(ClassData[classindex][class_description]) < ZR_CLASS_DESCRIPTION_MIN) + if (strlen(ClassData[classindex][Class_Description]) < ZR_CLASS_DESCRIPTION_MIN) { flags += ZR_CLASS_DESCRIPTION; } // Model path. decl String:model_path[PLATFORM_MAX_PATH]; - if (strcopy(model_path, sizeof(model_path), ClassData[classindex][class_model_path]) == 0) + if (strcopy(model_path, sizeof(model_path), ClassData[classindex][Class_ModelPath]) == 0) { flags += ZR_CLASS_MODEL_PATH; } else { - // Check if a model different from default or random is specified. - if (!StrEqual(model_path, "random", false) && !StrEqual(model_path, "default", false)) + // Check if a model different from a pre-defined setting. + if (!StrEqual(model_path, "random", false) && + !StrEqual(model_path, "default", false) && + !StrEqual(model_path, "nochange", false)) { // Check if the file exists. if (!FileExists(model_path)) @@ -164,21 +176,21 @@ stock ClassValidateAttributes(classindex) } // Alpha, initial. - new alpha_initial = ClassData[classindex][class_alpha_initial]; + new alpha_initial = ClassData[classindex][Class_AlphaInitial]; if (!(alpha_initial >= ZR_CLASS_ALPHA_INITIAL_MIN && alpha_initial <= ZR_CLASS_ALPHA_INITIAL_MAX)) { flags += ZR_CLASS_ALPHA_INITIAL; } // Alpha, damaged. - new alpha_damaged = ClassData[classindex][class_alpha_damaged]; + new alpha_damaged = ClassData[classindex][Class_AlphaDamaged]; if (!(alpha_damaged >= ZR_CLASS_ALPHA_DAMAGED_MIN && alpha_damaged <= ZR_CLASS_ALPHA_DAMAGED_MAX)) { flags += ZR_CLASS_ALPHA_DAMAGED; } // Alpha, damage. - new alpha_damage = ClassData[classindex][class_alpha_damage]; + new alpha_damage = ClassData[classindex][Class_AlphaDamage]; if (!(alpha_damage >= ZR_CLASS_ALPHA_DAMAGE_MIN && alpha_damage <= ZR_CLASS_ALPHA_DAMAGE_MAX)) { flags += ZR_CLASS_ALPHA_DAMAGE; @@ -187,7 +199,7 @@ stock ClassValidateAttributes(classindex) // Overlay path. decl String:overlay_path[PLATFORM_MAX_PATH]; decl String:overlay[PLATFORM_MAX_PATH]; - if (strcopy(overlay_path, sizeof(overlay_path), ClassData[classindex][class_overlay_path]) > 0) + if (strcopy(overlay_path, sizeof(overlay_path), ClassData[classindex][Class_OverlayPath]) > 0) { // Check if the file exists. Format(overlay, sizeof(overlay), "materials/%s.vmt", overlay_path); @@ -198,77 +210,80 @@ stock ClassValidateAttributes(classindex) } // Field of view. - new fov = ClassData[classindex][class_fov]; + new fov = ClassData[classindex][Class_Fov]; if (!(fov >= ZR_CLASS_FOV_MIN && fov <= ZR_CLASS_FOV_MAX)) { flags += ZR_CLASS_FOV; } // Napalm time. - new Float:napalm_time = ClassData[classindex][class_napalm_time]; + new Float:napalm_time = ClassData[classindex][Class_NapalmTime]; if (!(napalm_time >= ZR_CLASS_NAPALM_TIME_MIN && napalm_time <= ZR_CLASS_NAPALM_TIME_MAX)) { flags += ZR_CLASS_NAPALM_TIME; } + // Immunity mode (not implemented). + + // Health. - new health = ClassData[classindex][class_health]; + new health = ClassData[classindex][Class_Health]; if (!(health >= ZR_CLASS_HEALTH_MIN && health <= ZR_CLASS_HEALTH_MAX)) { flags += ZR_CLASS_HEALTH; } // Health regen interval. - new Float:regen_interval = ClassData[classindex][class_health_regen_interval]; + new Float:regen_interval = ClassData[classindex][Class_HealthRegenInterval]; if (!(regen_interval >= ZR_CLASS_REGEN_INTERVAL_MIN && regen_interval <= ZR_CLASS_REGEN_INTERVAL_MAX)) { flags += ZR_CLASS_HEALTH_REGEN_INTERVAL; } // Health regen amount. - new regen_amount = ClassData[classindex][class_health_regen_amount]; + new regen_amount = ClassData[classindex][Class_HealthRegenAmount]; if (!(regen_amount >= ZR_CLASS_REGEN_AMOUNT_MIN && regen_amount <= ZR_CLASS_REGEN_AMOUNT_MAX)) { flags += ZR_CLASS_HEALTH_REGEN_AMOUNT; } // Health infect gain. - new infect_gain = ClassData[classindex][class_health_infect_gain]; + new infect_gain = ClassData[classindex][Class_HealthInfectGain]; if (!(infect_gain >= ZR_CLASS_HEALTH_INFECT_GAIN_MIN && infect_gain <= ZR_CLASS_HEALTH_INFECT_GAIN_MAX)) { flags += ZR_CLASS_HEALTH_INFECT_GAIN; } // Kill bonus. - new kill_bonus = ClassData[classindex][class_kill_bonus]; + new kill_bonus = ClassData[classindex][Class_KillBonus]; if (!(kill_bonus >= ZR_CLASS_KILL_BONUS_MIN && kill_bonus <= ZR_CLASS_KILL_BONUS_MAX)) { flags += ZR_CLASS_KILL_BONUS; } // Speed. - new Float:speed = ClassData[classindex][class_speed]; + new Float:speed = ClassData[classindex][Class_Speed]; if (!(speed >= ZR_CLASS_SPEED_MIN && speed <= ZR_CLASS_SPEED_MAX)) { flags += ZR_CLASS_SPEED; } // Knockback. - new Float:knockback = ClassData[classindex][class_knockback]; + new Float:knockback = ClassData[classindex][Class_KnockBack]; if (!(knockback >= ZR_CLASS_KNOCKBACK_MIN && knockback <= ZR_CLASS_KNOCKBACK_MAX)) { flags += ZR_CLASS_KNOCKBACK; } // Jump height. - new Float:jump_height = ClassData[classindex][class_jump_height]; + new Float:jump_height = ClassData[classindex][Class_JumpHeight]; if (!(jump_height >= ZR_CLASS_JUMP_HEIGHT_MIN && jump_height <= ZR_CLASS_JUMP_HEIGHT_MAX)) { flags += ZR_CLASS_JUMP_HEIGHT; } // Jump distance. - new Float:jump_distance = ClassData[classindex][class_jump_distance]; + new Float:jump_distance = ClassData[classindex][Class_JumpDistance]; if (!(jump_distance >= ZR_CLASS_JUMP_DISTANCE_MIN && jump_distance <= ZR_CLASS_JUMP_DISTANCE_MAX)) { flags += ZR_CLASS_JUMP_DISTANCE; @@ -315,21 +330,21 @@ stock bool:ClassTeamCompare(index, teamid, cachetype = ZR_CLASS_CACHE_MODIFIED) { case ZR_CLASS_CACHE_ORIGINAL: { - if (ClassData[index][class_team] == teamid) + if (ClassData[index][Class_Team] == teamid) { return true; } } case ZR_CLASS_CACHE_MODIFIED: { - if (ClassDataCache[index][class_team] == teamid) + if (ClassDataCache[index][Class_Team] == teamid) { return true; } } case ZR_CLASS_CACHE_PLAYER: { - if (ClassPlayerCache[index][class_team] == teamid) + if (ClassPlayerCache[index][Class_Team] == teamid) { return true; } @@ -448,6 +463,68 @@ stock Float:ClassGetAttributeMultiplier(client, ClassMultipliers:attribute) } } +/** + * Check if a class pass the specified filter. + * + * @param index Index of the class in a class cache or a client index, + * depending on the cache type specified. + * @param filter Structure with filter settings. + * @param cachetype Optional. Specifies what class cache to read from. + * ZR_CLASS_CACHE_ORIGINAL - Unchanced class data. + * ZR_CLASS_CACHE_MODIFIED (default) - Changed/newest + * class data. + * ZR_CLASS_CACHE_PLAYER - Player cache. If this one is + * used index will be used as a client index. + * @return True if passed, false otherwise. + */ +stock bool:ClassFilterMatch(index, filter[ClassFilter], cachetype = ZR_CLASS_CACHE_MODIFIED) +{ + // Check if the class is disabled and the enabled attribute is NOT ignored. + if (!filter[ClassFilter_IgnoreEnabled] && !ClassIsEnabled(index, cachetype)) + { + return false; + } + + // Check if class flags pass the flag filter. + if (!ClassFlagFilterMatch(index, filter[ClassFilter_RequireFlags], filter[ClassFilter_DenyFlags], cachetype)) + { + return false; + } + + // Get class group name. + decl String:groupname[64]; + groupname[0] = 0; + ClassGetGroup(index, groupname, sizeof(groupname), cachetype); + + // Check if a client is specified in the filter. + new client = filter[ClassFilter_Client]; + if (ZRIsClientValid(client)) + { + // Check if a group is set on the class. + if (strlen(groupname)) + { + // Check if the client is not a member of that group. + if (!ZRIsClientInGroup(client, groupname)) + { + return false; + } + } + } + + // Check if classes with groups are set to be excluded. + if (client < 0) + { + // Exclude class if it has a group name. + if (strlen(groupname)) + { + return false; + } + } + + // The class passed the filter. + return true; +} + /** * Check if a class pass the specified flag filters. * @@ -515,6 +592,64 @@ stock bool:ClassFlagFilterMatch(index, require, deny, cachetype) } } +/** + * Decides whether a class selection menu should be enabled. The decision is + * based on zr_class_allow_* console variables. + * + * @param team Optional. Team ID to match. Default is all. + * @return True if allowed, false otherwise. + */ +bool:ClassAllowSelection(client, team = -1) +{ + // Get selection settings. + new bool:zombie = GetConVarBool(g_hCvarsList[CVAR_CLASSES_ZOMBIE_SELECT]); + new bool:human = GetConVarBool(g_hCvarsList[CVAR_CLASSES_HUMAN_SELECT]); + new bool:admin = GetConVarBool(g_hCvarsList[CVAR_CLASSES_ADMIN_SELECT]); + + // Since admin mode classes are optional they must be counted to verify + // that they exist. + new bool:adminexist; + + // Check if player is admin. + new bool:isadmin = ZRIsClientAdmin(client); + + // Only count admin mode classes if client is admin for better performance. + if (isadmin) + { + adminexist = ClassCountTeam(ZR_CLASS_TEAM_ADMINS) > 0; + } + + // Check if a team id is specified. + if (team >= 0) + { + // Check team and return the corresponding selection setting. + switch (team) + { + case ZR_CLASS_TEAM_ZOMBIES: + { + return zombie; + } + case ZR_CLASS_TEAM_HUMANS: + { + return human; + } + case ZR_CLASS_TEAM_ADMINS: + { + // Player must be admin to select admin mode classes. + return admin && isadmin && adminexist; + } + } + + // Team ID didn't match. + return false; + } + else + { + // Check zombie and human. + return zombie || human; + } +} + /** * Gets all class indexes or from a specified team, and adds them to the * specified array. @@ -522,12 +657,7 @@ stock bool:ClassFlagFilterMatch(index, require, deny, cachetype) * @param array The destination array to add class indexes. * @param teamfilter Optional. The team ID to filter. A negative value * for no filter (default). - * @param ignoreEnabled Optional. Ignore whether the class is enabled or - * not. Default is false. - * @param requireflags Optional. Require certain class flags to be set. - * Default is no filtering. - * @param denyflags Optional. Require certain class flags to be off. - * Default is no filtering. + * @param filter Optional. Structure with filter settings. * @param cachetype Optional. Specifies what class cache to read from. * Options: * ZR_CLASS_CACHE_ORIGINAL - Unchanced class data. @@ -536,7 +666,7 @@ stock bool:ClassFlagFilterMatch(index, require, deny, cachetype) * @return True on success. False on error or if no classes were added or * found. */ -stock bool:ClassAddToArray(Handle:array, teamfilter = -1, bool:ignoreEnabled = false, requireflags = 0, denyflags = 0, cachetype = ZR_CLASS_CACHE_MODIFIED) +stock bool:ClassAddToArray(Handle:array, teamfilter = -1, filter[ClassFilter] = ClassNoFilter, cachetype = ZR_CLASS_CACHE_MODIFIED) { // Validate the array. if (array == INVALID_HANDLE) @@ -551,46 +681,39 @@ stock bool:ClassAddToArray(Handle:array, teamfilter = -1, bool:ignoreEnabled = f } // Store a local boolean that says if the user specified a team filter or not. - new bool:has_filter = bool:(teamfilter >= 0); - new classes_added; + new bool:hasteamfilter = bool:(teamfilter >= 0); + new classesadded; // Loop through all classes. for (new classindex = 0; classindex < ClassCount; classindex++) { - if (!ignoreEnabled && !ClassIsEnabled(classindex, cachetype)) + // Validate filter settings. + if (!ClassFilterMatch(classindex, filter, cachetype)) { - // The class is disabled and the enabled attribute is NOT ignored. - // Skip to the next class. - continue; - } - - // Check flag filter match. - if (!ClassFlagFilterMatch(classindex, requireflags, denyflags, cachetype)) - { - // The class didn't pass filter. + // The class is didn't pass the filter, skip class. continue; } // Check team filtering. - if (has_filter) + if (hasteamfilter) { // Only add classes with matching team ID. if (ClassGetTeamID(classindex, cachetype) == teamfilter) { // Team ID match. Add class index to array. PushArrayCell(array, classindex); - classes_added++; + classesadded++; } } else { // No filter. Add any class to the array. PushArrayCell(array, classindex); - classes_added++; + classesadded++; } } - if (classes_added) + if (classesadded) { return true; } @@ -606,12 +729,7 @@ stock bool:ClassAddToArray(Handle:array, teamfilter = -1, bool:ignoreEnabled = f * * @param teamfilter Optional. The team ID to filter. Negative value for * no filter (default). - * @param ignoreEnabled Optional. Ignore whether the class is enabled or - * not. Default is false. - * @param requireflags Optional. Require certain class flags to be set. - * Default is no filtering. - * @param denyflags Optional. Require certain class flags to be off. - * Default is no filtering. + * @param filter Optional. Structure with filter settings. * @param cachetype Optional. Specifies what class cache to read from. * Options: * ZR_CLASS_CACHE_ORIGINAL - Unchanced class data. @@ -619,7 +737,7 @@ stock bool:ClassAddToArray(Handle:array, teamfilter = -1, bool:ignoreEnabled = f * class data. * @return Number of total classes or classes in the specified team. */ -stock ClassCountTeam(teamfilter = -1, bool:ignoreEnabled = false, requireflags = 0, denyflags = 0, cachetype = ZR_CLASS_CACHE_MODIFIED) +stock ClassCountTeam(teamfilter = -1, filter[ClassFilter] = ClassNoFilter, cachetype = ZR_CLASS_CACHE_MODIFIED) { // Check if there are no classes. if (ClassCount == 0) @@ -628,28 +746,21 @@ stock ClassCountTeam(teamfilter = -1, bool:ignoreEnabled = false, requireflags = } // Store a local boolean that says if the user specified a team filter or not. - new bool:has_filter = bool:(teamfilter >= 0); + new bool:hasteamfilter = bool:(teamfilter >= 0); new count; // Loop through all classes. for (new classindex = 0; classindex < ClassCount; classindex++) { - if (!ignoreEnabled && !ClassIsEnabled(classindex, cachetype)) + // Validate filter settings. + if (!ClassFilterMatch(classindex, filter, cachetype)) { - // The class is disabled and the enabled attribute is NOT ignored. - // Skip to the next class. - continue; - } - - // Check flag filter match. - if (!ClassFlagFilterMatch(classindex, requireflags, denyflags, cachetype)) - { - // The class didn't pass filter. + // The class is didn't pass the filter, skip class. continue; } // Check team filtering. - if (has_filter) + if (hasteamfilter) { // Only add classes with matching team ID. if (ClassGetTeamID(classindex, cachetype) == teamfilter) @@ -674,12 +785,7 @@ stock ClassCountTeam(teamfilter = -1, bool:ignoreEnabled = false, requireflags = * * @param teamfilter Optional. The team ID to filter. A negative value * for no filter (default). - * @param ignoreEnabled Optional. Ignore whether the class is enabled or - * not. Default is false. - * @param requireflags Optional. Require certain class flags to be set. - * Default is no filtering. - * @param denyflags Optional. Require certain class flags to be off. - * Default is no filtering. + * @param filter Optional. Structure with filter settings. * @param cachetype Optional. Specifies what class cache to read from. * Options: * ZR_CLASS_CACHE_ORIGINAL - Unchanced class data. @@ -687,7 +793,7 @@ stock ClassCountTeam(teamfilter = -1, bool:ignoreEnabled = false, requireflags = * class data. * @return The class index if successful, or -1 on error. */ -stock ClassGetRandomClass(teamfilter = -1, bool:ignoreEnabled = false, requireflags = 0, denyflags = 0, cachetype = ZR_CLASS_CACHE_MODIFIED) +stock ClassGetRandomClass(teamfilter = -1, filter[ClassFilter] = ClassNoSpecialClasses, cachetype = ZR_CLASS_CACHE_MODIFIED) { new Handle:classarray; new arraycount; @@ -697,7 +803,7 @@ stock ClassGetRandomClass(teamfilter = -1, bool:ignoreEnabled = false, requirefl classarray = CreateArray(); // Try to get a class list. - if (ClassAddToArray(classarray, teamfilter, ignoreEnabled, requireflags, denyflags, cachetype)) + if (ClassAddToArray(classarray, teamfilter, filter, cachetype)) { // Get a random index from the new class array. arraycount = GetArraySize(classarray); @@ -722,12 +828,7 @@ stock ClassGetRandomClass(teamfilter = -1, bool:ignoreEnabled = false, requirefl * * @param teamfilter Optional. The team ID to filter. A negative value * for no filter (default). - * @param ignoreEnabled Optional. Ignore whether the class is enabled or - * not. Default is false. - * @param requireflags Optional. Require certain class flags to be set. - * Default is no filtering. - * @param denyflags Optional. Require certain class flags to be off. - * Default is no filtering. + * @param filter Optional. Structure with filter settings. * @param cachetype Optional. Specifies what class cache to read from. * Options: * ZR_CLASS_CACHE_ORIGINAL - Unchanced class data. @@ -736,7 +837,7 @@ stock ClassGetRandomClass(teamfilter = -1, bool:ignoreEnabled = false, requirefl * @return The first class index, or the first class index with the specified * team ID. -1 on error. */ -stock ClassGetFirstClass(teamfilter = -1, bool:ignoreEnabled = false, requireflags = 0, denyflags = 0, cachetype = ZR_CLASS_CACHE_MODIFIED) +stock ClassGetFirstClass(teamfilter = -1, filter[ClassFilter] = ClassNoSpecialClasses, cachetype = ZR_CLASS_CACHE_MODIFIED) { // Check if there are no classes. if (ClassCount == 0) @@ -744,26 +845,19 @@ stock ClassGetFirstClass(teamfilter = -1, bool:ignoreEnabled = false, requirefla return false; } - new bool:has_filter = bool:(teamfilter >= 0); + new bool:hasteamfilter = bool:(teamfilter >= 0); // Loop through all classes. for (new classindex = 0; classindex < ClassCount; classindex++) { - if (!ignoreEnabled && !ClassIsEnabled(classindex, cachetype)) + // Validate filter settings. + if (!ClassFilterMatch(classindex, filter, cachetype)) { - // The class is disabled and the enabled attribute is NOT ignored. - // Skip to the next class. + // The class is didn't pass the filter, skip class. continue; } - // Check flag filter match. - if (!ClassFlagFilterMatch(classindex, requireflags, denyflags, cachetype)) - { - // The class didn't pass filter. - continue; - } - - if (has_filter) + if (hasteamfilter) { if (teamfilter == ClassGetTeamID(classindex, cachetype)) { @@ -785,12 +879,8 @@ stock ClassGetFirstClass(teamfilter = -1, bool:ignoreEnabled = false, requirefla * Gets the first class marked as default for the specified team. * * @param teamid The team ID. - * @param ignoreEnabled Optional. Ignore whether the class is enabled or - * not. Default is false. - * @param requireflags Optional. Require certain class flags to be set. - * Default is no filtering. - * @param denyflags Optional. Require certain class flags to be off. - * Default is to deny classes with special flags + * @param filter Optional. Structure with filter settings. Default + * is to deny classes with special flags * (ZR_CLASS_SPECIALFLAGS). * @param cachetype Optional. Specifies what class cache to read from. * Options: @@ -799,7 +889,7 @@ stock ClassGetFirstClass(teamfilter = -1, bool:ignoreEnabled = false, requirefla * class data. * @return The first default class index. -1 on error. */ -stock ClassGetDefaultClass(teamid, bool:ignoreEnabled = false, requireflags = 0, denyflags = ZR_CLASS_SPECIALFLAGS, cachetype = ZR_CLASS_CACHE_MODIFIED) +stock ClassGetDefaultClass(teamid, filter[ClassFilter] = ClassNoSpecialClasses, cachetype = ZR_CLASS_CACHE_MODIFIED) { new Handle:classarray; new arraycount; @@ -808,7 +898,7 @@ stock ClassGetDefaultClass(teamid, bool:ignoreEnabled = false, requireflags = 0, classarray = CreateArray(); // Get all classes from the specified team. - if (!ClassAddToArray(classarray, teamid, ignoreEnabled, requireflags, denyflags, cachetype)) + if (!ClassAddToArray(classarray, teamid, filter, cachetype)) { // Failed to get classes. CloseHandle(classarray); @@ -840,6 +930,8 @@ stock ClassGetDefaultClass(teamid, bool:ignoreEnabled = false, requireflags = 0, * when players join the server. * * @param teamid The team ID. + * @param filter Optional. Structure with filter settings. Default is to + * deny classes with special flags (ZR_CLASS_SPECIALFLAGS). * @param cachetype Optional. Specifies what class cache to read from. Options: * ZR_CLASS_CACHE_ORIGINAL - Unchanced class data. * ZR_CLASS_CACHE_MODIFIED (default) - Changed/newest class @@ -848,7 +940,7 @@ stock ClassGetDefaultClass(teamid, bool:ignoreEnabled = false, requireflags = 0, * successful. -1 on critical errors. Otherwise it will try to fall * back on the first class in the specified team. */ -stock ClassGetDefaultSpawnClass(teamid, cachetype = ZR_CLASS_CACHE_MODIFIED) +stock ClassGetDefaultSpawnClass(teamid, filter[ClassFilter] = ClassNoSpecialClasses, cachetype = ZR_CLASS_CACHE_MODIFIED) { decl String:classname[64]; new classindex; @@ -879,11 +971,11 @@ stock ClassGetDefaultSpawnClass(teamid, cachetype = ZR_CLASS_CACHE_MODIFIED) if (strlen(classname) > 0) { // Check if the user set "random" as default class. - if (strcmp(classname, "random", false) == 0) + if (StrEqual(classname, "random", false)) { // Get a list of all classes with the specified team ID. Deny // classes with special flags. - classindex = ClassGetRandomClass(teamid, _, _, ZR_CLASS_SPECIALFLAGS, cachetype); + classindex = ClassGetRandomClass(teamid, filter, cachetype); // Validate the result, in case there were errors. if (ClassValidateIndex(classindex)) @@ -915,7 +1007,7 @@ stock ClassGetDefaultSpawnClass(teamid, cachetype = ZR_CLASS_CACHE_MODIFIED) // The class index is invalid or the team IDs didn't match. // Because it's user input, we'll fall back to the first class // in the specified team, and log a warning. - classindex = ClassGetFirstClass(teamid, _, _, ZR_CLASS_SPECIALFLAGS, cachetype); + classindex = ClassGetFirstClass(teamid, filter, cachetype); LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Default Spawn Class", "Warning: Failed to set \"%s\" as default spawn class for team %d. The class doesn't exist or the team IDs doesn't match. Falling back to the first class in the team.", classname, teamid); @@ -928,7 +1020,8 @@ stock ClassGetDefaultSpawnClass(teamid, cachetype = ZR_CLASS_CACHE_MODIFIED) } else { - // Something went wrong. This is a critical error. + // Something went wrong. This is a critical error. There's + // probably missing classes with no special flags set. return -1; } } @@ -937,6 +1030,6 @@ stock ClassGetDefaultSpawnClass(teamid, cachetype = ZR_CLASS_CACHE_MODIFIED) else { // Blank class name, get the default class and return the index. - return ClassGetDefaultClass(teamid, _, _, ZR_CLASS_SPECIALFLAGS, cachetype); + return ClassGetDefaultClass(teamid, filter, cachetype); } } diff --git a/src/zr/playerclasses/playerclasses.inc b/src/zr/playerclasses/playerclasses.inc index 0de2ee4..ba13f15 100644 --- a/src/zr/playerclasses/playerclasses.inc +++ b/src/zr/playerclasses/playerclasses.inc @@ -31,7 +31,7 @@ - Zombies have to hurt humans so they loose hp. When the hp reach zero (or below) they turn into zombies. - Fully imune to all damage. Can't take or give damage. Sould only be used - on admin classes. + on admin mode classes. TODO: Make class attributes for for changing model render mode and colors. @@ -100,10 +100,11 @@ * @section Overall default class settings. Since this is a zombie plugin the * default values represent a zombie. */ -#define ZR_CLASS_DEFAULT_ENABLED true +#define ZR_CLASS_DEFAULT_ENABLED "yes" #define ZR_CLASS_DEFAULT_TEAM ZR_CLASS_TEAM_ZOMBIES -#define ZR_CLASS_DEFAULT_TEAM_DEFAULT true +#define ZR_CLASS_DEFAULT_TEAM_DEFAULT "yes" #define ZR_CLASS_DEFAULT_FLAGS 0 +#define ZR_CLASS_DEFAULT_GROUP "" #define ZR_CLASS_DEFAULT_NAME "classic" #define ZR_CLASS_DEFAULT_DESCRIPTION "Need brains!!! Arrrrggghh!" #define ZR_CLASS_DEFAULT_MODEL_PATH "models/player/zh/zh_zombie003.mdl" @@ -111,13 +112,13 @@ #define ZR_CLASS_DEFAULT_ALPHA_DAMAGED 255 #define ZR_CLASS_DEFAULT_ALPHA_DAMAGE 0 #define ZR_CLASS_DEFAULT_OVERLAY_PATH "overlays/zr/zvision" -#define ZR_CLASS_DEFAULT_NVGS true +#define ZR_CLASS_DEFAULT_NVGS "no" #define ZR_CLASS_DEFAULT_FOV 90 -#define ZR_CLASS_DEFAULT_HAS_NAPALM 1 +#define ZR_CLASS_DEFAULT_HAS_NAPALM "yes" #define ZR_CLASS_DEFAULT_NAPALM_TIME 10.0 #define ZR_CLASS_DEFAULT_IMMUNITY_MODE ZR_CLASS_IMMUNITY_DISABLED #define ZR_CLASS_DEFAULT_IMMUNITY_AMOUNT 0.0 -#define ZR_CLASS_DEFAULT_NO_FALL_DAMAGE true +#define ZR_CLASS_DEFAULT_NO_FALL_DAMAGE "on" #define ZR_CLASS_DEFAULT_HEALTH 6000 #define ZR_CLASS_DEFAULT_HEALTH_REGEN_INTERVAL 0.0 #define ZR_CLASS_DEFAULT_HEALTH_REGEN_AMOUNT 2 @@ -181,77 +182,94 @@ #define ZR_CLASS_TEAM (1<<1) #define ZR_CLASS_TEAM_DEFAULT (1<<2) #define ZR_CLASS_FLAGS (1<<3) -#define ZR_CLASS_NAME (1<<4) -#define ZR_CLASS_DESCRIPTION (1<<5) -#define ZR_CLASS_MODEL_PATH (1<<6) -#define ZR_CLASS_ALPHA_INITIAL (1<<7) -#define ZR_CLASS_ALPHA_DAMAGED (1<<8) -#define ZR_CLASS_ALPHA_DAMAGE (1<<9) -#define ZR_CLASS_OVERLAY_PATH (1<<10) -#define ZR_CLASS_NVGS (1<<11) -#define ZR_CLASS_FOV (1<<12) -#define ZR_CLASS_HAS_NAPALM (1<<13) -#define ZR_CLASS_NAPALM_TIME (1<<14) -#define ZR_CLASS_IMMUNITY_MODE (1<<15) -#define ZR_CLASS_IMMUNITY_AMOUNT (1<<16) -#define ZR_CLASS_NO_FALL_DAMAGE (1<<17) -#define ZR_CLASS_HEALTH (1<<18) -#define ZR_CLASS_HEALTH_REGEN_INTERVAL (1<<19) -#define ZR_CLASS_HEALTH_REGEN_AMOUNT (1<<20) -#define ZR_CLASS_HEALTH_INFECT_GAIN (1<<21) -#define ZR_CLASS_KILL_BONUS (1<<22) -#define ZR_CLASS_SPEED (1<<23) -#define ZR_CLASS_KNOCKBACK (1<<24) -#define ZR_CLASS_JUMP_HEIGHT (1<<25) -#define ZR_CLASS_JUMP_DISTANCE (1<<26) +#define ZR_CLASS_GROUP (1<<4) +#define ZR_CLASS_NAME (1<<5) +#define ZR_CLASS_DESCRIPTION (1<<6) +#define ZR_CLASS_MODEL_PATH (1<<7) +#define ZR_CLASS_ALPHA_INITIAL (1<<8) +#define ZR_CLASS_ALPHA_DAMAGED (1<<9) +#define ZR_CLASS_ALPHA_DAMAGE (1<<10) +#define ZR_CLASS_OVERLAY_PATH (1<<11) +#define ZR_CLASS_NVGS (1<<12) +#define ZR_CLASS_FOV (1<<13) +#define ZR_CLASS_HAS_NAPALM (1<<14) +#define ZR_CLASS_NAPALM_TIME (1<<15) +#define ZR_CLASS_IMMUNITY_MODE (1<<16) +#define ZR_CLASS_IMMUNITY_AMOUNT (1<<17) +#define ZR_CLASS_NO_FALL_DAMAGE (1<<18) +#define ZR_CLASS_HEALTH (1<<19) +#define ZR_CLASS_HEALTH_REGEN_INTERVAL (1<<20) +#define ZR_CLASS_HEALTH_REGEN_AMOUNT (1<<21) +#define ZR_CLASS_HEALTH_INFECT_GAIN (1<<22) +#define ZR_CLASS_KILL_BONUS (1<<23) +#define ZR_CLASS_SPEED (1<<24) +#define ZR_CLASS_KNOCKBACK (1<<25) +#define ZR_CLASS_JUMP_HEIGHT (1<<26) +#define ZR_CLASS_JUMP_DISTANCE (1<<27) /** * @endsection */ /** * Generic player attributes. + * + * Stuff that must be updated when new attributes are added: + * ZR_CLASS_DEFAULT_... define + * ZR_CLASS_..._MAX/MIN defines + * ZR_CLASS_... define (place in same order as listed in ClassAttributes, bump bit numbers + update numbers in docs) + * ClassLoad + * ClassReloadDataCache + * ClassReloadPlayerCache + * ClassDumpData + * attributes.inc - Add new Get-function + * ClassAttributeNameToFlag + * ClassGetAttributeType + * ClassValidateAttributes + * ClassModify* in classcommands.inc + * Update docs with detailed attribute description */ enum ClassAttributes { /* General */ - bool:class_enabled, - class_team, - bool:class_team_default, - class_flags, + bool:Class_Enabled, + Class_Team, + bool:Class_TeamDefault, + Class_Flags, + String:Class_Group[64], - String:class_name[64], - String:class_description[256], + String:Class_Name[64], + String:Class_Description[256], /* Model */ - String:class_model_path[PLATFORM_MAX_PATH], - class_alpha_initial, - class_alpha_damaged, - class_alpha_damage, + String:Class_ModelPath[PLATFORM_MAX_PATH], + Class_AlphaInitial, + Class_AlphaDamaged, + Class_AlphaDamage, /* Hud */ - String:class_overlay_path[PLATFORM_MAX_PATH], - bool:class_nvgs, - class_fov, + String:Class_OverlayPath[PLATFORM_MAX_PATH], + bool:Class_Nvgs, + Class_Fov, /* Effects */ - bool:class_has_napalm, - Float:class_napalm_time, + bool:Class_HasNapalm, + Float:Class_NapalmTime, /* Player behaviour */ - class_immunity_mode, - Float:class_immunity_amount, - bool:class_no_fall_damage, + Class_ImmunityMode, + Float:Class_ImmunityAmount, + bool:Class_NoFallDamage, - class_health, - Float:class_health_regen_interval, - class_health_regen_amount, - class_health_infect_gain, - class_kill_bonus, + Class_Health, + Float:Class_HealthRegenInterval, + Class_HealthRegenAmount, + Class_HealthInfectGain, + Class_KillBonus, - Float:class_speed, - Float:class_knockback, - Float:class_jump_height, - Float:class_jump_distance + Float:Class_Speed, + Float:Class_KnockBack, + Float:Class_JumpHeight, + Float:Class_JumpDistance } /** @@ -294,6 +312,27 @@ enum ClassDataTypes ClassDataType_String /** String value */ } +/** + * Structure for class filter settings passed to various functions. + */ +enum ClassFilter +{ + bool:ClassFilter_IgnoreEnabled, /** Ignore whether the class is disabled or not. */ + ClassFilter_RequireFlags, /** Flags the classes must have set. */ + ClassFilter_DenyFlags, /** Flags the classes cannot have set. */ + ClassFilter_Client /** The client to check for class group permissions. Use 0 to ignore group filter and negative to exclude classes with groups set. */ +} + +/** + * Empty filter structure. + */ +new ClassNoFilter[ClassFilter]; + +/** + * Filter structure for excluding special classes. + */ +new ClassNoSpecialClasses[ClassFilter] = {false, 0, ZR_CLASS_SPECIALFLAGS, -1}; + /** * Keyvalue handle to store class data. */ @@ -351,6 +390,11 @@ new ClassSelectedNext[MAXPLAYERS + 1][ZR_CLASS_TEAMCOUNT]; */ new ClassAdminTeamSelected[MAXPLAYERS + 1]; +/** + * Cookies for storing class indexes. + */ +new Handle:g_hClassCookieClassSelected[ZR_CLASS_TEAMCOUNT]; + /** * Cache for the currently selected attribute multiplier (admin menus). */ @@ -362,9 +406,13 @@ new ClassMultipliers:ClassAdminAttributeSelected[MAXPLAYERS + 1]; new bool:ClassPlayerInAdminMode[MAXPLAYERS + 1]; /** - * Specifies the admin class to use on next admin mode spawn. + * Specifies whether a player is allowed to change class with instant effect. + * This is only used on human classes, and in combination with the + * zr_classes_change_timelimit time limit, but could be used other places too. + * The class menu will automatically check this setting and apply attributes if + * set to true. */ -//new ClassPlayerNextAdminClass[MAXPLAYERS + 1]; +new bool:ClassAllowInstantChange[MAXPLAYERS + 1]; /** * Cache for storing original model path before applying custom models. Used @@ -431,6 +479,7 @@ ClassLoad(bool:keepMultipliers = false) } decl String:name[64]; + decl String:group[64]; decl String:description[256]; decl String:model_path[PLATFORM_MAX_PATH]; decl String:overlay_path[PLATFORM_MAX_PATH]; @@ -451,55 +500,58 @@ ClassLoad(bool:keepMultipliers = false) } /* General */ - ClassData[ClassCount][class_enabled] = bool:KvGetNum(kvClassData, "enabled", ZR_CLASS_DEFAULT_ENABLED); - ClassData[ClassCount][class_team] = KvGetNum(kvClassData, "team", ZR_CLASS_DEFAULT_TEAM); - ClassData[ClassCount][class_team_default] = bool:KvGetNum(kvClassData, "team_default", ZR_CLASS_DEFAULT_TEAM_DEFAULT); - ClassData[ClassCount][class_flags] = KvGetNum(kvClassData, "flags", ZR_CLASS_DEFAULT_FLAGS); + ClassData[ClassCount][Class_Enabled] = ConfigKvGetStringBool(kvClassData, "enabled", ZR_CLASS_DEFAULT_ENABLED); + ClassData[ClassCount][Class_Team] = KvGetNum(kvClassData, "team", ZR_CLASS_DEFAULT_TEAM); + ClassData[ClassCount][Class_TeamDefault] = ConfigKvGetStringBool(kvClassData, "team_default", ZR_CLASS_DEFAULT_TEAM_DEFAULT); + ClassData[ClassCount][Class_Flags] = KvGetNum(kvClassData, "flags", ZR_CLASS_DEFAULT_FLAGS); + + KvGetString(kvClassData, "group", group, sizeof(group), ZR_CLASS_DEFAULT_GROUP); + strcopy(ClassData[ClassCount][Class_Group], 64, group); KvGetString(kvClassData, "name", name, sizeof(name), ZR_CLASS_DEFAULT_NAME); - strcopy(ClassData[ClassCount][class_name], 64, name); + strcopy(ClassData[ClassCount][Class_Name], 64, name); KvGetString(kvClassData, "description", description, sizeof(description), ZR_CLASS_DEFAULT_DESCRIPTION); - strcopy(ClassData[ClassCount][class_description], 256, description); + strcopy(ClassData[ClassCount][Class_Description], 256, description); /* Model */ KvGetString(kvClassData, "model_path", model_path, sizeof(model_path), ZR_CLASS_DEFAULT_MODEL_PATH); - strcopy(ClassData[ClassCount][class_model_path], PLATFORM_MAX_PATH, model_path); + strcopy(ClassData[ClassCount][Class_ModelPath], PLATFORM_MAX_PATH, model_path); - ClassData[ClassCount][class_alpha_initial] = KvGetNum(kvClassData, "alpha_initial", ZR_CLASS_DEFAULT_ALPHA_INITIAL); - ClassData[ClassCount][class_alpha_damaged] = KvGetNum(kvClassData, "alpha_damaged", ZR_CLASS_DEFAULT_ALPHA_DAMAGED); - ClassData[ClassCount][class_alpha_damage] = KvGetNum(kvClassData, "alpha_damage", ZR_CLASS_DEFAULT_ALPHA_DAMAGE); + ClassData[ClassCount][Class_AlphaInitial] = KvGetNum(kvClassData, "alpha_initial", ZR_CLASS_DEFAULT_ALPHA_INITIAL); + ClassData[ClassCount][Class_AlphaDamaged] = KvGetNum(kvClassData, "alpha_damaged", ZR_CLASS_DEFAULT_ALPHA_DAMAGED); + ClassData[ClassCount][Class_AlphaDamage] = KvGetNum(kvClassData, "alpha_damage", ZR_CLASS_DEFAULT_ALPHA_DAMAGE); /* Hud */ KvGetString(kvClassData, "overlay_path", overlay_path, sizeof(overlay_path), ZR_CLASS_DEFAULT_OVERLAY_PATH); - strcopy(ClassData[ClassCount][class_overlay_path], PLATFORM_MAX_PATH, overlay_path); + strcopy(ClassData[ClassCount][Class_OverlayPath], PLATFORM_MAX_PATH, overlay_path); - ClassData[ClassCount][class_nvgs] = bool:KvGetNum(kvClassData, "nvgs", ZR_CLASS_DEFAULT_NVGS); - ClassData[ClassCount][class_fov] = KvGetNum(kvClassData, "fov", ZR_CLASS_DEFAULT_FOV); + ClassData[ClassCount][Class_Nvgs] = ConfigKvGetStringBool(kvClassData, "nvgs", ZR_CLASS_DEFAULT_NVGS); + ClassData[ClassCount][Class_Fov] = KvGetNum(kvClassData, "fov", ZR_CLASS_DEFAULT_FOV); /* Effects */ - ClassData[ClassCount][class_has_napalm] = bool:KvGetNum(kvClassData, "have_napalm", ZR_CLASS_DEFAULT_HAS_NAPALM); - ClassData[ClassCount][class_napalm_time] = KvGetFloat(kvClassData, "napalm_time", ZR_CLASS_DEFAULT_NAPALM_TIME); + ClassData[ClassCount][Class_HasNapalm] = ConfigKvGetStringBool(kvClassData, "have_napalm", ZR_CLASS_DEFAULT_HAS_NAPALM); + ClassData[ClassCount][Class_NapalmTime] = KvGetFloat(kvClassData, "napalm_time", ZR_CLASS_DEFAULT_NAPALM_TIME); /* Player behaviour */ - ClassData[ClassCount][class_immunity_mode] = KvGetNum(kvClassData, "immunity_mode", ZR_CLASS_DEFAULT_IMMUNITY_MODE); - ClassData[ClassCount][class_immunity_amount] = KvGetFloat(kvClassData, "immunity_amount", ZR_CLASS_DEFAULT_IMMUNITY_AMOUNT); - ClassData[ClassCount][class_no_fall_damage] = bool:KvGetNum(kvClassData, "no_fall_damage", ZR_CLASS_DEFAULT_NO_FALL_DAMAGE); + ClassData[ClassCount][Class_ImmunityMode] = KvGetNum(kvClassData, "immunity_mode", ZR_CLASS_DEFAULT_IMMUNITY_MODE); + ClassData[ClassCount][Class_ImmunityAmount] = KvGetFloat(kvClassData, "immunity_amount", ZR_CLASS_DEFAULT_IMMUNITY_AMOUNT); + ClassData[ClassCount][Class_NoFallDamage] = ConfigKvGetStringBool(kvClassData, "no_fall_damage", ZR_CLASS_DEFAULT_NO_FALL_DAMAGE); - ClassData[ClassCount][class_health] = KvGetNum(kvClassData, "health", ZR_CLASS_DEFAULT_HEALTH); - ClassData[ClassCount][class_health_regen_interval] = KvGetFloat(kvClassData, "health_regen_interval", ZR_CLASS_DEFAULT_HEALTH_REGEN_INTERVAL); - ClassData[ClassCount][class_health_regen_amount] = KvGetNum(kvClassData, "health_regen_amount", ZR_CLASS_DEFAULT_HEALTH_REGEN_AMOUNT); - ClassData[ClassCount][class_health_infect_gain] = KvGetNum(kvClassData, "health_infect_gain", ZR_CLASS_DEFAULT_HEALTH_INFECT_GAIN); - ClassData[ClassCount][class_kill_bonus] = KvGetNum(kvClassData, "kill_bonus", ZR_CLASS_DEFAULT_KILL_BONUS); + ClassData[ClassCount][Class_Health] = KvGetNum(kvClassData, "health", ZR_CLASS_DEFAULT_HEALTH); + ClassData[ClassCount][Class_HealthRegenInterval] = KvGetFloat(kvClassData, "health_regen_interval", ZR_CLASS_DEFAULT_HEALTH_REGEN_INTERVAL); + ClassData[ClassCount][Class_HealthRegenAmount] = KvGetNum(kvClassData, "health_regen_amount", ZR_CLASS_DEFAULT_HEALTH_REGEN_AMOUNT); + ClassData[ClassCount][Class_HealthInfectGain] = KvGetNum(kvClassData, "health_infect_gain", ZR_CLASS_DEFAULT_HEALTH_INFECT_GAIN); + ClassData[ClassCount][Class_KillBonus] = KvGetNum(kvClassData, "kill_bonus", ZR_CLASS_DEFAULT_KILL_BONUS); - ClassData[ClassCount][class_speed] = KvGetFloat(kvClassData, "speed", ZR_CLASS_DEFAULT_SPEED); - ClassData[ClassCount][class_knockback] = KvGetFloat(kvClassData, "knockback", ZR_CLASS_DEFAULT_KNOCKBACK); - ClassData[ClassCount][class_jump_height] = KvGetFloat(kvClassData, "jump_height", ZR_CLASS_DEFAULT_JUMP_HEIGHT); - ClassData[ClassCount][class_jump_distance] = KvGetFloat(kvClassData, "jump_distance", ZR_CLASS_DEFAULT_JUMP_DISTANCE); + ClassData[ClassCount][Class_Speed] = KvGetFloat(kvClassData, "speed", ZR_CLASS_DEFAULT_SPEED); + ClassData[ClassCount][Class_KnockBack] = KvGetFloat(kvClassData, "knockback", ZR_CLASS_DEFAULT_KNOCKBACK); + ClassData[ClassCount][Class_JumpHeight] = KvGetFloat(kvClassData, "jump_height", ZR_CLASS_DEFAULT_JUMP_HEIGHT); + ClassData[ClassCount][Class_JumpDistance] = KvGetFloat(kvClassData, "jump_distance", ZR_CLASS_DEFAULT_JUMP_DISTANCE); // Validate the class attributes. ClassErrorFlags = ClassValidateAttributes(ClassCount); @@ -507,7 +559,7 @@ ClassLoad(bool:keepMultipliers = false) { // There's one or more invalid class attributes. Disable the class // and log an error message. - ClassData[ClassCount][class_enabled] = false; + ClassData[ClassCount][Class_Enabled] = false; LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid class at index %d, disabled class. Class error flags: %d.", ClassCount, ClassErrorFlags); failedcount++; @@ -585,41 +637,42 @@ bool:ClassReloadDataCache() for (new classindex = 0; classindex < ClassCount; classindex++) { /* General */ - ClassDataCache[classindex][class_enabled] = ClassData[classindex][class_enabled]; - ClassDataCache[classindex][class_team] = ClassData[classindex][class_team]; - ClassDataCache[classindex][class_team_default] = ClassData[classindex][class_team_default]; - ClassDataCache[classindex][class_flags] = ClassData[classindex][class_flags]; - strcopy(ClassDataCache[classindex][class_name], 64, ClassData[classindex][class_name]); - strcopy(ClassDataCache[classindex][class_description], 256, ClassData[classindex][class_description]); + ClassDataCache[classindex][Class_Enabled] = ClassData[classindex][Class_Enabled]; + ClassDataCache[classindex][Class_Team] = ClassData[classindex][Class_Team]; + ClassDataCache[classindex][Class_TeamDefault] = ClassData[classindex][Class_TeamDefault]; + ClassDataCache[classindex][Class_Flags] = ClassData[classindex][Class_Flags]; + strcopy(ClassDataCache[classindex][Class_Group], 64, ClassData[classindex][Class_Group]); + strcopy(ClassDataCache[classindex][Class_Name], 64, ClassData[classindex][Class_Name]); + strcopy(ClassDataCache[classindex][Class_Description], 256, ClassData[classindex][Class_Description]); /* Model */ - strcopy(ClassDataCache[classindex][class_model_path], PLATFORM_MAX_PATH, ClassData[classindex][class_model_path]); - ClassDataCache[classindex][class_alpha_initial] = ClassData[classindex][class_alpha_initial]; - ClassDataCache[classindex][class_alpha_damaged] = ClassData[classindex][class_alpha_damaged]; - ClassDataCache[classindex][class_alpha_damage] = ClassData[classindex][class_alpha_damage]; + strcopy(ClassDataCache[classindex][Class_ModelPath], PLATFORM_MAX_PATH, ClassData[classindex][Class_ModelPath]); + ClassDataCache[classindex][Class_AlphaInitial] = ClassData[classindex][Class_AlphaInitial]; + ClassDataCache[classindex][Class_AlphaDamaged] = ClassData[classindex][Class_AlphaDamaged]; + ClassDataCache[classindex][Class_AlphaDamage] = ClassData[classindex][Class_AlphaDamage]; /* Hud */ - strcopy(ClassDataCache[classindex][class_overlay_path], PLATFORM_MAX_PATH, ClassData[classindex][class_overlay_path]); - ClassDataCache[classindex][class_nvgs] = ClassData[classindex][class_nvgs]; - ClassDataCache[classindex][class_fov] = ClassData[classindex][class_fov]; + strcopy(ClassDataCache[classindex][Class_OverlayPath], PLATFORM_MAX_PATH, ClassData[classindex][Class_OverlayPath]); + ClassDataCache[classindex][Class_Nvgs] = ClassData[classindex][Class_Nvgs]; + ClassDataCache[classindex][Class_Fov] = ClassData[classindex][Class_Fov]; /* Effects */ - ClassDataCache[classindex][class_has_napalm] = ClassData[classindex][class_has_napalm]; - ClassDataCache[classindex][class_napalm_time] = ClassData[classindex][class_napalm_time]; + ClassDataCache[classindex][Class_HasNapalm] = ClassData[classindex][Class_HasNapalm]; + ClassDataCache[classindex][Class_NapalmTime] = ClassData[classindex][Class_NapalmTime]; /* Player behaviour */ - ClassDataCache[classindex][class_immunity_mode] = ClassData[classindex][class_immunity_mode]; - ClassDataCache[classindex][class_immunity_amount] = ClassData[classindex][class_immunity_amount]; - ClassDataCache[classindex][class_no_fall_damage] = ClassData[classindex][class_no_fall_damage]; - ClassDataCache[classindex][class_health] = ClassData[classindex][class_health]; - ClassDataCache[classindex][class_health_regen_interval] = ClassData[classindex][class_health_regen_interval]; - ClassDataCache[classindex][class_health_regen_amount] = ClassData[classindex][class_health_regen_amount]; - ClassDataCache[classindex][class_health_infect_gain] = ClassData[classindex][class_health_infect_gain]; - ClassDataCache[classindex][class_kill_bonus] = ClassData[classindex][class_kill_bonus]; - ClassDataCache[classindex][class_speed] = ClassData[classindex][class_speed]; - ClassDataCache[classindex][class_knockback] = ClassData[classindex][class_knockback]; - ClassDataCache[classindex][class_jump_height] = ClassData[classindex][class_jump_height]; - ClassDataCache[classindex][class_jump_distance] = ClassData[classindex][class_jump_distance]; + ClassDataCache[classindex][Class_ImmunityMode] = ClassData[classindex][Class_ImmunityMode]; + ClassDataCache[classindex][Class_ImmunityAmount] = ClassData[classindex][Class_ImmunityAmount]; + ClassDataCache[classindex][Class_NoFallDamage] = ClassData[classindex][Class_NoFallDamage]; + ClassDataCache[classindex][Class_Health] = ClassData[classindex][Class_Health]; + ClassDataCache[classindex][Class_HealthRegenInterval] = ClassData[classindex][Class_HealthRegenInterval]; + ClassDataCache[classindex][Class_HealthRegenAmount] = ClassData[classindex][Class_HealthRegenAmount]; + ClassDataCache[classindex][Class_HealthInfectGain] = ClassData[classindex][Class_HealthInfectGain]; + ClassDataCache[classindex][Class_KillBonus] = ClassData[classindex][Class_KillBonus]; + ClassDataCache[classindex][Class_Speed] = ClassData[classindex][Class_Speed]; + ClassDataCache[classindex][Class_KnockBack] = ClassData[classindex][Class_KnockBack]; + ClassDataCache[classindex][Class_JumpHeight] = ClassData[classindex][Class_JumpHeight]; + ClassDataCache[classindex][Class_JumpDistance] = ClassData[classindex][Class_JumpDistance]; } return true; @@ -649,80 +702,82 @@ bool:ClassReloadPlayerCache(client, classindex, cachetype = ZR_CLASS_CACHE_MODIF case ZR_CLASS_CACHE_ORIGINAL: { /* General */ - ClassPlayerCache[client][class_enabled] = ClassData[classindex][class_enabled]; - ClassPlayerCache[client][class_team] = ClassData[classindex][class_team]; - ClassPlayerCache[client][class_team_default] = ClassData[classindex][class_team_default]; - ClassPlayerCache[client][class_flags] = ClassData[classindex][class_flags]; - strcopy(ClassPlayerCache[client][class_name], 64, ClassData[classindex][class_name]); - strcopy(ClassPlayerCache[client][class_description], 256, ClassData[classindex][class_description]); + ClassPlayerCache[client][Class_Enabled] = ClassData[classindex][Class_Enabled]; + ClassPlayerCache[client][Class_Team] = ClassData[classindex][Class_Team]; + ClassPlayerCache[client][Class_TeamDefault] = ClassData[classindex][Class_TeamDefault]; + ClassPlayerCache[client][Class_Flags] = ClassData[classindex][Class_Flags]; + strcopy(ClassPlayerCache[client][Class_Group], 64, ClassData[classindex][Class_Group]); + strcopy(ClassPlayerCache[client][Class_Name], 64, ClassData[classindex][Class_Name]); + strcopy(ClassPlayerCache[client][Class_Description], 256, ClassData[classindex][Class_Description]); /* Model */ - strcopy(ClassPlayerCache[client][class_model_path], PLATFORM_MAX_PATH, ClassData[classindex][class_model_path]); - ClassPlayerCache[client][class_alpha_initial] = ClassData[classindex][class_alpha_initial]; - ClassPlayerCache[client][class_alpha_damaged] = ClassData[classindex][class_alpha_damaged]; - ClassPlayerCache[client][class_alpha_damage] = ClassData[classindex][class_alpha_damage]; + strcopy(ClassPlayerCache[client][Class_ModelPath], PLATFORM_MAX_PATH, ClassData[classindex][Class_ModelPath]); + ClassPlayerCache[client][Class_AlphaInitial] = ClassData[classindex][Class_AlphaInitial]; + ClassPlayerCache[client][Class_AlphaDamaged] = ClassData[classindex][Class_AlphaDamaged]; + ClassPlayerCache[client][Class_AlphaDamage] = ClassData[classindex][Class_AlphaDamage]; /* Hud */ - strcopy(ClassPlayerCache[client][class_overlay_path], PLATFORM_MAX_PATH, ClassData[classindex][class_overlay_path]); - ClassPlayerCache[client][class_nvgs] = ClassData[classindex][class_nvgs]; - ClassPlayerCache[client][class_fov] = ClassData[classindex][class_fov]; + strcopy(ClassPlayerCache[client][Class_OverlayPath], PLATFORM_MAX_PATH, ClassData[classindex][Class_OverlayPath]); + ClassPlayerCache[client][Class_Nvgs] = ClassData[classindex][Class_Nvgs]; + ClassPlayerCache[client][Class_Fov] = ClassData[classindex][Class_Fov]; /* Effects */ - ClassPlayerCache[client][class_has_napalm] = ClassData[classindex][class_has_napalm]; - ClassPlayerCache[client][class_napalm_time] = ClassData[classindex][class_napalm_time]; + ClassPlayerCache[client][Class_HasNapalm] = ClassData[classindex][Class_HasNapalm]; + ClassPlayerCache[client][Class_NapalmTime] = ClassData[classindex][Class_NapalmTime]; /* Player behaviour */ - ClassPlayerCache[client][class_immunity_mode] = ClassData[classindex][class_immunity_mode]; - ClassPlayerCache[client][class_immunity_amount] = ClassData[classindex][class_immunity_amount]; - ClassPlayerCache[client][class_no_fall_damage] = ClassData[classindex][class_no_fall_damage]; - ClassPlayerCache[client][class_health] = ClassData[classindex][class_health]; - ClassPlayerCache[client][class_health_regen_interval] = ClassData[classindex][class_health_regen_interval]; - ClassPlayerCache[client][class_health_regen_amount] = ClassData[classindex][class_health_regen_amount]; - ClassPlayerCache[client][class_health_infect_gain] = ClassData[classindex][class_health_infect_gain]; - ClassPlayerCache[client][class_kill_bonus] = ClassData[classindex][class_kill_bonus]; - ClassPlayerCache[client][class_speed] = ClassData[classindex][class_speed]; - ClassPlayerCache[client][class_knockback] = ClassData[classindex][class_knockback]; - ClassPlayerCache[client][class_jump_height] = ClassData[classindex][class_jump_height]; - ClassPlayerCache[client][class_jump_distance] = ClassData[classindex][class_jump_distance]; + ClassPlayerCache[client][Class_ImmunityMode] = ClassData[classindex][Class_ImmunityMode]; + ClassPlayerCache[client][Class_ImmunityAmount] = ClassData[classindex][Class_ImmunityAmount]; + ClassPlayerCache[client][Class_NoFallDamage] = ClassData[classindex][Class_NoFallDamage]; + ClassPlayerCache[client][Class_Health] = ClassData[classindex][Class_Health]; + ClassPlayerCache[client][Class_HealthRegenInterval] = ClassData[classindex][Class_HealthRegenInterval]; + ClassPlayerCache[client][Class_HealthRegenAmount] = ClassData[classindex][Class_HealthRegenAmount]; + ClassPlayerCache[client][Class_HealthInfectGain] = ClassData[classindex][Class_HealthInfectGain]; + ClassPlayerCache[client][Class_KillBonus] = ClassData[classindex][Class_KillBonus]; + ClassPlayerCache[client][Class_Speed] = ClassData[classindex][Class_Speed]; + ClassPlayerCache[client][Class_KnockBack] = ClassData[classindex][Class_KnockBack]; + ClassPlayerCache[client][Class_JumpHeight] = ClassData[classindex][Class_JumpHeight]; + ClassPlayerCache[client][Class_JumpDistance] = ClassData[classindex][Class_JumpDistance]; } case ZR_CLASS_CACHE_MODIFIED: { /* General */ - ClassPlayerCache[client][class_enabled] = ClassDataCache[classindex][class_enabled]; - ClassPlayerCache[client][class_team] = ClassDataCache[classindex][class_team]; - ClassPlayerCache[client][class_team_default] = ClassDataCache[classindex][class_team_default]; - ClassPlayerCache[client][class_flags] = ClassDataCache[classindex][class_flags]; - strcopy(ClassPlayerCache[client][class_name], 64, ClassDataCache[classindex][class_name]); - strcopy(ClassPlayerCache[client][class_description], 256, ClassDataCache[classindex][class_description]); + ClassPlayerCache[client][Class_Enabled] = ClassDataCache[classindex][Class_Enabled]; + ClassPlayerCache[client][Class_Team] = ClassDataCache[classindex][Class_Team]; + ClassPlayerCache[client][Class_TeamDefault] = ClassDataCache[classindex][Class_TeamDefault]; + ClassPlayerCache[client][Class_Flags] = ClassDataCache[classindex][Class_Flags]; + strcopy(ClassPlayerCache[client][Class_Group], 64, ClassDataCache[classindex][Class_Group]); + strcopy(ClassPlayerCache[client][Class_Name], 64, ClassDataCache[classindex][Class_Name]); + strcopy(ClassPlayerCache[client][Class_Description], 256, ClassDataCache[classindex][Class_Description]); /* Model */ - strcopy(ClassPlayerCache[client][class_model_path], PLATFORM_MAX_PATH, ClassDataCache[classindex][class_model_path]); - ClassPlayerCache[client][class_alpha_initial] = ClassDataCache[classindex][class_alpha_initial]; - ClassPlayerCache[client][class_alpha_damaged] = ClassDataCache[classindex][class_alpha_damaged]; - ClassPlayerCache[client][class_alpha_damage] = ClassDataCache[classindex][class_alpha_damage]; + strcopy(ClassPlayerCache[client][Class_ModelPath], PLATFORM_MAX_PATH, ClassDataCache[classindex][Class_ModelPath]); + ClassPlayerCache[client][Class_AlphaInitial] = ClassDataCache[classindex][Class_AlphaInitial]; + ClassPlayerCache[client][Class_AlphaDamaged] = ClassDataCache[classindex][Class_AlphaDamaged]; + ClassPlayerCache[client][Class_AlphaDamage] = ClassDataCache[classindex][Class_AlphaDamage]; /* Hud */ - strcopy(ClassPlayerCache[client][class_overlay_path], PLATFORM_MAX_PATH, ClassDataCache[classindex][class_overlay_path]); - ClassPlayerCache[client][class_nvgs] = ClassDataCache[classindex][class_nvgs]; - ClassPlayerCache[client][class_fov] = ClassDataCache[classindex][class_fov]; + strcopy(ClassPlayerCache[client][Class_OverlayPath], PLATFORM_MAX_PATH, ClassDataCache[classindex][Class_OverlayPath]); + ClassPlayerCache[client][Class_Nvgs] = ClassDataCache[classindex][Class_Nvgs]; + ClassPlayerCache[client][Class_Fov] = ClassDataCache[classindex][Class_Fov]; /* Effects */ - ClassPlayerCache[client][class_has_napalm] = ClassDataCache[classindex][class_has_napalm]; - ClassPlayerCache[client][class_napalm_time] = ClassDataCache[classindex][class_napalm_time]; + ClassPlayerCache[client][Class_HasNapalm] = ClassDataCache[classindex][Class_HasNapalm]; + ClassPlayerCache[client][Class_NapalmTime] = ClassDataCache[classindex][Class_NapalmTime]; /* Player behaviour */ - ClassPlayerCache[client][class_immunity_mode] = ClassDataCache[classindex][class_immunity_mode]; - ClassPlayerCache[client][class_immunity_amount] = ClassDataCache[classindex][class_immunity_amount]; - ClassPlayerCache[client][class_no_fall_damage] = ClassDataCache[classindex][class_no_fall_damage]; - ClassPlayerCache[client][class_health] = ClassDataCache[classindex][class_health]; - ClassPlayerCache[client][class_health_regen_interval] = ClassDataCache[classindex][class_health_regen_interval]; - ClassPlayerCache[client][class_health_regen_amount] = ClassDataCache[classindex][class_health_regen_amount]; - ClassPlayerCache[client][class_health_infect_gain] = ClassDataCache[classindex][class_health_infect_gain]; - ClassPlayerCache[client][class_kill_bonus] = ClassDataCache[classindex][class_kill_bonus]; - ClassPlayerCache[client][class_speed] = ClassDataCache[classindex][class_speed]; - ClassPlayerCache[client][class_knockback] = ClassDataCache[classindex][class_knockback]; - ClassPlayerCache[client][class_jump_height] = ClassDataCache[classindex][class_jump_height]; - ClassPlayerCache[client][class_jump_distance] = ClassDataCache[classindex][class_jump_distance]; + ClassPlayerCache[client][Class_ImmunityMode] = ClassDataCache[classindex][Class_ImmunityMode]; + ClassPlayerCache[client][Class_ImmunityAmount] = ClassDataCache[classindex][Class_ImmunityAmount]; + ClassPlayerCache[client][Class_NoFallDamage] = ClassDataCache[classindex][Class_NoFallDamage]; + ClassPlayerCache[client][Class_Health] = ClassDataCache[classindex][Class_Health]; + ClassPlayerCache[client][Class_HealthRegenInterval] = ClassDataCache[classindex][Class_HealthRegenInterval]; + ClassPlayerCache[client][Class_HealthRegenAmount] = ClassDataCache[classindex][Class_HealthRegenAmount]; + ClassPlayerCache[client][Class_HealthInfectGain] = ClassDataCache[classindex][Class_HealthInfectGain]; + ClassPlayerCache[client][Class_KillBonus] = ClassDataCache[classindex][Class_KillBonus]; + ClassPlayerCache[client][Class_Speed] = ClassDataCache[classindex][Class_Speed]; + ClassPlayerCache[client][Class_KnockBack] = ClassDataCache[classindex][Class_KnockBack]; + ClassPlayerCache[client][Class_JumpHeight] = ClassDataCache[classindex][Class_JumpHeight]; + ClassPlayerCache[client][Class_JumpDistance] = ClassDataCache[classindex][Class_JumpDistance]; } default: { @@ -782,7 +837,7 @@ ClassResetMultiplierCache() } /** - * Resets the selected class indexes for next span on one or all clients. + * Resets the selected class indexes for next spawn on one or all clients. * * @param client Optional. Specify client to reset. Default is all. */ @@ -814,7 +869,7 @@ ClassResetNextIndexes(client = -1) * Note: Does not apply attributes. The classes are only marked as selected. * * @param client The client index. - * @param excludeTeam Do not restore the specified team. + * @param excludeTeam Optional. Do not restore the specified team. */ ClassRestoreNextIndexes(client, excludeTeam = -1) { @@ -870,52 +925,157 @@ ClassRestoreNextIndexes(client, excludeTeam = -1) * Sets default class indexes for each team on all players, or a single player * if specified. * - * @param client Optional. The client index. + * @param client Optional. The client index. If specified, cookies are used. */ ClassClientSetDefaultIndexes(client = -1) { - // Get indexes. - new zombieindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ZOMBIES); - new humanindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_HUMANS); - new adminindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ADMINS); + new bool:clientvalid = ZRIsClientValid(client); + new filter[ClassFilter]; + new bool:saveclasses = GetConVarBool(g_hCvarsList[CVAR_CLASSES_SAVE]); - // Validate zombie class index. + new zombieindex; + new humanindex; + new adminindex; + + new bool:haszombie; + new bool:hashuman; + new bool:hasadmin; + + // Check if a client is specified. + if (clientvalid) + { + // Get cookie indexes if enabled. + if (saveclasses) + { + zombieindex = CookiesGetInt(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_ZOMBIES]); + humanindex = CookiesGetInt(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_HUMANS]); + adminindex = CookiesGetInt(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_ADMINS]); + } + else + { + // Do not use indexes in cookies. Set invalid values so it will + // fall back to default class. + zombieindex = 0; + humanindex = 0; + adminindex = 0; + } + + // Note: When class indexes are set on cookies, they're incremented by + // one so zero means no class set and will result in a invalid + // class index when restored. + + // Setup filtering so group permission is checked on player first. + filter[ClassFilter_Client] = client; + + // Check if class indexes are set and that the client pass group + // permissions. If not, fall back to default class indexes. Otherwise + // substract index by one. + if (zombieindex <= 0 || !ClassFilterMatch(zombieindex, filter)) + { + zombieindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ZOMBIES, filter); + } + else + { + zombieindex--; + haszombie = true; + } + + if (humanindex <= 0 || !ClassFilterMatch(humanindex, filter)) + { + humanindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_HUMANS, filter); + } + else + { + humanindex--; + hashuman = true; + } + + if (adminindex <= 0 || !ClassFilterMatch(adminindex, filter)) + { + adminindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ADMINS, filter); + } + else + { + adminindex--; + hasadmin = true; + } + } + else + { + // Setup filtering so classes with groups set are excluded. + filter[ClassFilter_Client] = -1; + + // Get default class indexes. + zombieindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ZOMBIES, filter); + humanindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_HUMANS, filter); + adminindex = ClassGetDefaultSpawnClass(ZR_CLASS_TEAM_ADMINS, filter); + } + + // Validate indexes. if (!ClassValidateIndex(zombieindex)) { // Invalid class index. Fall back to default class in class config and // log a warning. - LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Set Default Indexes", "Warning: Failed to get default zombie class, falling back to default class in class config. Check spelling in \"zr_classes_default_zombie\"."); + LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Set Default Indexes", "Warning: Failed to get specified zombie class, falling back to default class in class config. Check spelling in \"zr_classes_default_zombie\"."); // Use default class. - zombieindex = ClassGetDefaultClass(ZR_CLASS_TEAM_ZOMBIES); + zombieindex = ClassGetDefaultClass(ZR_CLASS_TEAM_ZOMBIES, filter); } - // Validate human class index. + // Get human class index. if (!ClassValidateIndex(humanindex)) { // Invalid class index. Fall back to default class in class config and // log a warning. - LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Set Default Indexes", "Warning: Failed to get default human class, falling back to default class in class config. Check spelling in \"zr_classes_default_human\"."); + LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Set Default Indexes", "Warning: Failed to get specified human class, falling back to default class in class config. Check spelling in \"zr_classes_default_human\"."); // Use default class. - humanindex = ClassGetDefaultClass(ZR_CLASS_TEAM_HUMANS); + humanindex = ClassGetDefaultClass(ZR_CLASS_TEAM_HUMANS, filter); } - // Validate admin class index. + // Get admin class index. if (!ClassValidateIndex(adminindex)) { // Invalid class index. Fall back to default class in class config if // possible. A invalid class index (-1) can also be stored if there are // no admin classes at all. - adminindex = ClassGetDefaultClass(ZR_CLASS_TEAM_ADMINS); + adminindex = ClassGetDefaultClass(ZR_CLASS_TEAM_ADMINS, filter); } - // Check if a client isn't specified. - if (client < 1) + // Check if a client is specified. + if (clientvalid) + { + // Set selected class idexes. + ClassSelected[client][ZR_CLASS_TEAM_ZOMBIES] = zombieindex; + ClassSelected[client][ZR_CLASS_TEAM_HUMANS] = humanindex; + ClassSelected[client][ZR_CLASS_TEAM_ADMINS] = adminindex; + + // Copy human class data to player cache. + ClassReloadPlayerCache(client, humanindex); + + // Save indexes in cookies if enabled, and not already saved. + if (saveclasses) + { + if (!haszombie) + { + CookiesSetInt(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_ZOMBIES], zombieindex + 1); + } + if (!hashuman) + { + CookiesSetInt(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_HUMANS], humanindex + 1); + } + if (!hasadmin) + { + CookiesSetInt(client, g_hClassCookieClassSelected[ZR_CLASS_TEAM_ADMINS], adminindex + 1); + } + } + } + else { // No client specified. Loop through all players. - for (new clientindex = 1; clientindex <= MAXPLAYERS; clientindex++) + for (new clientindex = 1; clientindex <= MaxClients; clientindex++) { + // Set selected class idexes. ClassSelected[clientindex][ZR_CLASS_TEAM_ZOMBIES] = zombieindex; ClassSelected[clientindex][ZR_CLASS_TEAM_HUMANS] = humanindex; ClassSelected[clientindex][ZR_CLASS_TEAM_ADMINS] = adminindex; @@ -924,15 +1084,6 @@ ClassClientSetDefaultIndexes(client = -1) ClassReloadPlayerCache(client, humanindex); } } - else - { - ClassSelected[client][ZR_CLASS_TEAM_ZOMBIES] = zombieindex; - ClassSelected[client][ZR_CLASS_TEAM_HUMANS] = humanindex; - ClassSelected[client][ZR_CLASS_TEAM_ADMINS] = adminindex; - - // Copy human class data to player cache. - ClassReloadPlayerCache(client, humanindex); - } } /** @@ -976,6 +1127,10 @@ ClassDumpData(index, cachetype, String:buffer[], maxlen) Format(attribute, sizeof(attribute), "flags: \"%d\"\n", ClassGetFlags(index, cachetype)); cellcount += StrCat(buffer, maxlen, attribute); + ClassGetGroup(index, format_buffer, sizeof(format_buffer), cachetype); + Format(attribute, sizeof(attribute), "group: \"%s\"\n", format_buffer); + cellcount += StrCat(buffer, maxlen, attribute); + ClassGetName(index, format_buffer, sizeof(format_buffer), cachetype); Format(attribute, sizeof(attribute), "name: \"%s\"\n", format_buffer); cellcount += StrCat(buffer, maxlen, attribute); diff --git a/src/zr/translation.inc b/src/zr/translation.inc index 63658d8..812fdae 100644 --- a/src/zr/translation.inc +++ b/src/zr/translation.inc @@ -333,16 +333,13 @@ stock TranslationReplyToCommand(client, any:...) { // Format string to create plugin style. (color) TranslationPluginFormatString(translation, sizeof(translation)); - - // Print translated phrase to client's chat/console. - PrintToChat(client, translation); } else { // Format string to create plugin style. (no color) TranslationPluginFormatString(translation, sizeof(translation), false); - - // Print to server. - PrintToServer(translation); } + + // Print translated phrase to server or client's chat/console. + ReplyToCommand(client, translation); } diff --git a/src/zr/zadmin.inc b/src/zr/zadmin.inc index 0ec51a1..557f7ae 100644 --- a/src/zr/zadmin.inc +++ b/src/zr/zadmin.inc @@ -64,7 +64,7 @@ public Action:ZAdminCommand(client, argc) */ bool:ZAdminMenu(client) { - // If client isn't an admin, then stop. + // If client isn't an generic admin, then stop. if (!ZRIsClientAdmin(client)) { TranslationPrintToChat(client, "Must be admin"); @@ -94,16 +94,18 @@ bool:ZAdminMenu(client) Format(ztele, sizeof(ztele), "%t", "ZAdmin main force ztele"); // Get conditions for options. - new bool:hitgroupsenabled = GetConVarBool(g_hCvarsList[CVAR_HITGROUPS]); + new configdraw = MenuGetItemDraw(ZRIsClientPrivileged(client, OperationType_Configuration)); + new moderatordraw = MenuGetItemDraw(ZRIsClientPrivileged(client, OperationType_Generic)); + new bool:hitgroupsenabled = GetConVarBool(g_hCvarsList[CVAR_HITGROUPS]) && ZRIsClientPrivileged(client, OperationType_Configuration); // Add items to menu. SetMenuTitle(menu_zadmin, title); - AddMenuItem(menu_zadmin, "classmultipliers", classmultipliers); - AddMenuItem(menu_zadmin, "weapons", weapons); + AddMenuItem(menu_zadmin, "classmultipliers", classmultipliers, configdraw); + AddMenuItem(menu_zadmin, "weapons", weapons, configdraw); AddMenuItem(menu_zadmin, "hitgroups", hitgroups, MenuGetItemDraw(hitgroupsenabled)); - AddMenuItem(menu_zadmin, "infect", infect); - AddMenuItem(menu_zadmin, "zspawn", zspawn); - AddMenuItem(menu_zadmin, "ztele", ztele); + AddMenuItem(menu_zadmin, "infect", infect, moderatordraw); + AddMenuItem(menu_zadmin, "zspawn", zspawn, moderatordraw); + AddMenuItem(menu_zadmin, "ztele", ztele, moderatordraw); // Set "Back" button. SetMenuExitBackButton(menu_zadmin, true); diff --git a/src/zr/zombiereloaded.inc b/src/zr/zombiereloaded.inc index 4dbb895..f97041c 100644 --- a/src/zr/zombiereloaded.inc +++ b/src/zr/zombiereloaded.inc @@ -235,7 +235,7 @@ stock bool:ZRTeamHasClients(team = -1) } /** - * Returns whether a player is a generic admin or not. + * Returns whether a player is a admin or not. * * @param client The client index. * @param flag Optional. Flag to check. Default is generic admin flag. @@ -249,7 +249,7 @@ stock bool:ZRIsClientAdmin(client, AdminFlag:flag = Admin_Generic) return false; } - // If client doesn't have the Admin_Generic flag, then stop. + // If client doesn't have the specified flag, then stop. if (!GetAdminFlag(GetUserAdmin(client), flag)) { return false; diff --git a/src/zr/ztele.inc b/src/zr/ztele.inc index 33aec78..ca5d90e 100644 --- a/src/zr/ztele.inc +++ b/src/zr/ztele.inc @@ -233,7 +233,7 @@ bool:ZTeleClient(client, bool:force = false) ZTeleTeleportClient(client) { // Teleport client. - TeleportEntity(client, g_vecZTeleSpawn[client], NULL_VECTOR, NULL_VECTOR); + TeleportEntity(client, g_vecZTeleSpawn[client], NULL_VECTOR, Float:{0.0, 0.0, 0.0}); } /**