Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.
Показано с 1 по 9 из 9

Тема: random_switch.inc

  1. #1
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±

    random_switch.inc

    Что это такое?
    Этот инклуд позволяет использовать псевдооператор switchrand() - аналог switch со случайным выполнением одного из кейсов (case).

    Где это может понадобиться?
    В любых системах, где нужно случайно выбрать один из кейсов в switch, и при этом нумерация в кейсах прерывается (например, "1, 2, 3, 6, 7").
    Допустим, решили вы сделать пикап, который выдаёт случайный приз:
      Открыть/закрыть
    1. switch(random(6))
    2. {
    3. case 0:
    4. {
    5. new money = random(4000) + 1000;
    6. GivePlayerMoney(playerid, money);
    7. new string[14 + 4 + 1];
    8. format(string, sizeof(string), "Вы получили $%d!", money);
    9. SendClientMessage(playerid, -1, string);
    10. }
    11. case 1:
    12. {
    13. new money = random(2000) + 1000;
    14. GivePlayerMoney(playerid, -money);
    15. new string[14 + 4 + 1];
    16. format(string, sizeof(string), "Вы потеряли $%d!", money);
    17. SendClientMessage(playerid, -1, string);
    18. }
    19. case 2:
    20. {
    21. #if !defined FLOAT_INFINITY
    22. const Float:FLOAT_INFINITY = Float:0x7F800000;
    23. #endif
    24. SetPlayerHealth(playerid, FLOAT_INFINITY);
    25. SendClientMessage(playerid, -1, "Вы получили бессмертие (до респавна)!");
    26. }
    27. case 3:
    28. {
    29. GivePlayerWeapon(playerid, WEAPON_DEAGLE, 300);
    30. SendClientMessage(playerid, -1, "Вы получили пистолет!");
    31. }
    32. case 4, 5:
    33. {
    34. new Float:x, Float:y, Float:z, Float:a;
    35. GetPlayerPos(playerid, x, y, z);
    36. GetPlayerFacingAngle(playerid, a);
    37.  
    38. new vehicleid = CreateVehicle(562, x, y, z, a, -1, -1, 1 * 60);
    39. SetVehicleParamsEx(vehicleid,
    40. VEHICLE_PARAMS_ON, VEHICLE_PARAMS_OFF, VEHICLE_PARAMS_OFF,
    41. VEHICLE_PARAMS_OFF, VEHICLE_PARAMS_OFF, VEHICLE_PARAMS_OFF,
    42. VEHICLE_PARAMS_OFF);
    43. PutPlayerInVehicle(playerid, vehicleid, 0);
    44. SendClientMessage(playerid, -1, "Вы получили а-а-а-втомобиль!");
    45. }
    46. }

    И всё, вроде бы, хорошо, система работает, а игроки довольны. Но тут в одном из обновлений вам нужно убрать case 1, ибо игроки расстраиваются, когда у них отбирают деньги. Если вы просто удалите его - система не будет работать в 1 случае из 6, ибо random() всё ещё будет иногда выдавать единицу. Чтобы это исправить, придётся сделать что-то одно:
    1. Объединить 1-й кейс с каким-то другим (например, case 0, 1:), но это фактически увеличит шанс выпадения другого приза.
    2. Добавить какой-то новый приз вместо удалённого (придётся думать и писать новый код, что, естественно, большущий минус ).
    3. Сместить номера всех кейсов, которые шли после единицы, + исправить значение в random().
    4. Придумать костыль по типу добавления goto, который бы отправлял обработчик в позицию перед switch, дабы random() сгенерировало новое число.

    И если на подобном switch с малым количеством кейсов все перечисленные пункты могут показаться незначительными, то представьте, что switch у вас состоит из 50, 100 и т.д. кейсов. В этом случае switchrand довольно сильно упростит поддержку кода, сняв с вас и нужду думать насчёт того, чтоб все кейсы были по порядку, и нужду в изменении значения, указанного в random() при изменении количества кейсов.

    Ещё один пример ситуации - RP-мод с системой фракций (банд/мафий/организаций), в котором при выполнении какого-то задания нужно отправить игрока в одну из трёх армий. При этом так получилось, что ID армий в списке фракций - 2, 15, 22.
    В случае со switchrand решение будет выглядеть примерно так:
      Открыть/закрыть
    1. switchrand()
    2. {
    3. case 2:
    4. {
    5. // ...
    6. }
    7. case 15:
    8. {
    9. // ...
    10. }
    11. case 22:
    12. {
    13. // ...
    14. }
    15. }

    Без switchrand есть 2 варианта решения:
    • Поместить все нужные данные в массивы, определять рандомно индекс и уже отображать игроку нужную информацию из массивов согласно получившемуся индексу (но если у нас для каждой отдельной фракции нужно выполнить уникальный код, могут возникнуть проблемы. Да и в целом код получится довольно сложным и менее гибким относительно варианта switchrand).
    • Сделать нечто подобное:
      1. new army_frac[] = {2, 15, 22};
      2. new fid = army_frac[random(sizeof(army_frac))];
      3. switch(fid)
      4. {
      5. case 2:
      6. {
      7. // ...
      8. }
      9. case 15:
      10. {
      11. // ...
      12. }
      13. case 22:
      14. {
      15. // ...
      16. }
      17. }
      Но тут придётся следить за тем, чтоб значения в массиве и номера кейсов совпадали, что может быть проблематично (особенно в моменты когда кейсы наполнятся кодом и/или количество кейсов возрастёт).

    Иными словами, главная задача switchrand - упростить работу со связкой "random + switch", избавив пользователя от рутинной задачи по слежению за номерами кейсов и числом в random().

    Плюсы реализации:
    • Не требует наличия YSI, amx_assembly или каких-либо других сторонних инклудов.
    • Совместим с JIT.

    Недостатки:
    • Из-за использования оператора __emit (необходимо для совместимости с JIT) инклуд совместим только с новыми версиями компилятора (3.10.6 и новее).

    Использование
    Просто подключите random_switch.inc и используйте макрос switchrand(), как если бы использовали оператор switch:
    1. #include <a_samp>
    2. #include <random_switch>
    3.  
    4. main()
    5. {
    6. switchrand()
    7. {
    8. case 0: print("case 0");
    9. case 42: print("case 42");
    10. case 84: print("case 84");
    11. }
    12. }


    Скачать: https://www.dropbox.com/s/ew9px62sec...witch.zip?dl=1

    Благодарности:
    История версий:
    • (03.12.2019) Первый релиз.
    • (05.12.2019) Добавлен обход проблемы с падением компилятора (баг специфичен для версий компилятора 3.10.7 - 3.10.9).
    • (31.12.2019) Оптимизирован код.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  2. 6 пользователя(ей) сказали cпасибо:
    DeimoS (05.12.2019) Desulaid (06.12.2019) punkochel (25.01.2020) SteveStage (03.12.2019) vvw (03.12.2019) Web (13.12.2019)
  3. #2
    Аватар для vvw
    Пользователь

    Статус
    Оффлайн
    Регистрация
    09.08.2019
    Сообщений
    45
    Репутация:
    9 ±
    Может быть сделать отдельную тему, которая будет включать инклюд и описание DeimoS? Ну а эту тему удалить, ибо она некрасивая.

  4. #3
    Аватар для SteveStage
    Пользователь

    Статус
    Оффлайн
    Регистрация
    05.10.2019
    Адрес
    Планета Земля
    Сообщений
    318
    Репутация:
    7 ±
    Ну и 1 баг - зависает компилятор (крашит) при подключении данного инклуда, даже без его использования
    Компилятор от Zeex'а, версия 3.10.9

  5. #4
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    Цитата Сообщение от Steve_Stage Посмотреть сообщение
    Ну и 1 баг - зависает компилятор (крашит) при подключении данного инклуда, даже без его использования
    Компилятор от Zeex'а, версия 3.10.9
    Перед релизом я проверял инклуд на стандартном new.pwn (с подключением random_switch.inc и добавлением конструкции switchrand в OnGameModeInit()) с компиляторами версий 3.10.6-3.10.9, во всех случаях мод компилировался и работал нормально, причём как с JIT, так и без (в т.ч. с CrashDetect).
    Если у вас крашит компилятор, советую присмотреться к своему моду или другим инклудам, которые в нём используются. Либо выкладывайте сюда мод со всеми инклудами, вместе будем разбираться.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  6. Пользователь сказал cпасибо:
    execution (04.12.2019)
  7. #5
    Аватар для SteveStage
    Пользователь

    Статус
    Оффлайн
    Регистрация
    05.10.2019
    Адрес
    Планета Земля
    Сообщений
    318
    Репутация:
    7 ±
    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    Если у вас крашит компилятор, советую присмотреться к своему моду или другим инклудам, которые в нём используются. Либо выкладывайте сюда мод со всеми инклудами, вместе будем разбираться.
    Присмотрелся, и даже поочередно подключал каждый инклуд в моде - идеально компилирует.
    Кидаю все свои инклуды: https://www.dropbox.com/s/6e454xtjuc...clude.rar?dl=0
    Вот их подключение:

    1. //#include <jit>
    2. #include <crashdetect>
    3. #include <a_samp>
    4. #include <a_mysql>
    5. #include <foreach>
    6. #include <mxdate>
    7. #include <Pawn.CMD>
    8. #include <sscanf2>
    9. #include <streamer>
    10. #include <zmessage>
    11. //#include <random_switch>
    12. //если я раскомментирую комментарий выше - будет краш компилятора
    Последний раз редактировалось SteveStage; 05.12.2019 в 00:41.

  8. #6
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    Цитата Сообщение от vvw Посмотреть сообщение
    Может быть сделать отдельную тему, которая будет включать инклюд и описание DeimoS? Ну а эту тему удалить, ибо она некрасивая.
    Сделал, прежнюю тему переместил в архив.

    Цитата Сообщение от Steve_Stage Посмотреть сообщение
    ...
    Да, действительно, краш происходит, но только когда файл random_switch.inc подключен и switchrand при этом не используется. Связано это с __emit sysreq.c random, причём краш происходит только если функция random() ранее не использовлась. Я уже исправил этот баг ещё год назад, фикс приняли в июне этого года, поэтому в следующем релизе компилятора краша быть не должно.
    Тем не менее, я и в random_switch.inc добавил меры для обхода этой проблемы (причём "обход" вышел довольно бескровным, т.к. даже никакого лишнего кода из-за него не генерируется).

    random_switch.inc v1.1
    Скачать: https://www.dropbox.com/s/ew9px62sec...witch.zip?dl=1
    • Добавлен обход проблемы с падением компилятора (баг специфичен для версий компилятора 3.10.7 - 3.10.9).
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  9. #7
    Аватар для SteveStage
    Пользователь

    Статус
    Оффлайн
    Регистрация
    05.10.2019
    Адрес
    Планета Земля
    Сообщений
    318
    Репутация:
    7 ±
    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    Тем не менее, я и в random_switch.inc добавил меры для обхода этой проблемы (причём "обход" вышел довольно бескровным, т.к. даже никакого лишнего кода из-за него не генерируется).
    Спасибо большое, теперь не придется ждать релиза нового компилятора, а подключать и использовать его прямо из коробки, без переписывания

  10. #8
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    random_switch.inc v1.2
    Скачать: https://www.dropbox.com/s/ew9px62sec...witch.zip?dl=1
    • Немного оптимизирован код.
    • Исправлено случайное использование emit вместо __emit.
      Оператор emit будет удалён в следующей версии компилятора, в 3.10.10 останется только вариант __emit.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  11. #9
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    random_switch.inc v1.3
    Скачать: https://www.dropbox.com/s/ew9px62sec...witch.zip?dl=1
    • Добавлена функция GetSwitchRandValue(), возвращающая значение случайного кейса, выбранного при последнем использовании switchrand.
      Пример:
      1. switchrand()
      2. {
      3. case 2, 15, 22: printf("%d", GetSwitchRandValue()); // Выведет "2", "15" или "22"
      4. }

      Данная функция может пригодиться, когда нужно знать выбранное случайное значение после конструкции switchrand, либо внутри case из нескольких значений (как в примере выше).
      Отдельное спасибо DeimoS'у за идею.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  12. Пользователь сказал cпасибо:
    DeimoS (14.01.2021)
 

 

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •