Merged heads.

This commit is contained in:
richard 2009-07-16 10:06:39 +02:00
commit 62c1ab4cc5
70 changed files with 4839 additions and 806 deletions

View File

@ -10,6 +10,7 @@
// ----------------------------------------------------------------------------
// Defaults:
// ----------------------------------------------------------------------------
materials/models/player/zh/Zombie_Classic_sheet.vmt
materials/models/player/zh/corpse1.vmt
materials/models/player/zh/Charple1_sheet.vmt

View File

@ -12,7 +12,7 @@
// Attribute: Values: Description:
// ----------------------------------------------------------------------------
// index number The hitgroup index.
// damage yes/no Allow damage to be done on this hitgroup for zombies.
// damage on/off Allow damage to be done on this hitgroup for zombies.
// knockback decimal The knockback multiplier for this hitgroup.
"hitgroups" // Counter-Strike: Source hitgroups
@ -23,7 +23,7 @@
"index" "0"
// Damage
"damage" "yes"
"damage" "on"
// Knockback
"knockback" "1.0"
@ -35,7 +35,7 @@
"index" "1"
// Damage
"damage" "yes"
"damage" "on"
// Knockback
"knockback" "2.0"
@ -47,7 +47,7 @@
"index" "2"
// Damage
"damage" "yes"
"damage" "on"
// Knockback
"knockback" "1.3"
@ -59,7 +59,7 @@
"index" "3"
// Damage
"damage" "yes"
"damage" "on"
// Knockback
"knockback" "1.2"
@ -71,7 +71,7 @@
"index" "4"
// Damage
"damage" "yes"
"damage" "on"
// Knockback
"knockback" "1.0"
@ -83,7 +83,7 @@
"index" "5"
// Damage
"damage" "yes"
"damage" "on"
// Knockback
"knockback" "1.0"
@ -95,7 +95,7 @@
"index" "6"
// Damage
"damage" "yes"
"damage" "on"
// Knockback
"knockback" "0.9"
@ -107,7 +107,7 @@
"index" "7"
// Damage
"damage" "yes"
"damage" "on"
// Knockback
"knockback" "0.9"
@ -119,7 +119,7 @@
"index" "8"
// Damage
"damage" "yes"
"damage" "on"
// Knockback
"knockback" "1.0"

View File

@ -6,12 +6,25 @@
// Check the weapon configuration section in the manual for detailed info.
//
// ============================================================================
// Format:
// ----------------------------------------------------------------------------
// the/path/to/the/model ;public/hidden/adminonly/etc
// * ;public - The model will be treated as a model that any client has access to.
// * ;hidden - The model can only be accessed through explicit use of a player class.
// E.g. If a class uses the "random" setting for model, then any non-public
// models will not be chosen.
// ============================================================================
// * Each uncommented line will be used as a model path for clients to download,
// and classes to utilize.
// * If no ;<string> is specified, the model will be assumed as public.
// ----------------------------------------------------------------------------
// Defaults:
// ----------------------------------------------------------------------------
models/player/zh/zh_charple001
models/player/zh/zh_zombie003
models/player/zh/zh_corpse002
models/player/ics/hellknight_red/t_guerilla
models/player/zh/zh_charple001 ;public
models/player/zh/zh_zombie003 ;public
models/player/zh/zh_corpse002 ;public
models/player/ics/hellknight_red/t_guerilla ;public
// models/player/adminmodels/1337model ;adminonly // None of these models will be randomly chosen.
// models/player/donatormodels/donatormodel ;donator
// models/player/hiddenmodels/myhiddenmodel ;non-public

View File

@ -1,9 +1,8 @@
// ============================================================================
//
// ZOMBIE:RELOADED
// Class configuration
// Zombie:Reloaded Class configuration
//
// Check the class configuration section in the manual for detailed info.
// See Class Configuration (3.7) in the manual for detailed info.
//
// ============================================================================
//
@ -52,7 +51,7 @@
// Zombie classes
//
// ------------------------------------------
"classic"
"zombie_classic"
{
// General
"enabled" "1"
@ -95,7 +94,7 @@
"jump_distance" "1.2"
}
"fast"
"zombie_fast"
{
// General
"enabled" "1"
@ -138,7 +137,7 @@
"jump_distance" "1.2"
}
"mutated"
"zombie_mutated"
{
// General
"enabled" "1"
@ -181,7 +180,7 @@
"jump_distance" "1.3"
}
"heavy"
"zombie_heavy"
{
// General
"enabled" "1"
@ -224,6 +223,92 @@
"jump_distance" "0.9"
}
"mother_zombie"
{
// General
"enabled" "1"
"team" "0"
"team_default" "0"
"flags" "2"
"name" "Mother zombie"
"description" "+HP regen | +Speed | +Jump | -Knockback"
// Model
"model_path" "models/player/zh/zh_charple001.mdl"
"alpha_initial" "255"
"alpha_damaged" "255"
"alpha_damage" "0"
// Hud
"overlay_path" "overlays/zr/zvision"
"nvgs" "0"
"fov" "110"
// Effects
"has_napalm" "0"
"napalm_time" "5.0"
// Player behaviour
"immunity_mode" "0"
"immunity_amount" "0.0"
"no_fall_damage" "1"
"health" "2500"
"health_regen_interval" "0.25"
"health_regen_amount" "10"
"health_infect_gain" "700"
"kill_bonus" "1"
"speed" "400"
"knockback" "2.8"
"jump_height" "1.2"
"jump_distance" "1.3"
}
"mother_zombie_admin"
{
// General
"enabled" "1"
"team" "0"
"team_default" "0"
"flags" "3"
"name" "Admin mother zombie"
"description" "+HP regen | +Speed | +Jump | -Knockback"
// Model
"model_path" "models/player/zh/zh_charple001.mdl"
"alpha_initial" "255"
"alpha_damaged" "255"
"alpha_damage" "0"
// Hud
"overlay_path" "overlays/zr/zvision"
"nvgs" "0"
"fov" "110"
// Effects
"has_napalm" "0"
"napalm_time" "3.0"
// Player behaviour
"immunity_mode" "0"
"immunity_amount" "0.0"
"no_fall_damage" "1"
"health" "3500"
"health_regen_interval" "0.25"
"health_regen_amount" "10"
"health_infect_gain" "700"
"kill_bonus" "1"
"speed" "425"
"knockback" "2.8"
"jump_height" "1.2"
"jump_distance" "1.3"
}
// ------------------------------------------
//
// Human classes
@ -273,6 +358,92 @@
"jump_distance" "1.0"
}
"human_vip"
{
// General
"enabled" "1"
"team" "1"
"team_default" "0"
"flags" "0"
"name" "VIP Human"
"description" "Human class for important players"
// Model
"model_path" "default"
"alpha_initial" "255"
"alpha_damaged" "255"
"alpha_damage" "0"
// Hud
"overlay_path" ""
"nvgs" "0"
"fov" "90"
// Effects
"has_napalm" "1"
"napalm_time" "0.0"
// Player behaviour
"immunity_mode" "0"
"immunity_amount" "0.0"
"no_fall_damage" "0"
"health" "200"
"health_regen_interval" "1.0"
"health_regen_amount" "10"
"health_infect_gain" "0"
"kill_bonus" "2"
"speed" "320"
"knockback" "0"
"jump_height" "1.1"
"jump_distance" "1.1"
}
"human_admin"
{
// General
"enabled" "1"
"team" "1"
"team_default" "0"
"flags" "1"
"name" "Admin Human"
"description" "Human class for admins"
// Model
"model_path" "default"
"alpha_initial" "255"
"alpha_damaged" "255"
"alpha_damage" "0"
// Hud
"overlay_path" ""
"nvgs" "0"
"fov" "90"
// Effects
"has_napalm" "1"
"napalm_time" "0.0"
// Player behaviour
"immunity_mode" "0"
"immunity_amount" "0.0"
"no_fall_damage" "1"
"health" "200"
"health_regen_interval" "1.0"
"health_regen_amount" "10"
"health_infect_gain" "0"
"kill_bonus" "2"
"speed" "320"
"knockback" "0"
"jump_height" "1.1"
"jump_distance" "1.1"
}
"human_speedy"
{
// General

View File

@ -736,6 +736,7 @@
// General
"weapontype" "All, Equipment"
"weaponslot" "5"
// Restrict (core)

View File

@ -9,13 +9,6 @@
"Offsets"
{
"EyeAngles"
{
"windows" "206"
"linux" "207"
}
"TraceAttack"
{
"windows" "58"
@ -28,6 +21,24 @@
"linux" "61"
}
"StartTouch"
{
"windows" "88"
"linux" "89"
}
"Touch"
{
"windows" "89"
"linux" "90"
}
"EndTouch"
{
"windows" "90"
"linux" "91"
}
"Weapon_CanUse"
{
"windows" "216"

View File

@ -143,7 +143,7 @@
"Config command reload all stats begin"
{
"en" "Reloading all Zombie:Reloaded config files...\n ------------------------------------------------"
"en" "Reloading all Zombie:Reloaded config files...\n------------------------------------------------"
}
"Config command reload all stats successful"
@ -432,37 +432,37 @@
"Menu main zclass"
{
"#format" "{1:s}"
"en" "ZClass - Configure your class settings. (Command: {1})"
"en" "ZClass - Configure class settings. (Command: {1})"
}
"Menu main zcookies"
{
"#format" "{1:s}"
"en" "ZCookies - Toggle your personal ZR settings here. (Command: {1})"
"en" "ZCookies - Toggle personal ZR settings here. (Command: {1})"
}
"Menu main zspawn"
{
"#format" "{1:s}"
"en" "ZSpawn - Join late? Use this to spawn. (Command: {1})"
"en" "ZSpawn - Join late? Spawn with this. (Command: {1})"
}
"Menu main ztele"
{
"#format" "{1:s}"
"en" "ZTele - Teleport back to your spawn location. (Command: {1})"
"en" "ZTele - Stuck? Teleport back to spawn. (Command: {1})"
}
"Menu main zhp"
{
"#format" "{1:s}"
"en" "ZHP - Toggle real HP display when infected. (Command: {1})"
"en" "ZHP - Shows real HP as zombie. (Command: {1})"
}
"Menu main zmarket"
{
"#format" "{1:s}"
"en" "ZMarket - Need a weapon? Buy them here. (Command: {1})"
"en" "ZMarket - Customize loadouts here. (Command: {1})"
}
// ===========================
@ -634,7 +634,7 @@
"Weapons menu restrict types unrestrict all"
{
"#format" "{1:s}"
"en" "Unrestrict weapon type {1}\n "
"en" "Unrestrict weapon type {1}"
}
"Weapons menu restrict zmarket title"
@ -683,7 +683,7 @@
"Weapons menu zmarket loadout title"
{
"en" "ZMarket\nMy Current Loadout:\nRebuy refers to these weapons"
"en" "ZMarket\nMy Current Loadout:\nSelect weapon slot to clear.\nNote: Rebuy refers to these weapons."
}
"Weapons menu zmarket loadout primary"
@ -716,6 +716,12 @@
"en" "Explosive: {1}"
}
"Weapons menu zmarket loadout nvgs"
{
"#format" "{1:s}"
"en" "NVGs: {1}"
}
"Weapons menu zmarket loadout empty"
{
"en" "(None)"
@ -747,6 +753,25 @@
// Hitgroups (core)
// ===========================
// Menu
"Hitgroups menu hitgroups title"
{
"en" "Hitgroup Management\nCommands: zr_hitgroup(_enable_all/_headshots_only)\nSelect a Hitgroup to Toggle:"
}
"Hitgroups menu hitgroups enable all"
{
"en" "Enable All Hitgroups"
}
"Hitgroups menu hitgroups headshots only"
{
"en" "Headshots Only"
}
// Commands
"Hitgroups command syntax"
{
"en" "Toggles or sets if a zombie's hitgroup can be damaged. Usage: zr_hitgroup <hitgroup alias> [1/0]"
@ -790,6 +815,85 @@
"en" "Zombies may now only be damaged by headshots."
}
// ===========================
// ZAdmin (core)
// ===========================
"ZAdmin main title"
{
"en" "ZAdmin\nSelect Category:"
}
"ZAdmin main class multipliers"
{
"en" "Class Multipliers"
}
"ZAdmin main weapons"
{
"en" "Weapon Management"
}
"ZAdmin main hitgroups"
{
"en" "Hitgroup Management"
}
"ZAdmin main zombie"
{
"en" "Zombie Management"
}
"ZAdmin main force zspawn"
{
"en" "Force ZSpawn"
}
"ZAdmin main force ztele"
{
"en" "Force ZTele"
}
// ===========================
// AntiStick (module)
// ===========================
// Commands
"AntiStick command set width syntax"
{
"en" "Sets the width of a model's hull. (See zr_antistick_list_models) Usage: zr_antistick_set_width <model/player> <width>"
}
"AntiStick command list models list"
{
"en" "Showing all players' model data...\n------------------------------------------------"
}
"AntiStick command list models name"
{
"#format" "{1:s},{2:s},{3:f}"
"en" "Player Name: {1} | Model Name: {2} | Model Hull Width: {3}"
}
"AntiStick command set width successful"
{
"#format" "{1:s},{2:f}"
"en" "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}\""
}
"AntiStick command set width invalid width"
{
"#format" "{1:f}"
"en" "Invalid model hull width specified: \"{1}\""
}
// ===========================
// Spawn Protect (module)
// ===========================
@ -848,6 +952,8 @@
// ZSpawn (module)
// ===========================
// General
"ZSpawn double spawn"
{
"en" "ZSpawn can only be used if you joined late during a round in progress."
@ -859,6 +965,33 @@
"en" "The timelimit ({1} seconds), to use ZSpawn, has already expired."
}
// Menu
"ZSpawn clients title"
{
"en" "Force ZSpawn (zr_zspawn_force)\nSelect a Player:"
}
// Commands
"ZSpawn command force syntax"
{
"en" "Force ZSpawn on a client. Usage: zr_zspawn_force <client> ['0' = Spawn as human | '1' = Spawn as zombie]"
}
"ZSpawn command force successful"
{
"#format" "{1:s}"
"en" "Player {1} was successfully spawned."
}
"ZSpawn command force unsuccessful"
{
"#format" "{1:s}"
"en" "Player {1} couldn't be spawned."
}
// ===========================
// ZTele (module)
// ===========================
@ -906,6 +1039,33 @@
"en" "Teleported back to spawn. (Count: {1}/{2})"
}
// Menu
"ZTele clients title"
{
"en" "Force ZTele (zr_ztele_force)\nSelect a Player:"
}
// Commands
"ZTele command force syntax"
{
"en" "Force ZTele on a client. Usage: zr_ztele_force <client>"
}
"ZTele command force successful"
{
"#format" "{1:s}"
"en" "Player {1} was successfully teleported."
}
"ZTele command force unsuccessful"
{
"#format" "{1:s}"
"en" "Player {1} couldn't be teleported."
}
// ===========================
// ZHP (module)
// ===========================
@ -946,28 +1106,4 @@
"#format" "{1:s},{2:d}"
"en" "Player \"{1}\" has been slayed for camping in a restricted area (ID: {2})."
}
// ===========================
// ZAdmin Menu
// ===========================
"ZAdmin main title"
{
"en" "ZAdmin\n Select Category:"
}
"ZAdmin main class multipliers"
{
"en" "Class Multipliers"
}
"ZAdmin main weapons"
{
"en" "Weapon Management"
}
"ZAdmin main zombie"
{
"en" "Zombie Management"
}
}

View File

@ -324,12 +324,16 @@ zr_damage_block_blast "1"
// Suicide Intercept
// Intercept suicide commands attempted by zombies.
// Default: "0"
zr_damage_suicide_zombie "0"
// Intercept suicide commands attempted by mother zombies.
// Default: "1"
zr_damage_suicide_zombie "1"
zr_damage_suicide_mzombie "1"
// Intercept suicide commands attempted by humans.
// Default: "1"
zr_damage_suicide_human "1"
// Default: "0"
zr_damage_suicide_human "0"
// List of client commands to intercept as suicide attempts. [Delimiter: ", "]
// Default: "kill, spectate, jointeam"
@ -503,9 +507,13 @@ zr_ambientsounds_volume "0.8"
// Default: "1"
zr_antistick "1"
// Time between each check for stuck players. [Dependency: zr_antistick]
// Default: "0.5"
zr_antistick_interval "0.5"
// The default width of player models, don't touch if you don't know what you're doing. [Dependency: zr_antistick]
// Default: "32.0"
zr_antistick_default_width "32.0"
// File to store antistick model hull data. [Dependency: zr_antistick]
// Default: "data/antistick.dat"
zr_antistick_file_path "data/antistick.dat"
// ----------------------------------------------------------------------------

View File

@ -0,0 +1,236 @@
===============================================================================
Zombie:Reloaded Release Notes
Targets plugin version 3.0.0, (not released)
Written by Richard Helgeby
Last modified: 2009.06.27
===============================================================================
Release Notes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is a major release and it do break compatibility with older configuration!
Clean install of configuration files and reconfiguring is recommended.
A lot of work is put into this plugin. The entire plugin is completely recoded
with lots of improvements and fixes.
Our code base have expanded at least five times compared to version 2.5.1 (now
with source files above 20 000 lines). Of course, made with optimizing in mind.
This is, as far as we know, the biggest SourceMod plugin ever made!
We also hope Zombie:Reloaded will be a good learning resource for new or
existing coders to find out how certain things are done. The code is well
structured and documented with comments explaining almost what every line do.
In addition we try to make this a quality release with a well tested release
and a full user manual. Read the user manual for details on how to use or
configure individual features. Better quality and less time spent on support
gives more time for us to what we really want to do.
Richard Helgeby &
Greyscale
OVERVIEW OF MAJOR CHANGES
---------------------------
* New configuration style. Configuration files and CVARs are also validated so
errors and invalid values are caught early.
* Expanded class system with support for human classes and additional
attributes like setting transparency on player classes, effects or other
special behaviour.
* New weapon configurations that support knock back multipliers per weapon and
custom weapon groups for easy restriction.
* Market feature for pre configuring or buying equimpents and weapons from the
oposite team, also outside the buy zone if allowed in configuration.
* Improved knock back with support for scaling based on different modules. Now
it's possible to fully customize knock back behaviour.
* Volumetric features. Define areas in maps and do certain stuff to players in
those areas based on a selection of features like the anti-camp.
* Improved teleport settings. Delays and limits separated per team.
* Admin menu. Configure certain settings in-game, do generic commands like
infect, spawn and teleport.
* Cookies. This plugin is using the cookie system of SourceMod so player
preferences can be saved until next time they connect.
* New logging system that is fully customizable. Makes it possible to decide
what stuff to be logged by setting generic logging flags and applying module
filters.
CONFIGURATION CHANGES
-----------------------
Configuration settings are validated when the plugin starts. The plugin will
stop, use defaults or disable features if there are invalid values. Also a
warning is logged in the SourceMod error logs.
The validation prevents unexpected or invalid behaviour in the plugin. Dealing
with errors or warnings in error logs helps a lot troubleshooting eventual
eventual issues in the plugin caused by incorrect configurations.
It's also possible to specify the path of configuration files. This can be used
in combination with map configs, so it's possible to have different
configuration sets per map.
Support for post map configs is made. These are configs executed after the
plugin and its features are loaded. Some settings can only be changed or
overridden after loading. Those commands must be placed in post map configs.
IMPROVED CLASS SYSTEM
-----------------------
There's a lot of new features in the class system. The major change is that
it's made for multiple teams (humans and zombies). Now human classes can be
made. It has support for extended team filtering where admin-only and mother
zombie classes can be made.
In addition any attribute on one or more classes can be modified in-game, which
expands the possibilities in combination with map configs. This makes it easier
to improve the map balance by adjusting attributes like health, knock back and
running speed.
One of the new features is transparency on players. There are also immunity
modes (but it's currently a incomplete feature) that could implement slow
infection that requires zombies to stab multiple times with its knife before
the humans get infected, or stab them to death (turning into a zombie).
IMPROVED WEAPON SYSTEM
------------------------
The new weapon system lets makes it possible to do more than just restricting
weapons. Now it's possible to set knock back multipliers per weapon.
Hit groups can also be configured with knock back multipliers, or disable
damage on certain hit groups completely.
There's also a new market feature which is a custom buy menu with all available
weapons, including from the oposite team. It's also possible to buy weapons
outside the buy zone if the server admins enable that setting.
Weapon selections can be pre configured and even saved using cookies so they
can be quickly bought on next spawn or on next connect to the server.
CUSTOMIZING KNOCK BACK
------------------------
The knock back is based on three factors that are multiplied into the effective
knock back:
- Class knock back. This is the main value.
- Weapon knock back. Set knock back scale values per weapon.
- Hit group knock back. Customize or completely disable knock back scale
per hit group.
With these factors it's possible to fully customize knock back.
VOLUMETRIC FEATURES
---------------------
(Currently in development)
This is a brand new useful feature where custom defined areas in maps can do
certain stuff on players. These are the available features:
- Anti-camp: Slay or warn players that stay too long in a certain area.
Useful in unfair camping places, or to avoid glitches in maps.
- Modify class attributes.
- Restrict weapons. Takes away ammo, and gives back when leaving area.
- Modify ammo modes.
- Modify knock back set on players or knock back in hit groups.
- Teleporter.
Features that modify settings will be reverted when players leave volumes.
Example of usage is to only allow pistols in tubes, use the anti-camp to hurt
humans camping in a certain unfair place, or fine tune knock back for a area in
the map.
TELEPORTER
------------
The teleporter is improved with more settings like these:
- Deciding when players can teleport per team, before or after mother
zombie.
- Setting teleport delays per team.
- Limiting number of teleports per team.
- Abuse protection: Automatically aborting a teleport in progress if the
player moves a certain distance (configurable).
ADMIN MENU
------------
A menu for admins with basic commands to configure certain settings in-game
or do generic commands. That is:
- Infecting players.
- Spawning players.
- Modifying weapon restrictions.
- Modifying class data.
- Configuring log messages.
LOGGING SYSTEM
----------------
The logging system is based on logging flags and filtering that gives full
control of what log types and events to be logged.
There are a few generic log events and settings, like these:
- Core events: Executing config files, error messages, etc.
- Game events: Admin commands, suicide prevention, anticamp kills, etc.
- Player commands: Commands executed by non-admins: zspawn, teleport, class
change, etc.
- Debug messages, if any. Usually only developers use this one.
- Debug messages with more detail. It may cause spam, but this can be
avoided in combination with module filtering.
- Ignoring log events caused by server commands (console), like from map
configs.
- Logging to admin chat: A copy of the message is also logged to admins.
In addition a module filter can be enabled. Only log messages from modules
listed in the filter is logged, other are ignored. Except fatal errors, those
are logged anyways.
The logging system is made this way so server admins can monitor activity in
Zombie:Reloaded whithout reading spammed log messages.

164
docs/zr_dev_manual.txt Normal file
View File

@ -0,0 +1,164 @@
===============================================================================
Zombie:Reloaded
Technical Manual For Developers
Targets plugin version 3.0.0, (not released)
Written by Richard Helgeby
Manual last modified: 2009.04.25
===============================================================================
INDEX
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1.0 . . Introduction
1.1 . . . About This Manual
1.2 . . . Briefing
2.0 . . Plugin Core
2.1 . . . Startup
2.2 . . . Console Variables
2.3 . . . Translations
2.4 . . . Offsets
2.5 . . . Models
2.6 . . . Round End Handler
2.7 . . . Infection Handler
2.8 . . . Damage Handler
(account)
(say hooks)
(menu)
(commands)
(events)
3.0 . . Log Module
3.1 . . . Description
3.2 . . . Logging Flags And Filters
3.3 . . . Formatted Log Messages
3.4 . . . The Log Function
4.0 . . Class Module
5.0 . . Weapon Module
6.0 . . Hit Groups Module
7.0 . . Visual Effects Module
8.0 . . Sound Effects Module
9.0 . . Anti-Stick Module
10.0 . Knockback Module
11.0 . Respawn Module
12.0 . Spawn Protect Module
13.0 . Napalm Grenades Module
14.0 . HP Display Module
15.0 . Teleport Module
1.0 INTRODUCTION
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1.1 ABOUT THIS MANUAL
------------------------
The point of this manual is to describe how the plugin technically works so
it's easier for other developers to make modifications or join the development.
1.2 BRIEFING
---------------
The entire plugin is based on events in the game. It has a lot of features and
each feature is separated in its own module. Events are then forwarded to those
modules.
There are core modules and optional modules. Core modules is features and stuff
that cannot be disabled and is required for the plugin to function. Optional
modules like respawning, sound effects or weapon restrictions can be disabled.
Each module takes care of its own task. But modules have dependency, and events
should be forwarded in correct order. Usually only core modules depends on other
core modules. Optional modules should be independent.
2.0 PLUGIN CORE
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3.0 LOG MODULE
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3.1 DESCRIPTION
------------------
A plugin with a lots of modules and events needs a good logging system. The
user should be able to decide what's logged with logging flags.
3.2 LOGGING FLAGS AND FILTERS
--------------------------------
The log system decides what to log based on some generic log flags. Those flags
are stored in a bid field (http://en.wikipedia.org/wiki/Bit_field).
An optional module filter can be enabled to only allow log events from user
specified modules.
For performance reasons there should be a single function that tells whether it
should log or not, based on the specified module and generic event. The logging
function itself should not do such check and always log a message when called.
(incomplete)
How to store module flags? Key/values? Reading those flags MUST be cheap, so
the overall plugin performance isn't affected.
3.3 FORMATTED LOG MESSAGES
-----------------------------
Each log message should tell the module and what part of it that fired the
log event. Also it must be possible to supply additional info as parameters
(the "any:..." parameter) to be used in the log message. These are easily'
parsed with VFormat.
3.4 THE LOG FUNCTION
-----------------------
Syntax:
LogMessageFormatted(client,
const String:module[],
const String:block[],
const String:message[],
type = LOG_FORMAT_TYPE_FULL,
any:...)
Parameter: Description:
---------------------------------------------------------------------------
client Specifies what client that triggered the event. Use -1 for
core events.
module What module the event was triggered in.
block What function or block that triggered the event.
message The log message. Can be formatted.
type Optional. Specifies what log type to use. Defaults to full.
any:... Formatting parameters for the log message.
Example usage with flag check:
if (LogCheckFlag(LOG_CORE_EVENTS, LOG_MODULE_CLASSES))
{
LogMessageFormatted(-1, "Classes", "Load", "Warning: Maximum classes reached (%d). Skipping other classes.", _, ZR_CLASS_MAX + 1);
}
The module check will be replaced with something other than defined constants.

2120
docs/zr_manual.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -125,6 +125,81 @@ native ZRTools_HookOnTakeDamage(client, ZRTools_OnTakeDamage:callback);
*/
native ZRTools_UnhookOnTakeDamage(hookid);
/**
* Callback function for StartTouch.
*
* @param client The client index.
* @param entity The entity index of the entity being touched.
*/
functag public ZRTools_Action:ZRTools_StartTouch(client, entity);
/**
* Hooks StartTouch on a client.
*
* @param client The client index.
* @param callback The callback function for hook.
* @return The unique HookID.
*/
native ZRTools_HookStartTouch(client, ZRTools_StartTouch:callback);
/**
* Unhooks StartTouch on a client.
*
* @param hookid The HookID of the hook.
* @error Invalid HookID.
*/
native ZRTools_UnhookStartTouch(hookid);
/**
* Callback function for Touch.
*
* @param client The client index.
* @param entity The entity index of the entity being touched.
*/
functag public ZRTools_Action:ZRTools_Touch(client, entity);
/**
* Hooks Touch on a client.
*
* @param client The client index.
* @param callback The callback function for hook.
* @return The unique HookID.
*/
native ZRTools_HookTouch(client, ZRTools_Touch:callback);
/**
* Unhooks Touch on a client.
*
* @param hookid The HookID of the hook.
* @error Invalid HookID.
*/
native ZRTools_UnhookTouch(hookid);
/**
* Callback function for EndTouch.
*
* @param client The client index.
* @param entity The entity index of the entity being touched.
*/
functag public ZRTools_Action:ZRTools_EndTouch(client, entity);
/**
* Hooks EndTouch on a client.
*
* @param client The client index.
* @param callback The callback function for hook.
* @return The unique HookID.
*/
native ZRTools_HookEndTouch(client, ZRTools_EndTouch:callback);
/**
* Unhooks EndTouch on a client.
*
* @param hookid The HookID of the hook.
* @error Invalid HookID.
*/
native ZRTools_UnhookEndTouch(hookid);
/**
* Callback function for Weapon_CanUse.
* Called when a client attempts to pick up a weapon.

View File

@ -136,7 +136,6 @@ public OnMapStart()
RoundEndOnMapStart();
InfectOnMapStart();
SEffectsOnMapStart();
AntiStickOnMapStart();
ZSpawnOnMapStart();
}
@ -163,6 +162,7 @@ public OnConfigsExecuted()
InfectLoad();
VEffectsLoad();
SEffectsLoad();
AntiStickLoad();
ClassLoad();
VolLoad();
@ -185,6 +185,7 @@ public OnClientPutInServer(client)
InfectClientInit(client);
DamageClientInit(client);
SEffectsClientInit(client);
AntiStickClientInit(client);
SpawnProtectClientInit(client);
RespawnClientInit(client);
ZTeleClientInit(client);
@ -203,6 +204,7 @@ public OnClientDisconnect(client)
WeaponsOnClientDisconnect(client);
InfectOnClientDisconnect(client);
DamageOnClientDisconnect(client);
AntiStickOnClientDisconnect(client);
ZSpawnOnClientDisconnect(client);
VolOnPlayerDisconnect(client);
}

View File

@ -35,58 +35,58 @@
*/
/**
* @section Offsets relating to player hull dimensions.
*/
#define ANTISTICK_PLAYER_HULL_XY_OFFSET 32
#define ANTISTICK_PLAYER_HULL_Z_OFFSET 12
#define ANTISTICK_PLAYER_HULL_STACK_OFFSET 14
/**
* @endsection
*/
* Default player hull width.
*/
#define ANTISTICK_DEFAULT_HULL_WIDTH GetConVarFloat(g_hCvarsList[CVAR_ANTISTICK_DEFAULT_WIDTH])
/**
* Variable to store antistick offset value.
* Handle to keyvalue structure where data is stored.
*/
new g_iToolsCollisionGroup;
new Handle:g_kvAntiStick = INVALID_HANDLE;
/**
* Handle to keep track of AntiStickTimer.
* Stores "StartTouch" HookID's for each client.
*/
new Handle:tAntiStick = INVALID_HANDLE;
new g_iStartTouchHookID[MAXPLAYERS + 1] = {-1, ...};
/**
* Find antistick-specific offsets here.
* List of components that make up the model's rectangular boundaries.
*
* F = Front
* B = Back
* L = Left
* R = Right
* U = Upper
* D = Down
*/
AntiStickOnOffsetsFound()
enum AntiStickBoxBound
{
// If offset "m_CollisionGroup" can't be found, then stop the plugin.
g_iToolsCollisionGroup = FindSendPropInfo("CBaseEntity", "m_CollisionGroup");
if (g_iToolsCollisionGroup == -1)
{
LogEvent(false, LogType_Fatal, LOG_CORE_EVENTS, LogModule_Antistick, "Offsets", "Offset \"CBaseEntity::m_CollisionGroup\" was not found.");
}
BoxBound_FUR = 0, /** Front upper right */
BoxBound_FUL, /** etc.. */
BoxBound_FDR,
BoxBound_FDL,
BoxBound_BUR,
BoxBound_BUL,
BoxBound_BDR,
BoxBound_BDL,
}
/**
* Create commands related to antistick here.
*/
AntiStickOnCommandsCreate()
{
// Create public command to list model data.
RegConsoleCmd("zr_antistick_list_models", AntiStickListModelsCommand, "Lists all players and their model data in console. Usage: zr_antistick_list_models");
// Create admin command to set model hull width.
RegAdminCmd("zr_antistick_set_width", AntiStickSetWidthCommand, ADMFLAG_GENERIC, "Sets the width of a model's hull. (See zr_antistick_list_models) Usage: zr_antistick_set_width <model/player> <width>");
}
/**
* Map is starting.
* Creates/loads antistick data file.
*/
AntiStickOnMapStart()
AntiStickLoad()
{
// Reset timer handle.
tAntiStick = INVALID_HANDLE;
}
/**
* The round is starting.
*/
AntiStickOnRoundStart()
{
// If timer is running, kill it.
if (tAntiStick != INVALID_HANDLE)
{
KillTimer(tAntiStick);
}
// If antistick is disabled, then stop.
new bool:antistick = GetConVarBool(g_hCvarsList[CVAR_ANTISTICK]);
if (!antistick)
@ -94,118 +94,441 @@ AntiStickOnRoundStart()
return;
}
new Float:interval = GetConVarFloat(g_hCvarsList[CVAR_ANTISTICK_INTERVAL]);
tAntiStick = CreateTimer(interval, AntiStickTimer, _, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE);
// Create antistick keyvalues if it hasn't been created yet.
if (g_kvAntiStick == INVALID_HANDLE)
{
g_kvAntiStick = CreateKeyValues("antistick");
}
// Initialize keyvalues.
if (!AntiStickLoadData())
{
AntiStickSaveData();
}
}
/**
* Client is joining the server.
*
* @param client The client index.
*/
AntiStickClientInit(client)
{
// Hook "StartTouch" and "EndTouch" on client.
g_iStartTouchHookID[client] = ZRTools_HookStartTouch(client, AntiStickStartTouch);
}
/**
* Unhook StartTouch and EndTouch function on a client.
*
* @param client The client index.
*/
AntiStickOnClientDisconnect(client)
{
// Unhook "StartTouch" callback, and reset variable.
if (g_iStartTouchHookID[client] != -1)
{
ZRTools_UnhookStartTouch(g_iStartTouchHookID[client]);
g_iStartTouchHookID[client] = -1;
}
}
/**
* Load antistick data from file.
*
* @return True if loaded successfully, false if file wasn't found.
*/
stock bool:AntiStickLoadData()
{
// Get cvar's path.
decl String:filepath[PLATFORM_MAX_PATH];
GetConVarString(g_hCvarsList[CVAR_ANTISTICK_FILE_PATH], filepath, sizeof(filepath));
// Build full path in return string.
decl String:fullpath[PLATFORM_MAX_PATH];
BuildPath(Path_SM, fullpath, PLATFORM_MAX_PATH, filepath);
// Log action to game events.
LogEvent(false, LogType_Normal, LOG_GAME_EVENTS, LogModule_AntiStick, "Loaded Data", "Antistick data has been loaded from \"%s\"", fullpath);
// Retrieve keyvalue data from a file, and store in server's memory.
KvRewind(g_kvAntiStick);
return FileToKeyValues(g_kvAntiStick, fullpath);
}
/**
* Save antistick data to file.
*/
stock AntiStickSaveData()
{
// Get cvar's path.
decl String:filepath[PLATFORM_MAX_PATH];
GetConVarString(g_hCvarsList[CVAR_ANTISTICK_FILE_PATH], filepath, sizeof(filepath));
// Build full path in return string.
decl String:fullpath[PLATFORM_MAX_PATH];
BuildPath(Path_SM, fullpath, PLATFORM_MAX_PATH, filepath);
// Log action to game events.
LogEvent(false, LogType_Normal, LOG_GAME_EVENTS, LogModule_AntiStick, "Saved Data", "Antistick data has been saved to \"%s\"", fullpath);
// Dump keyvalue structure into a file from the server's memory.
KvRewind(g_kvAntiStick);
KeyValuesToFile(g_kvAntiStick, fullpath);
}
/**
* Get hull width on a client's model. (or return default)
*
* @param client The client index.
* @param model If a client index of 0 is given, this model is used.
*/
stock Float:AntiStickGetModelHullWidth(client, const String:model[] = "")
{
decl String:clientmodel[PLATFORM_MAX_PATH];
if (ZRIsClientValid(client))
{
// Get client's model.
GetClientModel(client, clientmodel, sizeof(clientmodel));
}
else
{
// Copy given model to 'clientmodel.'
strcopy(clientmodel, sizeof(clientmodel), model);
}
// Find model in antistick data.
KvRewind(g_kvAntiStick);
if (KvJumpToKey(g_kvAntiStick, clientmodel))
{
// Return value from file.
return KvGetFloat(g_kvAntiStick, "hull_width", ANTISTICK_DEFAULT_HULL_WIDTH);
}
else
{
// Return default CS:S hull width.
return ANTISTICK_DEFAULT_HULL_WIDTH;
}
}
/**
* Set hull width on a client's model.
*
* @param client The client index.
* @param model If a client index of 0 is given, this model is used.
* @param hull_width The width of the model hull.
*/
stock AntiStickSetModelHullWidth(client, const String:model[] = "", Float:hull_width)
{
decl String:clientmodel[PLATFORM_MAX_PATH];
if (ZRIsClientValid(client))
{
// Get client's model.
GetClientModel(client, clientmodel, sizeof(clientmodel));
}
else
{
// Copy given model to 'clientmodel.'
strcopy(clientmodel, sizeof(clientmodel), model);
}
// Replace "/" with "-" because a slash indicates a new level in keyvalues.
ReplaceString(clientmodel, sizeof(clientmodel), "/", "-");
// Find model in antistick data.
KvRewind(g_kvAntiStick);
// Create key if it doesn't already exist.
KvJumpToKey(g_kvAntiStick, clientmodel, true);
// Set value.
KvSetFloat(g_kvAntiStick, "hull_width", hull_width);
}
/**
* Callback function for StartTouch.
*
* @param client The client index.
* @param entity The entity index of the entity being touched.
*/
public ZRTools_Action:AntiStickStartTouch(client, entity)
{
// If client isn't in-game, then stop.
if (!IsClientInGame(client))
{
return;
}
// If client is touching themselves, then leave them alone :P
if (client == entity)
{
return;
}
// If touched entity isn't a valid client, then stop.
if (!ZRIsClientValid(entity))
{
return;
}
// If the clients aren't colliding, then stop.
if (!AntiStickIsModelBoxColliding(client, entity))
{
return;
}
// Disable collisions to unstick, and start timers to re-solidify.
if (AntiStickClientCollisionGroup(client) == ANTISTICK_COLLISIONS_ON)
{
AntiStickClientCollisionGroup(client, true, ANTISTICK_COLLISIONS_OFF);
CreateTimer(0.0, AntiStickSolidifyTimer, client, TIMER_FLAG_NO_MAPCHANGE|TIMER_REPEAT);
}
if (AntiStickClientCollisionGroup(entity) == ANTISTICK_COLLISIONS_ON)
{
AntiStickClientCollisionGroup(entity, true, ANTISTICK_COLLISIONS_OFF);
CreateTimer(0.0, AntiStickSolidifyTimer, entity, TIMER_FLAG_NO_MAPCHANGE|TIMER_REPEAT);
}
}
/**
* Callback function for EndTouch.
*
* @param client The client index.
* @param entity The entity index of the entity being touched.
*/
public Action:AntiStickSolidifyTimer(Handle:timer, any:client)
{
// If client has left, then stop the timer.
if (!IsClientInGame(client))
{
return Plugin_Stop;
}
// If the client's collisions are already on, then stop.
if (AntiStickClientCollisionGroup(client) == ANTISTICK_COLLISIONS_ON)
{
return Plugin_Stop;
}
// Loop through all client's and check if client is stuck in them.
for (new x = 1; x <= MaxClients; x++)
{
// If client isn't in-game, then stop.
if (!IsClientInGame(x))
{
continue;
}
// Don't compare the same clients.
if (client == x)
{
continue;
}
// If the client is colliding with a client, then allow timer to continue.
if (AntiStickIsModelBoxColliding(client, x))
{
return Plugin_Continue;
}
}
// Change collisions back to normal.
AntiStickClientCollisionGroup(client, true, ANTISTICK_COLLISIONS_ON);
return Plugin_Stop;
}
/**
* Build the model box by finding all vertices.
*
* @param client The client index.
* @param boundaries Array with 'AntiStickBoxBounds' for indexes to return bounds into.
* @param width The width of the model box.
*/
stock AntiStickBuildModelBox(client, Float:boundaries[AntiStickBoxBound][3], Float:width)
{
new Float:clientloc[3];
new Float:twistang[3];
new Float:cornerang[3];
new Float:sideloc[3];
new Float:finalloc[4][3];
// Get needed vector info.
GetClientAbsOrigin(client, clientloc);
// Set the pitch to 0.
twistang[1] = 90.0;
cornerang[1] = 0.0;
for (new x = 0; x < 4; x++)
{
// Jump to point on player's left side.
AntiStickJumpToPoint(clientloc, twistang, width / 2, sideloc);
// From this point, jump to the corner, which would be half the width from the middle of a side.
AntiStickJumpToPoint(sideloc, cornerang, width / 2, finalloc[x]);
// Twist 90 degrees to find next side/corner.
twistang[1] += 90.0;
cornerang[1] += 90.0;
// Fix angles.
if (twistang[1] > 180.0)
{
twistang[1] -= 360.0;
}
if (cornerang[1] > 180.0)
{
cornerang[1] -= 360.0;
}
}
// Copy all horizontal model box data to array.
boundaries[BoxBound_FUR][0] = finalloc[3][0];
boundaries[BoxBound_FUR][1] = finalloc[3][1];
boundaries[BoxBound_FUL][0] = finalloc[0][0];
boundaries[BoxBound_FUL][1] = finalloc[0][1];
boundaries[BoxBound_FDR][0] = finalloc[3][0];
boundaries[BoxBound_FDR][1] = finalloc[3][1];
boundaries[BoxBound_FDL][0] = finalloc[0][0];
boundaries[BoxBound_FDL][1] = finalloc[0][1];
boundaries[BoxBound_BUR][0] = finalloc[2][0];
boundaries[BoxBound_BUR][1] = finalloc[2][1];
boundaries[BoxBound_BUL][0] = finalloc[1][0];
boundaries[BoxBound_BUL][1] = finalloc[1][1];
boundaries[BoxBound_BDR][0] = finalloc[2][0];
boundaries[BoxBound_BDR][1] = finalloc[2][1];
boundaries[BoxBound_BDL][0] = finalloc[1][0];
boundaries[BoxBound_BDL][1] = finalloc[1][1];
// Set Z bounds.
new Float:eyeloc[3];
GetClientEyePosition(client, eyeloc);
boundaries[BoxBound_FUR][2] = eyeloc[2];
boundaries[BoxBound_FUL][2] = eyeloc[2];
boundaries[BoxBound_FDR][2] = clientloc[2] + 15.0;
boundaries[BoxBound_FDL][2] = clientloc[2] + 15.0;
boundaries[BoxBound_BUR][2] = eyeloc[2];
boundaries[BoxBound_BUL][2] = eyeloc[2];
boundaries[BoxBound_BDR][2] = clientloc[2] + 15.0;
boundaries[BoxBound_BDL][2] = clientloc[2] + 15.0;
}
/**
* Jumps from a point to another based off angle and distance.
*
* @param vec Point to jump from.
* @param ang Angle to base jump off of.
* @param distance Distance to jump
* @param result Resultant point.
*/
stock AntiStickJumpToPoint(const Float:vec[3], const Float:ang[3], Float:distance, Float:result[3])
{
new Float:viewvec[3];
// Turn client angle, into a vector.
GetAngleVectors(ang, viewvec, NULL_VECTOR, NULL_VECTOR);
// Normalize vector.
NormalizeVector(viewvec, viewvec);
// Scale to the given distance.
ScaleVector(viewvec, distance);
// Add the vectors together.
AddVectors(vec, viewvec, result);
}
/**
* Get the max/min value of a 3D box on any axis.
*
* @param axis The axis to check.
* @param boundaries The boundaries to check.
* @param min Return the min value instead.
*/
stock Float:AntiStickGetBoxMaxBoundary(axis, Float:boundaries[AntiStickBoxBound][3], bool:min = false)
{
// Create 'outlier' with initial value of first boundary.
new Float:outlier = boundaries[0][axis];
// x = Boundary index. (Start at 1 because we initialized 'outlier' with the 0 index's value)
new size = sizeof(boundaries);
for (new x = 1; x < size; x++)
{
if (!min && boundaries[x][axis] > outlier)
{
outlier = boundaries[x][axis];
}
else if (min && boundaries[x][axis] < outlier)
{
outlier = boundaries[x][axis];
}
}
// Return value.
return outlier;
}
/**
* Checks if a player is currently stuck within another player.
*
* @param client The client index.
* @return The client index of the other stuck player, -1 when
* player is not stuck.
* @param client1 The first client index.
* @param client2 The second client index.
* @return True if they are stuck together, false if not.
*/
AntiStickIsStuck(client)
stock bool:AntiStickIsModelBoxColliding(client1, client2)
{
new Float:clientloc[3];
new Float:stuckloc[3];
new Float:client1modelbox[AntiStickBoxBound][3];
new Float:client2modelbox[AntiStickBoxBound][3];
GetClientAbsOrigin(client, clientloc);
// Get model hull widths.
new Float:hull_width1 = AntiStickGetModelHullWidth(client1);
new Float:hull_width2 = AntiStickGetModelHullWidth(client2);
// x = client index.
for (new x = 1; x <= MaxClients; x++)
// Build model boxes for each client.
AntiStickBuildModelBox(client1, client1modelbox, hull_width1);
AntiStickBuildModelBox(client2, client2modelbox, hull_width2);
// Compare x values.
new Float:max1x = AntiStickGetBoxMaxBoundary(0, client1modelbox);
new Float:max2x = AntiStickGetBoxMaxBoundary(0, client2modelbox);
new Float:min1x = AntiStickGetBoxMaxBoundary(0, client1modelbox, true);
new Float:min2x = AntiStickGetBoxMaxBoundary(0, client2modelbox, true);
if (max1x < min2x || min1x > max2x)
{
// Validate player is in-game, alive, and isn't the player being checked. ('client')
if (!IsClientInGame(x) || !IsPlayerAlive(x) || x == client)
{
continue;
return false;
}
GetClientAbsOrigin(x, stuckloc);
// Compare y values.
new Float:max1y = AntiStickGetBoxMaxBoundary(1, client1modelbox);
new Float:max2y = AntiStickGetBoxMaxBoundary(1, client2modelbox);
new Float:min1y = AntiStickGetBoxMaxBoundary(1, client1modelbox, true);
new Float:min2y = AntiStickGetBoxMaxBoundary(1, client2modelbox, true);
// x-y plane distance formula: sqrt((x2-x1)^2 + (y2-y1)^2)
new Float:xydistance = SquareRoot(Pow(stuckloc[0] - clientloc[0], 2.0) + Pow(stuckloc[1] - clientloc[1], 2.0));
if (xydistance < ANTISTICK_PLAYER_HULL_XY_OFFSET)
if (max1y < min2y || min1y > max2y)
{
if (clientloc[2] <= stuckloc[2])
return false;
}
// Compare z values.
new Float:max1z = AntiStickGetBoxMaxBoundary(2, client1modelbox);
new Float:max2z = AntiStickGetBoxMaxBoundary(2, client2modelbox);
new Float:min1z = AntiStickGetBoxMaxBoundary(2, client1modelbox, true);
new Float:min2z = AntiStickGetBoxMaxBoundary(2, client2modelbox, true);
if (max1z < min2z || min1z > max2z)
{
new Float:eyeloc[3];
GetClientEyePosition(client, eyeloc);
// Get the distance between the eyes and feet and subtract the stack "view crush."
new Float:eyedistance = FloatAbs(eyeloc[2] - clientloc[2]) - ANTISTICK_PLAYER_HULL_STACK_OFFSET;
new Float:zdistance = FloatAbs(stuckloc[2] - clientloc[2]);
if (zdistance <= eyedistance + ANTISTICK_PLAYER_HULL_Z_OFFSET)
{
return x;
}
}
}
return false;
}
return -1;
}
/**
* Timer callback, automatically unsticks players that are stuck together.
*/
public Action:AntiStickTimer(Handle:timer)
{
// x = client index
for (new x = 1; x <= MaxClients; x++)
{
// Validate player is in-game and alive.
if (!IsClientInGame(x) || !IsPlayerAlive(x))
{
continue;
}
// Stop if the player isn't stuck.
new stuckindex = AntiStickIsStuck(x);
if (stuckindex == -1)
{
continue;
}
if (AntiStickClientCollisionGroup(x, false) == ANTISTICK_COLLISIONS_ON)
{
AntiStickClientCollisionGroup(x, true, ANTISTICK_COLLISIONS_OFF);
CreateTimer(0.5, AntiStickSolidify, x, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE);
}
if (AntiStickClientCollisionGroup(stuckindex, false) == ANTISTICK_COLLISIONS_ON)
{
AntiStickClientCollisionGroup(stuckindex, true, ANTISTICK_COLLISIONS_OFF);
CreateTimer(0.5, AntiStickSolidify, stuckindex, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE);
}
}
}
/**
* Repeated timer function.
* Re-solidifies a player being unstuck.
*
* @param timer The timer handle.
* @param client The client index.
*/
public Action:AntiStickSolidify(Handle:timer, any:client)
{
// Validate player is in-game, alive, and is being unstuck.
if (!IsClientInGame(client) || !IsPlayerAlive(client) || AntiStickClientCollisionGroup(client, false) == ANTISTICK_COLLISIONS_ON)
{
return Plugin_Stop;
}
// Stop if the player is still stuck.
if (AntiStickIsStuck(client) > -1)
{
return Plugin_Continue;
}
AntiStickClientCollisionGroup(client, true, ANTISTICK_COLLISIONS_ON);
return Plugin_Stop;
// They are intersecting.
return true;
}
/**
@ -214,14 +537,110 @@ public Action:AntiStickSolidify(Handle:timer, any:client)
* @param collisiongroup Collision group flag.
* @return The collision group on the client, -1 if applying collision group.
*/
AntiStickClientCollisionGroup(client, bool:apply = true, collisiongroup = 0)
AntiStickClientCollisionGroup(client, bool:apply = false, collisiongroup = 0)
{
if (apply)
{
SetEntData(client, g_iToolsCollisionGroup, collisiongroup, 1, true);
SetEntProp(client, Prop_Data, "m_CollisionGroup", collisiongroup);
return -1;
}
return GetEntData(client, g_iToolsCollisionGroup, 1);
return GetEntProp(client, Prop_Data, "m_CollisionGroup");
}
/**
* Command callback (zr_antistick_list_models)
* Lists all player's models and model hull data.
*
* @param client The client index.
* @param argc Argument count.
*/
public Action:AntiStickListModelsCommand(client, argc)
{
// Tell client we are listing model data.
TranslationPrintToConsole(client, "AntiStick command list models list");
decl String:clientname[MAX_NAME_LENGTH];
decl String:modelname[PLATFORM_MAX_PATH];
new Float:hull_width;
// x = Client index.
for (new x = 1; x <= MaxClients; x++)
{
// If client isn't in-game, then stop.
if (!IsClientInGame(x))
{
continue;
}
// Get all needed data.
GetClientName(x, clientname, sizeof(clientname));
GetClientModel(x, modelname, sizeof(modelname));
hull_width = AntiStickGetModelHullWidth(x);
TranslationPrintToConsole(client, "AntiStick command list models name", clientname, modelname, hull_width);
}
return Plugin_Handled;
}
/**
* Command callback (zr_antistick_set_width)
* Set the hull width on any model.
*
* @param client The client index.
* @param argc Argument count.
*/
public Action:AntiStickSetWidthCommand(client, argc)
{
// If not enough arguments given, then stop.
if (argc < 2)
{
TranslationReplyToCommand(client, "AntiStick command set width syntax");
return Plugin_Handled;
}
// Get target model.
decl String:model[PLATFORM_MAX_PATH];
GetCmdArg(1, model, sizeof(model));
// If model doesn't exist, then stop.
if (!FileExists(model))
{
new target = FindTarget(client, model);
if (target <= 0)
{
TranslationReplyToCommand(client, "AntiStick command set width invalid model", model);
return Plugin_Handled;
}
else
{
// Get the target's model.
GetClientModel(target, model, sizeof(model));
}
}
// Get the given hull width..
decl String:strHullwidth[PLATFORM_MAX_PATH];
GetCmdArg(2, strHullwidth, sizeof(strHullwidth));
new Float:hull_width = StringToFloat(strHullwidth);
if (hull_width <= 0.0)
{
TranslationReplyToCommand(client, "AntiStick command set width invalid width", hull_width);
return Plugin_Handled;
}
// Set hull width.
AntiStickSetModelHullWidth(0, model, hull_width);
// Tell client it was successful.
TranslationReplyToCommand(client, "AntiStick command set width successful", model, hull_width);
// Save data.
AntiStickSaveData();
return Plugin_Handled;
}

View File

@ -39,6 +39,7 @@ CommandsInit()
InfectOnCommandsCreate();
MenuOnCommandsCreate();
ZAdminOnCommandsCreate();
AntiStickOnCommandsCreate();
ZCookiesOnCommandsCreate();
ZSpawnOnCommandsCreate();
ZTeleOnCommandsCreate();
@ -51,200 +52,3 @@ CommandsInit()
ClassOnCommandsHook();
DamageOnCommandsHook();
}
/*CreateCommands()
{
RegAdminCmd("zr_infect", Command_Infect, ADMFLAG_GENERIC, "Infects the specified player");
RegAdminCmd("zr_spawn", Command_Respawn, ADMFLAG_GENERIC, "Respawns the specified player following auto-respawning rules");
//RegAdminCmd("zr_set_class_knockback", Command_SetClassKnockback, ADMFLAG_GENERIC, "Sets the knockback to the specified class. Usage: zr_set_class_knockback <class name> <value>");
//RegAdminCmd("zr_get_class_knockback", Command_GetClassKnockback, ADMFLAG_GENERIC, "Gets the knockback to the specified class. Usage: zr_get_class_knockback <class name>");
RegAdminCmd("zr_admin", Command_AdminMenu, ADMFLAG_GENERIC, "Displays the admin menu for Zombie: Reloaded.");
//RegAdminCmd("zr_knockback_m", Command_KnockbackMMenu, ADMFLAG_GENERIC, "Displays the knockback multiplier menu.");
RegAdminCmd("zr_anticamp_create_volume", Command_AnticampCreateVolume, ADMFLAG_GENERIC, "Creates a rectangular hurt volume between two points. Usage: ht_create_volume <damage> <interval> <x1> <y1> <z1> <x2> <y2> <z2>");
RegAdminCmd("zr_anticamp_remove_volume", Command_AnticampRemoveVolume, ADMFLAG_GENERIC, "Removes a volume. Use zr_anticamp_list to list volumes. Usage: zr_anticamp_remove_volume <volume index>");
RegAdminCmd("zr_anticamp_list", Command_AnticampList, ADMFLAG_GENERIC, "List current volumes.");
RegConsoleCmd("zr_log_flags", Command_LogFlags, "List available logging flags.");
}*/
/*public Action:Command_Infect(client, argc)
{
new bool:enabled = GetConVarBool(g_hCvarsList[CVAR_ENABLE]);
if (argc < 1 || !enabled)
{
return Plugin_Handled;
}
decl String:arg1[32];
GetCmdArg(1, arg1, sizeof(arg1));
decl String:target_name_list[MAX_TARGET_LENGTH];
new targets[MAXPLAYERS];
new bool:tn_is_ml;
new tcount = ProcessTargetString(arg1, client, targets, MAXPLAYERS, COMMAND_FILTER_ALIVE, target_name_list, sizeof(target_name_list), tn_is_ml);
if (tcount <= 0)
{
ReplyToTargetError(client, tcount);
return Plugin_Handled;
}
decl String:target_name[64];
decl String:client_name[64];
if (client > 0)
{
GetClientName(client, client_name, sizeof(client_name));
}
else
{
client_name = "Console\0";
}
for (new x = 0; x < tcount; x++)
{
InfectHumanToZombie(targets[x]);
if (LogCheckFlag(LOG_GAME_EVENTS, LOG_MODULE_COMMANDS))
{
GetClientName(targets[x], target_name, sizeof(target_name));
LogMessageFormatted(client, "admin commands", "infect", "\"%s\" infected \"%s\".", true, client_name, target_name);
}
}
return Plugin_Handled;
}
public Action:Command_Respawn(client, argc)
{
new bool:enabled = GetConVarBool(g_hCvarsList[CVAR_ENABLE]);
if (argc < 1 || !enabled)
{
return Plugin_Handled;
}
decl String:arg1[32];
GetCmdArg(1, arg1, sizeof(arg1));
decl String:target_name_list[MAX_TARGET_LENGTH];
new targets[MAXPLAYERS];
new bool:tn_is_ml;
new tcount = ProcessTargetString(arg1, client, targets, MAXPLAYERS, COMMAND_FILTER_DEAD, target_name_list, sizeof(target_name_list), tn_is_ml);
if (tcount <= 0)
{
ReplyToTargetError(client, tcount);
return Plugin_Handled;
}
decl String:client_name[64];
decl String:target_name[64];
if (client > 0)
{
GetClientName(client, client_name, sizeof(client_name));
}
else
{
client_name = "Console\0";
}
new team;
for (new x = 0; x < tcount; x++)
{
team = GetClientTeam(targets[x]);
if (team == CS_TEAM_T || team == CS_TEAM_CT)
{
if (LogCheckFlag(LOG_GAME_EVENTS, LOG_MODULE_COMMANDS))
{
GetClientName(targets[x], target_name, sizeof(target_name));
LogMessageFormatted(targets[x], "admin commands", "spawn", "\"%s\" spawned player \"%s\".", true, client_name, target_name);
}
RespawnSpawnClient(targets[x]);
}
}
return Plugin_Handled;
}*/
/*public Action:Command_AdminMenu(client, argc)
{
if (ZRIsClientValid(client))
{
ZRAdminMenu(client);
}
else
{
// BAD!
// ReplyToCommand(client, "This menu cannot be used from the console.");
}
return Plugin_Handled;
}
public Action:Command_KnockbackMMenu(client, argc)
{
if (ZRIsClientValid(client))
{
// Disabled, under construction.
// ZRKnockbackMMenu(client);
}
else
{
// Tsk tsk no translation? :P
//ReplyToCommand(client, "This menu cannot be used from the console.");
}
return Plugin_Handled;
}
public Action:Command_TeleMenu(client, argc)
{
if (ZRIsClientValid(client))
{
ZRZTeleMenu(client);
}
else
{
// BAD!
ReplyToCommand(client, "This menu cannot be used from the console.");
}
return Plugin_Handled;
}
public Action:Command_LogFlags(client, argc)
{
decl String:message[2048];
message[0] = 0;
StrCat(message, sizeof(message), "LOG_CORE_EVENTS (1) - Log core events like executing files, error messages, etc.\n");
StrCat(message, sizeof(message), "LOG_GAME_EVENTS (2) - Log game events like admin commands, suicide prevention and anticamp kills.\n");
StrCat(message, sizeof(message), "LOG_PLAYER_COMMANDS (4) - Log commands made by the player.\n");
StrCat(message, sizeof(message), "LOG_DEBUG (8) - Enable debug messages (if they exist).\n");
StrCat(message, sizeof(message), "LOG_DEBUG_DETAIL (16) - Detailed debug messages. May cause spam.\n");
StrCat(message, sizeof(message), "LOG_DEBUG_MAX_DETAIL (32) - Low level detailed debug messages. Causes spam! Only enable right before and after testing.\n");
StrCat(message, sizeof(message), "LOG_LOG_TO_ADMINS (64) - Display log messages to admin chat.\n");
StrCat(message, sizeof(message), "LOG_LOG_TO_CLIENT (128) - Display log messages to the client that executed the event/command.\n");
StrCat(message, sizeof(message), "LOG_IGNORE_CONSOLE (256) - Don't log messages from client 0 (console).\n");
StrCat(message, sizeof(message), "LOG_MODULES_ENABLED (512) - Enable detailed log control for developers. Module logs overrides previous flags.\n");
StrCat(message, sizeof(message), "LOG_MODULE_ZOMBIE (1024) - zombie.inc");
ReplyToCommand(client, message);
message[0] = 0;
StrCat(message, sizeof(message), "LOG_MODULE_AMBIENTSOUNDS (2048) - ambientsounds.inc\n");
StrCat(message, sizeof(message), "LOG_MODULE_OVERLAYS (4096) - overlays.inc\n");
StrCat(message, sizeof(message), "LOG_MODULE_SAYTRIGGERS (8192) - sayhooks.inc\n");
StrCat(message, sizeof(message), "LOG_MODULE_TELEPORT (16384) - teleport.inc\n");
StrCat(message, sizeof(message), "LOG_MODULE_CLASSES (32768) - classes.inc\n");
StrCat(message, sizeof(message), "LOG_MODULE_WEAPONRESTICT (65536) - weaponrestrict.inc\n");
StrCat(message, sizeof(message), "LOG_MODULE_COMMANDS (131072) - commands.inc\n");
StrCat(message, sizeof(message), "LOG_MODULE_ANTICAMP (262144) - anticamp.inc\n");
StrCat(message, sizeof(message), "LOG_MODULE_DAMAGE (524288) - damage.inc\n");
StrCat(message, sizeof(message), "LOG_MODULE_OFFSETS (524288) - offsets.inc");
ReplyToCommand(client, message);
return Plugin_Handled;
}*/

View File

@ -254,7 +254,7 @@ ConfigOnModulesLoaded()
ServerCommand("exec %s", mapconfig);
// Log action.
LogEvent(false, LogType_Normal, LOG_CORE_EVENTS, LogModule_Config, "Executed post map config file: %s", path);
LogEvent(false, LogType_Normal, LOG_CORE_EVENTS, LogModule_Config, "Post Map Configs", "Executed post map config file: %s", path);
}
/**
@ -422,9 +422,10 @@ stock ConfigGetConfigAlias(ConfigFile:config, String:alias[], maxlen)
*
* @param config The config file to load.
* @param arrayConfig Handle of the main array containing file data.
* @param blocksize The max length of the contained strings.
* @return True if file was loaded successfuly, false otherwise.
*/
stock bool:ConfigLoadConfig(ConfigFile:config, &Handle:arrayConfig)
stock bool:ConfigLoadConfig(ConfigFile:config, &Handle:arrayConfig, blocksize = CONFIG_MAX_LENGTH)
{
// Get config's structure.
new ConfigStructure:structure = ConfigGetConfigStructure(config);
@ -441,7 +442,7 @@ stock bool:ConfigLoadConfig(ConfigFile:config, &Handle:arrayConfig)
if (arrayConfig == INVALID_HANDLE)
{
// Create array in handle.
arrayConfig = CreateArray(CONFIG_MAX_LENGTH);
arrayConfig = CreateArray(blocksize);
}
switch(structure)
@ -463,17 +464,8 @@ stock bool:ConfigLoadConfig(ConfigFile:config, &Handle:arrayConfig)
decl String:line[PLATFORM_MAX_PATH];
while(!IsEndOfFile(hFile))
while(ReadFileLine(hFile, line, sizeof(line)))
{
// Get current line text.
ReadFileLine(hFile, line, sizeof(line));
// If line contains a ";", then stop.
if (StrContains(line, ";") > -1)
{
continue;
}
// Cut out comments at the end of a line.
if (StrContains(line, "//") > -1)
{
@ -518,7 +510,7 @@ stock bool:ConfigLoadConfig(ConfigFile:config, &Handle:arrayConfig)
do
{
// Create new array to store information for config entry.
new Handle:arrayConfigEntry = CreateArray(CONFIG_MAX_LENGTH);
new Handle:arrayConfigEntry = CreateArray(blocksize);
// Push the key name into the config entry's array.
decl String:keyname[CONFIG_MAX_LENGTH];
@ -884,15 +876,15 @@ public Action:ConfigReloadAllCommand(client, argc)
}
/**
* Converts string of "yes" or "no" to a boolean value.
* Converts string of "yes/on" or "no/off" to a boolean value.
*
* @param option "yes" or "no" string to be converted.
* @param option "yes/on" or "no/off" string to be converted.
* @return True if string is "yes", false otherwise.
*/
stock bool:ConfigSettingToBool(const String:option[])
{
// If option is equal to "yes," then return true.
if (StrEqual(option, "yes", false))
if (StrEqual(option, "yes", false) || StrEqual(option, "on", false))
{
return true;
}
@ -904,8 +896,8 @@ stock bool:ConfigSettingToBool(const String:option[])
/**
* Converts boolean value to "yes" or "no".
*
* @param bOption True/false value to be converted to "yes"/"no", respectively.
* @param option Destination string buffer to store "yes" or "no" in.
* @param bOption True/false value to be converted to "yes/on"/"no/off", respectively.
* @param option Destination string buffer to store "yes/on" or "no/off" in.
* @param maxlen Length of destination string buffer (wont't be more than 4).
* @param yesno When true, returns "yes/no", false returns "on/off."
*/

View File

@ -73,6 +73,7 @@ enum CvarsList
Handle:CVAR_DAMAGE_BLOCK_FF,
Handle:CVAR_DAMAGE_BLOCK_BLAST,
Handle:CVAR_DAMAGE_SUICIDE_ZOMBIE,
Handle:CVAR_DAMAGE_SUICIDE_MZOMBIE,
Handle:CVAR_DAMAGE_SUICIDE_HUMAN,
Handle:CVAR_DAMAGE_SUICIDE_CMDS,
Handle:CVAR_SAYHOOKS_QUIET,
@ -125,7 +126,8 @@ enum CvarsList
Handle:CVAR_AMBIENTSOUNDS_LENGTH,
Handle:CVAR_AMBIENTSOUNDS_VOLUME,
Handle:CVAR_ANTISTICK,
Handle:CVAR_ANTISTICK_INTERVAL,
Handle:CVAR_ANTISTICK_DEFAULT_WIDTH,
Handle:CVAR_ANTISTICK_FILE_PATH,
Handle:CVAR_SPAWNPROTECT,
Handle:CVAR_SPAWNPROTECT_TIME,
Handle:CVAR_SPAWNPROTECT_SPEED,
@ -234,7 +236,7 @@ CvarsCreate()
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_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", "random", "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_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]");
@ -307,8 +309,9 @@ CvarsCreate()
g_hCvarsList[CVAR_DAMAGE_BLOCK_BLAST] = CreateConVar("zr_damage_block_blast", "1", "Block blast damage inflicted on self or teammates.");
// Suicide Intercept
g_hCvarsList[CVAR_DAMAGE_SUICIDE_ZOMBIE] = CreateConVar("zr_damage_suicide_zombie", "1", "Intercept suicide commands attempted by zombies.");
g_hCvarsList[CVAR_DAMAGE_SUICIDE_HUMAN] = CreateConVar("zr_damage_suicide_human", "1", "Intercept suicide commands attempted by humans.");
g_hCvarsList[CVAR_DAMAGE_SUICIDE_ZOMBIE] = CreateConVar("zr_damage_suicide_zombie", "0", "Intercept suicide commands attempted by zombies.");
g_hCvarsList[CVAR_DAMAGE_SUICIDE_MZOMBIE] = CreateConVar("zr_damage_suicide_mzombie", "1", "Intercept suicide commands attempted by mother zombies.");
g_hCvarsList[CVAR_DAMAGE_SUICIDE_HUMAN] = CreateConVar("zr_damage_suicide_human", "0", "Intercept suicide commands attempted by humans.");
g_hCvarsList[CVAR_DAMAGE_SUICIDE_CMDS] = CreateConVar("zr_damage_suicide_cmds", "kill, spectate, jointeam", "List of client commands to intercept as suicide attempts. [Delimiter: \", \"]");
@ -386,7 +389,8 @@ CvarsCreate()
// Anti-Stick (module)
// ===========================
g_hCvarsList[CVAR_ANTISTICK] = CreateConVar("zr_antistick", "1", "Automatically unstick players when stuck within each others' collision hull.");
g_hCvarsList[CVAR_ANTISTICK_INTERVAL] = CreateConVar("zr_antistick_interval", "0.5", "Time between each check for stuck players. [Dependency: zr_antistick]");
g_hCvarsList[CVAR_ANTISTICK_DEFAULT_WIDTH] = CreateConVar("zr_antistick_default_width", "32.0", "The default width of player models, don't touch if you don't know what you're doing. [Dependency: zr_antistick]");
g_hCvarsList[CVAR_ANTISTICK_FILE_PATH] = CreateConVar("zr_antistick_file_path", "data/antistick.dat", "File to store antistick model hull data. [Dependency: zr_antistick]");
// ===========================
// Spawn Protect (module)

View File

@ -48,6 +48,11 @@ new g_iDamageTraceAttackHookID[MAXPLAYERS + 1] = {-1, ...};
*/
new g_iDamageOnTakeDamageHookID[MAXPLAYERS + 1] = {-1, ...};
/**
* Array to keep track of normal/mother zombies.
*/
new bool:g_bDamageMotherZombie[MAXPLAYERS + 1];
/**
* Hook commands related to damage here.
*/
@ -106,6 +111,18 @@ DamageOnClientDisconnect(client)
}
}
/**
* A client was infected.
*
* @param client The client index.
* @param motherinfect True if the zombie is mother, false if not.
*/
DamageOnClientInfected(client, bool:motherinfect)
{
// Update if client is a mother zombie or not.
g_bDamageMotherZombie[client] = motherinfect;
}
/**
* Hook: TraceAttack
* Called right before the bullet enters a client.
@ -153,6 +170,13 @@ public ZRTools_Action:DamageTraceAttack(client, inflictor, attacker, Float:damag
// Here we know that attacker and client are different teams.
// If client is a human, then allow damage.
if (InfectIsClientHuman(client))
{
// Allow damage.
return ZRTools_Continue;
}
// If damage hitgroups cvar is disabled, then allow damage.
new bool:damagehitgroups = GetConVarBool(g_hCvarsList[CVAR_DAMAGE_HITGROUPS]);
if (!damagehitgroups)
@ -317,18 +341,29 @@ public Action:DamageSuicideIntercept(client, argc)
return Plugin_Continue;
}
// Get zombie flag on client.
new bool:clientzombie = InfectIsClientInfected(client);
// Get cvar values for suicide interception.
new bool:suicidezombie = GetConVarBool(g_hCvarsList[CVAR_DAMAGE_SUICIDE_ZOMBIE]);
new bool:suicidezombiemother = GetConVarBool(g_hCvarsList[CVAR_DAMAGE_SUICIDE_MZOMBIE]);
new bool:suicidehuman = GetConVarBool(g_hCvarsList[CVAR_DAMAGE_SUICIDE_HUMAN]);
// Determine whether to block suicide based off of the client's zombie flag and cvar values.
new bool:blocksuicide = clientzombie ? suicidezombie : suicidehuman;
// Check if client is a zombie.
if (InfectIsClientInfected(client))
{
// If client is a normal zombie, and suicide intercept is disabled for zombies, then let command go.
if (!g_bDamageMotherZombie[client] && !suicidezombie)
{
return Plugin_Continue;
}
// If cvar for this team is disabled, then stop.
if (!blocksuicide)
// If client is a mother zombie, and suicide intercept is disabled for mother zombies, then let command go.
if (g_bDamageMotherZombie[client] && !suicidezombiemother)
{
return Plugin_Continue;
}
}
// If client is a human, and suicide intercept is disabled for humans, then let command go.
if (InfectIsClientHuman(client) && !suicidehuman)
{
return Plugin_Continue;
}

View File

@ -53,7 +53,7 @@ DownloadsLoad()
ConfigSetConfigPath(File_Downloads, pathdownloads);
// Load config from file and create array structure.
new bool:success = ConfigLoadConfig(File_Downloads, arrayDownloads);
new bool:success = ConfigLoadConfig(File_Downloads, arrayDownloads, PLATFORM_MAX_PATH);
// Unexpected error, stop plugin.
if (!success)

View File

@ -87,7 +87,6 @@ public Action:EventRoundStart(Handle:event, const String:name[], bool:dontBroadc
RoundEndOnRoundStart();
InfectOnRoundStart();
SEffectsOnRoundStart();
AntiStickOnRoundStart();
ZSpawnOnRoundStart();
VolOnRoundStart();
@ -138,6 +137,7 @@ public Action:EventRoundEnd(Handle:event, const String:name[], bool:dontBroadcas
new reason = GetEventInt(event, "reason");
// Forward event to modules.
WeaponsOnRoundEnd();
RoundEndOnRoundEnd(reason);
InfectOnRoundEnd();
SEffectsOnRoundEnd();
@ -184,23 +184,13 @@ public Action:EventPlayerSpawn(Handle:event, const String:name[], bool:dontBroad
AccountOnClientSpawn(index); // Some modules depend on this to finish first.
ClassOnClientSpawn(index);
WeaponsOnClientSpawn(index);
RoundStartOnClientSpawn(index);
SEffectsOnClientSpawn(index);
SpawnProtectOnClientSpawn(index);
RespawnOnClientSpawn(index);
ZTeleOnClientSpawn(index);
ZHPOnClientSpawn(index);
VolOnPlayerSpawn(index);
// Get public chat trigger prefix.
decl String:publictrigger[4];
SayHooksGetPublicChatTrigger(publictrigger, sizeof(publictrigger));
// If public chat trigger is disabled, then don't print message.
if (!StrEqual(publictrigger, "N/A", false))
{
TranslationPrintToChat(index, "General zmenu reminder", publictrigger, SAYHOOKS_KEYWORD_ZMENU);
}
// Fire post player_spawn event.
CreateTimer(0.0, EventPlayerSpawnPost, index);
}

View File

@ -349,6 +349,154 @@ stock Float:HitgroupsGetKnockback(index)
return Float:GetArrayCell(arrayHitgroup, _:HITGROUPS_DATA_KNOCKBACK);
}
/**
* Sends list of hitgroups to client.
*
* @param client The client index.
* @return True if sent successfully, false if not.
*/
bool:HitgroupsMenuHitgroups(client)
{
// If hitgroups is disabled, then stop.
new bool:hitgroups = GetConVarBool(g_hCvarsList[CVAR_HITGROUPS]);
if (!hitgroups)
{
return false;
}
// Create menu handle.
new Handle:menu_hitgroups = CreateMenu(HitgroupsMenuHitgroupsHandle);
// Set client as translation target.
SetGlobalTransTarget(client);
SetMenuTitle(menu_hitgroups, "%t\n ", "Hitgroups menu hitgroups title");
decl String:enableall[32];
decl String:headshotsonly[32];
// Format menu options.
Format(enableall, sizeof(enableall), "%t", "Hitgroups menu hitgroups enable all");
Format(headshotsonly, sizeof(headshotsonly), "%t\n ", "Hitgroups menu hitgroups headshots only");
// Add options to menu.
AddMenuItem(menu_hitgroups, "Enable All", enableall);
AddMenuItem(menu_hitgroups, "Headshots Only", headshotsonly);
decl String:hitgroupoption[MAX_NAME_LENGTH];
decl String:hitgroupcandamage[MAX_NAME_LENGTH];
decl String:hitgroupid[4];
// x = Hitgroup index.
new size = GetArraySize(arrayHitgroups);
for (new x = 0; x < size; x++)
{
// Get hitgroup name.
HitgroupsGetName(x, hitgroupoption, sizeof(hitgroupoption));
IntToString(x, hitgroupid, sizeof(hitgroupid));
// Convert bool to "On/Off"
ConfigBoolToSetting(HitgroupsCanDamage(x), hitgroupcandamage, sizeof(hitgroupcandamage), false);
// Format "on/off" to the option.
Format(hitgroupoption, sizeof(hitgroupoption), "%s: %s", hitgroupoption, hitgroupcandamage);
// Add option to menu.
AddMenuItem(menu_hitgroups, hitgroupid, hitgroupoption);
}
// Create a "Back" button to the main admin menu.
SetMenuExitBackButton(menu_hitgroups, true);
// Send menu.
DisplayMenu(menu_hitgroups, client, MENU_TIME_FOREVER);
return true;
}
/**
* Called when client selects option in the infect clients menu, and handles it.
* @param menu_weapons_main Handle of the menu being used.
* @param action The action done on the menu (see menus.inc, enum MenuAction).
* @param client The client index.
* @param slot The slot index selected (starting from 0).
*/
public HitgroupsMenuHitgroupsHandle(Handle:menu_hitgroups, MenuAction:action, client, slot)
{
// Client selected an option.
if (action == MenuAction_Select)
{
switch(slot)
{
// Enable all hitgroups.
case 0:
{
// x = Hitgroup index.
new size = GetArraySize(arrayHitgroups);
for (new x = 0; x < size; x++)
{
// Enable hitgroup.
HitgroupsSetDamage(x, true);
}
// Tell the server that all hitgroups have been enabled.
TranslationPrintToChatAll(true, false, "Hitgroups command enable all successful");
}
// Headshots only.
case 1:
{
// x = Hitgroup index.
new size = GetArraySize(arrayHitgroups);
for (new x = 0; x < size; x++)
{
if (HitgroupsGetIndex(x) == HITGROUP_HEAD)
{
// Enable hitgroup.
HitgroupsSetDamage(x, true);
continue;
}
// Disable hitgroup.
HitgroupsSetDamage(x, false);
}
// Tell the server that headshots only been enabled.
TranslationPrintToChatAll(true, false, "Hitgroups command headshots only successful");
}
default:
{
// Get selected hitgroup index.
decl String:hitgroupid[4];
GetMenuItem(menu_hitgroups, slot, hitgroupid, sizeof(hitgroupid));
new hitgroup = StringToInt(hitgroupid);
// Toggle value.
new bool:hitgroupcandamage = HitgroupsCanDamage(hitgroup);
HitgroupsSetDamage(hitgroup, !hitgroupcandamage);
}
}
// Re-send menu.
HitgroupsMenuHitgroups(client);
}
// Client closed the menu.
if (action == MenuAction_Cancel)
{
// Client hit "Back" button.
if (slot == MenuCancel_ExitBack)
{
// Re-open admin menu.
ZAdminMenu(client);
}
}
// Client hit "Exit" button.
else if (action == MenuAction_End)
{
CloseHandle(menu_hitgroups);
}
}
/**
* Command callback (zr_hitgroup)
* Toggles or sets if a zombie's hitgroup can be damaged.
@ -358,6 +506,14 @@ stock Float:HitgroupsGetKnockback(index)
*/
public Action:HitgroupsCommand(client, argc)
{
// If module is disabled, then stop.
new bool:hitgroups = GetConVarBool(g_hCvarsList[CVAR_HITGROUPS]);
if (!hitgroups)
{
TranslationReplyToCommand(client, "Feature is disabled");
return Plugin_Handled;
}
// If not enough arguments given, then stop.
if (argc < 1)
{
@ -436,17 +592,20 @@ public Action:HitgroupsCommand(client, argc)
*/
public Action:HitgroupsEnableAllCommand(client, argc)
{
new hitgroup;
// If module is disabled, then stop.
new bool:hitgroups = GetConVarBool(g_hCvarsList[CVAR_HITGROUPS]);
if (!hitgroups)
{
TranslationReplyToCommand(client, "Feature is disabled");
return Plugin_Handled;
}
// x = Hitgroup index.
new size = GetArraySize(arrayHitgroups);
for (new x = 0; x < size; x++)
{
// Get CS:S hitgroup index.
hitgroup = HitgroupsGetIndex(x);
// Set that hitgroup index to true for damage.
HitgroupsSetDamage(hitgroup, true);
HitgroupsSetDamage(x, true);
}
// Tell the server that all hitgroups have been enabled.
@ -467,27 +626,30 @@ public Action:HitgroupsEnableAllCommand(client, argc)
*/
public Action:HitgroupsHeadshotsOnlyCommand(client, argc)
{
new hitgroup;
// If module is disabled, then stop.
new bool:hitgroups = GetConVarBool(g_hCvarsList[CVAR_HITGROUPS]);
if (!hitgroups)
{
TranslationReplyToCommand(client, "Feature is disabled");
return Plugin_Handled;
}
// x = Hitgroup index.
new size = GetArraySize(arrayHitgroups);
for (new x = 0; x < size; x++)
{
// Get CS:S hitgroup index.
hitgroup = HitgroupsGetIndex(x);
// If this hitgroup is the head, then enable it and stop.
if (hitgroup == HITGROUP_HEAD)
if (HitgroupsGetIndex(x) == HITGROUP_HEAD)
{
HitgroupsSetDamage(hitgroup, true);
HitgroupsSetDamage(x, true);
continue;
}
// Set that hitgroup index to true for damage.
HitgroupsSetDamage(hitgroup, false);
HitgroupsSetDamage(x, false);
}
// Tell the server that all hitgroups have been enabled.
// Tell the server that headshots only been enabled.
TranslationPrintToChatAll(true, false, "Hitgroups command headshots only successful");
// Log action to game events.

View File

@ -556,47 +556,6 @@ InfectHumanToZombie(client, attacker = -1, bool:motherinfect = false, bool:respa
// Mark player as zombie.
bZombie[client] = true;
// Get a list of all client's weapon indexes.
new weapons[WeaponsSlot];
WeaponsGetClientWeapons(client, weapons);
// Check if weapons drop is enabled.
new bool:weaponsdrop = GetConVarBool(g_hCvarsList[CVAR_INFECT_WEAPONS_DROP]);
// Loop through array slots and force drop.
// x = weapon slot.
for (new x = 0; x < WEAPONS_SLOTS_MAX; x++)
{
// If weapon is invalid, then stop.
if (weapons[x] == -1)
{
continue;
}
if (weaponsdrop)
{
// If this is the knife slot, then stop.
if (WeaponsSlot:x == Slot_Melee)
{
continue;
}
// Force client to drop weapon.
WeaponsForceClientDrop(client, weapons[x]);
}
else
{
// Strip weapon.
RemovePlayerItem(client, weapons[x]);
}
}
// If client has no knife, give them one.
if (GetPlayerWeaponSlot(client, _:Slot_Melee) == -1)
{
GivePlayerItem(client, "weapon_knife");
}
// Check if consecutive infection protection is enabled.
new bool:infectconsecutiveblock = GetConVarBool(g_hCvarsList[CVAR_INFECT_CONSECUTIVE_BLOCK]);
@ -669,9 +628,52 @@ InfectHumanToZombie(client, attacker = -1, bool:motherinfect = false, bool:respa
// Forward event to modules.
ClassOnClientInfected(client, motherinfect);
RoundEndOnClientInfected();
DamageOnClientInfected(client, motherinfect);
SEffectsOnClientInfected(client);
ZTeleOnClientInfected(client);
ZHPOnClientInfected(client);
// Get a list of all client's weapon indexes.
new weapons[WeaponsSlot];
WeaponsGetClientWeapons(client, weapons);
// Check if weapons drop is enabled.
new bool:weaponsdrop = GetConVarBool(g_hCvarsList[CVAR_INFECT_WEAPONS_DROP]);
// This must be after the event forwarding because it fixes a problem caused by changing models in ClassOnClientInfected.
// Loop through array slots and force drop.
// x = weapon slot.
for (new x = 0; x < WEAPONS_SLOTS_MAX; x++)
{
// If weapon is invalid, then stop.
if (weapons[x] == -1)
{
continue;
}
if (weaponsdrop)
{
// If this is the knife slot, then stop.
if (WeaponsSlot:x == Slot_Melee)
{
// Strip knife.
RemovePlayerItem(client, weapons[x]);
continue;
}
// Force client to drop weapon.
WeaponsForceClientDrop(client, weapons[x]);
}
else
{
// Strip weapon.
RemovePlayerItem(client, weapons[x]);
}
}
// Give zombie a new knife. (If you leave the old one there will be glitches with the knife positioning)
GivePlayerItem(client, "weapon_knife");
}
/**
@ -701,6 +703,14 @@ InfectZombieToHuman(client, bool:respawn = false, bool:protect = false)
RoundEndOnClientInfected();
ZTeleOnClientInfected(client);
// Give human a new knife. (If you leave the old one there will be glitches with the knife positioning)
new knife = GetPlayerWeaponSlot(client, _:Slot_Melee);
if (knife != -1)
{
RemovePlayerItem(client, knife);
GivePlayerItem(client, "weapon_knife");
}
// Check if we should respawn the client.
if (respawn)
{
@ -806,7 +816,7 @@ InfectMenuClients(client)
SetMenuTitle(menu_infect_clients, "%t\n ", "Infect menu clients title");
decl String:clientoption[MAX_NAME_LENGTH];
decl String:clientoption[64];
decl String:clientuserid[8];
// x = Client index.
@ -861,11 +871,8 @@ public InfectMenuClientsHandle(Handle:menu_infect_clients, MenuAction:action, cl
// Client selected an option.
if (action == MenuAction_Select)
{
decl String:clientuserid[8];
GetMenuItem(menu_infect_clients, slot, clientuserid, sizeof(clientuserid));
// Get the targetted client through their userid which was set into the menu slot's info param.
new target = GetClientOfUserId(StringToInt(clientuserid));
// Get selected client index.
new target = MenuGetClientIndex(menu_infect_clients, slot);
// If target has left the server, then stop.
if (!target)
@ -899,7 +906,7 @@ public InfectMenuClientsHandle(Handle:menu_infect_clients, MenuAction:action, cl
if (slot == MenuCancel_ExitBack)
{
// Re-open admin menu.
ZRAdminMenu(client);
ZAdminMenu(client);
}
}
// Client hit "Exit" button.

View File

@ -47,7 +47,7 @@ JumpBoostOnClientJumpPost(client)
// Get client's current velocity.
ToolsClientVelocity(client, vecVelocity, false);
// If they have exceeded the max jump speed, then stop.
// Only apply horizontal multiplier if it's not a bhop.
if (!JumpBoostIsBHop(vecVelocity))
{
// Apply horizontal multipliers to jump vector.

View File

@ -78,7 +78,7 @@ KnockbackOnClientHurt(client, attacker, const String:weapon[], hitgroup, dmg_hea
// Get attackers eye angles.
new Float:attackerang[3];
KnockbackGetClientEyeAngles(attacker, attackerang);
GetClientEyeAngles(attacker, attackerang);
// Calculate knockback end-vector.
TR_TraceRayFilter(attackerloc, attackerang, MASK_ALL, RayType_Infinite, KnockbackTRFilter);
@ -199,14 +199,3 @@ KnockbackFindExplodingGrenade(Float:heLoc[3])
// Didn't find the grenade.
return -1;
}
/**
* Get client's eye angles.
*
* @param client The client index.
* @param vecAngles The angle vector of the client's eyes.
*/
KnockbackGetClientEyeAngles(client, Float:vecAngles[3])
{
SDKCall(g_hToolsEyeAngles, client, vecAngles);
}

View File

@ -70,7 +70,7 @@ enum LogModules
bool:LogModule_Invalid = 0, /** Used as return value when an error occoured.*/
bool:LogModule_Account,
bool:LogModule_Antistick,
bool:LogModule_AntiStick,
bool:LogModule_Config,
bool:LogModule_Cvars,
bool:LogModule_Damage,
@ -84,7 +84,9 @@ enum LogModules
bool:LogModule_Tools,
bool:LogModule_Volfeatures,
bool:LogModule_Weapons,
bool:LogModule_Weaponrestrict
bool:LogModule_Weaponrestrict,
bool:LogModule_ZSpawn,
bool:LogModule_ZTele,
}
/**

View File

@ -59,7 +59,7 @@ LogGetModuleNameString(String:buffer[], maxlen, LogModules:module, bool:shortNam
{
return shortName ? strcopy(buffer, maxlen, "account") : strcopy(buffer, maxlen, "Account");
}
case LogModule_Antistick:
case LogModule_AntiStick:
{
return shortName ? strcopy(buffer, maxlen, "antistick") : strcopy(buffer, maxlen, "Anti-Stick");
}
@ -119,6 +119,14 @@ LogGetModuleNameString(String:buffer[], maxlen, LogModules:module, bool:shortNam
{
return shortName ? strcopy(buffer, maxlen, "weaponrestrict") : strcopy(buffer, maxlen, "Weapon Restrictions");
}
case LogModule_ZSpawn:
{
return shortName ? strcopy(buffer, maxlen, "zspawn") : strcopy(buffer, maxlen, "ZSpawn");
}
case LogModule_ZTele:
{
return shortName ? strcopy(buffer, maxlen, "ztele") : strcopy(buffer, maxlen, "ZTele");
}
}
// Module mismatch.
@ -140,7 +148,7 @@ LogModules:LogGetModule(const String:moduleName[])
}
else if (StrEqual(moduleName, "antistick", false))
{
return LogModule_Antistick;
return LogModule_AntiStick;
}
else if (StrEqual(moduleName, "config", false))
{
@ -198,6 +206,14 @@ LogModules:LogGetModule(const String:moduleName[])
{
return LogModule_Weaponrestrict;
}
else if (StrEqual(moduleName, "zspawn", false))
{
return LogModule_ZSpawn;
}
else if (StrEqual(moduleName, "ztele", false))
{
return LogModule_ZTele;
}
// No match.
return LogModule_Invalid;

View File

@ -90,10 +90,10 @@ ZMenuMain(client)
decl String:zmarket[256];
// Translate each line into client's language.
Format(zadmin, sizeof(zadmin), "%t", "Menu main zadmin", SAYHOOKS_KEYWORD_ZMENU);
Format(zclass, sizeof(zclass), "%t", "Menu main zclass", SAYHOOKS_KEYWORD_ZADMIN);
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_ZCLASS);
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);
@ -138,7 +138,7 @@ public ZMenuMainHandle(Handle:menu, MenuAction:action, client, slot)
case 0:
{
// Copy return to resend variable.
resend = !ZRAdminMenu(client);
resend = !ZAdminMenu(client);
}
// Select ZClass.
case 1:
@ -197,12 +197,78 @@ public ZMenuMainHandle(Handle:menu, MenuAction:action, client, slot)
}
}
/**
* Shows a list of all clients to a client, different handlers can be used for this, as well as title.
*
* @param client The client index.
* @param handler The menu handler.
* @param any Title is a translations phrase.
*/
stock MenuClientList(client, MenuHandler:handler, any:...)
{
// Create menu handle.
new Handle:menu_clients = CreateMenu(handler);
// Set client as translation target.
SetGlobalTransTarget(client);
// Translate phrase.
decl String:translation[TRANSLATION_MAX_LENGTH_CHAT];
VFormat(translation, sizeof(translation), "%t", 3);
// Set menu title to the translated phrase.
SetMenuTitle(menu_clients, translation);
decl String:clientoption[MAX_NAME_LENGTH];
decl String:clientuserid[8];
// x = Client index.
for (new x = 1; x <= MaxClients; x++)
{
// If client isn't in-game, then stop.
if (!IsClientInGame(x))
{
continue;
}
// Get client info.
GetClientName(x, clientoption, sizeof(clientoption));
IntToString(GetClientUserId(x), clientuserid, sizeof(clientuserid));
// Add option to menu.
AddMenuItem(menu_clients, clientuserid, clientoption);
}
// Create a "Back" button to the main admin menu.
SetMenuExitBackButton(menu_clients, true);
// Send menu.
DisplayMenu(menu_clients, client, MENU_TIME_FOREVER);
}
/**
* Gets the client index of the selected client in the menu.
*
* @param menu The menu handle.
* @param slot The menu slot that was selected.
* @return The client index, 0 if the selected client is no longer in the server.
*/
stock MenuGetClientIndex(Handle:menu, slot)
{
// Get menu slot's information.
decl String:clientuserid[8];
GetMenuItem(menu, slot, clientuserid, sizeof(clientuserid));
// Return the targetted client through their userid which was set into the menu slot's info param.
return GetClientOfUserId(StringToInt(clientuserid));
}
/**
* Return itemdraw flag for SM menus.
*
* @param condition If this is true, item will be drawn normally.
*/
MenuGetItemDraw(bool:condition)
stock MenuGetItemDraw(bool:condition)
{
return condition ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED;
}

View File

@ -63,7 +63,7 @@ ModelsLoad()
ConfigSetConfigPath(File_Models, pathmodels);
// Load config from file and create array structure.
new bool:success = ConfigLoadConfig(File_Models, arrayModels);
new bool:success = ConfigLoadConfig(File_Models, arrayModels, PLATFORM_MAX_PATH);
// Unexpected error, stop plugin.
if (!success)
@ -72,7 +72,8 @@ ModelsLoad()
}
new modelcount;
new modelvalidcount;
new modelpublicvalidcount;
new modelnonpublicvalidcount;
new modelfilecount;
decl String:modelbase[PLATFORM_MAX_PATH];
@ -91,9 +92,9 @@ ModelsLoad()
// x = model array index.
for (new x = 0; x < models; x++)
{
// Get base model path (rawline in models.txt)
GetArrayString(arrayModels, x, modelbase, sizeof(modelbase));
// Get base model path, excluding the public/non-public setting.
ModelReturnPath(x, modelbase, sizeof(modelbase));
PrintToServer("PATH BASE [%s]", modelbase);
// Explode path into pieces. (separated by "/")
new strings = ExplodeString(modelbase, "/", baseexploded, MODELS_PATH_MAX_DEPTH, MODELS_PATH_DIR_MAX_LENGTH);
@ -147,10 +148,18 @@ ModelsLoad()
modelfilecount++;
}
// Increment modelvalidcount if model files are valid.
// Increment variable if model files are valid.
if (modelfilecount)
{
modelvalidcount++;
// Increment proper variable.
if (ModelIsPublic(x))
{
modelpublicvalidcount++;
}
else
{
modelnonpublicvalidcount++;
}
}
else
{
@ -169,12 +178,12 @@ ModelsLoad()
}
// Log model validation info.
LogEvent(false, LogType_Normal, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Total: %d | Successful: %d | Unsuccessful: %d", modelcount, modelvalidcount, modelcount - modelvalidcount);
LogEvent(false, LogType_Normal, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "Total: %d | Successful Public: %d | Successful Non-Public: %d | Unsuccessful: %d", modelcount, modelpublicvalidcount, modelnonpublicvalidcount, modelcount - (modelpublicvalidcount + modelnonpublicvalidcount));
// If none of the model paths are valid, then log and fail.
if (!modelvalidcount)
if (!modelpublicvalidcount)
{
LogEvent(false, LogType_Fatal, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "No usable model paths in %s", pathmodels);
LogEvent(false, LogType_Fatal, LOG_CORE_EVENTS, LogModule_Models, "Config Validation", "No usable (public) model paths in %s", pathmodels);
}
// Set config data.
@ -191,3 +200,118 @@ public ModelsOnConfigReload(ConfigFile:config)
// Reload models config.
ModelsLoad();
}
/**
* Checks if a model is public.
*
* @param modelindex The array index of the model to check.
* @return True if public, false if not.
*/
stock bool:ModelIsPublic(modelindex)
{
// Get the entire model string to parse for what we need.
decl String:modelsetting[PLATFORM_MAX_PATH + 16];
GetArrayString(arrayModels, modelindex, modelsetting, sizeof(modelsetting));
// We define this to use as little memory as possible, because the value won't be used.
decl String:modelpath[1];
decl String:strpublic[32];
if (StrContains(modelsetting, ";") > -1)
{
// Get string index of where the public setting starts.
new strindex = SplitString(modelsetting, ";", modelpath, sizeof(modelpath));
// Copy setting to new string
strcopy(strpublic, sizeof(strpublic), modelsetting[strindex]);
// Trim the whitespace.
TrimString(strpublic);
// If public, return true, non-public returns false.
return StrEqual(strpublic, "public", false);
}
// If nothing is specified, assume public.
return true;
}
/**
* Returns the path of a given model index.
*
* @param modelindex The array index of the model.
* @param modelpath The output string of the model path.
* @param maxlen The maximum length of the output string.
*/
stock ModelReturnPath(modelindex, String:modelpath[], maxlen)
{
// Get the entire model string to parse for what we need.
decl String:modelsetting[PLATFORM_MAX_PATH + 16];
GetArrayString(arrayModels, modelindex, modelsetting, sizeof(modelsetting));
// Copy to path before split just in case the string has no ";"
strcopy(modelpath, maxlen, modelsetting);
if (StrContains(modelsetting, ";") > -1)
{
SplitString(modelsetting, ";", modelpath, maxlen);
}
// Trim whitespace.
TrimString(modelpath);
}
/**
* Get a random model index in arrayModels, allows you to specify a filter.
*
* @param modelpath The output string of the model path.
* @param maxlen The maximum length of the output string.
* @param all True to choose any of the models in the file, false to use 'publicmodels' param.
* @param publicmodels True to find a random public model, false to find non-public.
*/
stock ModelsGetRandomModelIndex(String:modelpath[], maxlen, bool:all = true, bool:publicmodels = true)
{
new modelindex = -1;
// Return any random model.
if (all)
{
// Get random model index and return the string in it.
modelindex = GetRandomInt(0, GetArraySize(arrayModels) - 1);
}
else
{
new Handle:modelsarray = CreateArray(PLATFORM_MAX_PATH);
decl String:modelsetting[PLATFORM_MAX_PATH];
// x = Array index.
new size = GetArraySize(arrayModels);
for (new x = 0; x < size; x++)
{
if (publicmodels == ModelIsPublic(x))
{
// Transfer model to temp array.
GetArrayString(arrayModels, x, modelsetting, sizeof(modelsetting));
PushArrayString(modelsarray, modelsetting);
}
}
// y = Array index.
size = GetArraySize(modelsarray);
// If there are no models then copy a blank string to the output.
if (size == 0)
{
strcopy(modelpath, maxlen, "");
return;
}
// Get random model index from the temp list, and return the string in it.
modelindex = GetRandomInt(0, GetArraySize(modelsarray) - 1);
// Destroy the handle.
CloseHandle(modelsarray);
}
// Get the path to the selected model.
ModelReturnPath(modelindex, modelpath, maxlen);
}

View File

@ -39,6 +39,12 @@
*/
NapalmOnClientHurt(client, attacker, const String:weapon[])
{
// If there's no attacker, then stop.
if (!attacker)
{
return;
}
// If player isn't a zombie, then stop.
if (!InfectIsClientInfected(client))
{
@ -107,8 +113,8 @@ NapalmOnWeaponFire(client, const String:weapon[])
return;
}
// Wait .1 seconds.
CreateTimer(0.1, NapalmIgniteGrenade);
// Wait .15 seconds.
CreateTimer(0.15, NapalmIgniteGrenade);
}
/**

View File

@ -93,8 +93,7 @@ bool:ClassApplyModel(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER)
if (strcmp(modelpath, "random", false) == 0)
{
// TODO: Make a function that gets a random model from the specified team.
new randmodel = GetRandomInt(0, GetArraySize(arrayModels) - 1);
GetArrayString(arrayModels, randmodel, modelpath, sizeof(modelpath));
ModelsGetRandomModelIndex(modelpath, sizeof(modelpath), false, true);
Format(modelpath, sizeof(modelpath), "%s.mdl", modelpath);
}
@ -211,8 +210,8 @@ bool:ClassApplyNightVision(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER
nvgs = ClassGetNvgs(classindex, cachetype);
}
ToolsClientNightVision(client, nvgs);
ToolsClientNightVision(client, nvgs, false);
ToolsSetClientNightVision(client, nvgs);
ToolsSetClientNightVision(client, nvgs, false);
return true;
}

View File

@ -93,6 +93,12 @@ ClassOnClientSpawn(client)
GetClientModel(client, originalmodel, sizeof(originalmodel));
strcopy(ClassOriginalPlayerModel[client], PLATFORM_MAX_PATH, originalmodel);
// Exclude special class flags like mother zombies and admin classes.
new denyflags = ZR_CLASS_SPECIALFLAGS;
// Allow admin classes if admin.
denyflags -= ZRIsClientAdmin(client) ? ZR_CLASS_FLAG_ADMIN_ONLY : 0;
// Get random class setting.
new bool:randomclass = GetConVarBool(g_hCvarsList[CVAR_CLASSES_RANDOM]);
@ -101,15 +107,15 @@ ClassOnClientSpawn(client)
if (randomclass || StrEqual(steamid, "BOT"))
{
// Get random classes for each type.
new randomzombie = ClassGetRandomClass(ZR_CLASS_TEAM_ZOMBIES);
new randomhuman = ClassGetRandomClass(ZR_CLASS_TEAM_HUMANS);
new randomzombie = ClassGetRandomClass(ZR_CLASS_TEAM_ZOMBIES, _, _, denyflags);
new randomhuman = ClassGetRandomClass(ZR_CLASS_TEAM_HUMANS, _, _, denyflags);
// Mark zombie class as selected.
// Save selected zombie class index.
ClassSelected[client][ZR_CLASS_TEAM_ZOMBIES] = randomzombie;
ClassGetName(randomzombie, classname, sizeof(classname), ZR_CLASS_TEAM_ZOMBIES);
TranslationPrintToChat(client, "Classes random assignment", classname);
// Mark human class as selected.
// Save selected human class index.
ClassSelected[client][ZR_CLASS_TEAM_HUMANS] = randomhuman;
ClassGetName(randomhuman, classname, sizeof(classname), ZR_CLASS_TEAM_HUMANS);
TranslationPrintToChat(client, "Classes random assignment", classname);
@ -193,11 +199,29 @@ ClassOnClientInfected(client, bool:motherzombie = false)
{
// Get random regular zombie class. Remove admin flag if admin.
motherindex = ClassGetRandomClass(ZR_CLASS_TEAM_ZOMBIES, _, _, ZR_CLASS_SPECIALFLAGS - isadmin);
// Validate index. Do not change class if it's invalid.
if (ClassValidateIndex(motherindex))
{
// Change class.
classindex = motherindex;
}
}
else if (StrEqual(motherzombiesetting, "motherzombies", false))
{
// Get random mother zombie class.
motherindex = ClassGetRandomClass(ZR_CLASS_TEAM_ZOMBIES, _, ZR_CLASS_FLAG_MOTHER_ZOMBIE, isadmin);
// 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);
// Validate index. Do not change class if it's invalid.
if (ClassValidateIndex(motherindex))
{
// This is a mother zombie class. Reset mother zombie setting so
// class skills aren't improved.
motherzombie = false;
// Change class.
classindex = motherindex;
}
}
else
{

View File

@ -384,7 +384,7 @@ public ClassTeamSelectHandle(Handle:menu, MenuAction:action, client, slot)
{
if (slot == MenuCancel_ExitBack)
{
ZRAdminMenu(client);
ZAdminMenu(client);
}
}
}

View File

@ -73,12 +73,8 @@ ClassOverlayOnCommandsHook()
*/
ClassOverlayOnCookiesCreate()
{
// If cookie doesn't already exist, then create it.
g_hOverlayEnabledCookie = FindClientCookie(CLASSOVERLAY_COOKIE_ENABLED);
if (g_hOverlayEnabledCookie == INVALID_HANDLE)
{
g_hOverlayEnabledCookie = RegClientCookie(CLASSOVERLAY_COOKIE_ENABLED, "The toggle state of the class overlay.", CookieAccess_Public);
}
// Create overlay cookie.
g_hOverlayEnabledCookie = RegClientCookie(CLASSOVERLAY_COOKIE_ENABLED, "The toggle state of the class overlay.", CookieAccess_Protected);
}
/**
@ -141,7 +137,7 @@ ClassOverlayInitialize(client, const String:overlay[])
decl String:togglecmds[CLASSOVERLAY_TOGGLE_MAX_CMDS * CLASSOVERLAY_TOGGLE_MAX_LENGTH];
GetConVarString(g_hCvarsList[CVAR_CLASSES_OVERLAY_TOGGLECMDS], togglecmds, sizeof(togglecmds));
TranslationPrintHUDText(client, "Classes overlay toggle", togglecmds);
TranslationPrintHintText(client, "Classes overlay toggle", togglecmds);
}
// Display class overlays.

View File

@ -166,9 +166,9 @@
#define ZR_CLASS_SPEED_MAX 2000.0
#define ZR_CLASS_KNOCKBACK_MIN -30.0
#define ZR_CLASS_KNOCKBACK_MAX 30.0
#define ZR_CLASS_JUMP_HEIGHT_MIN -500.0
#define ZR_CLASS_JUMP_HEIGHT_MAX 500.0
#define ZR_CLASS_JUMP_DISTANCE_MIN -5.0
#define ZR_CLASS_JUMP_HEIGHT_MIN 0.0
#define ZR_CLASS_JUMP_HEIGHT_MAX 5.0
#define ZR_CLASS_JUMP_DISTANCE_MIN 0.0
#define ZR_CLASS_JUMP_DISTANCE_MAX 5.0
/**
* @endsection
@ -488,7 +488,7 @@ ClassLoad(bool:keepMultipliers = false)
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_bonus", ZR_CLASS_DEFAULT_HEALTH_INFECT_GAIN);
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_speed] = KvGetFloat(kvClassData, "speed", ZR_CLASS_DEFAULT_SPEED);

View File

@ -162,6 +162,26 @@ RoundEndOnRoundEnd(reason)
// Get outcome of the round.
new RoundEndOutcome:outcome = RoundEndReasonToOutcome(reason);
// Update team scores.
new teamscore;
switch(outcome)
{
// Zombies won the round.
case ZombiesWin:
{
// Increment T score.
teamscore = GetTeamScore(CS_TEAM_T);
SetTeamScore(CS_TEAM_T, ++teamscore);
}
// Humans won the round.
case HumansWin:
{
// Increment CT score.
teamscore = GetTeamScore(CS_TEAM_CT);
SetTeamScore(CS_TEAM_CT, ++teamscore);
}
}
// Display the overlay to all clients.
RoundEndOverlayStart(outcome);

View File

@ -30,6 +30,33 @@
*/
#define ROUNDSTART_OBJECTIVE_ENTITIES "func_bomb_target|func_hostage_rescue|c4|hostage_entity"
/**
* Client is spawning into the game.
*
* @param client The client index.
*/
RoundStartOnClientSpawn(client)
{
// If client hasn't spawned yet, then stop.
if (!IsPlayerAlive(client))
{
return;
}
// Get public chat trigger prefix.
decl String:publictrigger[4];
SayHooksGetPublicChatTrigger(publictrigger, sizeof(publictrigger));
// If public chat trigger is disabled, then don't print message.
if (StrEqual(publictrigger, "N/A", false))
{
return;
}
// Print to client, how to access ZMenu.
TranslationPrintToChat(client, "General zmenu reminder", publictrigger, SAYHOOKS_KEYWORD_ZMENU);
}
/**
* The round is starting.
*/

View File

@ -90,6 +90,9 @@ SayHooksGetPublicChatTrigger(String:trigger[], maxlen)
{
strcopy(trigger, maxlen, "N/A");
}
// Close the handle.
CloseHandle(kvCore);
}
/**
@ -126,4 +129,7 @@ SayHooksGetSilentChatTrigger(String:trigger[], maxlen)
{
strcopy(trigger, maxlen, "N/A");
}
// Close the handle.
CloseHandle(kvCore);
}

View File

@ -47,14 +47,17 @@ SpawnProtectClientInit(client)
}
/**
* Client is spawning into the game.
* Client is spawning into the game. *Post
*
* @param client The client index.
*/
SpawnProtectOnClientSpawn(client)
SpawnProtectOnClientSpawnPost(client)
{
// Disable spawn protection on client.
bInfectImmune[client][INFECT_TYPE_NORMAL] = false;
// If client is dead, then they are joining the server, not spawning.
if (!IsPlayerAlive(client))
{
return;
}
// If timer is currently running, kill it.
if (tSpawnProtect[client] != INVALID_HANDLE)
@ -64,15 +67,7 @@ SpawnProtectOnClientSpawn(client)
// Reset timer handle.
tSpawnProtect[client] = INVALID_HANDLE;
}
/**
* Client is spawning into the game. *Post
*
* @param client The client index.
*/
SpawnProtectOnClientSpawnPost(client)
{
// If protect cvar is disabled, then stop.
new bool:protect = GetConVarBool(g_hCvarsList[CVAR_SPAWNPROTECT]);
if (!protect)
@ -80,6 +75,9 @@ SpawnProtectOnClientSpawnPost(client)
return;
}
// Disable spawn protection on client.
bInfectImmune[client][INFECT_TYPE_NORMAL] = false;
// Start spawn protection.
SpawnProtectStart(client);
}
@ -145,7 +143,7 @@ SpawnProtectStart(client)
TranslationPrintToChat(client, "Spawn protection begin", protect_time);
// Send time left in a hud message.
TranslationPrintHUDText(client, "Spawn Protect", pSpawnProtectTime[client]);
TranslationPrintHintText(client, "Spawn Protect", pSpawnProtectTime[client]);
// Start repeating timer.
tSpawnProtect[client] = CreateTimer(1.0, SpawnProtectTimer, client, TIMER_FLAG_NO_MAPCHANGE|TIMER_REPEAT);
@ -179,7 +177,7 @@ public Action:SpawnProtectTimer(Handle:timer, any:client)
pSpawnProtectTime[client]--;
// Print time left to client.
TranslationPrintHUDText(client, "Spawn Protect", pSpawnProtectTime[client]);
TranslationPrintHintText(client, "Spawn Protect", pSpawnProtectTime[client]);
// Time has expired.
if (pSpawnProtectTime[client] <= 0)
@ -188,7 +186,7 @@ public Action:SpawnProtectTimer(Handle:timer, any:client)
bInfectImmune[client][INFECT_TYPE_NORMAL] = false;
// Tell client spawn protection is over.
TranslationPrintHUDText(client, "Spawn protection end");
TranslationPrintHintText(client, "Spawn protection end");
// Fix attributes.
ToolsSetClientAlpha(client, ClassGetAlphaInitial(client));

View File

@ -42,7 +42,6 @@ new g_iToolsDefaultFOV;
* Initialize global SDKTools handles.
*/
new Handle:g_hToolsGameConfig = INVALID_HANDLE;
new Handle:g_hToolsEyeAngles = INVALID_HANDLE;
new Handle:g_hToolsTerminateRound = INVALID_HANDLE;
new Handle:g_hToolsCSWeaponDrop = INVALID_HANDLE;
/**
@ -108,7 +107,6 @@ ToolsFindOffsets()
WeaponsOnOffsetsFound();
AccountOnOffsetsFound();
VEffectsOnOffsetsFound();
AntiStickOnOffsetsFound();
ZMarketOnOffsetsFound();
}
@ -126,18 +124,6 @@ ToolsSetupGameData()
LogEvent(false, LogType_Fatal, LOG_CORE_EVENTS, LogModule_Tools, "GameData", "Can't load game config file (plugin.zombiereloaded.txt) from the gamedata directory.");
}
// Prep the SDKCall for "EyeAngles."
StartPrepSDKCall(SDKCall_Player);
PrepSDKCall_SetFromConf(g_hToolsGameConfig, SDKConf_Virtual, "EyeAngles");
PrepSDKCall_SetReturnInfo(SDKType_QAngle, SDKPass_ByValue);
g_hToolsEyeAngles = EndPrepSDKCall();
// If offset "EyeAngles" can't be found, then stop the plugin.
if(g_hToolsEyeAngles == INVALID_HANDLE)
{
LogEvent(false, LogType_Fatal, LOG_CORE_EVENTS, LogModule_Tools, "GameData", "Offset \"EyeAngles\" was not found.");
}
// Prep the SDKCall for "TerminateRound."
StartPrepSDKCall(SDKCall_GameRules);
PrepSDKCall_SetFromConf(g_hToolsGameConfig, SDKConf_Signature, "TerminateRound");

View File

@ -33,7 +33,7 @@
* @param stack If modifying velocity, then true will stack new velocity onto the client's
* current velocity, false will reset it.
*/
ToolsClientVelocity(client, Float:vecVelocity[3], bool:apply = true, bool:stack = true)
stock ToolsClientVelocity(client, Float:vecVelocity[3], bool:apply = true, bool:stack = true)
{
// If retrieve if true, then get client's velocity.
if (!apply)
@ -72,12 +72,30 @@ ToolsClientVelocity(client, Float:vecVelocity[3], bool:apply = true, bool:stack
* @param client The client index.
* @param value LMV value. (1.0 = default, 2.0 = double)
*/
ToolsSetClientLMV(client, Float:fLMV)
stock ToolsSetClientLMV(client, Float:fLMV)
{
// Set lagged movement value of client.
SetEntDataFloat(client, g_iToolsLMV, fLMV / 300.0, true);
}
/**
* Get nightvision values on a client.
* @param client The client index.
* @param ownership If true, function will return the value of the client's ownership of nightvision.
* If false, function will return the value of the client's on/off state of the nightvision.
* @return True if aspect of nightvision is enabled on the client, false if not.
*/
stock bool:ToolsGetClientNightVision(client, bool:ownership = true)
{
// If ownership is true, then toggle the ownership of nightvision on client.
if (ownership)
{
return bool:GetEntData(client, g_iToolsHasNightVision, 1);
}
return bool:GetEntData(client, g_iToolsNightVisionOn, 1);
}
/**
* Control nightvision values on a client.
* @param client The client index.
@ -85,7 +103,7 @@ ToolsSetClientLMV(client, Float:fLMV)
* @param ownership If true, enable will toggle the client's ownership of nightvision.
* If false, enable will toggle the client's on/off state of the nightvision.
*/
ToolsClientNightVision(client, bool:enable, bool:ownership = true)
stock ToolsSetClientNightVision(client, bool:enable, bool:ownership = true)
{
// If ownership is true, then toggle the ownership of nightvision on client.
if (ownership)
@ -104,7 +122,7 @@ ToolsClientNightVision(client, bool:enable, bool:ownership = true)
* @param client The client index.
* @param FOV The field of vision of the client.
*/
ToolsSetClientDefaultFOV(client, FOV)
stock ToolsSetClientDefaultFOV(client, FOV)
{
SetEntData(client, g_iToolsDefaultFOV, FOV, 1, true);
}
@ -118,7 +136,7 @@ ToolsSetClientDefaultFOV(client, FOV)
* @param value The value of the client's score or deaths.
* @return The score or death count of the client, -1 if setting.
*/
ToolsClientScore(client, bool:score = true, bool:apply = true, value = 0)
stock ToolsClientScore(client, bool:score = true, bool:apply = true, value = 0)
{
if (!apply)
{
@ -156,7 +174,7 @@ ToolsClientScore(client, bool:score = true, bool:apply = true, value = 0)
* @param alpha The alpha value to set client's alpha to. (0-255)
* @param weapons Apply alpha to all client's weapons.
*/
ToolsSetClientAlpha(client, alpha)
stock ToolsSetClientAlpha(client, alpha)
{
// Turn rendermode on, on the client.
SetEntityRenderMode(client, RENDER_TRANSALPHA);
@ -174,7 +192,7 @@ ToolsSetClientAlpha(client, alpha)
* @param client The client index.
* @return The alpha value of the client. (0-255)
*/
ToolsGetEntityAlpha(entity)
stock ToolsGetEntityAlpha(entity)
{
static bool:gotconfig = false;
static String:prop[32];

View File

@ -49,11 +49,6 @@
* @endsection
*/
/**
* HUD text usermsg
*/
#define TRANSLATION_USERMSG_HINTTEXT "HintText"
/**
* Load translations file here.
*/
@ -285,7 +280,7 @@ stock TranslationPrintCenterText(client, any:...)
* @param client The client index.
* @param ... Translation formatting parameters.
*/
stock TranslationPrintHUDText(client, any:...)
stock TranslationPrintHintText(client, any:...)
{
// Set translation target
SetGlobalTransTarget(client);
@ -295,17 +290,7 @@ stock TranslationPrintHUDText(client, any:...)
VFormat(translation, sizeof(translation), "%t", 2);
// Print translated phrase to client.
// If hint text usermsg is invalid, then stop.
new Handle:hHintText = StartMessageOne(TRANSLATION_USERMSG_HINTTEXT, client);
if (hHintText == INVALID_HANDLE)
{
return;
}
BfWriteByte(hHintText, -1);
BfWriteString(hHintText, translation);
EndMessage();
PrintHintText(client, translation);
}
/**

View File

@ -104,7 +104,7 @@ public WeaponsMenuMainHandle(Handle:menu_weapons_main, MenuAction:action, client
if (slot == MenuCancel_ExitBack)
{
// Re-open admin menu.
ZRAdminMenu(client);
ZAdminMenu(client);
}
}
// Client hit "Exit" button.
@ -210,7 +210,7 @@ WeaponsMenuTypeWeapons(client)
decl String:unrestrictall[64];
Format(restrictall, sizeof(restrictall), "%t", "Weapons menu restrict types restrict all", typename);
Format(unrestrictall, sizeof(unrestrictall), "%t", "Weapons menu restrict types unrestrict all", typename);
Format(unrestrictall, sizeof(unrestrictall), "%t\n ", "Weapons menu restrict types unrestrict all", typename);
// Draw items as selectable only if not all weapons within the type are restricted or unrestricted.
AddMenuItem(menu_weapons_typeweapons, "restrictall", restrictall, MenuGetItemDraw(!RestrictIsTypeUniform(true, g_iWeaponsCurType[client])));

View File

@ -43,6 +43,11 @@ enum RestrictData
*/
new g_iCanUseHookID[MAXPLAYERS + 1] = {-1, ...};
/**
* Array to block any client from picking up weapons.
*/
new bool:g_bRestrictBlockWeapon[MAXPLAYERS + 1];
/**
* Array to store a list of different weapon groups
*/
@ -134,6 +139,9 @@ RestrictClientInit(client)
{
// Hook "Weapon_CanUse" on client.
g_iCanUseHookID[client] = ZRTools_HookWeapon_CanUse(client, RestrictCanUse);
// Reset block weapons flag.
g_bRestrictBlockWeapon[client] = false;
}
/**
@ -161,6 +169,48 @@ RestrictOnClientSpawn(client)
// Re-hook "canuse" on client.
ZRTools_UnhookWeapon_CanUse(g_iCanUseHookID[client]);
g_iCanUseHookID[client] = ZRTools_HookWeapon_CanUse(client, RestrictCanUse);
// H4x. Unfortunately I can't disable this flag early enough for CS:S to give start USP/Glock.
// So I have to give BOOTLEG weapons. (Sorry Valve)
if (g_bRestrictBlockWeapon[client])
{
// Reset block weapons flag.
g_bRestrictBlockWeapon[client] = false;
if (ZRIsClientOnTeam(client, CS_TEAM_T))
{
GivePlayerItem(client, WEAPONS_SPAWN_T_WEAPON);
}
else if (ZRIsClientOnTeam(client, CS_TEAM_CT))
{
GivePlayerItem(client, WEAPONS_SPAWN_CT_WEAPON);
}
}
}
/**
* The round is ending.
*/
RestrictOnRoundEnd()
{
// x = Client index.
for (new x = 1; x <= MaxClients; x++)
{
// If client isn't in-game, then stop.
if (!IsClientInGame(x))
{
continue;
}
// If client is a human, then stop.
if (InfectIsClientHuman(x))
{
continue;
}
// Enable block weapon flag.
g_bRestrictBlockWeapon[x] = true;
}
}
/**
@ -181,7 +231,7 @@ public Action:RestrictBuyCommand(client, argc)
return Plugin_Handled;
}
decl String:weapon[64];
decl String:weapon[WEAPONS_MAX_LENGTH];
GetCmdArg(1, weapon, sizeof(weapon));
ReplaceString(weapon, sizeof(weapon), "weapon_", "");
@ -199,7 +249,11 @@ public Action:RestrictBuyCommand(client, argc)
// If weapon is restricted, then stop.
if (RestrictIsWeaponRestricted(index))
{
TranslationPrintToChat(client, "Weapon is restricted", weapon);
// Get display name.
decl String:weapondisplay[WEAPONS_MAX_LENGTH];
WeaponsGetName(index, weapondisplay, sizeof(weapondisplay));
TranslationPrintToChat(client, "Weapon is restricted", weapondisplay);
// Block command.
return Plugin_Handled;
@ -545,6 +599,18 @@ public ZRTools_Action:RestrictCanUse(client, weapon)
return ZRTools_Continue;
}
// If the player is a zombie, then prevent pickup.
if (InfectIsClientInfected(client))
{
return ZRTools_Handled;
}
// If client is flagged for not picking up weapons, then stop.
if (g_bRestrictBlockWeapon[client])
{
return ZRTools_Handled;
}
// If weapons module is disabled, then stop.
new bool:weapons = GetConVarBool(g_hCvarsList[CVAR_WEAPONS]);
if (!weapons)
@ -575,12 +641,6 @@ public ZRTools_Action:RestrictCanUse(client, weapon)
return ZRTools_Handled;
}
// If the player is a zombie, then prevent pickup.
if (InfectIsClientInfected(client))
{
return ZRTools_Handled;
}
// Forward event to weapons module.
WeaponsOnItemPickup(client, weapon);

View File

@ -31,10 +31,19 @@
#define WEAPONS_MAX_LENGTH 32
/**
* Number of weapon slots (For CS:S)
* Number of REAL weapon slots (For CS:S)
*/
#define WEAPONS_SLOTS_MAX 5
/**
* @section CS:S start weapons.
*/
#define WEAPONS_SPAWN_T_WEAPON "weapon_glock"
#define WEAPONS_SPAWN_CT_WEAPON "weapon_usp"
/**
* @endsection
*/
/**
* Weapon config data indexes.
*/
@ -73,6 +82,7 @@ enum WeaponsSlot
Slot_Melee = 2, /** Melee (knife) weapon slot. */
Slot_Projectile = 3, /** Projectile (grenades, flashbangs, etc) weapon slot. */
Slot_Explosive = 4, /** Explosive (c4) weapon slot. */
Slot_NVGs = 5, /** NVGs (fake) equipment slot. */
}
/**
@ -325,6 +335,15 @@ WeaponsOnClientSpawnPost(client)
ZMarketOnClientSpawnPost(client);
}
/**
* The round is ending.
*/
WeaponsOnRoundEnd()
{
// Forward event to sub-modules.
RestrictOnRoundEnd();
}
/**
* Called when a client picks up an item.
*

View File

@ -65,25 +65,21 @@ ZMarketOnCommandsCreate()
ZMarketOnCookiesCreate()
{
// If auto-rebuy cookie doesn't already exist, then create all ZMarket cookies.
g_hZMarketAutoRebuyCookie = FindClientCookie(ZMARKET_COOKIE_AUTOREBUY);
if (g_hZMarketAutoRebuyCookie == INVALID_HANDLE)
{
g_hZMarketAutoRebuyCookie = RegClientCookie(ZMARKET_COOKIE_AUTOREBUY, "The toggle state of auto-rebuy.", CookieAccess_Public);
// Create all cookies.
g_hZMarketAutoRebuyCookie = RegClientCookie(ZMARKET_COOKIE_AUTOREBUY, "The toggle state of auto-rebuy.", CookieAccess_Protected);
decl String:rebuycookiename[32];
decl String:rebuycookiedesc[64];
// x = Weapon slot.
for (new x = 0; x < WEAPONS_SLOTS_MAX; x++)
for (new x = 0; x < WEAPONS_SLOTS_MAX + 1; x++)
{
// Format cookie name and description.
Format(rebuycookiename, sizeof(rebuycookiename), "%s_%d", ZMARKET_COOKIE_REBUY, x);
Format(rebuycookiedesc, sizeof(rebuycookiedesc), "Current loadout weapon for slot %d", x);
// Register client cookie.
RegClientCookie(rebuycookiename, rebuycookiedesc, CookieAccess_Public);
}
RegClientCookie(rebuycookiename, rebuycookiedesc, CookieAccess_Protected);
}
}
@ -161,6 +157,12 @@ ZMarketOnClientSpawnPost(client)
return;
}
// If client is a zombie, then stop.
if (InfectIsClientInfected(client))
{
return;
}
// If auto-rebuy is enabled, then force client to rebuy weapons.
if (CookiesGetClientCookieBool(client, g_hZMarketAutoRebuyCookie))
{
@ -382,10 +384,11 @@ bool:ZMarketMenuLoadout(client)
decl String:meleeweapon[WEAPONS_MAX_LENGTH];
decl String:projectileweapon[WEAPONS_MAX_LENGTH];
decl String:explosiveweapon[WEAPONS_MAX_LENGTH];
decl String:nvgsweapon[WEAPONS_MAX_LENGTH];
// Transfer cookie values into an array.
new String:rebuyweapons[WeaponsSlot][WEAPONS_MAX_LENGTH];
ZMarketCookiesToArray(client, rebuyweapons, WEAPONS_SLOTS_MAX, sizeof(rebuyweapons[]));
ZMarketCookiesToArray(client, rebuyweapons, WEAPONS_SLOTS_MAX + 1, sizeof(rebuyweapons[]));
// Return the display name for all the client's weapon classname's.
WeaponsClassnameToDisplay(rebuyweapons[Slot_Primary], sizeof(rebuyweapons[]), primaryweapon, sizeof(primaryweapon));
@ -393,6 +396,7 @@ bool:ZMarketMenuLoadout(client)
WeaponsClassnameToDisplay(rebuyweapons[Slot_Melee], sizeof(rebuyweapons[]), meleeweapon, sizeof(meleeweapon));
WeaponsClassnameToDisplay(rebuyweapons[Slot_Projectile], sizeof(rebuyweapons[]), projectileweapon, sizeof(projectileweapon));
WeaponsClassnameToDisplay(rebuyweapons[Slot_Explosive], sizeof(rebuyweapons[]), explosiveweapon, sizeof(explosiveweapon));
WeaponsClassnameToDisplay(rebuyweapons[Slot_NVGs], sizeof(rebuyweapons[]), nvgsweapon, sizeof(nvgsweapon));
// Get the empty translation.
decl String:empty[64];
@ -420,11 +424,16 @@ bool:ZMarketMenuLoadout(client)
strcopy(explosiveweapon, sizeof(explosiveweapon), empty);
}
// Copy "Yes/No" to NVGs string.
decl String:nvgsbool[8];
ConfigBoolToSetting(bool:nvgsweapon[0], nvgsbool, sizeof(nvgsbool));
decl String:primary[64];
decl String:secondary[64];
decl String:melee[64];
decl String:projectile[64];
decl String:explosive[64];
decl String:nvgs[64];
// Format all the lines of the menu.
Format(primary, sizeof(primary), "%t", "Weapons menu zmarket loadout primary", primaryweapon);
@ -432,13 +441,15 @@ bool:ZMarketMenuLoadout(client)
Format(melee, sizeof(melee), "%t", "Weapons menu zmarket loadout melee", meleeweapon);
Format(projectile, sizeof(projectile), "%t", "Weapons menu zmarket loadout projectile", projectileweapon);
Format(explosive, sizeof(explosive), "%t", "Weapons menu zmarket loadout explosive", explosiveweapon);
Format(nvgs, sizeof(nvgs), "%t", "Weapons menu zmarket loadout nvgs", nvgsbool);
// Add formatted options to menu.
AddMenuItem(menu_zmarket_loadout, primary, primary, ITEMDRAW_DISABLED);
AddMenuItem(menu_zmarket_loadout, secondary, secondary, ITEMDRAW_DISABLED);
AddMenuItem(menu_zmarket_loadout, melee, melee, ITEMDRAW_DISABLED);
AddMenuItem(menu_zmarket_loadout, projectile, projectile, ITEMDRAW_DISABLED);
AddMenuItem(menu_zmarket_loadout, explosive, explosive, ITEMDRAW_DISABLED);
AddMenuItem(menu_zmarket_loadout, "0", primary, MenuGetItemDraw(!StrEqual(primaryweapon, empty)));
AddMenuItem(menu_zmarket_loadout, "1", secondary, MenuGetItemDraw(!StrEqual(secondaryweapon, empty)));
AddMenuItem(menu_zmarket_loadout, "2", melee, MenuGetItemDraw(!StrEqual(meleeweapon, empty)));
AddMenuItem(menu_zmarket_loadout, "3", projectile, MenuGetItemDraw(!StrEqual(projectileweapon, empty)));
AddMenuItem(menu_zmarket_loadout, "4", explosive, MenuGetItemDraw(!StrEqual(explosiveweapon, empty)));
AddMenuItem(menu_zmarket_loadout, "5", nvgs, MenuGetItemDraw(bool:nvgsweapon[0]));
// Set exit back button.
SetMenuExitBackButton(menu_zmarket_loadout, true);
@ -457,6 +468,15 @@ bool:ZMarketMenuLoadout(client)
*/
public ZMarketMenuLoadoutHandle(Handle:menu_zmarket_loadout, MenuAction:action, client, slot)
{
// Client selected an option.
if (action == MenuAction_Select)
{
// Clear rebuy slot.
ZMarketSetRebuyCookie(client, WeaponsSlot:slot, "");
// Re-send menu.
ZMarketMenuLoadout(client);
}
// Client closed the menu.
if (action == MenuAction_Cancel)
{
@ -779,6 +799,32 @@ stock bool:ZMarketEquip(client, const String:weapon[], bool:rebuy = false)
return false;
}
// Copy the 'weapon' variable into a local variable for indexing.
decl String:weaponname[WEAPONS_MAX_LENGTH];
strcopy(weaponname, sizeof(weaponname), weapon);
// Get the display name for the weapon.
decl String:weapondisplay[WEAPONS_MAX_LENGTH];
WeaponsClassnameToDisplay(weaponname, sizeof(weaponname), weapondisplay, sizeof(weapondisplay));
// Check to make sure the weapon isn't restricted.
new bool:restricted = RestrictIsWeaponRestricted(weaponindex);
if (restricted)
{
TranslationPrintToChat(client, "Weapon is restricted", weapondisplay);
return false;
}
// Get the purchase count information for this weapon.
new purchasemax = WeaponsGetZMarketPurchaseMax(weaponindex);
new purchasecount = ZMarketGetPurchaseCount(client, weapon);
new purchasesleft = purchasemax - purchasecount;
if (purchasemax > 0 && purchasesleft <= 0)
{
TranslationPrintToChat(client, "Weapons zmarket purchase max", weapondisplay, purchasemax);
return false;
}
// Get client's current money.
new cash = AccountGetClientCash(client);
@ -795,34 +841,16 @@ stock bool:ZMarketEquip(client, const String:weapon[], bool:rebuy = false)
// Set client's new cash after purchase.
AccountSetClientCash(client, cash - itemprice);
// Check to make sure the weapon isn't restricted.
new bool:restricted = RestrictIsWeaponRestricted(weaponindex);
if (restricted)
{
TranslationPrintToChat(client, "Weapon is restricted", weapon);
return false;
}
// Get the purchase count information for this weapon.
new purchasemax = WeaponsGetZMarketPurchaseMax(weaponindex);
new purchasecount = ZMarketGetPurchaseCount(client, weapon);
new purchasesleft = purchasemax - purchasecount;
if (purchasemax > 0 && purchasesleft <= 0)
{
TranslationPrintToChat(client, "Weapons zmarket purchase max", weapon, purchasemax);
return false;
}
// Get a list of the client's current weapons.
new weapons[WeaponsSlot];
WeaponsGetClientWeapons(client, weapons);
// Check if client is buying the weapon or ammo for it.
if (!hasweapon || slot == Slot_Invalid || slot == Slot_Projectile)
if (!hasweapon || slot == Slot_Projectile || slot == Slot_NVGs)
{
// Check if the slot is valid and NOT a projectile (grenade).
if (slot != Slot_Invalid && slot != Slot_Projectile)
// If the item is a projectile or NVGs, then skip.
if (slot != Slot_Projectile && slot != Slot_NVGs)
{
// If there is already a weapon in the slot, then force client to drop it.
if (weapons[slot] > -1)
@ -835,8 +863,8 @@ stock bool:ZMarketEquip(client, const String:weapon[], bool:rebuy = false)
// Format name into entity name.
decl String:weaponentity[WEAPONS_MAX_LENGTH];
// If the slot is invalid, this means the item is not a usable weapon, it's equipment.
if (slot == Slot_Invalid)
// If this is the NVGs slot, then format "item_" in front instead of "weapon_".
if (slot == Slot_NVGs)
{
Format(weaponentity, sizeof(weaponentity), "item_%s", weapon);
}
@ -854,10 +882,10 @@ stock bool:ZMarketEquip(client, const String:weapon[], bool:rebuy = false)
// Add 1 to the client's purchase count.
ZMarketSetPurchaseCount(client, weapon, 1, true);
if (slot != Slot_Invalid && slot != Slot_Projectile)
if (slot != Slot_Projectile && slot != Slot_NVGs)
{
// Tell client they bought a weapon.
TranslationPrintToChat(client, "Weapons zmarket purchase", weapon);
TranslationPrintToChat(client, "Weapons zmarket purchase", weapondisplay);
}
}
else if (!rebuy)
@ -926,6 +954,18 @@ bool:ZMarketGetCurrentLoadout(client)
ZMarketSetRebuyCookie(client, WeaponsSlot:x, weaponname);
}
// Update nightvision ownership.
new bool:nightvision = ToolsGetClientNightVision(client);
if (!nightvision)
{
// Empty rebuy slot.
ZMarketSetRebuyCookie(client, Slot_NVGs, "");
}
else
{
ZMarketSetRebuyCookie(client, Slot_NVGs, "nvgs");
}
// Tell client their loadout has been updated.
TranslationPrintToChat(client, "Weapons zmarket get current loadout");
@ -997,6 +1037,12 @@ stock ZMarketSetRebuyCookie(client, WeaponsSlot:slot, const String:value[])
// Find cookie handle, and retrieve its value.
new Handle:rebuycookie = FindClientCookie(rebuycookiename);
if (rebuycookie == INVALID_HANDLE)
{
return;
}
// Set weapon to cookie.
SetClientCookie(client, rebuycookie, value);
}
@ -1023,10 +1069,10 @@ ZMarketRebuy(client, bool:autorebuy = false)
// Transfer cookie values into an array.
new String:rebuyweapons[WeaponsSlot][WEAPONS_MAX_LENGTH];
ZMarketCookiesToArray(client, rebuyweapons, WEAPONS_SLOTS_MAX, sizeof(rebuyweapons[]));
ZMarketCookiesToArray(client, rebuyweapons, WEAPONS_SLOTS_MAX + 1, sizeof(rebuyweapons[]));
// x = Weapon slot.
for (new x = 0; x < WEAPONS_SLOTS_MAX; x++)
for (new x = 0; x < WEAPONS_SLOTS_MAX + 1; x++)
{
// If slot is empty, then stop.
if (!rebuyweapons[x][0])
@ -1036,9 +1082,6 @@ ZMarketRebuy(client, bool:autorebuy = false)
ZMarketEquip(client, rebuyweapons[x], true);
}
// Copy values back to cookies.
ZMarketArrayToCookies(client, rebuyweapons, WEAPONS_SLOTS_MAX, sizeof(rebuyweapons));
}
/**

View File

@ -51,7 +51,7 @@ public Action:ZAdminCommand(client, argc)
}
// Send admin menu.
ZRAdminMenu(client);
ZAdminMenu(client);
// This stops the "Unknown command" message in client's console.
return Plugin_Handled;
@ -62,7 +62,7 @@ public Action:ZAdminCommand(client, argc)
*
* @param client The client index.
*/
bool:ZRAdminMenu(client)
bool:ZAdminMenu(client)
{
// If client isn't an admin, then stop.
if (!ZRIsClientAdmin(client))
@ -72,7 +72,7 @@ bool:ZRAdminMenu(client)
}
// Create menu handle.
new Handle:menu_zadmin = CreateMenu(ZRAdminMenuHandle);
new Handle:menu_zadmin = CreateMenu(ZAdminMenuHandle);
// Set translation target as the client.
SetGlobalTransTarget(client);
@ -80,24 +80,31 @@ bool:ZRAdminMenu(client)
SetMenuTitle(menu_zadmin, "%t\n ", "ZAdmin main title");
decl String:classmultipliers[64];
//decl String:zspawn[64];
//decl String:ztele[64];
decl String:weapons[64];
decl String:hitgroups[64];
decl String:infect[64];
decl String:zspawn[64];
decl String:ztele[64];
//decl String:logflags[64];
Format(classmultipliers, sizeof(classmultipliers), "%t", "ZAdmin main class multipliers");
Format(weapons, sizeof(weapons), "%t", "ZAdmin main weapons");
Format(hitgroups, sizeof(hitgroups), "%t", "ZAdmin main hitgroups");
Format(infect, sizeof(infect), "%t", "ZAdmin main zombie");
//Format(zspawn, sizeof(zspawn), "%t", "!zadmin main spawn");
//Format(ztele, sizeof(ztele), "%t", "!zadmin main tele");
Format(zspawn, sizeof(zspawn), "%t", "ZAdmin main force zspawn");
Format(ztele, sizeof(ztele), "%t", "ZAdmin main force ztele");
//Format(logflags, sizeof(logflags), "%t", "!zadmin main logflags");
// Get conditions for options.
new bool:hitgroupsenabled = GetConVarBool(g_hCvarsList[CVAR_HITGROUPS]);
// Add items to menu.
AddMenuItem(menu_zadmin, "classmultipliers", classmultipliers);
AddMenuItem(menu_zadmin, "weapons", weapons);
AddMenuItem(menu_zadmin, "hitgroups", hitgroups, MenuGetItemDraw(hitgroupsenabled));
AddMenuItem(menu_zadmin, "infect", infect);
//AddMenuItem(menu_zadmin, "zspawn", zspawn);
//AddMenuItem(menu_zadmin, "ztele", ztele, ITEMDRAW_DISABLED);
AddMenuItem(menu_zadmin, "zspawn", zspawn);
AddMenuItem(menu_zadmin, "ztele", ztele);
//AddMenuItem(menu_zadmin, "logflags", logflags);
// Set "Back" button.
@ -118,7 +125,7 @@ bool:ZRAdminMenu(client)
* @param client The client index.
* @param slot The menu slot selected. (starting from 0)
*/
public ZRAdminMenuHandle(Handle:menu_zadmin, MenuAction:action, client, slot)
public ZAdminMenuHandle(Handle:menu_zadmin, MenuAction:action, client, slot)
{
if (action == MenuAction_Select)
{
@ -137,8 +144,13 @@ public ZRAdminMenuHandle(Handle:menu_zadmin, MenuAction:action, client, slot)
{
resend = !WeaponsMenuMain(client);
}
// Zombie management.
// Hitgroup management.
case 2:
{
resend = !HitgroupsMenuHitgroups(client);
}
// Zombie management.
case 3:
{
// We're not resending this menu.
resend = false;
@ -146,12 +158,30 @@ public ZRAdminMenuHandle(Handle:menu_zadmin, MenuAction:action, client, slot)
// Send list of clients to infect.
InfectMenuClients(client);
}
// Force ZSpawn.
case 4:
{
// We're not resending this menu.
resend = false;
// Send list of clients to infect.
MenuClientList(client, ZSpawnForceHandle, "ZSpawn clients title");
}
// Force ZTele.
case 5:
{
// We're not resending this menu.
resend = false;
// Send list of clients to infect.
MenuClientList(client, ZTeleForceHandle, "ZTele clients title");
}
}
// Re-send menu if selection failed.
if (resend)
{
ZRAdminMenu(client);
ZAdminMenu(client);
}
}

View File

@ -54,12 +54,8 @@ ZHPOnCommandsCreate()
*/
ZHPOnCookiesCreate()
{
// If cookie doesn't already exist, then create it.
g_hZHPEnabledCookie = FindClientCookie(ZHP_COOKIE_ENABLED);
if (g_hZHPEnabledCookie == INVALID_HANDLE)
{
g_hZHPEnabledCookie = RegClientCookie(ZHP_COOKIE_ENABLED, "The toggle state of ZHP.", CookieAccess_Public);
}
// Create ZHP cookie.
g_hZHPEnabledCookie = RegClientCookie(ZHP_COOKIE_ENABLED, "The toggle state of ZHP.", CookieAccess_Protected);
}
/**
@ -242,7 +238,7 @@ ZHPUpdateHUD(client)
}
// Display HP
TranslationPrintHUDText(client, "Display HP", health);
TranslationPrintHintText(client, "Display HP", health);
}
/**

View File

@ -248,6 +248,7 @@ stock bool:ZRIsClientAdmin(client, AdminFlag:flag = Admin_Generic)
{
return false;
}
// If client doesn't have the Admin_Generic flag, then stop.
if (!GetAdminFlag(GetUserAdmin(client), flag))
{

View File

@ -42,6 +42,9 @@ ZSpawnOnCommandsCreate()
{
// Register ZSpawn command.
RegConsoleCmd(SAYHOOKS_KEYWORD_ZSPAWN, ZSpawnCommand, "Spawn into the game after joining late.");
// Register admin command to force ZSpawn.
RegAdminCmd("zr_zspawn_force", ZSpawnForceCommand, ADMFLAG_GENERIC, "Force ZSpawn on a client. Usage: zr_zspawn_force <client> ['0' = Spawn as human | '1' = Spawn as zombie]");
}
/**
@ -69,6 +72,12 @@ ZSpawnOnMapStart()
*/
ZSpawnOnClientDisconnect(client)
{
// So people who are connecting that click "cancel" aren't added to the list.
if (!IsClientInGame(client))
{
return;
}
// Check if client is a bot.
if (IsFakeClient(client))
{
@ -162,13 +171,15 @@ ZSpawnOnRoundEnd()
* Spawns a late-joining client into the game.
*
* @param client The client index.
* @param force (Optional) True to force spawning of the client, false to follow rules.
* @param zombie (Optional) If you are forcing spawn, you must override the team here.
* @return True if successful, false otherwise.
*/
bool:ZSpawnClient(client)
bool:ZSpawnClient(client, bool:force = false, bool:zombie = false)
{
// If zspawn is disabled, then stop.
new bool:zspawn = GetConVarBool(g_hCvarsList[CVAR_ZSPAWN]);
if (!zspawn)
if (!force && !zspawn)
{
TranslationPrintToChat(client, "Feature is disabled");
return false;
@ -176,31 +187,43 @@ bool:ZSpawnClient(client)
// If client isn't on a team, then stop.
if (!ZRIsClientOnTeam(client))
{
if (!force)
{
// Tell client the command may only be used when on a team.
TranslationPrintToChat(client, "Must be on team");
}
return false;
}
// If client is alive, then stop.
if (IsPlayerAlive(client))
{
if (!force)
{
// Tell client the command may only be used when dead.
TranslationPrintToChat(client, "Must be dead");
}
return false;
}
// Block if client has already played during this round.
if (SteamidCacheClientExists(g_hZSpawnSteamIDCache, client))
if (!force && SteamidCacheClientExists(g_hZSpawnSteamIDCache, client))
{
// Tell client the command may only be used when joining late.
TranslationPrintToChat(client, "ZSpawn double spawn");
return false;
}
new bool:teamzombie;
if (!force)
{
// Check if zspawn override is enabled, and if so get overidden value.
new bool:teamoverride = GetConVarBool(g_hCvarsList[CVAR_ZSPAWN_TEAM_OVERRIDE]);
new bool:teamzombie = teamoverride ? GetConVarBool(g_hCvarsList[CVAR_ZSPAWN_TEAM_ZOMBIE]) : GetConVarBool(g_hCvarsList[CVAR_RESPAWN_TEAM_ZOMBIE]);
teamzombie = teamoverride ? GetConVarBool(g_hCvarsList[CVAR_ZSPAWN_TEAM_ZOMBIE]) : GetConVarBool(g_hCvarsList[CVAR_RESPAWN_TEAM_ZOMBIE]);
// Block is the time limit is up.
new bool:zspawntimelimit = GetConVarBool(g_hCvarsList[CVAR_ZSPAWN_TIMELIMIT]);
@ -231,6 +254,12 @@ bool:ZSpawnClient(client)
}
}
}
}
else
{
// Use the override team in the function if were forcing the spawn.
teamzombie = zombie;
}
// Tell respawn module to respawn client.
RespawnSpawnClient(client, teamzombie);
@ -238,6 +267,60 @@ bool:ZSpawnClient(client)
return true;
}
/**
* Menu callback (zspawn_force)
* Forces ZSpawn on a client.
*
* @param menu The menu handle.
* @param action Action client is doing in menu.
* @param client The client index.
* @param slot The menu slot selected. (starting from 0)
*/
public ZSpawnForceHandle(Handle:menu_zspawn_force, MenuAction:action, client, slot)
{
// Client selected an option.
if (action == MenuAction_Select)
{
// Get the client index of the selected client.
new target = MenuGetClientIndex(menu_zspawn_force, slot);
// Get the target's name for future use.
decl String:targetname[MAX_NAME_LENGTH];
GetClientName(target, targetname, sizeof(targetname));
// Force ZSpawn on the target.
new bool:success = ZSpawnClient(target, true);
// Tell admin the outcome of the action.
if (success)
{
TranslationReplyToCommand(client, "ZSpawn command force successful", targetname);
}
else
{
TranslationReplyToCommand(client, "ZSpawn command force unsuccessful", targetname);
}
// Re-send the menu.
MenuClientList(client, ZSpawnForceHandle, "ZSpawn clients title");
}
// Client closed the menu.
if (action == MenuAction_Cancel)
{
// Client hit "Back" button.
if (slot == MenuCancel_ExitBack)
{
// Re-open admin menu.
ZAdminMenu(client);
}
}
// Client exited menu.
if (action == MenuAction_End)
{
CloseHandle(menu_zspawn_force);
}
}
/**
* Command callback (zspawn)
* Spawn into the game after joining late.
@ -261,6 +344,72 @@ public Action:ZSpawnCommand(client, argc)
return Plugin_Handled;
}
/**
* Command callback (zr_zspawn_force)
* Force ZSpawn on a client.
*
* @param client The client index.
* @param argc Argument count.
*/
public Action:ZSpawnForceCommand(client, argc)
{
// If not enough arguments given, then stop.
if (argc < 1)
{
TranslationReplyToCommand(client, "ZSpawn command force syntax");
return Plugin_Handled;
}
decl String:target[MAX_NAME_LENGTH], String:targetname[MAX_NAME_LENGTH];
new targets[MAXPLAYERS], bool:tn_is_ml, result;
// Get targetname.
GetCmdArg(1, target, sizeof(target));
// Find a target.
result = ProcessTargetString(target, client, targets, sizeof(targets), COMMAND_FILTER_DEAD, targetname, sizeof(targetname), tn_is_ml);
// Check if there was a problem finding a client.
if (result <= 0)
{
ZRReplyToTargetError(client, result);
return Plugin_Handled;
}
// Get item to give to client.
decl String:strZombie[4];
GetCmdArg(2, strZombie, sizeof(strZombie));
// Copy value of second (optional) parameter to 'zombie'.
// It will be false if the parameter wasn't specified.
new bool:zombie = bool:StringToInt(strZombie);
// x = Client index.
for (new x = 0; x < result; x++)
{
// Give client the item.
new bool:success = ZSpawnClient(targets[x], true, zombie);
// Tell admin the outcome of the command if only 1 client was targetted.
if (result == 1)
{
if (success)
{
TranslationReplyToCommand(client, "ZSpawn command force successful", targetname);
}
else
{
TranslationReplyToCommand(client, "ZSpawn command force unsuccessful", targetname);
}
}
}
// Log action to game events.
LogEvent(false, LogType_Normal, LOG_GAME_EVENTS, LogModule_ZSpawn, "Force ZSpawn", "Admin \"%L\" forced player(s) to spawn. (zr_zspawn_force)", client);
return Plugin_Handled;
}
/**
* Timer callback, resets handle.
*

View File

@ -55,8 +55,11 @@ new g_iZTeleTimeLeft[MAXPLAYERS + 1];
*/
ZTeleOnCommandsCreate()
{
// Register ZMenu command.
// Register ZTele command.
RegConsoleCmd(SAYHOOKS_KEYWORD_ZTELE, ZTeleCommand, "Teleport back to spawn if you are stuck.");
// Register admin command to force ZTele.
RegAdminCmd("zr_ztele_force", ZTeleForceCommand, ADMFLAG_GENERIC, "Force ZTele on a client. Usage: zr_ztele_force <client>");
}
/**
@ -131,9 +134,11 @@ ZTeleOnClientInfected(client)
* Teleports a client back to spawn if conditions are met.
*
* @param client The client index.
* @param force (Optional) True to force teleporting of the client, false to follow rules.
* @param zombie (Optional) True to teleport instantly, false to use delay.
* @return True if teleport was successful, false otherwise.
*/
bool:ZTeleClient(client)
bool:ZTeleClient(client, bool:force = false)
{
// If the client is dead, then stop.
if (!IsPlayerAlive(client))
@ -145,7 +150,7 @@ bool:ZTeleClient(client)
// If zombie cvar is disabled and the client is a zombie, then stop.
new bool:ztelezombie = GetConVarBool(g_hCvarsList[CVAR_ZTELE_ZOMBIE]);
if (infected && !ztelezombie)
if (!force && infected && !ztelezombie)
{
// Tell client they must be human to use this feature.
TranslationPrintToChat(client, "Must be human");
@ -155,7 +160,7 @@ bool:ZTeleClient(client)
// If zombie has spawned, get before value, get the after value otherwise.
// If the cvar is disabled and the client is a human, then stop.
new bool:ztelehuman = g_bZombieSpawned ? GetConVarBool(g_hCvarsList[CVAR_ZTELE_HUMAN_AFTER]) : GetConVarBool(g_hCvarsList[CVAR_ZTELE_HUMAN_BEFORE]);
if (!infected && !ztelehuman)
if (!force && !infected && !ztelehuman)
{
// Tell client that feature is restricted at this time.
TranslationPrintToChat(client, "ZTele restricted human");
@ -164,7 +169,7 @@ bool:ZTeleClient(client)
// If the tele limit has been reached, then stop.
new ztelemax = infected ? GetConVarInt(g_hCvarsList[CVAR_ZTELE_MAX_ZOMBIE]) : GetConVarInt(g_hCvarsList[CVAR_ZTELE_MAX_HUMAN]);
if (g_iZTeleCount[client] >= ztelemax)
if (!force && g_iZTeleCount[client] >= ztelemax)
{
// Tell client that they have already reached their limit.
TranslationPrintToChat(client, "ZTele max", ztelemax);
@ -173,20 +178,27 @@ bool:ZTeleClient(client)
// If teleport is already in progress, then stop.
if (tZTele[client] != INVALID_HANDLE)
{
if (!force)
{
TranslationPrintToChat(client, "ZTele in progress");
}
return false;
}
// If we are forcing, then teleport now and stop.
if (force)
{
// Teleport client to spawn.
ZTeleTeleportClient(client);
return true;
}
// Get current location.
GetClientAbsOrigin(client, g_vecZTeleOrigin[client]);
// If timer is running, kill it.
if (tZTele[client] != INVALID_HANDLE)
{
KillTimer(tZTele[client]);
}
// Set timeleft array to value of respective cvar.
g_iZTeleTimeLeft[client] = infected ? GetConVarInt(g_hCvarsList[CVAR_ZTELE_DELAY_ZOMBIE]) : GetConVarInt(g_hCvarsList[CVAR_ZTELE_DELAY_HUMAN]);
if (g_iZTeleTimeLeft[client] > 0)
@ -199,12 +211,10 @@ bool:ZTeleClient(client)
}
else
{
// Reset timer handle.
tZTele[client] = INVALID_HANDLE;
// Teleport client to spawn.
ZTeleTeleportClient(client);
// If we're forcing the ZTele, then don't increment the count or print how many teleports they have used.
// Tell client they've been teleported.
TranslationPrintCenterText(client, "ZTele countdown end", g_iZTeleCount[client], ztelemax);
@ -227,7 +237,119 @@ ZTeleTeleportClient(client)
}
/**
* Command callback (zmenu)
* Menu callback (ztele_force)
* Forces ZTele on a client.
*
* @param menu The menu handle.
* @param action Action client is doing in menu.
* @param client The client index.
* @param slot The menu slot selected. (starting from 0)
*/
public ZTeleForceHandle(Handle:menu_ztele_force, MenuAction:action, client, slot)
{
// Client selected an option.
if (action == MenuAction_Select)
{
// Get the client index of the selected client.
new target = MenuGetClientIndex(menu_ztele_force, slot);
// Get the target's name for future use.
decl String:targetname[MAX_NAME_LENGTH];
GetClientName(target, targetname, sizeof(targetname));
// Force ZSpawn on the target.
new bool:success = ZTeleClient(target, true);
// Tell admin the outcome of the action.
if (success)
{
TranslationReplyToCommand(client, "ZTele command force successful", targetname);
}
else
{
TranslationReplyToCommand(client, "ZTele command force unsuccessful", targetname);
}
// Re-send the menu.
MenuClientList(client, ZTeleForceHandle, "ZTele clients title");
}
// Client closed the menu.
if (action == MenuAction_Cancel)
{
// Client hit "Back" button.
if (slot == MenuCancel_ExitBack)
{
// Re-open admin menu.
ZAdminMenu(client);
}
}
// Client exited menu.
if (action == MenuAction_End)
{
CloseHandle(menu_ztele_force);
}
}
/**
* Command callback (zr_ztele_force)
* Force ZSpawn on a client.
*
* @param client The client index.
* @param argc Argument count.
*/
public Action:ZTeleForceCommand(client, argc)
{
// If not enough arguments given, then stop.
if (argc < 1)
{
TranslationReplyToCommand(client, "ZTele command force syntax");
return Plugin_Handled;
}
decl String:target[MAX_NAME_LENGTH], String:targetname[MAX_NAME_LENGTH];
new targets[MAXPLAYERS], bool:tn_is_ml, result;
// Get targetname.
GetCmdArg(1, target, sizeof(target));
// Find a target.
result = ProcessTargetString(target, client, targets, sizeof(targets), COMMAND_FILTER_ALIVE, targetname, sizeof(targetname), tn_is_ml);
// Check if there was a problem finding a client.
if (result <= 0)
{
ZRReplyToTargetError(client, result);
return Plugin_Handled;
}
// x = Client index.
for (new x = 0; x < result; x++)
{
// Give client the item.
new bool:success = ZTeleClient(targets[x], true);
// Tell admin the outcome of the command if only 1 client was targetted.
if (result == 1)
{
if (success)
{
TranslationReplyToCommand(client, "ZTele command force successful", targetname);
}
else
{
TranslationReplyToCommand(client, "ZTele command force unsuccessful", targetname);
}
}
}
// Log action to game events.
LogEvent(false, LogType_Normal, LOG_GAME_EVENTS, LogModule_ZTele, "Force ZTele", "Admin \"%L\" forced player(s) to teleport to spawn. (zr_ztele_force)", client);
return Plugin_Handled;
}
/**
* Command callback (ztele)
* Teleport back to spawn if you are stuck.
*
* @param client The client index.