Склад полезного кода [GoldSrc]

wopox1337

Разработчик
Скриптер
Проверенный
Сообщения
40
Реакции
56
Помог
1 раз(а)
Баллы
18
Здесь собираем готовые решения, стоки и полезные функции для GoldSrc/AMX Mod X.
Формат: код + краткое описание + пример использования (если нужно).
Правила оформления:
  • Используйте тег C-подобный для кода;
  • Добавляйте описание того, что делает функция;
  • Указывайте параметры и возвращаемое значение;
  • Свой сток добавляйте в оглавление по категории


Оглавление

КатегорияНазвание функцииОписание
Геометрия и физикаis_wall_between_pointsПроверка наличия препятствий (стен) между двумя точками.
Геометрия и физикаCreateInCircleСоздание сущностей, равномерно распределенных по окружности.
Геометрия и физикаUTIL_GetModelHitBoxПолучение размеров хитбокса модели (динамически).
Строки и данныеparseTimeПарсинг строки времени (1d, 2h, 30m) в секунды.
Строки и данныеUTIL_SqlEscapeStringЭкранирование специальных символов для SQL запросов.
ВизуализацияPlayerScreenBlinking"Мигалка" эффектом ScreenFade с поддержкой цветов.
Конфиги и файлыGetConfigPathПоиск пути к конфигу карты (по приоритету: карта -> префикс -> дефолт).
Обновляйте таблицу при добавлении новых стоков.
 
Последнее редактирование:
Данный сток предназначен для для проверки наличия физических препятствий (в основном стен) между двумя точками. Метод выполняет трассировку луча между заданными координатами, игнорируя стекло, монстров, игроков и снаряды, и возвращает true, если луч столкнулся с чем-то на пути, и false, если путь свободен.

C-подобный:
stock is_wall_between_points(Float: start[3], Float: end[3], ignore_ent) {
    // Create the trace handle! It is best to create it!
    new ptr = create_tr2()

    // The main traceline function!
    // This function ignores GLASS, MISSILE and MONSTERS!
    // Here is an example of how you should combine all the flags!
    engfunc(EngFunc_TraceLine, start, end, IGNORE_GLASS | IGNORE_MONSTERS | IGNORE_MISSILE, ignore_ent, ptr)

    // We are interested in the fraction parameter
    new fraction
    get_tr2(ptr, TR_flFraction, fraction)

    // Free the trace handle (don't forget to do this!)
    free_tr2(ptr)

    // If = 1.0 then it didn't hit anything!
    return (fraction != 1.0)
}
 
Стоки с прошлого сайта. Возможно, немного модифицированные.
C-подобный:
/**
 * Парсинг строки для получения времени в секундах
 *
 * @param szTime    Строка для парсинга
 *
 * @return          Время в секундах
 */
stock parseTime(const szTime[])
{
    const SecondsInMinute = 60;
    const SecondsInHour = 3600;
    const SecondsInDay = 86400;
    const SecondsInWeek = 604800;
    const SecondsInMonth = 25920000;
    const SecondsInYear = 31536000;

    new k, t, h, bool:check_chars;

    for (new i; szTime[i] != EOS; i++)
    {
        if (isspace(szTime[i]))
        {
            check_chars = true;
            continue;
        }

        switch(szTime[i])
        {
            case '0'..'9':
            {
                if (check_chars)
                {
                    check_chars = false;
                    k += t;
                }

                t = (t * 10) + (szTime[i] - '0');
            }

            case 's', 'S':
            {
                k += t;
                t = 0;
            }

            case 'i','I':
            {
                k += t * SecondsInMinute;
                t = 0;
            }

            case 'h','H':
            {
                k += t * SecondsInHour;
                t = 0;
            }

            case 'd','D':
            {
                k += t * SecondsInDay;
                t = 0;
            }

            case 'w','W':
            {
                k += t * SecondsInWeek;
                t = 0;
            }

            case 'm','M':
            {
                k += t * SecondsInMonth;
                t = 0;
            }

            case 'y','Y':
            {
                k += t * SecondsInYear;
                t = 0;
            }

            default:
            {
                break;
            }
        }
    }

    return k + t;
}

/**
 * Замена для SQL запроса
 *
 * @param szDest   Строка
 * @param iLen     Длина строки
 *
 * @noreturn
 */
stock UTIL_SqlEscapeString(szDest, const iLen)
{
    replace_all(szDest, iLen, "\\", "\\\\");
    replace_all(szDest, iLen, "\0", "\\0");
    replace_all(szDest, iLen, "\n", "\\n");
    replace_all(szDest, iLen, "\r", "\\r");
    replace_all(szDest, iLen, "\x1a", "\Z");
    replace_all(szDest, iLen, "'", "\'");
    replace_all(szDest, iLen, "^"", "\^"");
}
 
C-подобный:
/**
 * Получает размеры хитбоксов у модели (динамически)
 *
 * @param szModel  Путь до модели (Пр. "models/bag.mdl")
 * @param flMaxs   Local BB max.
 * @param flMins   Local BB min.
 * @param flSize   max - min
 *
 * @return         true/false
 */

stock bool:UTIL_GetModelHitBox(const szModel[], Float: flMaxs[3], Float: flMins[3], Float: flSize[3])
{
    new hFile = fopen(szModel, "rb");

    if (!hFile)
    {
        server_print("Can`t open %s", szModel);
        return false;
    }

    fseek(hFile, 160, SEEK_SET);

    new iBlock;
    fread(hFile, iBlock, BLOCK_INT);
    fseek(hFile, iBlock + 8, SEEK_SET);
    
    new Float: flVec[6];
    fread_blocks(hFile, _:flVec, 6, BLOCK_INT);
    fclose(hFile);

    flMins[0] = flVec[0];
    flMins[1] = flVec[1];
    flMins[2] = flVec[2];
    flMaxs[0] = flVec[3];
    flMaxs[1] = flVec[4];
    flMaxs[2] = flVec[5];
    flSize[0] = flVec[3] - flVec[0];
    flSize[1] = flVec[4] - flVec[1];
    flSize[2] = flVec[5] - flVec[2];

    return true;
}
 
stock UTIL_SqlEscapeString(szDest, const iLen)
Неправильный сток, поскольку намешаны CTRLCHAR "\" и "^", должно быть, для "^" (по-умолчанию):
C-подобный:
#define _SC(%0)                     %0, charsmax(%0)

replace_all(_SC(s_tmp), "^r", "");
replace_all(_SC(s_tmp), "^n", "");
replace_all(_SC(s_tmp), "^t", "");
replace_all(_SC(s_tmp), "^x1a", "");
replace_all(_SC(s_tmp), "\", "\\");
replace_all(_SC(s_tmp), "`", "\`");
replace_all(_SC(s_tmp), "^'", "\^'");
replace_all(_SC(s_tmp), "^"", "\^"");

Но 8 раз проходиться по всем символам исходной строки, с вызовом натива, на мой взгляд, глупое решение. Я бы сделал, с учетом специфики ее использования, как-то так:

C-подобный:
stock UTIL_SqlEscapeString(const str[]) {
 
    new i, j, result[ACS_MAX_BUFFER_SIZE];

    for (; str[i] && j < charsmax(result); i++) {
        switch (str[i]) {
            case '^r', '^n', '^t', '^x1a':
                continue;
            case '`', '^'', '^"', '\': {
                result[j++] = '\';
                if (j >= charsmax(result))
                    break;
            }
        }
        result[j++] = str[i];
    }

    return result;
}
 
Последнее редактирование:
Потрясающие названия переменных. Когда уже забудете про венгерскую нотацию?

Возвращать из функции массив не очень хорошая идея. Во первых, будет лишнее копирование массива из стэка. Во вторых, размер массива ограничивается в функции. В третьих, всегда создается новый массив, даже когда он не нужен.
 
Потрясающие названия переменных. Когда уже забудете про венгерскую нотацию?

Возвращать из функции массив не очень хорошая идея. Во первых, будет лишнее копирование массива из стэка. Во вторых, размер массива ограничивается в функции. В третьих, всегда создается новый массив, даже когда он не нужен.

Это пример, можно перевести на любую нотацию. Копирование будет в любом случае, из стека или из буфера в оригинальную строку, если это делать также безопасно. Ограничение на размер массива задается в #define инклуда ваших проектов, обычно буфер 256 ячеек.

Идея состоит в том, чтобы использовать это в одной строке:
C-подобный:
formatex(_SC(s_query), "SELECT * FROM `player_names` WHERE name = '%s'", _qq(s_name));

На мой непрофессиональный взгляд это просто удобнее и быстрее. У меня этот натив "в 3 строки кода", вообще в модуле.
 
Последнее редактирование:
Это пример, можно перевести на любую нотацию.
Я не просто так придираюсь. Этот топик для примеров кода, верно? Логично ведь тогда подавать хорошие примеры.
А хорошие примеры не могут быть с именами вроде i_i, i_j или _qq. Потому что другие могут решить, что это нормально и будут повторять у себя в коде.
Копирование будет в любом случае
Вообще-то, можно избежать копирования, но при этом, алгоритм будет сложнее, что в данном случае не желательно и не имеет смысла.
В любом случае, массив s_result тогда уж должен быть статическим, потому что при больших размерах массива будет упор в размер стэка. А статические массивы\переменные располагаются в другой части памяти.

Но а самое забавное в том, что эта функция вообще не нужна, потому что в amxx уже есть готовая функция SQL_QuoteString 🤦‍♂️
 
@Refresh, можно делать так:
C-подобный:
new name[MAX_NAME_LENGTH * 2];
SQL_QuoteStringFmt(Empty_Handle, name, charsmax(name), "%n", playerId);
// use name var
  
new string[MAX_FMT_LENGTH * 2];
SQL_QuoteStringFmt(Empty_Handle, string, charsmax(string), "text");
// use string var
 
массив s_result тогда уж должен быть статическим, потому что при больших размерах массива
Используется new чтобы избежать необходимости писать 0 в конец строки, никаких проблем со стеком не возникало даже на стандарном #pragma dynamic.

@Albertio, cпасибо, %n я вырезал из всех плагинов из-за особенности реализации форматирования для отключившегося игрока - он выдает имя сервера. Я форматирую тысячи запросов во вспомогательных плагинах и выполняю их с определенной приоритезацией в десятки потоков в основном - на полном сервере это в сотни раз быстрее, даже если использовать дохлый db4.myarena.ru. Я считаю, что накладные расходы на использование штатных нативов sqlx для этой задачи, кратно больше, "корявей" и не имеет смысла. Я привык получать все данные "одной строкой":

C-подобный:
g_acs_client[id][acs_cl_ip_db_id] = rf_sql_nm_field(query, "id");
rf_sql_nm_field_str_c(query, "country", acs_cl_country);

Возможно, это по-венгерски, но это экономит кучу времени и делает код компактным (один плагин уменьшился с 63 до 27 КБ).

PS: Если бы вы написали какую-то инструкцию, по унификации именования переменных различных типов, принятых в вашем сообществе, это было бы полезно. Просто когда я вижу именования строки szName или My_Handle у меня глаз начинает дергаться.. Мои переменные унифицированы по типам и я могу одним нажатием кнопки переформатировать свой код в любой удобный формат.
 
Последнее редактирование:
@Refresh, ну, как то так:
C-подобный:
#include <amxmodx>

#define test_function(%0) (charsmax(%0))

#define CONST_VAR1 "test"
#define CONST_VAR2 1
#define CONST_VAR3 1.0
#define CONST_VAR4 true

new const CONST_VAR5[] = "test";

const CONST_VAR6 = 1;
const bool:CONST_VAR7 = true;
const Float:CONST_VAR8 = 1.0;

enum Struct {
    DATA1,
    DATA2,
    DATA3
};
new GlobalVar1[Struct];

new GlobalVar2 = 1;
new bool:GlobalVar3 = true;
new Float:GlobalVar4 = 1.0;

public plugin_init() {
    register_plugin("Test CodeStyle", "1.0.0", "Albertio");

    new localVar1[Struct];
    new localVar2 = 1;
    new bool:localVar3 = true;
    new Float:localVar4 = 1.0;
}
 
Последнее редактирование:
Данный сток предназначен для для проверки наличия физических препятствий (в основном стен) между двумя точками. Метод выполняет трассировку луча между заданными координатами, игнорируя стекло, монстров, игроков и снаряды, и возвращает true, если луч столкнулся с чем-то на пути, и false, если путь свободен.

C-подобный:
stock is_wall_between_points(Float: start[3], Float: end[3], ignore_ent) {
    // Create the trace handle! It is best to create it!
    new ptr = create_tr2()

    // The main traceline function!
    // This function ignores GLASS, MISSILE and MONSTERS!
    // Here is an example of how you should combine all the flags!
    engfunc(EngFunc_TraceLine, start, end, IGNORE_GLASS | IGNORE_MONSTERS | IGNORE_MISSILE, ignore_ent, ptr)

    // We are interested in the fraction parameter
    new fraction
    get_tr2(ptr, TR_flFraction, fraction)

    // Free the trace handle (don't forget to do this!)
    free_tr2(ptr)

    // If = 1.0 then it didn't hit anything!
    return (fraction != 1.0)
}
C-подобный:
public fw_TraceAttack_Pre(victim, attacker, Float:damage, Float:direction[3], tracehandle, damageBits)
{
    if (!get_pcvar_num(g_iCvar))
        return HC_CONTINUE;
    
    if (!(damageBits & DMG_BULLET))
        return HC_CONTINUE;
    
    if (!is_user_connected(attacker) || !is_user_alive(attacker))
        return HC_CONTINUE;
    
    if (!is_user_connected(victim) || !is_user_alive(victim))
        return HC_CONTINUE;
    
    new weapon = get_member(attacker, m_pActiveItem);
    
    if (!weapon)
        return HC_CONTINUE;
    
    new WeaponIdType:weaponId = get_member(weapon, m_iId);
    
    if (weaponId == WEAPON_M3 || weaponId == WEAPON_XM1014)
        return HC_CONTINUE;
    
    new Float:vecSrc[3], Float:vecEnd[3], Float:vecViewOfs[3];
    
    get_entvar(attacker, var_origin, vecSrc);
    get_entvar(attacker, var_view_ofs, vecViewOfs);
    xs_vec_add(vecSrc, vecViewOfs, vecSrc);
    
    get_tr2(tracehandle, TR_vecEndPos, vecEnd);
    
    new trace = create_tr2();
    engfunc(EngFunc_TraceLine, vecSrc, vecEnd, IGNORE_MONSTERS | IGNORE_GLASS, attacker, trace);
    
    new Float:fraction;
    get_tr2(trace, TR_flFraction, fraction);
    new hitEnt = get_tr2(trace, TR_pHit);
    free_tr2(trace);
    
    if (fraction < 1.0 && hitEnt != victim)
    {
        SetHookChainReturn(ATYPE_INTEGER, 0);
        return HC_SUPERCEDE;
    }
    
    return HC_CONTINUE;
}
этот код мб лучше?
 
этот код мб лучше?
Смотря для чего. Первый код всего лишь проверяет на наличие препятствия от трейса точки А до точки В. Второй код блочит нормальное поведение функции в случае, если на пути трейса от точки А до точки В было препятствие и попадание было зарегистрировано не в жертву. А в жертву так и так зарегано попадание не будет, потому что второй трейс идет с флагом IGNORE_MONSTERS, поэтому надобность во втором блоке условия
C-подобный:
if (fraction < 1.0 && hitEnt != victim)
автоматически отпадает
 
Смотря для чего. Первый код всего лишь проверяет на наличие препятствия от трейса точки А до точки В. Второй код блочит нормальное поведение функции в случае, если на пути трейса от точки А до точки В было препятствие и попадание было зарегистрировано не в жертву. А в жертву так и так зарегано попадание не будет, потому что второй трейс идет с флагом IGNORE_MONSTERS, поэтому надобность во втором блоке условия
C-подобный:
if (fraction < 1.0 && hitEnt != victim)
автоматически отпадает
Все верно
 
Данная функция позволяет сделать "мигалку" из мессаги ScreenFade.
C-подобный:
// blinksNum - Количество миганий.
// playerId - Айди игрока или 0 для всех.
// duration - Продолжительность эффекта затухания в секундах.
// holdTime - Время в секундах, необходимое для удержания затухания на максимальном уровне перед его исчезновением.
// color1 - Первый цвет(указывать в виде RGBA).
// color2 - Второй цвет(указывать в виде RGBA).
// isBlind - Проверять игрока на слепоту(если под флешкой, то мессага ему не отправляется).
PlayerScreenBlinking(10, playerId, 0.15, 0.15, {255, 0, 0, 100}, {0, 255, 0, 100}, true);
C-подобный:
enum _:RGBA {
    R,
    G,
    B,
    A
};

enum _:MessageDataStruct {
    RECIPIENT_ID,
    Float:DURATION,
    Float:HOLD_TIME,
    COLOR1[RGBA],
    COLOR2[RGBA],
    bool:IS_BLIND
};

enum _:TaskDataStruct {
    ARRAY_HANDLE,
    BLINKS_NUM,
    BLINKS_ITER
};

stock PlayerScreenBlinking(
    const blinksNum = 10,
    const playerId,
    const Float:duration = 1.0,
    const Float:holdTime = 1.0,
    const color1[RGBA] = {255, 255, 255, 255},
    const color2[RGBA] = {255, 255, 255, 255},
    const bool:isBlind = true
) {
    const TASK_ID_BASE = 13131313;

    new messageData[MessageDataStruct];
    messageData[RECIPIENT_ID] = playerId;
    messageData[DURATION] = duration;
    messageData[HOLD_TIME] = holdTime;
    messageData[COLOR1][R] = color1[R];
    messageData[COLOR1][G] = color1[G];
    messageData[COLOR1][B] = color1[B];
    messageData[COLOR1][A] = color1[A];
    messageData[COLOR2][R] = color2[R];
    messageData[COLOR2][G] = color2[G];
    messageData[COLOR2][B] = color2[B];
    messageData[COLOR2][A] = color2[A];
    messageData[IS_BLIND] = isBlind;

    new Array:messageDataArray = ArrayCreate(MessageDataStruct);
    ArrayPushArray(messageDataArray, messageData);

    new taskData[TaskDataStruct];
    taskData[ARRAY_HANDLE] = any:messageDataArray;
    taskData[BLINKS_NUM] = blinksNum;

    for(new i = 1; i <= blinksNum; i++) {
        taskData[BLINKS_ITER] = i;
        set_task(duration * i, "ScreenBlinkingTask", TASK_ID_BASE, taskData, sizeof(taskData));
    }
}

public ScreenBlinkingTask(taskData[TaskDataStruct]) {
    new messageData[MessageDataStruct];
    new Array:messageDataArray = any:taskData[ARRAY_HANDLE];
    ArrayGetArray(messageDataArray, 0, messageData);

    if(taskData[BLINKS_NUM] == taskData[BLINKS_ITER])
        ArrayDestroy(messageDataArray);

    new color[RGBA];

    if(taskData[BLINKS_ITER] % 2) {
        color[R] = messageData[COLOR1][R];
        color[G] = messageData[COLOR1][G];
        color[B] = messageData[COLOR1][B];
        color[A] = messageData[COLOR1][A];
    } else {
        color[R] = messageData[COLOR2][R];
        color[G] = messageData[COLOR2][G];
        color[B] = messageData[COLOR2][B];
        color[A] = messageData[COLOR2][A];
    }
    
    SendScreenFadeMessage(
        messageData[RECIPIENT_ID],
        messageData[DURATION],
        messageData[HOLD_TIME],
        color,
        messageData[IS_BLIND]
    );
}

stock SendScreenFadeMessage(
    const playerId,
    const Float:duration = 1.0,
    const Float:holdTime = 1.0,
    const color[RGBA] = {255, 255, 255, 255},
    const bool:isBlind = true
) {
    const FFADE_OUT = 0x0001;

    static msgId;

    if(msgId == 0 && (msgId = get_user_msgid("ScreenFade")) == 0)
        return;

    for(new i = (playerId == 0) ? 1 : playerId; i <= MaxClients; i++) {
        if(!is_user_connected(i))
            continue;

        if(isBlind && PlayerIsBlind(i))
            continue;

        message_begin(MSG_ONE_UNRELIABLE, msgId, {0, 0, 0}, i);
        write_short(FixedUnsigned16(duration));
        write_short(FixedUnsigned16(holdTime));
        write_short(FFADE_OUT);
        write_byte(color[R]);
        write_byte(color[G]);
        write_byte(color[B]);
        write_byte(color[A]);
        message_end();
    }
}

stock bool:PlayerIsBlind(const playerId) {
    return bool:(Float:get_member(playerId, m_blindUntilTime) > get_gametime());
}

stock FixedUnsigned16(Float:value) {
    return clamp(floatround(value * 4096.0), 0, 0xFFFF);
}
 
Сток создаёт указанное количество сущностей, равномерно распределённых по окружности на расстоянии R от её центра.

В те дни, когда я в настроении бываю, Сидя у огня, черепа перебираю (С)


C-подобный:
/**
 * Создаёт указанное количество сущностей, равномерно распределённых по окружности
 * в заданной плоскости Z (горизонтальной плоскости XY).
 *
 * @param Float:centerX -  X-координата центра окружности.
 * @param Float:centerY - Y-координата центра окружности.
 * @param Float:centerZ - Z-координата (высота), на которой будут созданы все сущности.
 * @param Float:radius - Радиус окружности (должен быть ≥ 0; при radius = 0 все сущности будут в центре).
 * @param sectorsCount  -  Количество секторов (и, соответственно, сущностей) для создания.
 * Если sectorsCount ≤ 0 — функция завершается без действий.
 */


#include <amxmodx>
#include <float>
#include <xs>

public CreateInCircle(Float:centerX, Float:centerY, Float:centerZ, Float:radius, const sectorsCount)
{
    if (sectorsCount <= 0) {
        return;
    }
    
    new Float:angleStep = 360.0 / float(sectorsCount);
    new Float:angle = 0.0;
    
    for (new i = 0; i < sectorsCount; i++)
    {
        new Float:stepRadians = xs_deg2rad(angle);
        new Float:newX = floatadd(centerX, floatmul(radius, floatcos(stepRadians)));
        new Float:newY = floatadd(centerY, floatmul(radius, floatsin(stepRadians)));        
        new Float:spawnPos[3] = {newX, newY, centerZ};        
        new ent = CreateEntity(spawnPos); // Ваш собственный метод для спавна сущности на координатах        
        angle += angleStep;
    }
}
 
Функция ищет и возвращает путь к конфигу карты по приоритету: точный map config → config по префиксу карты → общий config, с ошибкой при отсутствии файла.

C-подобный:
/**
 * Resolves configuration file path using fallback priority:
 * 1. Map specific config.
 * 2. Map prefix config.
 * 3. Default config.
 *
 * Search order:
 * <configDir>/maps/<configName>/<MapName>
 * <configDir>/maps/<configName>/prefix_<mapPrefix>
 * <configDir>/<configName>
 *
 * Example:
 * de_dust2 ->
 * prefix_de.json
 *
 * @param configDir   Base configuration directory.
 * @param configName  Configuration file name without extension.
 * @param dest        Destination buffer for resolved path.
 * @param len         Destination buffer size.
 * @param stopFail    If true, plugin will stop when config file does not exist.
 *                    If false, error will only be logged.
 *
 * @noreturn
 */
stock GetConfigPath(const configDir[], const configName[], dest[], len, const bool: stopFail = true)
{
  new mapPrefix[8], configFile[128];
  strtok2(MapName, mapPrefix, charsmax(mapPrefix), "", 0, '_');
  // 1. map specific
  formatex(configFile, charsmax(configFile),
    "%s/maps/%s/%s.json",
    configDir, configName, MapName
  );
  if (!file_exists(configFile))
  {
    // 2. prefix
    formatex(configFile, charsmax(configFile),
      "%s/maps/%s/prefix_%s.json",
      configDir, configName, mapPrefix
    );
  }
  if (!file_exists(configFile))
  {
    // 3. default
    formatex(configFile, charsmax(configFile),
      "%s/%s.json",
      configDir, configName
    );
  }
  if (!file_exists(configFile))
  {
    if (stopFail)
      set_fail_state("[Helper] Config file does not exist (%s)", configFile);
    else
      log_error(AMX_ERR_NATIVE, "[Helper] Config file does not exist (%s)", configFile);
   
    return;
  }
  copy(dest, len, configFile);
}

Пример, где файл flash_control.json будет искаться поочередно в нескольких директориях:
1. в addons/amxmodx/configs/plugins/gameplay/maps/flash_control/de_dust2.json
2. в addons/amxmodx/configs/plugins/gameplay/maps/flash_control/prefix_de.json
3. в addons/amxmodx/configs/plugins/gameplay/flash_control.json
C-подобный:
new const MAIN_CONFIGS_DIR[] = "addons/amxmodx/configs/plugins/gameplay";

ParseFlashControlConfig()
{
  new configPath[128];
  GetConfigPath(MAIN_CONFIGS_DIR, "flash_control", configPath, charsmax(configPath));

  new JSON: configFile = json_parse(configPath, .is_file = true);

  //some code
}
 
Последнее редактирование:
Парсинг цвета в HEX -> RGB

Автор Fantom
C-подобный:
stock ParseHexColor(const hex[])
{
  new buffer[3];

  if (hex[0] != '#' || strlen(hex) != 7)
    return buffer;

  buffer[0] = Parse16Bit(hex[1], hex[2]);
  buffer[1] = Parse16Bit(hex[3], hex[4]);
  buffer[2] = Parse16Bit(hex[5], hex[6]);

  return buffer;
}

stock Parse16Bit(ch1, ch2)
{
  return HexToChar(ch1) * 16 + HexToChar(ch2);
}

stock HexToChar(const ch)
{
  if ('0' <= ch <= '9') return ch - '0';
  if ('a' <= ch <= 'f') return ch - 'a' + 10;
  if ('A' <= ch <= 'F') return ch - 'A' + 10;

  return 0;
}

Использование:
C-подобный:
new rgb[3] = ParseHexColor("#ffffff");
console_print("%d %d %d", rgb[0], rgb[1], rgb[2]);

Output:
255 255 255
 
Generates a text based progress bar

C-подобный:
/**
 * Generates a progress bar string.
 *
 * @param iCurrent     Current value
 * @param iMax         Max value
 * @param iWidth       Width of the bar in characters (e.g., 10)
 * @param szOut        Output buffer
 * @param iLen         Buffer length
 */
stock GetProgressBar(iCurrent, iMax, iWidth, szOut[], iLen) {
    new iFilled = (iMax > 0) ? (iCurrent * iWidth / iMax) : 0;
    iFilled = clamp(iFilled, 0, iWidth);

    new iPos = 0;
    szOut[iPos++] = '[';
    
    for (new i = 0; i < iWidth; i++) {
        szOut[iPos++] = (i < iFilled) ? '|' : '.';
    }
    
    szOut[iPos++] = ']';
    szOut[iPos] = EOS;
    
    format(szOut[iPos], iLen - iPos, " %d%%", (iMax > 0) ? (iCurrent * 100 / iMax) : 0);
}

C-подобный:
new g_iPlayerExp[MAX_PLAYERS+ 1];
const MAX_EXP_LEVEL = 1000;

public plugin_init() {
    register_clcmd("say /exp", "@AddExp");
}

@AddExp(const pPlayer) {
    new iRandom = random_num(50, 150);
    g_iPlayerExp[pPlayer] += iRandom;

    static szBar[64];
    GetProgressBar(g_iPlayerExp[pPlayer], MAX_EXP_LEVEL, 20, szBar, charsmax(szBar));

    set_hudmessage(0, 255, 0, 0.02, 0.2, 0, 0.0, 3.0, 0.0, 0.0, -1);
    show_hudmessage(pPlayer, "Exp progress:^n%s", szBar);

    return PLUGIN_HANDLED;
}

stock GetProgressBar(iCurrent, iMax, iWidth, szOut[], iLen) {
    new iFilled = (iMax > 0) ? (iCurrent * iWidth / iMax) : 0;
    iFilled = clamp(iFilled, 0, iWidth);

    new iPos = 0;
    szOut[iPos++] = '[';
    for (new i = 0; i < iWidth; i++) {
        szOut[iPos++] = (i < iFilled) ? '|' : '.';
    }
    szOut[iPos++] = ']';
    szOut[iPos] = EOS;
    
    format(szOut[iPos], iLen - iPos, " %d%%", (iMax > 0) ? (iCurrent * 100 / iMax) : 0);
}
 

Кто просматривает тему

Назад
Верх