Часть 1. Циклы
for(new i = GetMaxPlayers() - 1; i != -1; --i)
{
if(!IsPlayerConnected(i) || IsPlayerNPC(i)) continue;
SetPlayerArmour(i, 100.0);
}
В этом цикле всем игрокам сервера максимально быстро установится 100 единиц брони. А теперь давайте разберём интересные моменты по "кусочкам":
new i = GetMaxPlayers() - 1; - данная запись создаст переменную, величина которой будет равняться количеству слотов, записанных в конфиге сервера, но так как всего 800 игроков, то ID будут от 0 до 799, поэтому -1.
i != 0 - самое быстрое сравнение - не равно. В нашем случае мы ждём момента, пока уменьшаемая переменная не достигнет нуля.
--i - префиксный декремент, который сначала уменьшит переменную, а потом подставит её в цикл. (если писать i--, то нужно выполнять сравнение i != -1). Почему именно он? Потому что уменьшение переменной происходит быстрее, чем увеличение.
if(!IsPlayerConnected(i)) continue; - если игрок отсутствует на сервере, сразу переходим к следующей итерации, игнорируя дальнейшие условия.
if(IsPlayerNPC(i)) continue; - если игрок - бот, запускаем следующую итерацию.
В случае, когда нужно найти циклом игрока с определённым именем:
new PlayerName[MAX_PLAYER_NAME]; // вынесем из цикла, дабы не создавать бесполезные переменные
for(new i = GetMaxPlayers() - 1; i != -1; --i)
{
if(!IsPlayerConnected(i) || IsPlayerNPC(i)) continue;
GetPlayerName(i, PlayerName, sizeof(PlayerName));
if(strcmp(PlayerName, "OKStyle", false, MAX_PLAYER_NAME) != 0) continue; // если не нашелся, переключаем итерацию
SetPlayerArmour(i, 100.0);
break; // обрываем цикл при найденном игроке
}
В данном цикле мы максимально быстро выдадим игроку с ником OKStyle 100 единиц брони.
Как вы видите, MAX_PLAYERS вообще отсутствует.
Часть 2. Имя игрока
До этого момента некоторые из вас могли знать только о двух вариантах получения имени игрока:
а) Каждый раз функцией GetPlayerName, вида:
new PlayerName[MAX_PLAYER_NAME];
GetPlayerName(playerid, PlayerName, sizeof(PlayerName))
, где приходилось каждый раз создавать переменную.
б) Каждый раз функцией GetPlayerNameEx (например), вида:
GetPlayerNameEx(playerid)
, которая возвращала string с именем игрока с указанным id.
Сейчас я расскажу о небольшом "трике", который позволит быстро обращаться к имени заданного игрока.
В ваш enum с данными игрока нужно внести переменную:
В OnPlayerConnect:
GetPlayerName(playerid, PlayerInfo[playerid][pName], MAX_PLAYER_NAME); // мы не используем sizeof, т.к. он не может получить размер двойного массива.
И всё! Теперь в любом месте кода вы можете использовать эту переменную. Например:
printf("%s был замечен на сервере", PlayerInfo[playerid][pName]);
Как вариант, вы можете использовать PVar'ы, но в этом случае вам всегда нужно будет создавать переменную для извлечения данных и PVar'а. Так что этот вариант - не вариант. Однако запомните, что PVar'ами, созданными в моде, можно оперировать в ФС, без их объявления, и наоборот - созданными PVar'ами из ФС можно оперировать в моде (например, в фс в PVar записывается имя, в моде можно извлекать из PVar'а имя).
Часть 3. Public vs Stock
Тут всё просто. Для public-функции нужен форвардинг (forward) и служит она для использования в таймерах или в Call*-образных функциях (CallLocal..., CallRemote...). Stock служит для вне'Call'овых и внетаймерных функций. Не использованный в коде stock компилятор не записывает в amx, в этом случае я просто рекомендую удалять stock'и, которые в моде не используются, чтобы не засорять сорсы.
Могу порекомендовать такой макрос, который позволяет не форвардить функции:
Использовать (например):
fpublic SaveAccounts()
{
//действие
}
Часть 4. Условие перебора
Я думаю, что уже все поняли, что switch куда быстрее, чем if/else if, а if/else if быстрее, чем if/if.
Давайте разберем OnDialogResponse на примере:
switch(dialogid)
{
case 0:
{
if(!response) return 1;
switch(listitem)
{
case 0: {} //действие
}
}
}
Важно: не забывайте после последнего case дописывать:
default: {} // тут стандартное действие для элементов, которые вы не указали в переборе (если его нет, пишу то же самое, что в case 0)
Полезно: в case можно использовать несколько значений сразу, если них существуют одинаковые действия:
case 1..3: {} // от 1 до 3х
case 1, 2, 5, 8: {} // для 1, 2, 5 и 8 элемента
Немного об inputtext. Будет полезно и для команд (в OnPlayerCommandText - cmdtext):
if(inputtext[0] == EOS) return SendClientMessage(playerid, 0xFF0000FF, "Поле пустое!"); // EOS - End of String/Конец строки
И запомните главное: ставить задефайненную константу или цифру - дело ваше, в игре это никак не отразится, т.к. компилятор при компилировании ставит цифру в тех местах, где вы использовали обозначенную константу. Обычно константы создают для удобства программера.
Автор: OKStyle
for(new i = GetMaxPlayers() - 1; i != -1; --i)
{
if(!IsPlayerConnected(i) || IsPlayerNPC(i)) continue;
SetPlayerArmour(i, 100.0);
}
В этом цикле всем игрокам сервера максимально быстро установится 100 единиц брони. А теперь давайте разберём интересные моменты по "кусочкам":
new i = GetMaxPlayers() - 1; - данная запись создаст переменную, величина которой будет равняться количеству слотов, записанных в конфиге сервера, но так как всего 800 игроков, то ID будут от 0 до 799, поэтому -1.
i != 0 - самое быстрое сравнение - не равно. В нашем случае мы ждём момента, пока уменьшаемая переменная не достигнет нуля.
--i - префиксный декремент, который сначала уменьшит переменную, а потом подставит её в цикл. (если писать i--, то нужно выполнять сравнение i != -1). Почему именно он? Потому что уменьшение переменной происходит быстрее, чем увеличение.
if(!IsPlayerConnected(i)) continue; - если игрок отсутствует на сервере, сразу переходим к следующей итерации, игнорируя дальнейшие условия.
if(IsPlayerNPC(i)) continue; - если игрок - бот, запускаем следующую итерацию.
В случае, когда нужно найти циклом игрока с определённым именем:
new PlayerName[MAX_PLAYER_NAME]; // вынесем из цикла, дабы не создавать бесполезные переменные
for(new i = GetMaxPlayers() - 1; i != -1; --i)
{
if(!IsPlayerConnected(i) || IsPlayerNPC(i)) continue;
GetPlayerName(i, PlayerName, sizeof(PlayerName));
if(strcmp(PlayerName, "OKStyle", false, MAX_PLAYER_NAME) != 0) continue; // если не нашелся, переключаем итерацию
SetPlayerArmour(i, 100.0);
break; // обрываем цикл при найденном игроке
}
В данном цикле мы максимально быстро выдадим игроку с ником OKStyle 100 единиц брони.
Как вы видите, MAX_PLAYERS вообще отсутствует.
Часть 2. Имя игрока
До этого момента некоторые из вас могли знать только о двух вариантах получения имени игрока:
а) Каждый раз функцией GetPlayerName, вида:
new PlayerName[MAX_PLAYER_NAME];
GetPlayerName(playerid, PlayerName, sizeof(PlayerName))
, где приходилось каждый раз создавать переменную.
б) Каждый раз функцией GetPlayerNameEx (например), вида:
GetPlayerNameEx(playerid)
, которая возвращала string с именем игрока с указанным id.
Сейчас я расскажу о небольшом "трике", который позволит быстро обращаться к имени заданного игрока.
В ваш enum с данными игрока нужно внести переменную:
Код:
pName[MAX_PLAYER_NAME]
GetPlayerName(playerid, PlayerInfo[playerid][pName], MAX_PLAYER_NAME); // мы не используем sizeof, т.к. он не может получить размер двойного массива.
И всё! Теперь в любом месте кода вы можете использовать эту переменную. Например:
printf("%s был замечен на сервере", PlayerInfo[playerid][pName]);
Как вариант, вы можете использовать PVar'ы, но в этом случае вам всегда нужно будет создавать переменную для извлечения данных и PVar'а. Так что этот вариант - не вариант. Однако запомните, что PVar'ами, созданными в моде, можно оперировать в ФС, без их объявления, и наоборот - созданными PVar'ами из ФС можно оперировать в моде (например, в фс в PVar записывается имя, в моде можно извлекать из PVar'а имя).
Часть 3. Public vs Stock
Тут всё просто. Для public-функции нужен форвардинг (forward) и служит она для использования в таймерах или в Call*-образных функциях (CallLocal..., CallRemote...). Stock служит для вне'Call'овых и внетаймерных функций. Не использованный в коде stock компилятор не записывает в amx, в этом случае я просто рекомендую удалять stock'и, которые в моде не используются, чтобы не засорять сорсы.
Могу порекомендовать такой макрос, который позволяет не форвардить функции:
Код:
#define fpublic %1(%2) forward %1(%2); public %1(%2)
fpublic SaveAccounts()
{
//действие
}
Часть 4. Условие перебора
Я думаю, что уже все поняли, что switch куда быстрее, чем if/else if, а if/else if быстрее, чем if/if.
Давайте разберем OnDialogResponse на примере:
switch(dialogid)
{
case 0:
{
if(!response) return 1;
switch(listitem)
{
case 0: {} //действие
}
}
}
Важно: не забывайте после последнего case дописывать:
default: {} // тут стандартное действие для элементов, которые вы не указали в переборе (если его нет, пишу то же самое, что в case 0)
Полезно: в case можно использовать несколько значений сразу, если них существуют одинаковые действия:
case 1..3: {} // от 1 до 3х
case 1, 2, 5, 8: {} // для 1, 2, 5 и 8 элемента
Немного об inputtext. Будет полезно и для команд (в OnPlayerCommandText - cmdtext):
if(inputtext[0] == EOS) return SendClientMessage(playerid, 0xFF0000FF, "Поле пустое!"); // EOS - End of String/Конец строки
И запомните главное: ставить задефайненную константу или цифру - дело ваше, в игре это никак не отразится, т.к. компилятор при компилировании ставит цифру в тех местах, где вы использовали обозначенную константу. Обычно константы создают для удобства программера.
Автор: OKStyle