Параметры массива
Вы можете передавать массивы или строки в качестве параметров. Важно отметить, что они идут как ссылка, то есть копия данных не делается, а отдается непосредственно ссылка на данные. Существует простой способ объяснить это более конкретно.
new example[] = {1, 2, 3, 4, 5};
ChangeArray(example, 2, 29);
ChangeArray(array[], index, value)
{
array[index] = value;
}
Эта функция устанавливает заданный индекс в массиве с учетом значения. Когда она запускается на примере нашего массива, она меняет индекс 2 для значения 3 на 29. То есть:
example[2] = 29;
Это возможно лишь потому, что массив может быть непосредственно изменён. Чтобы предотвратить массив от изменения, можно пометить его как постоянную const. Это позволит понизить риск на ошибку в коде от её изменения. Например:
CantChangeArray(const array[], index, value)
{
array[index] = value; // Не компилируется
}
Используя const в параметрах массивов, вы будете точно знать, что он не будет изменен.
Выражения
Выражения являются точно такими же, какими они существуют в математике. Это группы операторов/символов, которые приходятся на один фрагмент данных. Они часто заключены в скобках (внутри скобок). Они содержат строгий "порядок операций". Они могут содержать переменные, функции, цифры и выражения сами могут быть вложенные внутрь других выражений, и даже приняты в качестве параметров.
Приведем пример простейшего выражения:
PHP:
0; // Возвращает число 0
(0); // Также возвращает число 0
Хотя выражения могут возвращать значения, они также могут ответить какое значение содержит ответ ноль или не ноль. В этом смысле, ноль является ложью (false), а не нулевое значение истиной (true). Например, -1 истина в Pawn, поскольку она не является нулем. Не думайте, что отрицательные числа являются ложными.
Порядок операций выражения аналогичен языку C. Вот несколько примеров выражений:
PHP:
5 + 6; // Вычисляет как 11
5 * 6 + 3; // Вычисляет как 33
5 * (6 + 3); // Вычисляет как 45
5.0 + 2.3; // Вычисляет как 7.3
(5 * 6) % 7; // Модульный оператор, вычисляет как 2
(5 + 3) / 2 * 4 - 9; // Вычисляет как 7
Как уже отмечалось, выражения могут содержать переменные, или даже функции:
PHP:
new a = 5 * 6;
new b = a * 3; // Вычисляет как 90
new c = AddTwoNumbers(a, b) + (a * b);
Операторы
Есть несколько полезных дополнительных операторов в Pawn. Например:
PHP:
new a = 5;
a = a + 5;
Может быть переписан, как:
PHP:
new a = 5;
a += 5;
Это верно в отношении следующих операторов в Pawn:
*, /, -, +
|, &, ^, ~, <<, >>
Кроме того, существуют операторы увеличение/понижения:
PHP:
a = a + 1;
a = a - 1;
Может быть упрощено, как:
PHP:
a++;
a--;
Дополнительно отметим, что ++ или -- может быть представлен до переменной (до-инкремент, до-декремент) или после переменной (пост-инкремент, пост-декремент). Разница заключается в том, как остальная часть выражения видит результат.
До: переменная увеличивается до определения и остальная часть выражения видит новое значение.
Пост: переменная увеличивается после определения и остальная часть выражения видит старое значение.
Например:
PHP:
new a = 5;
new b = a++; // b = 5, a = 6
new c = ++a; // a = 7, c = 7
Операторы сравнения
Существуют шесть операторов для сравнения двух числовых значений, а результат является либо истиной (не ноль), либо ложью (ноль):
- a == b - действительно, если a и b имеют одинаковые значения.
- a != b - действительно, если b имеет другое значение.
- a > b - действительно, если a больше b
- a >= b - действительно, если a больше или равно b
- a < b - действительно, если a меньше b
- a <= b - действительно, если a меньше или равно b
Например:
PHP:
(1 != 3); // Определяется как истина, поскольку 1 не равно 3
(3 + 3 == 6); // Определяется как истина, поскольку 3+3 равно 6
(5 - 2 >= 4); // Определяется как ложь, поскольку 3 меньше 4
Заметим, что эти операторы не работают с массивами и строками. То есть, вы не можете сравнить их с помощью == .
Действительные операторы
Действительные операторы могут быть скомбинированы тремя булевыми (boolean) операторами:
- a && b - истина, если a и b истинные. Ложь, если a и (или) b ложные.
- a || b - истина, если a или b (или обе переменные) истинные. Ложь, если обе переменные a и b ложные.
- !a - Истина, если a ложь. Ложь, если a истина.
Например:
PHP:
(1 || 0); // Определяется как истина, так как выражение 1 истинное
(1 && 0); // Определяется как ложь, так как выражение 0 ложное
(!1 || 0); // Определяется как ложь, так как выражение !1 ложное
Левое/правое значения
Два важных понятия левого и правого значений или левостороннее и правостороннее значения. Левостороннее значение имеет то, что появляется на левой стороне выражения, а правостороннее значение - на правой стороне выражения.
Например:
PHP:
new a = 5;
В этом примере a является левосторонним значением и 5 является правосторонним значением.
Правила:
- Выражения никогда не будут левосторонними значениями.
- Переменные являются двумя: левосторонними и правосторонними значениями.
Условия
Условия позволяют вам запускать код, если определенное условие выполнено.
Если соответствует (if/else if)
Если соответствует одно или более условий. Например:
PHP:
if (a == 5)
{
/* Код будет запущен, если условие будет истинным */
}
Они могут быть расширены для более сложных случаев:
PHP:
if (a == 5)
{
/* Код */
}
else if (a == 6)
{
/* Код */
}
else if (a == 7)
{
/* Код */
}
Вы также можете обрабатывать случаи, даже если выражение не верно. Например:
PHP:
if (a == 5)
{
/* Код */
}
else
{
/* Код, который будет запущен если нет истинного выражения */
}
Оператор выбора (switch)
Оператор выбора будет ограничен условием. Он необходим для выражения, выполняющего код для целого ряда возможных значений. Например:
PHP:
switch (a)
{
case 5:
{
/* Код, который будет выполняться, если a равно 5 */
}
case 6:
{
/* Код, который будет выполняться, если a равно 6 */
}
case 7:
{
/* Код, который будет выполняться, если a равно 7 */
}
case 8, 9, 10:
{
/* Код, который будет выполняться, если a равно или 5, или 9, или 10 */
}
default:
{
/* Код, который будет выполняться, если ни одно условие не соответствует */
}
}
Циклы
Циклы позволяют вам без труда повторять выполнение кода, пока условие истинно.
For циклы
For циклы - это циклы, которые состоят из четырех частей:
- Оператор инициализации - запускается один раз перед первым циклом.
- Оператор условия - проверяет условие и запускает следующий цикл, в том числе первый. Цикл прекращается, когда это выражение становится ложным.
- Оператор итерации - запускается после каждого цикла.
- Тело цикла - запускается каждый раз, пока оператор условия вычисляется как истинный.
PHP:
for ( /* инициализация */ ; /* условие */ ; /* итерация */ )
{
/* тело */
}
Простым примером является функция сложения массива:
PHP:
new array[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
new sum = SumArray(array, 10);
SumArray(const array[], count)
{
new total;
for (new i = 0; i < count; i++)
{
total += array[i];
}
return total;
}
Разберем подробнее:
new i = 0 - создает новую переменную для цикла i и устанавливает её в 0.
i < count - только запускает цикл, если i меньше, чем count. Это гарантирует, что чтение цикла прекращается в определенный момент. В этом случае, мы не хотим читать недействительные индексы в массиве.
i++ - увеличивает i на единицу после каждого цикла. Это гарантирует, что цикл не будет запущен вечно. В конце концов i станет слишком большим и цикл завершится.
Таким образом, функция SumArray будет циклом каждого действительного индекса массива, каждый раз добавляя это значение в sum.
While циклы
While циклы являются менее распространенными, чем for циклы, но на самом деле это более простые циклы. Они имеют только две части:
- Оператор условия - проверяется перед началом каждого цикла. Цикл прекращается, когда условие становится ложным.
- Тело цикла - запускается каждый раз пока цикл выполняется.
PHP:
while ( /* условие */ )
{
/* тело */
}
До тех пор, пока условие выражения остается истинным, цикл будет выполняться. Каждый for цикл может быть переписан, как while цикл:
PHP:
/* инициализация */
while ( /* условие */ )
{
/* тело */
/* итерация */
}
Пример предыдущего for цикла, переписанного, как while цикл:
PHP:
SumArray(const array[], count)
{
new total, i;
while (i < count)
{
total += array[i];
i++;
}
return total;
}
Существуют также do...while циклы, которые используются еще реже. Они работают как и while циклы, но проверяют условие после каждого цикла, а не перед ним. Это означает, что цикл всегда будет запущен, по крайней мере один раз. Например:
PHP:
do
{
/* тело */
}
while ( /* условие */ );
Управление циклами
Существуют два случая, когда нам нужно контролировать цикл:
- пропустить одну итерацию или цикл и продолжить выполнение цикла, как обычно;
- разорвать цикл целиком, прежде чем он закончится.
Допустим у вас есть функция, которая принимает массив и ищет соответствия цифр. Вы хотите его остановить, когда число будет найдено:
PHP:
/**
* Возвращает массив, если индекс значения или -1, или не найден.
*/
SearchInArray(const array[], count, value)
{
new index = -1;
for (new i = 0; i < count; i++)
{
if (array[i] == value)
{
index = i;
break;
}
}
return index;
}
Конечно, эту функцию можно вернуть и способом return i, но пример показывает, как break может остановить цикл.
Кроме того, ключевое слово continue пропускает итерации цикла. Например, мы хотим суммировать все четные числа:
PHP:
SumEvenNumbers(const array[], count)
{
new sum;
for (new i = 0; i < count; i++)
{
if (array[i] % 2 == 1)
{
/* Пропускаем оставшуюся часть итерации цикла */
continue;
}
sum += array[i];
}
return sum;
}
Область действия
Область действия относится к удобочитаемости кода. Это означает, что код одного уровня не может быть "виден" в коде другого уровня. Пример:
PHP:
new A, B, C;
Function1()
{
new B;
Function2();
}
Function2()
{
new C;
}
В этом примере A, B и C имеют глобальную область действия. Их можно увидеть в любой функции. Вместе с тем, B в функции Function1 не является той же переменной, как B на глобальном уровне. Вместо этого она находится в локальной области действия, и поэтому является локально изменяемой. Кроме того, функции Function1 и Function2 ничего не знают о существовании других переменных. Она так же является не только локальной переменной функции Function1, но и создается заново каждый раз, когда функция вызывается. Попробуйте представить это:
PHP:
Function1()
{
new B;
Function1();
}
В приведенном выше примере, функция Function1 вызывает сама себя. Конечно, это бесконечная рекурсия, что очень плохо, но идея заключается в том, что каждый раз, когда функция срабатывает, то создается новая копия B. Когда функция завершается, B уничтожается и её значение теряется.
Это свойство можно объяснить проще тем, что область действия переменной равна уровню её вложенности. То есть переменная на глобальной области действия видна для всех функций. Переменная в локальной области действия видна всему блоку кода расположенному "ниже" этого уровня. Например:
PHP:
Function1()
{
new A;
if (A)
{
A = 5;
}
}
Приведенный выше код является действительным, поскольку область действия распространяется по всей функции. Однако этот код, работать не будет:
PHP:
Function1()
{
new A;
if (A)
{
new B = 5;
}
B = 5;
}
Отметим, что B объявляется в новом блоке кода. Это означает, что B доступна только в том блоке кода, в котором была создана (и всем под-блокам вложенных внутри него). Как только блок кода прекращается, B становится не действительной.
Динамические массивы
Динамические массивы - это массивы, которые не имеют фиксированного размера. Например:
PHP:
Function1(size)
{
new array[size];
/* Код */
}
Динамические массивы могут иметь любое выражение, соответствующее их размеру до тех пор, пока выражение вычисляется. Как и для обычных массивов, SourcePawn не сможет узнать размер массива после того, как он будет создан. Вы должны задать его, если хотите использовать массив позднее.
Динамические массивы действительны только в локальной области действия , так как код не может существовать на глобальном уровне.
Расширенное объявление переменных
Переменные могут быть объявлены другим путем, чем просто new.
decl
По умолчанию, все переменные в Pawn будут инициализированы, как нуль. Если есть явная инициализация, переменная инициализируется для выражения = определенным символам. В локальной области действия, это может потребовать время на выполнение. Ключевое слово decl (которое действительно только в локальной области действия) было введено, чтобы позволить решать пользователю: хочет ли он инициализировать переменную или нет.
Примечание: decl не должно быть использовано на одну однострочную переменную. Это будет не выгодно.
Например:
PHP:
new c = 5;
new d;
new String:blah[512];
Format(blah, sizeof(blah), "%d %d", c, d);
В этом коде c равна 5 и d равна 0. Во время выполнения этого кода затраты на инициализацию незначительные. Вместе с тем, blah является большим массивом и затраты на инициализацию всего массива могут быть больше 0 сек. и иметь плохие последствия в определенных ситуациях.
Заметим, что blah не должен быть нулевой. В период времени с объявления new и и перемещения в Format(), массив blah никогда не будет загружен или прочитан. Данный код будет более эффективен, если будет написанный следующим образом:
PHP:
new c = 5;
new d;
decl String:blah[512];
Format(blah, sizeof(blah), "%d %d", c, d);
Предостережения: Обратная сторона decl состоит в том, что его переменные будут начинаться с "ненужного" содержания. Например, если мы будем использовать:
PHP:
new c = 5;
new d;
decl String:blah[512];
PrintToServer("%s", blah);
Format(blah, sizeof(blah), "%d %d", c, d);
Этот код может привести к падению сервера, так как массив blah может быть полностью испорчен (строки требуют нулевой символ, который может отсутствовать). Точно так же, если мы сделаем:
PHP:
new c = 5;
decl d;
decl String:blah[512];
Format(blah, sizeof(blah), "%d %d", c, d);
Значение d в настоящее время не определено. Оно может быть любым значением, отрицательным или положительным.
Заметим, что это легко и эффективно обезопасит строки. Пример ниже показывает, как предотвратить строки от мусора:
PHP:
decl String:blah[512];
blah[0] = '\0';
Золотые правила:
- Используйте decl только, если в период объявления и загрузки/чтения значения, вы абсолютно уверены, что есть по крайней мере одно хранилище/операция, которая отдает переменной действительные данные.
- Не оптимизируйте преждевременно. Кроме того, нет необходимости использовать decl на не массивы, поскольку нет никаких дополнительных затрат на инициализацию одного однострочного значения.
Этот пример не является столь эффективным, как decl:
PHP:
new String:blah[512] = "a";
Даже несмотря на то, что строка имеет только один символ, оператор new гарантирует, что остальная часть массива будет нулевой.
Также обратите внимание, что он является неправильным для явной инициализации decl:
PHP:
decl String:blah[512] = "a";
Приведенный выше код не будет компилироваться, потому что цель decl состоит в том, чтобы избежать каких-либо инициализаций.
static
Ключевое слово static входит в глобальную и локальную область действия. Оно имеет различные значения в каждой из них.
Глобальный static
Глобальные static переменные могут быть доступны только в рамках того же файла.
Например:
PHP:
// file1.inc
static Float:g_value1 = 0.15f;
// file2.inc
static Float:g_value2 = 0.15f;
Если плагин включает в себя оба этих файла, он не сможет использовать g_value1 или g_value2. Это простой механизм сокрытия информации и аналогичен элементам объявления переменных, например, private в таких языках, как C++, Java или C#.
Локальный static
Локальная static переменная является видимой лишь из её местной области действия.
Например:
PHP:
MyFunction(inc)
{
static counter = -1;
counter += inc;
return counter;
}
В этом примере counter технически глобальная переменная - она инициализируется один раз как -1 и никогда не инициализируется заново. Это означает, что при каждом запуске функции MyFunction, переменная counter и ее хранение в памяти одно и тоже.
Возьмем следующий пример:
PHP:
MyFunction(5);
MyFunction(6);
MyFunction(10);
В этом примере, counter может быть -1 +5 +6 +10 или 20, поскольку она сохраняется за рамками этой функции. Это может создавать проблемы для рекурсивной функции: если ваша функция может быть рекурсивной, то static, как правило, не является хорошей идеей, если только ваш код не реентерабельный.
Преимуществом локальных static переменных является то, что вам не придется загромождать свой сценарий глобальными переменными. До тех пор, пока переменной не нужно читать другую функцию, Вы можете его пихать внутрь функции и её сохранение будет гарантировано.
Заметим, что static может существовать в любой локальной области действия:
PHP:
MyFunction(inc)
{
if (inc > 0)
{
static counter;
return (counter += inc);
}
return -1;
}
**Пример моего кода на SourcePawn ( Простой пример ) :**
#pragma semicolon 1
#include <sourcemod>
#pragma newdecls required
int g_iTime;
bool g_bProtect, g_bColor;
ConVar g_hCvar[2];
Handle g_hTimer;
#define zaLoop() for(int i = 1; i <= MaxClients; ++i) if(IsClientInGame(i) && IsPlayerAlive(i))
public Plugin myinfo =
{
name = "Spawn Protected",
author = "plagueswamp",
version = "1.0",
};
public void OnPluginStart()
{
HookEvent("round_start", OnRoundStart);
HookEvent("player_spawn", OnPlayerSpawn);
g_hCvar[0] = CreateConVar("sm_easysp_time", "7", "Время защиты");
g_hCvar[1] = CreateConVar("sm_easysp_color", "1", "Красить ли защищенных игроков в зеленый? 1 - Да; 0 - Нет.", _, true, _, true, 1.0);
for(int i; i < 2; ++i) HookConVarChange(g_hCvar[i], CvarChanged);
AutoExecConfig(true, "easysp");
}
public void CvarChanged(Handle hCvar, char[] oldValue, char[] newValue)
{
if(hCvar == g_hCvar[0])
g_iTime = StringToInt(newValue);
else
g_bColor = !!StringToInt(newValue);
}
public void OnConfigsExecuted()
{
g_iTime = GetConVarInt(g_hCvar[0]);
g_bColor = GetConVarBool(g_hCvar[1]);
}
public void OnRoundStart(Handle hEvent, char[] name, bool dontBroadcast)
{
g_bProtect = true;
if(g_hTimer) delete g_hTimer;
g_hTimer = CreateTimer(float(g_iTime), ProtectEnd);
PrintToChatAll(" \x05\x01Все игроки защищены от урона на \x04%i\x01 сек.", g_iTime);
zaLoop()
{
SetEntProp(i, Prop_Data, "m_takedamage", 0);
if(g_bColor) SetEntityRenderColor(i, 0, 255, 0, 255);
}
}
public Action ProtectEnd(Handle hTimer)
{
g_hTimer = null;
g_bProtect = false;
PrintToChatAll(" \x05\x01Защита от урона снята!");
zaLoop()
{
SetEntProp(i, Prop_Data, "m_takedamage", 2);
if(g_bColor) SetEntityRenderColor(i, 255, 255, 255, 255);
}
}
public void OnPlayerSpawn(Handle hEvent, char[] name, bool dontBroadcast)
{
if(g_bProtect)
{
int client = GetClientOfUserId(GetEventInt(hEvent, "userid"));
SetEntProp(client, Prop_Data, "m_takedamage", 0);
if(g_bColor) SetEntityRenderColor(client, 0, 255, 0, 255);
}
}