• Новые темы в этом разделе публикуются автоматически при добавлении файла в менеджер ресурсов.
    Ручное создание новых тем невозможно.
Пишем кастомный квест на AI в птс

Мануал Пишем кастомный квест на AI в птс

Maksim

Легендарный
Местный
Знаток Lineage2
Любитель реакций
Неукротимое пламя
Старожил I степени
Сообщения
982
Розыгрыши
0
Решения
6
Репутация
596
Реакции
844
Баллы
1 708
Maksim добавил(а) новый ресурс:

Пишем кастомный квест на AI в птс - Краткое руководство для создания своего квеста.

Здравствуйте, сегодня расскажу как сделать кастомный квест на птс.

Вам понадобится nasc-decompiler для декомпиляции AI.

Чтобы сделать квест повторяемым, нам нужно основываться на уже повторяемом квесте, скажем, на "Ghost Explorer" (Relics of the Old Empire).

NPC у которого есть квест в AI - explorer_ghost_a, найдите его и декомпилируйте, вы должны получить этот код:
Код:
class 1 explorer_ghost_a : citizen

{

handler:

EventHandler TALK_SELECTED( fhtml0, talker )

{...

Узнать больше об этом ресурсе...
 

подскажите а что за параметры в скрипте _from_choice, _choiceN, _code ?
и они где храняться?
 
подскажите а что за параметры в скрипте _from_choice, _choiceN, _code ?
и они где храняться?
С телефона не могу посмотреть, в оригинале квеста есть.
Скорее всего выполняют для игрока временные переменные для дальнейших действий по квесту.
 
С телефона не могу посмотреть, в оригинале квеста есть.
Скорее всего выполняют для игрока временные переменные для дальнейших действий по квесту.
да просто смотришь ну например на примере нерупы неочевидно откуда должны взяться _from_choice _choiceN _code
если их нет в параметрах TALKED,
C#:
public class nerupa : citizen
{
    /*handler*/
    protected void TALKED(quest_id, talker)
    {
        if (_from_choice == 0)
        {
            if (OwnItemCount(talker, @leaf_of_mothertree) > 0)
            {
                _choiceN = _choiceN + 1;
                _code = 0;
                myself.AddChoice(0, 20301);
            }
            if (OwnItemCount(talker, @leaf_of_mothertree) == 0 && GetMemoStateEx(talker, 255, 1) > 3)
            {
                _choiceN = _choiceN + 1;
                _code = 1;
                myself.AddChoice(1, 20301);
            }
            if (OwnItemCount(talker, @leaf_of_mothertree) == 0 && GetMemoStateEx(talker, 255, 1) <= 3)
            {
                _choiceN = _choiceN + 1;
                _code = 2;
                myself.AddChoice(2, 20301);
            }
            if (_choiceN > 1)
            {
                myself.ShowChoicePage(talker, 0);
                return;
            }
        }


или это уже "нюансы" декомпилятора? в nasc же

C#:
handler 3 659    //  TALKED
   variable_begin
       "quest_id"
       "talker"
       "myself"
       "_choiceN"
       "_code"
       "_from_choice"
   variable_end
 
да просто смотришь ну например на примере нерупы неочевидно откуда должны взяться _from_choice _choiceN _code
если их нет в параметрах TALKED,
1732014601834.png 1732014666652.png
_from_choice является полем в ScriptEvent и при вызове хендлера TALKED инициируется нулевым значением по умолчанию, но уже при вызове TALK_SELECTED, ему устанавливается значение s3 параметра quest_choice байпасса.

Код:
/* 2750 */
struct __cppobj __declspec(align(8)) ScriptEvent : MemoryObject, ScriptAccessibleEx
{
  int m_nEventId;
  int _sav_start_;
  CSharedCreatureData *talker;
  CSharedCreatureData *attacker;
  CSharedCreatureData *victim;
  CSharedCreatureData *_private;
  CSharedCreatureData *_friend;
  CSharedCreatureData *commander;
  CSharedCreatureData *speller;
  CSharedCreatureData *target;
  CSharedCreatureData *from;
  CSharedCreatureData *creature;
  CSharedStaticObjectData *victim_so;
  CSharedItemData *item;
  wchar_t *script;
  int damage;
  int state;
  unsigned int who;
  int quest_id;
  int ask;
  int skill_id;
  int skill_level;
  int skill_sub_level;
  int need_quest;
  int occupation_name_id;
  int way_point_index;
  int next_way_point_index;
  int time;
  int weapon_class_id;
  int success;
  int x;
  int y;
  int z;
  int pledge_index;
  int desire;
  int residence_id;
  int inzone_id;
  int inzone_type_id;
  int event_id;
  int question_id;
  int timer_id;
  int script_event_arg1;
  int script_event_arg2;
  int script_event_arg3;
  int level;
  int action_id;
  int IsCritical;
  int scene_id;
  int rank;
  int reward_flag;
  __int64 reply;
  __int64 skill_name_id;
  __int64 i0;
  __int64 i1;
  __int64 i2;
  __int64 i3;
  __int64 i4;
  __int64 i5;
  __int64 i6;
  __int64 i7;
  __int64 i8;
  __int64 i9;
  __int64 i10;
  __int64 i11;
  float f0;
  float f1;
  float f2;
  float f3;
  float f4;
  float f5;
  float f6;
  float f7;
  float f8;
  float f9;
  CSharedCreatureData *c0;
  CSharedCreatureData *c1;
  CSharedCreatureData *c2;
  CSharedCreatureData *c3;
  CSharedCreatureData *c4;
  CSharedPledgeData *pledge0;
  CSharedPledgeData *pledge1;
  CSharedPartyData *party0;
  CSharedPartyData *party1;
  CSharedItemData *item0;
  CSharedItemData *item1;
  CSharedStaticObjectData *so0;
  HateInfo *h0;
  HateInfo *h1;
  HateInfo *h2;
  HateInfo *h3;
  HateInfo *h4;
  CNPC *npc0;
  CNPC *npc1;
  CNPC *npc2;
  wchar_t *s0;
  wchar_t *s1;
  CFHTML *fhtml0;
  CFHTML *fhtml1;
  CSharedCreatureData *c5;
  CSharedCreatureData *c6;
  CSharedCreatureData *c7;
  CSharedCreatureData *c8;
  CSharedCreatureData *c9;
  HateInfo *h5;
  HateInfo *h6;
  HateInfo *h7;
  HateInfo *h8;
  HateInfo *h9;
  Position *pos2;
  Position *pos3;
  Position *pos4;
  NpcMakerEx *maker2;
  NpcMakerEx *maker3;
  NpcMakerEx *maker4;
  wchar_t *s2;
  wchar_t *s3;
  wchar_t *s4;
  int _code;
  int _choiceN;
  int _from_choice;
  NpcMakerEx *maker0;
  NpcMakerEx *maker1;
  RoomInfo *room0;
  RoomInfo *room1;
  RoomInfoList *rlist0;
  RoomInfoList *rlist1;
  Position *pos0;
  Position *pos1;
  PositionList *plist0;
  PositionList *plist1;
  GlobalObject *gg;
  int _sav_end_;
};

Важно понимать, что NASC, это не конечный код, а лишь интерпретируемый скрипт, который исполняется внутри NPC сервера и там под капотом происходит еще оооочень много всего, что явно не отображено в скриптах.
 
Последнее редактирование:
В общем часть функций в нативах птс сервера по идее.
На самом деле квест можно любой написать, берем квест на фрею и делаем с свои боссом. 🍻
 
Посмотреть вложение 79469Посмотреть вложение 79470
_from_choice является полем в ScriptEvent и при вызове хендлера TALKED инициируется нулевым значением по умолчанию, но уже при вызове TALK_SELECTED, ему устанавливается значение s3 параметра quest_choice байпасса.

Код:
/* 2750 */
struct __cppobj __declspec(align(8)) ScriptEvent : MemoryObject, ScriptAccessibleEx
{
  int m_nEventId;
  int _sav_start_;
  CSharedCreatureData *talker;
  CSharedCreatureData *attacker;
  CSharedCreatureData *victim;
  CSharedCreatureData *_private;
  CSharedCreatureData *_friend;
  CSharedCreatureData *commander;
  CSharedCreatureData *speller;
  CSharedCreatureData *target;
  CSharedCreatureData *from;
  CSharedCreatureData *creature;
  CSharedStaticObjectData *victim_so;
  CSharedItemData *item;
  wchar_t *script;
  int damage;
  int state;
  unsigned int who;
  int quest_id;
  int ask;
  int skill_id;
  int skill_level;
  int skill_sub_level;
  int need_quest;
  int occupation_name_id;
  int way_point_index;
  int next_way_point_index;
  int time;
  int weapon_class_id;
  int success;
  int x;
  int y;
  int z;
  int pledge_index;
  int desire;
  int residence_id;
  int inzone_id;
  int inzone_type_id;
  int event_id;
  int question_id;
  int timer_id;
  int script_event_arg1;
  int script_event_arg2;
  int script_event_arg3;
  int level;
  int action_id;
  int IsCritical;
  int scene_id;
  int rank;
  int reward_flag;
  __int64 reply;
  __int64 skill_name_id;
  __int64 i0;
  __int64 i1;
  __int64 i2;
  __int64 i3;
  __int64 i4;
  __int64 i5;
  __int64 i6;
  __int64 i7;
  __int64 i8;
  __int64 i9;
  __int64 i10;
  __int64 i11;
  float f0;
  float f1;
  float f2;
  float f3;
  float f4;
  float f5;
  float f6;
  float f7;
  float f8;
  float f9;
  CSharedCreatureData *c0;
  CSharedCreatureData *c1;
  CSharedCreatureData *c2;
  CSharedCreatureData *c3;
  CSharedCreatureData *c4;
  CSharedPledgeData *pledge0;
  CSharedPledgeData *pledge1;
  CSharedPartyData *party0;
  CSharedPartyData *party1;
  CSharedItemData *item0;
  CSharedItemData *item1;
  CSharedStaticObjectData *so0;
  HateInfo *h0;
  HateInfo *h1;
  HateInfo *h2;
  HateInfo *h3;
  HateInfo *h4;
  CNPC *npc0;
  CNPC *npc1;
  CNPC *npc2;
  wchar_t *s0;
  wchar_t *s1;
  CFHTML *fhtml0;
  CFHTML *fhtml1;
  CSharedCreatureData *c5;
  CSharedCreatureData *c6;
  CSharedCreatureData *c7;
  CSharedCreatureData *c8;
  CSharedCreatureData *c9;
  HateInfo *h5;
  HateInfo *h6;
  HateInfo *h7;
  HateInfo *h8;
  HateInfo *h9;
  Position *pos2;
  Position *pos3;
  Position *pos4;
  NpcMakerEx *maker2;
  NpcMakerEx *maker3;
  NpcMakerEx *maker4;
  wchar_t *s2;
  wchar_t *s3;
  wchar_t *s4;
  int _code;
  int _choiceN;
  int _from_choice;
  NpcMakerEx *maker0;
  NpcMakerEx *maker1;
  RoomInfo *room0;
  RoomInfo *room1;
  RoomInfoList *rlist0;
  RoomInfoList *rlist1;
  Position *pos0;
  Position *pos1;
  PositionList *plist0;
  PositionList *plist1;
  GlobalObject *gg;
  int _sav_end_;
};

Важно понимать, что NASC, это не конечный код, а лишь интерпретируемый скрипт, который исполняется внутри NPC сервера и там под капотом происходит еще оооочень много всего, что явно не отображено в скриптах.
благодарю за подсказку, а _code и _choiceN просто переменная внутри метода?
п.с. а как так же L2Npc.exe открыть? =)
 
благодарю за подсказку, а _code и _choiceN просто переменная внутри метода?
п.с. а как так же L2Npc.exe открыть? =)
Ну здесь на помощь Ida, ghidra.
Опять же pdb в шаре есть, можно подключить и увидеть функции или сделать патч на asm.
К примеру у MasterToma есть реверснутый сервер с1 уже поднятый до интерлюда.
В отличии от нас с экстендером там уже можно без поиска оффсета и прочего просто пару строчек добавить в коде, видел в разных местах темы и это очень долгая работа.

В шаре лежат исходники l2npc от с1 после декомпила, до hf5 можно поднять.
 
Последнее редактирование:
Назад
Сверху Снизу