С перва не большое введение: Как известно компьютер в процессе своей работы оперирует лишь 2мя десятичными числами
(или другими словами в двоичной системе счисления) 0/1
(истина/ложь). Понимая это - можно читать дальше
Представление десятичных чисел в двоичном виде:Не залезая в с++ или иные высшие
/низшие языки программирования, возьмем за стандарт
pawn (по скольку именно он нас интересует и на данный момент работаем только с положительными значениями). В павне любая целочисленная переменная имеет 32 разряда
(32 битные). Это значит что целочисленная переменная может содержать 32 bool значения. Что такое бит ? - это минимальный размер ячейки, возможные значения естественно - 0/1. Итак пример чисел:
1 - десятичное, это 1 в минус 31 степени число в двоичном представлении, дабы не писать 31 нолей перед единицей - допущу упращение записи "01"
(не путать с 8 меричной системой счисления, о ней речь не идет) - ("1" - десятичное)
ПримерыЦитата:
1 = 01
2 = 010
3 = 011
4 = 0100
5 = 0101 ...
В общем говоря переводы из 10й в 2ную
(и наоборот) легко делаются в уме, исключения составляют лишь огромные значения на типо сотней тысяч ...
Знаки побитовых операцийПодробнее о логических операциях смотрите в других источниках, тут только освежение памяти Цитата:
& - и (true - только при совпадении true разрядов или другими словами при совпадении "1")
011 001 010 // 202
&
110 011 000 // 408
=
010 001 000 // 136
~ - не (побитовое отрицание)
~001 101 010 = 110 010 101
| - или
010 111 101
|
001 100 001
=
011 111 101
^ - исключающие или (либо)
111 000 111
^
010 110 011
=
101 110 100
Операции смещений<< - Побитовое смещение влево на указанное колличество разрядов
(Другими словами умножение на 2, при попытке умножения применять нужно разумно!)>> - Побитовое смещение вправо на указанное колличество разрядов
(Другими словами деление на 2, при попытке деления применять нужно разумно!)Принцип записи в переменнуюnew i |= 1
<< (id - 1)
// id - индекс игрока, операция "- 1" уменьшение на еденицу - нужно для того что бы не выйти за пределы переменной i, к примеру при значении id = 32 именно это и должно произойти, но на деле этого не происходит и при превышении разрядов - запускается 2й круг. Вычитание по сути является действием по желанию, лично я его применяюДля удобочитаемости и применения данных методов - рекомендую использовать макросы
(О них смотрите в других источниках)#define bit_owner_add(%1) i |= 1 << (%1 - 1)#define bit_owner_sub(%1) i &= ~(1 << (%1 - 1))#define bit_owner(%1) i & 1 << (%1 - 1)В итоге имеем 3 "функции"
(-_-): записи, чтения, удаления bool значений
Пример использования:Цитата:
new g_connected
#define bit_connected_add(%1) g_connected |= 1 << (%1 - 1)
#define bit_connected_sub(%1) g_connected &= ~(1 << (%1 - 1))
#define bit_connected(%1) g_connected & 1 << (%1 - 1)
public client_connected(id)
{
bit_connected_add(id)
}
public client_disconnect(id)
{
bit_connected_sub(id)
}
function()
{
for(new i = 1; i <= g_maxplayers; i++)
{
if(bit_connected(i)) // альтернатива функции - is_user_connected()
{
// code
}
}
}
По аналогии можно выполнить любые булевые проверки, к примеру проверку на жизнь игрока.
Немного фичей:Цитата:
1)
Можно используя -
^ выполнить проверку "!="
if(killer
^ victim)
// Так-же можно сравнивать любые другие числа{
// Выполнится только если убийца НЕ жертва, причем индексы могут быть больше "32" }
2)
Можно так-же применить проверку булевого значения методом отрицания псевдо массива
(методом "отпротивного"). Иногда именно такой метод приходиться применять для оптимизации кода
if(1
<< (id - 1)
& ~g_connected)
// вернет true - если игрока нету, иначе если он есть вернет - false3)
Метод применения "исключающего или"
(либо)if(
g_Flags & flag_Server)
// Если такой флаг есть{
g_Flags ^= flag_Server
// Этой манипуляцией мы исключим флаг, НО если применить подобные метод без проверки на наличие флага - флаг наоборот присвоится. Получается закономерность - если значения НЕТУ - оно присвоится и наоборот - если есть - УНИЧТОЖИТСЯ!. Хотелось бы еще отметить, что это применимо только к побитовым операциям, обычные десятичные числа так лучше не складывать или вычитать, по-скольку последствия будут не предсказуемыми!// Безопасным методом исключения флага является этот - g_Flags &= ~flag_Server , его можно применять без проверок наличия флага, если флага нету ничего не произойдет}
4)
Превосходство флагов и побитовых операций
(Особенно сильный эффект при проверке 2х и более флагов), так к примеру можно выполнить подобные сравнения:
#define _ADMIN_FLAGS (ADMIN_IMMUNITY | ADMIN_RESERVATION) // Возможно так-же сравнение флагов энтити - родных или тех которые содержаться в ваших переменныхif(
!(((
get_user_flags(id)
& _ADMIN_FLAGS)
^ _ADMIN_FLAGS)))
{
// юзер обязательно должен иметь 2 этих флага}
if(
get_user_flags(id)
& _ADMIN_FLAGS)
{
// Юзер должен иметь хотябы 1н флаг из перечня или некоторые из них или все флаги}
5)
Возможность обьединения разных систем счислений
set_pev(
beam, pev_rendermode, (
pev(
beam, pev_rendermode)
& 0xF0)
| 1
& 0x0F)
6)
Одной из важнейших особенностей подобных проверок является то, что - хоть переменные и содержат по сути булевые значения, они
(значения) являются целочисленными. Это позволяет производить манипуляции с функцией -
switchswitch(
g_Flags & flag_Server)
{
case 0:
{
// false - флага нету}
default:
{
// true - флаг есть}
}
Еще пример:switch(1
<< (id - 1)
& ~g_connected)
// вспоминаем что происходит при отрицании псевдо-массива{
case 0:
{
// true}
default:
{
// false}
}