[FIX] Multiple TAKE quests on the same NPC – wrong QUEST_SELECT / stuck cinematic bars

Aici poți găsi sisteme complete, verificate și recomandate de echipa 4Metin.
Avatar utilizator
metin2rospeed
4Metin
4Metin
Mesaje: 10
Membru din: Dum Dec 24, 2023 6:31 pm
Status: Activ
Multumiri primite: 1
Contact:

[FIX] Multiple TAKE quests on the same NPC – wrong QUEST_SELECT / stuck cinematic bars

Mesaj de metin2rospeed »

Descrierea problemei

Problema apare atunci când avem două sau mai multe questuri compilate pentru același eveniment take al unui NPC.

Exemplu:

Cod: Selectaţi tot

object/9009/take/fisher.start
object/9009/take/eveniment_pescar.start
Unul dintre questuri deschide un dialog folosind select(), însă după el motorul de quest execută și următorul fișier .start.

Clientul continuă să afișeze întrebarea, dar serverul nu mai are questul suspendat în SUSPEND_STATE_SELECT.

La apăsarea unei opțiuni apare:

Cod: Selectaţi tot

QUEST ScriptAnswer pid 11475 answer 0
Select: wrong QUEST_SELECT request! : 11475
În anumite cazuri, fereastra dispare, dar benzile cinematice negre rămân pe ecran până la un click pe NPC sau până la relog.

Cauza

În NPC::HandleEvent(), toate questurile asociate evenimentului sunt executate succesiv:

Cod: Selectaţi tot

CQuestManager::ExecuteQuestScript(...);
Dacă primul quest ajunge la:

Cod: Selectaţi tot

select(...)
ExecuteQuestScript() întoarce true, indicând că questul a rămas suspendat și așteaptă răspunsul jucătorului.

Valoarea returnată este însă ignorată, iar motorul execută următorul handler take.

Al doilea quest înlocuiește starea questului activ. Chiar dacă nu îndeplinește nicio condiție și se termină imediat, dialogul primului quest rămâne afișat pe client.

Rezultatul este o desincronizare:

Cod: Selectaţi tot

Client: așteaptă alegerea din select()
Server: nu mai are un quest în SUSPEND_STATE_SELECT
Rezolvare

Deschide:

Cod: Selectaţi tot

game/src/questnpc.cpp
Caută funcția:

Cod: Selectaţi tot

bool NPC::HandleEvent(PC& pc, int EventIndex)
Modificarea buclei fMatch

Caută:

Cod: Selectaţi tot

CQuestManager::ExecuteQuestScript(pc, fMatch.vdwQuesIndices[i], fMatch.viPCStates[i],
    fMatch.vcodes[i], fMatch.vcode_sizes[i]);
Înlocuiește cu:

Cod: Selectaţi tot

if (CQuestManager::ExecuteQuestScript(
        pc,
        fMatch.vdwQuesIndices[i],
        fMatch.viPCStates[i],
        fMatch.vcodes[i],
        fMatch.vcode_sizes[i]))
{
    return true;
}
Modificarea buclei fMiss

Caută:

Cod: Selectaţi tot

CQuestManager::ExecuteQuestScript(
    pc,
    fMiss.vdwNewStartQuestIndices[i],
    0,
    script.GetCode(),
    script.GetSize());
Înlocuiește cu:

Cod: Selectaţi tot

if (CQuestManager::ExecuteQuestScript(
        pc,
        fMiss.vdwNewStartQuestIndices[i],
        0,
        script.GetCode(),
        script.GetSize()))
{
    return true;
}
Trebuie modificate ambele bucle.

fMatch tratează questurile deja existente în starea jucătorului, iar fMiss tratează questurile pornite pentru prima dată. Dacă este modificată doar una dintre ele, problema poate apărea diferit după relog sau după prima utilizare.

Cum funcționează fixul

ExecuteQuestScript() întoarce true atunci când questul rămâne suspendat într-una dintre următoarele stări:

Cod: Selectaţi tot

select
wait
input
confirm
select_item
În acel moment, HandleEvent() se oprește și nu mai execută alte questuri pentru aceeași acțiune.

Scripturile care se termină imediat și nu așteaptă un răspuns întorc false, iar procesarea poate continua către următorul handler.

Prin urmare, fixul:

- permite mai multor questuri take să fie înregistrate pe același NPC;
- protejează questul care a deschis un dialog interactiv;
- previne suprascrierea stării active;
- elimină eroarea wrong QUEST_SELECT request;
- previne blocarea interfeței cinematice.

Dupa modificarea sursei, nu uitati sa o recompilati. Mult succes!
Scrie răspuns