Quest structures and functions

This forum is for discussions on how to edit what can not be edited through the txt files, needless to say this isn't about battle net hacking.

Moderators: Nefarius, Havvoric

0
No votes
 
Total votes: 0

User avatar
SVR
Retired staff
Arch-Angel
Posts: 1449
Joined: Sat Nov 02, 2002 11:04 pm
Location: Texas
United States of America

Hand-picked

Quest structures and functions

Post by SVR » Mon Dec 06, 2004 6:59 am

I've been working on quest structures. It's looking promising to be able to completely replace all the quests, at least from the higher level point of view.
Problem is it's a massive amount of data. I wanted to share what I've got so far so everyone can explore from there.

Here are the structures.
QuestList is the struct pointed to by ptGame +10F4.
It contains the first pointer in a link list of Quest structs.
The Quest structs are the key component in quest control and contain the pointers to all the quest code called serverside.

Code: Select all

//=======================================================================
// Quests
//=======================================================================
struct QParam;

typedef DWORD ( FASTCALL * QCB)(Quest *pQuest,QParam *pqParam);
typedef DWORD ( FASTCALL * QFN1)(Quest *pQuest,QParam *pqParam);
typedef DWORD ( FASTCALL * QFN2)(Quest *pQuest,DWORD p1,DWORD p2,DWORD p3,DWORD p4);
typedef DWORD ( FASTCALL * QFN3)(Quest *pQuest);

// Callback function values - for Quest +A0[]
#define QUESTCALLBACK_NPCACTIVATE                       0

#define QUESTCALLBACK_NPCDEACTIVATE                     2
#define QUESTCALLBACK_CHANGEDLEVEL                      3

#define QUESTCALLBACK_MONSTERKILLED                     8
#define QUESTCALLBACK_PLAYERDROPPEDWITHQUESTITEM        9

#define QUESTCALLBACK_UPDATEMENU                        11

#define QUESTCALLBACK_PLAYERSTARTEDGAME                 13
#define QUESTCALLBACK_PLAYERJOINEDGAME                  14


struct QParam {
        D2Game* ptGame;         // +00
        DWORD   eCallback;      // +04 Callback ID
[color=red]
        Unit*   ptTargetUnit;   // +08 - NPC/Monster/Object... other Player ?[/color]
        Unit*   ptPlayer;       // +0C
[color=red]
        // above seem to always be so, below vary with eCallback
        DWORD dwUnk10;          // +10
        TextList  pTextHead;    // +14 - Used in CB 0 to show msg[/color]
};

struct QuestInit {
        void* pfnInit;          // +00
        DWORD bAct;             // +04
        DWORD bUnk08;           // +08
        DWORD dwUnk[3];
};

[color=red]
struct NPCInfo  {
        DWORD   NpcID;          // +00
        DWORD   MsgIndex;       // +04
        DWORD   MenuIndex;      // +08
};      // END                  // +0C

struct NPCMsgs  {               // - Quest message table entry.
        NPCInfo aMsgs[16]       //
        DWORD msgCount;         // - number of valid NPCInfo items in array.
};

NPCMsgs sgaA1Q1Msgs[8];         // - Quest message table

DWORD msgIndex[]= {...};        // - variable number of indexes,one for each state msg (up to 8).

struct Text {
        WORD    wStrNdx;        // +00
        WORD    wMenuID;        // +02
        DWORD   dwUnk04;        // +04
        Text*  Next;            // +08
};  //END                       // +0C

struct TextHead {
        WORD    wUnk00;         // +00
        WORD    wUnk02;	        // +02
        DWORD   dwUnk04;        // +04
        Text*   pText;          // +08
};  //END                       // +0C
[/color]

struct QuestBytes {
        void* pfn;              // +00 - function ptr? 
        BYTE bUnk00[0x10];      // +04 - +10 incr in helforge test.(QEvent function D4)
[color=red]
        // more depending on quest. This is a "user defined structure", the game code doesn't use it, only the quest code. So for your own quests, define as needed.
[/color]
};

struct Quest {
        DWORD QuestID;          // +00 - 
        D2Game *ptGame;         // +04
[color=red]
        BYTE    bAct;           // +08 - act the quest is for
        BYTE    bUnk09;         // +09 - checked in Callback 0 (+A0)
        BYTE    bUnk0A;         // +0A - checked in dispatch, if(!=1) skip callbacks
        BYTE    bUnk0B;         // +0B - gets tweaked in Callback CC (which then calls +A0)
        BYTE    bState;         // +0C - Quest state counter
        BYTE    bUnk0D;         // +0D -
        BYTE    bUnk0E;         // +0E -
        BYTE    bUnk0F;         // +0F -
[/color]
        DWORD nID;              // +10 - used in pSequence, calls Quest[nId]->pSequence
        DWORD dwUnk14;          // +14
        QuestBytes* pUnk18;     // +18 - quest specific data
[color=red]
        DWORD dwUnk1C[0x20];    // +1C - playerID's - either players active in quest or completed ? (probably the later)
        DWORD dwUnk9C;          // +9C - playerID count
[/color]
        QCB pCallback[15];      // +A0 - CallBack functions
[color=red]
        NPCMsgs* pNPCMsgs;      // +DC - ptr to NPC Messages 
[/color]
        DWORD eFilter;          // +E0 - index used in bit calls (#11107 etc)
        QFN3 pStatusFilter;     // +E4 - function ptr
        QFN2 pActiveFilter;     // +E8 - function ptr - return 1 to activate (!) bubble
        QFN3 pSequence;         // +EC - function ptr
        Quest* ptNext;          // +F0

};      //END                   // +F4

struct QuestList {
        Quest* ptQuest;         // +00
        BOOL bExecuting;        // +04
        BOOL bPickedSet;        // +08
        DWORD dwUnk0C;          // +0C
        DWORD dwUnk10;          // +10
        DWORD dwUnk14;          // +14
        DWORD dwUnk18;          // +18  - Lo seed
        DWORD dwUnk1C;          // +1C  - Hi seed, used in reward gen
        DWORD dwUnk20;          // +20

};      //END                   // +24

D2Game.6FC93DC0 allocates the Quest structures. The structures are initialized using a hardcoded table of QuestInit structs in d2game sgptQuestInit. The number of entries is sgnQuestInit immediately after the table.
There is a second table with 4 more quests after that. Don't know what they are.

The pCallback functions do the "house keeping" , checking if you have the right item, have talked to the right NPC,dropping rewards etc.
These are called in loops for several quests. Clicking on akara will spit out a stream of calls to pCallBack[0];
Most of the things we've changed in quests have been in these functions.

The pXXXFilter functions are called consistantly through the game loop. Probably used to see if quest processing is needed for the current state of the game.
I haven't gone to deep here.

All this is D2Game side. Clientside I haven't explored.

I have a txt of *all* the function addresses and initial values here ...

http://svr.d2mods.com/Quest1.zip

Happy hunting :)


EDIT:
Updated structure infos. Changes are in red.

EDIT:
Corrected NPCMsg table format.
Here's the A1Q1 message table ...

Code: Select all

NPCMsgs sNPCInfoA1Q1[8] = {
    {
        {0x94,0x40,0x00},
        1
    },
    {
        {0x94,0x41,0x02,
         0x9B,0x46,0x02,
         0x93,0x45,0x02,
         0x96,0x42,0x02,
         0x9A,0x43,0x02},
        5
    },
    {
        {0x96,0x48,0x02,
         0x9B,0x4B,0x02,
         0x9A,0x49,0x02,
         0x94,0x47,0x02,
         0x93,0x4A,0x02},
        5
    },
    {
        {0x96,0x4D,0x02,
         0x9B,0x50,0x02,
         0x9A,0x4E,0x02,
         0x94,0x4C,0x02,
         0x93,0x4F,0x02},
        5
    },
    {
        {0x9B,0x50,0x02,
         0x9A,0x4E,0x02,
         0x93,0x4F,0x02},
        3
    },
    {
        {-1,0,0x02},
        0
    },
    {
        {-1,0,0x02},
        0
    },
    {
        {-1,0,0x02},
        0
    }

};

DWORD msgTbl[]= {-1,0,1,2,3,4}; // Act1-Quest1 state message table (index's NPCInfo table)

Last edited by SVR on Thu Mar 19, 2009 9:00 pm, edited 3 times in total.

User avatar
Joel
Moderator
Dominion
Posts: 6921
Joined: Mon May 27, 2002 7:19 am
Location: Orsay

Hand-picked

Post by Joel » Mon Dec 06, 2004 9:26 am

That's great news !!

I'm currently fiddling on thsoe currently, maybe a team effort on rewriting the quest system to softcode it can be nice.

A basic data driven condition list can be nice. A multiple boolean condition filter (like the message filter of the Book of Lore) and varible check ?
"How much suffering, mortal, does it take before you lose your grace?"
Shadow Empire (coming soon) | forum

User avatar
afj666
Retired staff
Champion of the Light
Posts: 479
Joined: Sun Apr 20, 2003 10:15 pm
Location: Hvam St. (very small town)
Denmark

Hand-picked

Re: Quest structures and functions

Post by afj666 » Mon Dec 06, 2004 2:36 pm

Really nice SVR.

* afj666 presses the Sticky Thread button *
Extended Levels Plugin | Extended Object | Plugin | CustomTbl Plugin
Your Weight Is Appropriate.... For A Medium Sized Russian Tank
Avatar Created By Go][um

User avatar
SVR
Retired staff
Arch-Angel
Posts: 1449
Joined: Sat Nov 02, 2002 11:04 pm
Location: Texas
United States of America

Hand-picked

Post by SVR » Mon Dec 06, 2004 3:16 pm

A basic data driven condition list can be nice.
Yep, I'm thinking of a txt that has a column(s) for each Callback.
Looking at the quest1.txt, each quest has it's own function there (except +C8 reuses some generic functions).

We could see what's common in each set, write that and add the different things from the txt columns.

Have to explore more to see what all is done in there. You have your basic "Is this item equipped, set flag" etc tests, these could be softcoded in a generic function with all the kinds of tests and use the txt for the target item,NPC or condition.

EDIT:

Here's a D2Mod project to help explore the functions...

http://svr.d2mods.com/QuestSrc.zip

Need the Quest structs from above put into D2UnitStructs.h.

This code hooks the QuestInit code and copies the Quest structures. It then replaces the Callback functions with local hooks that call a log/dispatch function.
Last edited by SVR on Thu Mar 19, 2009 9:01 pm, edited 2 times in total.

User avatar
Myhrginoc
Retired Admin
Cherub
Posts: 12100
Joined: Sat May 25, 2002 7:28 am
Location: Percussion U
United States of America

Hand-picked

Post by Myhrginoc » Tue Dec 07, 2004 2:54 am

This thread contains some quest information from the player's perspective. It includes client- and server-side information, so there might be clues for finding the client-side handlers by putting memory breakpoints in those structures. The code references are v1.09x, but I don't think these structures have changed.

ptUnit(player)+70 is now ptUnit(player)+14 in v1.10.
Last edited by Necrolis on Tue Dec 07, 2004 2:54 am, edited 3 times in total.
Reason: fixed the URL
Do the right thing. It will gratify some people and astonish the rest.
~ Mark Twain
Run Diablo II in any version for mods: tutorial
The Terms of Service!! Know them, abide by them, and enjoy the forums at peace.
The Beginner's Guide v1.4: (MS Word | PDF) || Mod Running Scripts || TFW: Awakening

User avatar
kingpin
Retired Admin
Cherub
Posts: 10954
Joined: Sat Jan 11, 2003 12:51 pm
Sweden

Hand-picked

Re: Quest structures and functions

Post by kingpin » Tue Dec 07, 2004 10:21 am

On client they check the state of the quest with this function:

This is from den of evil on d2client.dll:
6FB23A02 6A 01 PUSH 1 ; Quest1 - Den of Evil
6FB23A04 6A 00 PUSH 0
6FB23A06 E8 E554F9FF CALL D2Client.#10002 ; return quest status

dafoe
Posts: 3
Joined: Sun Feb 15, 2004 2:15 pm

Post by dafoe » Tue Dec 07, 2004 3:40 pm

omg, I have been wanting to look at doing something like this for ages, just not had the time, or knew how to get started really.

I'll take a grande look at it later today.

If this makes it possible to add more then the already existant quests, then I'll be there exploring and using that possibility right away.

Great work, now I just need to get digging :)

User avatar
kingpin
Retired Admin
Cherub
Posts: 10954
Joined: Sat Jan 11, 2003 12:51 pm
Sweden

Hand-picked

Re: Quest structures and functions

Post by kingpin » Tue Dec 07, 2004 5:43 pm

If this makes it possible to add more then the already existant quests, then I'll be there exploring and using that possibility right away.
You need to use custom savefile since the unmodded savefile is pretty used up already for other needed informations.

User avatar
SVR
Retired staff
Arch-Angel
Posts: 1449
Joined: Sat Nov 02, 2002 11:04 pm
Location: Texas
United States of America

Hand-picked

Post by SVR » Tue Dec 07, 2004 5:50 pm

Ok, here's the first run through with the Quest log plugin ...

Code: Select all

Araka Clicked - a1q1 started.

Quest 19 -> +A0h( 0) - returned 00000094h
Quest 06 -> +A0h( 0) - returned 00000000h
Quest 05 -> +A0h( 0) - returned ffffffffh
Quest 04 -> +A0h( 0) - returned 00000000h
Quest 03 -> +A0h( 0) - returned 00000000h
Quest 02 -> +A0h( 0) - returned 00000000h
Quest 01 -> +A0h( 0) - returned 00000001h
Quest 00 -> +A0h( 0) - returned 00000094h

Quest 06 -> +CCh(11) - returned 02972300h
Quest 05 -> +CCh(11) - returned 0111e600h
Quest 04 -> +CCh(11) - returned 01ca0040h
Quest 03 -> +CCh(11) - returned 0111fa00h
Quest 02 -> +CCh(11) - returned 0111fa00h

    Quest 19 -> +A0h( 0) - returned 00000094h
    Quest 06 -> +A0h( 0) - returned 00000000h
    Quest 05 -> +A0h( 0) - returned ffffffffh
    Quest 04 -> +A0h( 0) - returned 00000000h
    Quest 03 -> +A0h( 0) - returned 00000000h
    Quest 02 -> +A0h( 0) - returned 00000000h

    Data Changed before :+0c = 00000402 was 00000401 
    Quest 01 -> +A0h( 0) - returned 00000005h
    Quest 00 -> +A0h( 0) - returned 00000094h

Quest 01 -> +CCh(11) - returned 00000089h
Quest 00 -> +CCh(11) - returned 00000040h

leave
Quest 06 -> +A8h( 2) - returned 0292cc00h
Quest 04 -> +A8h( 2) - returned 00000094h
Quest 03 -> +A8h( 2) - returned 0292cc00h
Quest 02 -> +A8h( 2) - returned 0292cc00h
Quest 01 -> +A8h( 2) - returned 00000000h
Data Changed after :+08 = 01010100 was 00010100 

//==============================================

Hashya clicked -  no quest action
Quest 19 -> +A0h( 0) - returned 00000096h
Quest 06 -> +A0h( 0) - returned 00000000h
Quest 05 -> +A0h( 0) - returned ffffffffh
Quest 04 -> +A0h( 0) - returned 00000000h
Quest 03 -> +A0h( 0) - returned 00000000h
Quest 02 -> +A0h( 0) - returned 00000000h
Quest 01 -> +A0h( 0) - returned 00000005h
Quest 00 -> +A0h( 0) - returned 00000096h
leave
Quest 06 -> +A8h( 2) - returned 0255dc00h
Quest 04 -> +A8h( 2) - returned 00000096h
Quest 03 -> +A8h( 2) - returned 0255dc00h
Quest 02 -> +A8h( 2) - returned 0255dc00h
Quest 01 -> +A8h( 2) - returned 0255dc00h

//==============================================

Leaving town ....

Quest 24 -> +ACh( 3) - returned 00000000h
Data Changed after :+08 = 02010104 was 00010104 
Quest 23 -> +ACh( 3) - returned 00000001h
Quest 22 -> +ACh( 3) - returned 00000001h
Quest 21 -> +ACh( 3) - returned 00000003h
Quest 20 -> +ACh( 3) - returned 00000085h
Quest 1f -> +ACh( 3) - returned 00000085h
Quest 18 -> +ACh( 3) - returned 00000003h
Quest 17 -> +ACh( 3) - returned 00000001h
Quest 16 -> +ACh( 3) - returned 00000003h
Quest 14 -> +ACh( 3) - returned 00000000h
Data Changed after :+0c = 00000601 was 00000600 
Quest 13 -> +ACh( 3) - returned 00000001h
Quest 12 -> +ACh( 3) - returned 00000003h
Quest 11 -> +ACh( 3) - returned 00000001h
Quest 10 -> +ACh( 3) - returned 00000003h
Quest 0f -> +ACh( 3) - returned 00000001h
Quest 0d -> +ACh( 3) - returned 00000003h
Quest 0c -> +ACh( 3) - returned 00000028h
Quest 0b -> +ACh( 3) - returned 00000085h
Quest 0a -> +ACh( 3) - returned 00000001h
Quest 09 -> +ACh( 3) - returned 00000003h
Quest 08 -> +ACh( 3) - returned 00000003h
Quest 06 -> +ACh( 3) - returned 00000001h
Quest 05 -> +ACh( 3) - returned 00000001h
Quest 04 -> +ACh( 3) - returned 00000085h
Quest 03 -> +ACh( 3) - returned 00000001h
Quest 02 -> +ACh( 3) - returned 00000085h
Quest 01 -> +ACh( 3) - returned 00000000h
Data Changed after :+0c = 00000403 was 00000402 

//==============================================
Returned to town
Quest 24 -> +ACh( 3) - returned 00000085h
Quest 23 -> +ACh( 3) - returned 00000085h
Quest 22 -> +ACh( 3) - returned 00000085h
Quest 21 -> +ACh( 3) - returned 00000003h
Quest 20 -> +ACh( 3) - returned 00000001h
Quest 1f -> +ACh( 3) - returned 00000001h
Quest 18 -> +ACh( 3) - returned 00000003h
Quest 17 -> +ACh( 3) - returned 00000085h
Quest 16 -> +ACh( 3) - returned 00000003h
Quest 14 -> +ACh( 3) - returned 00000085h
Quest 13 -> +ACh( 3) - returned 00000085h
Quest 12 -> +ACh( 3) - returned 00000003h
Quest 11 -> +ACh( 3) - returned 00000085h
Quest 10 -> +ACh( 3) - returned 00000003h
Quest 0f -> +ACh( 3) - returned 00000085h
Quest 0d -> +ACh( 3) - returned 00000003h
Quest 0c -> +ACh( 3) - returned 00000028h
Quest 0b -> +ACh( 3) - returned 00000001h
Quest 0a -> +ACh( 3) - returned 00000085h
Quest 09 -> +ACh( 3) - returned 00000003h
Quest 08 -> +ACh( 3) - returned 00000003h
Quest 06 -> +ACh( 3) - returned 00000085h
Quest 05 -> +ACh( 3) - returned 00000001h
Quest 04 -> +ACh( 3) - returned 00000000h
Quest 03 -> +ACh( 3) - returned 00000085h
Quest 02 -> +ACh( 3) - returned 00000001h
Quest 01 -> +ACh( 3) - returned 00000085h

//==============================================

Went to Graveyard through stairs 
Quest 24 -> +ACh( 3) - returned 00000001h
Quest 23 -> +ACh( 3) - returned 00000001h
Quest 22 -> +ACh( 3) - returned 00000001h
Quest 21 -> +ACh( 3) - returned 00000003h
Quest 20 -> +ACh( 3) - returned 00000011h
Quest 1f -> +ACh( 3) - returned 00000011h
Quest 18 -> +ACh( 3) - returned 00000003h
Quest 17 -> +ACh( 3) - returned 00000001h
Quest 16 -> +ACh( 3) - returned 00000003h
Quest 14 -> +ACh( 3) - returned 00000001h
Quest 13 -> +ACh( 3) - returned 00000001h
Quest 12 -> +ACh( 3) - returned 00000003h
Quest 11 -> +ACh( 3) - returned 00000001h
Quest 10 -> +ACh( 3) - returned 00000003h
Quest 0f -> +ACh( 3) - returned 00000001h
Quest 0d -> +ACh( 3) - returned 00000003h
Quest 0c -> +ACh( 3) - returned 00000028h
Quest 0b -> +ACh( 3) - returned 00000011h
Quest 0a -> +ACh( 3) - returned 00000001h
Quest 09 -> +ACh( 3) - returned 00000003h
Quest 08 -> +ACh( 3) - returned 00000003h
Quest 06 -> +ACh( 3) - returned 00000001h
Quest 05 -> +ACh( 3) - returned 00000001h
Quest 04 -> +ACh( 3) - returned 00000011h
Quest 03 -> +ACh( 3) - returned 00000001h
Quest 02 -> +ACh( 3) - returned 00000000h
Data Changed after :+08 = 02010100 was 00010100 +0c = 00000403 was 00000400 
Quest 01 -> +ACh( 3) - returned 00000001h


//==============================================
Returned to town
Quest 24 -> +ACh( 3) - returned 00000011h
Quest 23 -> +ACh( 3) - returned 00000011h
Quest 22 -> +ACh( 3) - returned 00000011h
Quest 21 -> +ACh( 3) - returned 00000003h
Quest 20 -> +ACh( 3) - returned 00000001h
Quest 1f -> +ACh( 3) - returned 00000001h
Quest 18 -> +ACh( 3) - returned 00000003h
Quest 17 -> +ACh( 3) - returned 00000011h
Quest 16 -> +ACh( 3) - returned 00000003h
Quest 14 -> +ACh( 3) - returned 00000011h
Quest 13 -> +ACh( 3) - returned 00000011h
Quest 12 -> +ACh( 3) - returned 00000003h
Quest 11 -> +ACh( 3) - returned 00000011h
Quest 10 -> +ACh( 3) - returned 00000003h
Quest 0f -> +ACh( 3) - returned 00000011h
Quest 0d -> +ACh( 3) - returned 00000003h
Quest 0c -> +ACh( 3) - returned 00000028h
Quest 0b -> +ACh( 3) - returned 00000001h
Quest 0a -> +ACh( 3) - returned 00000011h
Quest 09 -> +ACh( 3) - returned 00000003h
Quest 08 -> +ACh( 3) - returned 00000003h
Quest 06 -> +ACh( 3) - returned 00000011h
Quest 05 -> +ACh( 3) - returned 00000001h
Quest 04 -> +ACh( 3) - returned 044cea00h
Quest 03 -> +ACh( 3) - returned 00000011h
Quest 02 -> +ACh( 3) - returned 00000001h
Quest 01 -> +ACh( 3) - returned 00000011h

//==============================================
Died - Returned to town
Quest 24 -> +ACh( 3) - returned 00000085h
Quest 23 -> +ACh( 3) - returned 00000085h
Quest 22 -> +ACh( 3) - returned 00000085h
Quest 21 -> +ACh( 3) - returned 00000003h
Quest 20 -> +ACh( 3) - returned 00000001h
Quest 1f -> +ACh( 3) - returned 00000001h
Quest 18 -> +ACh( 3) - returned 00000003h
Quest 17 -> +ACh( 3) - returned 00000085h
Quest 16 -> +ACh( 3) - returned 00000003h
Quest 14 -> +ACh( 3) - returned 00000085h
Quest 13 -> +ACh( 3) - returned 00000085h
Quest 12 -> +ACh( 3) - returned 00000003h
Quest 11 -> +ACh( 3) - returned 00000085h
Quest 10 -> +ACh( 3) - returned 00000003h
Quest 0f -> +ACh( 3) - returned 00000085h
Quest 0d -> +ACh( 3) - returned 00000003h
Quest 0c -> +ACh( 3) - returned 00000028h
Quest 0b -> +ACh( 3) - returned 00000001h
Quest 0a -> +ACh( 3) - returned 00000085h
Quest 09 -> +ACh( 3) - returned 00000003h
Quest 08 -> +ACh( 3) - returned 00000003h
Quest 06 -> +ACh( 3) - returned 00000085h
Quest 05 -> +ACh( 3) - returned 00000001h
Quest 04 -> +ACh( 3) - returned 029aa400h
Quest 03 -> +ACh( 3) - returned 00000085h
Quest 02 -> +ACh( 3) - returned 00000001h
Quest 01 -> +ACh( 3) - returned 00000085h

//==============================================
Talk to geed - intro
Quest 19 -> +A0h( 0) - returned 00000093h
Quest 06 -> +A0h( 0) - returned 00000000h
Quest 05 -> +A0h( 0) - returned ffffffffh
Quest 04 -> +A0h( 0) - returned 00000000h
Quest 03 -> +A0h( 0) - returned 00000000h
Quest 02 -> +A0h( 0) - returned 00000005h
Quest 01 -> +A0h( 0) - returned 00000005h
Quest 00 -> +A0h( 0) - returned 00000093h

Quest 06 -> +CCh(11) - returned 02542300h
Quest 05 -> +CCh(11) - returned 0111e600h
Quest 04 -> +CCh(11) - returned 01ca0093h
Quest 03 -> +CCh(11) - returned 0111fa00h
Quest 02 -> +CCh(11) - returned 0111fa00h
Quest 01 -> +CCh(11) - returned 02546c00h
Quest 00 -> +CCh(11) - returned 0000002dh

leave intro
Quest 06 -> +A8h( 2) - returned 044c2a00h
Quest 04 -> +A8h( 2) - returned 00000093h
Quest 03 -> +A8h( 2) - returned 044c2a00h
Quest 02 -> +A8h( 2) - returned 044c2a00h
Quest 01 -> +A8h( 2) - returned 044c2a00h

menu
Quest 19 -> +A0h( 0) - returned 00000093h
Quest 06 -> +A0h( 0) - returned 00000000h
Quest 05 -> +A0h( 0) - returned ffffffffh
Quest 04 -> +A0h( 0) - returned 00000000h
Quest 03 -> +A0h( 0) - returned 00000000h
Quest 02 -> +A0h( 0) - returned 00000005h
Quest 01 -> +A0h( 0) - returned 00000005h
Quest 00 -> +A0h( 0) - returned 00000093h
leave menu
Quest 06 -> +A8h( 2) - returned 044c2a00h
Quest 04 -> +A8h( 2) - returned 00000093h
Quest 03 -> +A8h( 2) - returned 044c2a00h
Quest 02 -> +A8h( 2) - returned 044c2a00h
Quest 01 -> +A8h( 2) - returned 044c2a00h

//==============================================
entered den of evil
Quest 24 -> +ACh( 3) - returned 00000002h
Quest 23 -> +ACh( 3) - returned 00000002h
Quest 22 -> +ACh( 3) - returned 00000002h
Quest 21 -> +ACh( 3) - returned 00000003h
Quest 20 -> +ACh( 3) - returned 00000008h
Quest 1f -> +ACh( 3) - returned 00000008h
Quest 18 -> +ACh( 3) - returned 00000003h
Quest 17 -> +ACh( 3) - returned 00000002h
Quest 16 -> +ACh( 3) - returned 00000003h
Quest 14 -> +ACh( 3) - returned 00000002h
Quest 13 -> +ACh( 3) - returned 00000002h
Quest 12 -> +ACh( 3) - returned 00000003h
Quest 11 -> +ACh( 3) - returned 00000002h
Quest 10 -> +ACh( 3) - returned 00000003h
Quest 0f -> +ACh( 3) - returned 00000002h
Quest 0d -> +ACh( 3) - returned 00000003h
Quest 0c -> +ACh( 3) - returned 00000028h
Quest 0b -> +ACh( 3) - returned 00000008h
Quest 0a -> +ACh( 3) - returned 00000002h
Quest 09 -> +ACh( 3) - returned 00000003h
Quest 08 -> +ACh( 3) - returned 00000003h
Quest 06 -> +ACh( 3) - returned 00000002h
Quest 05 -> +ACh( 3) - returned 00000001h
Quest 04 -> +ACh( 3) - returned 00000008h
Quest 03 -> +ACh( 3) - returned 00000002h
Quest 02 -> +ACh( 3) - returned 00000008h
Quest 01 -> +ACh( 3) - returned 00000000h
Data Changed after :+08 = 02010100 was 01010100 

//==============================================
killed a monster
Quest 01 -> +C0h( 8) - returned 0000001ah
once for each monster
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 0000001ah
Quest 01 -> +C0h( 8) - returned 00000000h
Data Changed after :+08 = 04010100 was 02010100 +14 = 00000020 was 00000000 
... 5 monsters remaining
Quest 01 -> +C0h( 8) - returned 00000000h
Quest 01 -> +C0h( 8) - returned 00000000h
Quest 01 -> +C0h( 8) - returned 00000000h

Quest 01 -> +C0h( 8) - returned 00000000h
Quest 01 -> +C0h( 8) - returned 03171d40h
Data Changed after :+0c = 00000404 was 00000403 
... quest completes

//==============================================
// return to town
Quest 24 -> +ACh( 3) - returned 00000008h
Quest 23 -> +ACh( 3) - returned 00000008h
Quest 22 -> +ACh( 3) - returned 00000008h
Quest 21 -> +ACh( 3) - returned 00000003h
Quest 20 -> +ACh( 3) - returned 00000001h
Quest 1f -> +ACh( 3) - returned 00000001h
Quest 18 -> +ACh( 3) - returned 00000003h
Quest 17 -> +ACh( 3) - returned 00000008h
Quest 16 -> +ACh( 3) - returned 00000003h
Quest 14 -> +ACh( 3) - returned 00000008h
Quest 13 -> +ACh( 3) - returned 00000008h
Quest 12 -> +ACh( 3) - returned 00000003h
Quest 11 -> +ACh( 3) - returned 00000008h
Quest 10 -> +ACh( 3) - returned 00000003h
Quest 0f -> +ACh( 3) - returned 00000008h
Quest 0d -> +ACh( 3) - returned 00000003h
Quest 0c -> +ACh( 3) - returned 00000028h
Quest 0b -> +ACh( 3) - returned 00000001h
Quest 0a -> +ACh( 3) - returned 00000008h
Quest 09 -> +ACh( 3) - returned 00000003h
Quest 08 -> +ACh( 3) - returned 00000003h
Quest 06 -> +ACh( 3) - returned 00000008h
Quest 05 -> +ACh( 3) - returned 00000001h
Quest 04 -> +ACh( 3) - returned 05157b00h
Quest 03 -> +ACh( 3) - returned 00000008h
Quest 02 -> +ACh( 3) - returned 00000001h

Data Changed before :+08 = 05010100 was 04010100 +14 = 00000000 was 00000020 
Quest 01 -> +ACh( 3) - returned 00000008h

//==============================================
// talk to akara
Quest 19 -> +A0h( 0) - returned 00000094h
Quest 06 -> +A0h( 0) - returned 00000000h
Quest 05 -> +A0h( 0) - returned ffffffffh
Quest 04 -> +A0h( 0) - returned 00000000h
Quest 03 -> +A0h( 0) - returned 00000000h
Quest 02 -> +A0h( 0) - returned 00000005h
Quest 01 -> +A0h( 0) - returned 00000005h
Quest 00 -> +A0h( 0) - returned 00000094h
Quest 06 -> +CCh(11) - returned 051f2300h
Quest 05 -> +CCh(11) - returned 0111e600h
Quest 04 -> +CCh(11) - returned 01ca004ch
Quest 03 -> +CCh(11) - returned 0111fa00h
Quest 02 -> +CCh(11) - returned 0111fa00h
Quest 19 -> +A0h( 0) - returned 00000094h
Quest 06 -> +A0h( 0) - returned 00000000h
Quest 05 -> +A0h( 0) - returned ffffffffh
Quest 04 -> +A0h( 0) - returned 00000000h
Quest 03 -> +A0h( 0) - returned 00000000h
Quest 02 -> +A0h( 0) - returned 00000005h

Data Changed before :+08 = 0D010100 was 05010100 +0c = 00000405 was 00000404 +1c = 00000001 was 00000000 +9c = 00000001 was 00000000 
Quest 01 -> +A0h( 0) - returned 00000003h
Quest 00 -> +A0h( 0) - returned 00000094h
Quest 01 -> +CCh(11) - returned 00000089h
Quest 00 -> +CCh(11) - returned 0000004ch

leave akara
Quest 06 -> +A8h( 2) - returned 0515c900h
Quest 04 -> +A8h( 2) - returned 00000094h
Quest 03 -> +A8h( 2) - returned 0515c900h
Quest 02 -> +A8h( 2) - returned 0515c900h

//==============================================
goto graveyard
Quest 24 -> +ACh( 3) - returned 00000001h
Quest 23 -> +ACh( 3) - returned 00000001h
Quest 22 -> +ACh( 3) - returned 00000001h
Quest 21 -> +ACh( 3) - returned 00000003h
Quest 20 -> +ACh( 3) - returned 00000011h
Quest 1f -> +ACh( 3) - returned 00000011h
Quest 18 -> +ACh( 3) - returned 00000003h
Quest 17 -> +ACh( 3) - returned 00000001h
Quest 16 -> +ACh( 3) - returned 00000003h
Quest 14 -> +ACh( 3) - returned 00000001h
Quest 13 -> +ACh( 3) - returned 00000001h
Quest 12 -> +ACh( 3) - returned 00000003h
Quest 11 -> +ACh( 3) - returned 00000001h
Quest 10 -> +ACh( 3) - returned 00000003h
Quest 0f -> +ACh( 3) - returned 00000001h
Quest 0d -> +ACh( 3) - returned 00000003h
Quest 0c -> +ACh( 3) - returned 00000028h
Quest 0b -> +ACh( 3) - returned 00000011h
Quest 0a -> +ACh( 3) - returned 00000001h
Quest 09 -> +ACh( 3) - returned 00000003h
Quest 08 -> +ACh( 3) - returned 00000003h
Quest 06 -> +ACh( 3) - returned 00000001h
Quest 05 -> +ACh( 3) - returned 00000001h
Quest 04 -> +ACh( 3) - returned 00000011h
Quest 03 -> +ACh( 3) - returned 00000001h
Quest 02 -> +ACh( 3) - returned 00000000h
Quest 01 -> +ACh( 3) - returned 00000000h
Data Changed after :+9c = 00000000 was 00000001 

//==============================================
Killed blood raven
Quest 02 -> +C0h( 8) - returned 00000020h
Data Changed after :+0c = 00000404 was 00000403 

//==============================================
return to town
Quest 24 -> +ACh( 3) - returned 00000011h
Quest 23 -> +ACh( 3) - returned 00000011h
Quest 22 -> +ACh( 3) - returned 00000011h
Quest 21 -> +ACh( 3) - returned 00000003h
Quest 20 -> +ACh( 3) - returned 00000001h
Quest 1f -> +ACh( 3) - returned 00000001h
Quest 18 -> +ACh( 3) - returned 00000003h
Quest 17 -> +ACh( 3) - returned 00000011h
Quest 16 -> +ACh( 3) - returned 00000003h
Quest 14 -> +ACh( 3) - returned 00000011h
Quest 13 -> +ACh( 3) - returned 00000011h
Quest 12 -> +ACh( 3) - returned 00000003h
Quest 11 -> +ACh( 3) - returned 00000011h
Quest 10 -> +ACh( 3) - returned 00000003h
Quest 0f -> +ACh( 3) - returned 00000011h
Quest 0d -> +ACh( 3) - returned 00000003h
Quest 0c -> +ACh( 3) - returned 00000028h
Quest 0b -> +ACh( 3) - returned 00000001h
Quest 0a -> +ACh( 3) - returned 00000011h
Quest 09 -> +ACh( 3) - returned 00000003h
Quest 08 -> +ACh( 3) - returned 00000003h
Quest 06 -> +ACh( 3) - returned 00000011h
Quest 05 -> +ACh( 3) - returned 00000001h
Quest 04 -> +ACh( 3) - returned 05157b00h
Quest 03 -> +ACh( 3) - returned 00000011h

Data Changed before :+08 = 03010100 was 02010100 
Quest 02 -> +ACh( 3) - returned 00000001h
Quest 01 -> +ACh( 3) - returned 00000011h

//==============================================
Talked to Kashya
Quest 19 -> +A0h( 0) - returned 00000096h
Quest 06 -> +A0h( 0) - returned 00000000h
Quest 05 -> +A0h( 0) - returned ffffffffh
Quest 04 -> +A0h( 0) - returned 00000000h
Quest 03 -> +A0h( 0) - returned 00000000h
Quest 02 -> +A0h( 0) - returned 00000005h
Quest 01 -> +A0h( 0) - returned 00000000h
Quest 00 -> +A0h( 0) - returned 00000096h
Quest 06 -> +CCh(11) - returned 051f2300h
Quest 05 -> +CCh(11) - returned 0111e600h
Quest 04 -> +CCh(11) - returned 01ca0096h
Quest 03 -> +CCh(11) - returned 0111fa00h
    Quest 19 -> +A0h( 0) - returned 00000096h
    Quest 06 -> +A0h( 0) - returned 00000000h
    Quest 05 -> +A0h( 0) - returned ffffffffh

    Data Changed before :+0c = 00000601 was 00000600 
    Quest 04 -> +A0h( 0) - returned 00000001h
    Quest 03 -> +A0h( 0) - returned 00000000h

    Data Changed before :+08 = 0D010100 was 03010100 +0c = 00000405 was 00000404 +1c = 00000001 was 00000000 +9c = 00000001 was 00000000 
    Quest 02 -> +A0h( 0) - returned 00000004h
    Quest 01 -> +A0h( 0) - returned 00000000h
    Quest 00 -> +A0h( 0) - returned 00000096h
Quest 02 -> +CCh(11) - returned 0000013bh
Quest 01 -> +CCh(11) - returned 051f6c00h
Quest 00 -> +CCh(11) - returned 0000005ch
leave Kashya
Quest 06 -> +A8h( 2) - returned 05157900h
Quest 04 -> +A8h( 2) - returned 00000096h
Quest 03 -> +A8h( 2) - returned 05157900h


//==============================================
exit game
Quest 24 -> +C8h(10) - returned 00000000h
Quest 23 -> +C8h(10) - returned 00000000h
Quest 22 -> +C8h(10) - returned 00000000h
Quest 21 -> +C8h(10) - returned 00000000h
Quest 20 -> +C8h(10) - returned 00000000h
Quest 1f -> +C8h(10) - returned 00000000h
Quest 18 -> +C8h(10) - returned 00000000h
Quest 16 -> +C8h(10) - returned 00000000h
Quest 14 -> +C8h(10) - returned 00000000h
Quest 13 -> +C8h(10) - returned 00000000h
Quest 12 -> +C8h(10) - returned 00000000h
Quest 10 -> +C8h(10) - returned 0000000ah
Quest 0f -> +C8h(10) - returned 00000000h
Quest 0d -> +C8h(10) - returned 00000000h
Quest 0c -> +C8h(10) - returned 00000000h
Quest 0b -> +C8h(10) - returned 00000000h
Quest 0a -> +C8h(10) - returned 00000000h
Quest 09 -> +C8h(10) - returned 00000000h
Quest 08 -> +C8h(10) - returned 00000000h
Quest 07 -> +C8h(10) - returned 00000000h
Quest 05 -> +C8h(10) - returned 00000000h
Quest 04 -> +C8h(10) - returned 00000000h
Quest 03 -> +C8h(10) - returned 00000000h
Quest 02 -> +C8h(10) - returned 00000001h
Data Changed after :+9c = 00000000 was 00000001 
Quest 01 -> +C8h(10) - returned 00000001h

Looks like the returns on the pCallbacks are meaningless.

Callback notes:

0 (+A0)- called when selecting an NPC. The quests that get called are; current act = act. status != complete. Updates/changes quest status.

2 (+A8) - called on leaving NPC, The quests that get called are; current act = act. status != complete. updates +08 if quest started.

3 (+AC)- called when entering a level. All quests called. updates status.

8 (+C0)- called on monster killed. Only active quest called. What determines active quest is unkown ATM. Blood Raven quest was activated during Den of Evil kills but only Quest 1 was called.
could have something to do with +08 status...

entering GraveYard
Quest 02 -> +ACh( 3) - returned 00000000h
Data Changed after :+08 = 02010100 was 00010100 +0c = 00000403 was 00000400

entering Den of Evil
Quest 01 -> +ACh( 3) - returned 00000000h
Data Changed after :+08 = 02010100 was 01010100

But it could just be to set the +Quest button icon ;-)

10 (+C8) - called on exit. All quests.

11 (+CC) - NPC Message pending on NPC selection. current act = act, status != complete. Some handling here, causes pCallBack[0] calls if quest start message.

13 (+D4) - Start game. All quests called. Functions examine all quest conditions and setup flags/data.

Status dwords: +08, +0C, +14, +1C, +9C

The meaning of the status is unclear ATM. Each function uses/sets the bytes as needed.

+08 - is related to messages (hi byte only?), changes when message are read.
+0C - is the stage counter (lo byte only?). Changes every quest step.
+14 - intermediate flag

+1C ... +98 - Player Id's.
...
+9C - Player Id count.


Quest ID's:
pQuest +00 is the QuestID. It is used when calling GetQuestInfo(ID);
pQuest +10 is a "QuestID" used in pSequence to call the next quest sequence function.
pQuest +E0 is what we all are used to seeing as the Quest ID from using ptPlayer[ +14]+10 (pHistory or Quest bits).
This is what is used in calls to d2common.#11107 etc.
Some of the functions use a hardcoded ID in that call, possibly to effect the next quest. In our code we need to use some other mechanism for the effect.

Code:

It's time to examine the these functions now that we know when they're called.
I'm sure we'll see some familiar code that looked completely out of place at the time. But now in context it should seem more logical (hopefully ;-)
Last edited by SVR on Sat Jan 01, 2005 12:30 am, edited 3 times in total.

User avatar
SVR
Retired staff
Arch-Angel
Posts: 1449
Joined: Sat Nov 02, 2002 11:04 pm
Location: Texas
United States of America

Hand-picked

Re: Quest structures and functions

Post by SVR » Sun Dec 19, 2004 6:02 pm

Here's the entire A1Q1 recoded into C ...

Code: Select all


// Quest.cpp : Sample mod dll for use with D2Mod.dll mod system
// Author: SVR 2004
//
//=======================================================================

#include <d2mod.h>  // main include for all import definitions/library functions
#include "Quest.h"  // add your header here

void A1Q1Init(Quest *pQuest);

//=======================================================================
// mod data - put needed global data here

QuestInit A1Q1= {A1Q1Init,0,0x64,0,1,1};        // Act1-Quest1 Init table entry

NPCMenu sNPCInfoA1Q1[8] = {
    {
        {0x94,0x40,0x00},
        1
    },
    {
        {0x94,0x41,0x02,
         0x9B,0x46,0x02,
         0x93,0x45,0x02,
         0x96,0x42,0x02,
         0x9A,0x43,0x02},
        5
    },
    {
        {0x96,0x48,0x02,
         0x9B,0x4B,0x02,
         0x9A,0x49,0x02,
         0x94,0x47,0x02,
         0x93,0x4A,0x02},
        5
    },
    {
        {0x96,0x4D,0x02,
         0x9B,0x50,0x02,
         0x9A,0x4E,0x02,
         0x94,0x4C,0x02,
         0x93,0x4F,0x02},
        5
    },
    {
        {0x9B,0x50,0x02,
         0x9A,0x4E,0x02,
         0x93,0x4F,0x02},
        3
    },
    {
        {-1,0,0x02},
        0
    },
    {
        {-1,0,0x02},
        0
    },
    {
        {-1,0,0x02},
        0
    }

};

DWORD msgTbl[]= {-1,0,1,2,3,4}; // Act1-Quest1 state message table (index's NPCInfo table)




//=========================================
// fog exports

FUNC(const void*,   FASTCALL ,D2Malloc,(DWORD,DWORD,char *,DWORD,DWORD),(DWORD)&Fog_10045);
/*
inline void* D2Malloc(DWORD heap,DWORD size,char *file, DWORD line)
{
    void* tmpRet;
    __asm {
        push line
        push file
        mov edx,size
        mov ecx,heap
        call DWORD PTR [Fog_10045]
        mov [tmpRet],eax
    }
    return tmpRet;
}
*/

//=========================================
// d2common exports

FUNC(int,   STDCALL,    GetActFromLevel,(int),(DWORD)&D2Common_10001);

FUNC(DWORD , STDCALL, GetRoomsSeen,(Act* ptAct,DWORD eLevelID),(DWORD)&D2Common_10090);

FUNC(DWORD , STDCALL, GetBit,(DWORD* pHist,DWORD index,DWORD bit),(DWORD)&D2Common_11107);
FUNC(DWORD , STDCALL, SetBit,(DWORD* pHist,DWORD index,DWORD bit),(DWORD)&D2Common_11108);
FUNC(DWORD , STDCALL, ClearBit,(DWORD* pHist,DWORD index,DWORD bit),(DWORD)&D2Common_11109);
FUNC(DWORD , STDCALL, UpdateHist,(DWORD* pHist,DWORD index),(DWORD)&D2Common_11110); // ??

FUNC(PlayerData*, STDCALL, GetPlayerData,(Unit* pPlayer),(DWORD)&D2Common_10424);

//=========================================
// local d2game functions - used by all quests
FUNC(Quest* , FASTCALL, GetQuestInfo    ,(D2Game* ptGame,DWORD questID),(DWORD)(D2GameBase+0x0063B90));

FUNC(DWORD  , FASTCALL, QuestFindPlayer ,(Quest* pQuest,DWORD playerID),(DWORD)(D2GameBase+0x00668B0));
FUNC(void   , FASTCALL, QuestRemovePlayer,(Quest* pQuest, QParam* pqParam),(DWORD)(D2GameBase+0x00668F0));
FUNC(void   , FASTCALL, AllRemovePlayer,(void* pPlayers, DWORD playerID),(DWORD)(D2GameBase+0x0066840));

FUNC(DWORD  , FASTCALL, SetGameState    ,(Quest* pQuest,DWORD state,void *file,DWORD line),(DWORD)(D2GameBase+0x0064CF0));
FUNC(DWORD  , FASTCALL, Notify          ,(D2Game* ptGame,char* msg,void *file,DWORD line),(DWORD)(D2GameBase+0x0064A30));

FUNC(DWORD  , FASTCALL, IterateFn1      ,(D2Game* ptGame,DWORD unk1,DWORD unk2,void *pfnIterate),(DWORD)(D2GameBase+0x008CC40));
FUNC(DWORD  , FASTCALL, IterateFn2      ,(Quest* pQuest,DWORD unk1,DWORD unk2,void *pfnIterate,DWORD unk3),(DWORD)(D2GameBase+0x0064CA0));
FUNC(DWORD  , FASTCALL, IterateFn3      ,(Quest* pQuest,void *pfnIterate,DWORD unk),(DWORD)(D2GameBase+0x0064690));

FUNC(DWORD  , FASTCALL, DisplayNPCMsg   ,(D2Game* ptGame,Unit* ptPlayer,Unit* pNpcUnit),(DWORD)(D2GameBase+0x0066F10));
FUNC(void   , FASTCALL, PostNPCMsg      ,(Quest* pQuest,TextHead* pTextHead,DWORD npcID,DWORD msgID),(DWORD)(D2GameBase+0x0063D60));

FUNC(DWORD  , FASTCALL, QDataFindPlayer ,(Quest1Data* pQData,DWORD playerID),(DWORD)(D2GameBase+0x0066880));
FUNC(DWORD  , FASTCALL, QDataAddPlayer  ,(Quest1Data* pQData,DWORD playerID),(DWORD)(D2GameBase+0x0066810));

FUNC(DWORD  , FASTCALL, D2Game_X656C0   ,(D2Game* ptGame,DWORD eFilter,DWORD value),(DWORD)(D2GameBase+0x00656C0));
FUNC(DWORD  , FASTCALL, D2Game_X66D60   ,(D2Game* ptGame,Quest1Data* pQData,DWORD eFilter,DWORD value),(DWORD)(D2GameBase+0x0066D60));

FUNC(DWORD  , FASTCALL, D2Game_X66E80   ,(D2Game* ptGame,DWORD value),(DWORD)(D2GameBase+0x0066E80));




//=========================================
// this are quest specific
// DWORD *msgTbl=(DWORD *)(D2GameBase+0x102558);

void *pfnIterate1=(void *)(D2GameBase+0x0067D30);
void *pfnIterate2=(void *)(D2GameBase+0x0067B30);


void *pfn_X68160=(void *)(D2GameBase+0x0068160);
void *pfn_X680C0=(void *)(D2GameBase+0x00680C0);
void *pfn_X68120=(void *)(D2GameBase+0x0068120);
void *pfn_X681B0=(void *)(D2GameBase+0x00681B0);
 

// NPCInfo *A1Q1Msg=(NPCInfo *)sNPCInfoA1Q1; //(D2GameBase+0x0101F38);

QuestInit* sgptQuestInit=(QuestInit*)(D2GameBase+0x0101278);


//=======================================================================
// mod functions - put functions to call from edits here

Quest Quests[100];

static int Depth=-1;

void EventFunc(Quest *pQuest,QParam *pqParam)
{
    DWORD event=pqParam->eCallback;

    Quest *pTmp=&Quests[pQuest->QuestID];
    Depth++;
    if(memcmp( pTmp,pQuest,0xA0))
    {
        char s[1000];
        char s1[100];
        DWORD *p1=(DWORD *)pQuest;
        DWORD *p2=(DWORD *)pTmp;

        lstrcpyn(s,"\n                                                  ",Depth*4+3);
        lstrcat(s,"Data Changed before :");
        for(int i=0;i<(0xA0>>2);i++)
        {
            if(*p1!=*p2)
            {
                wsprintf(s1,"+%02x = %08X was %08X ",i*4,*p1,*p2);
                lstrcat(s,s1);
            }
            p1++;p2++;
        }

        if(pQuest->eFilter != pTmp->eFilter )
        {
            wsprintf(s1," !!! eFilter = %08X was %08X ",i*4,pQuest->eFilter ,pTmp->eFilter );
            lstrcat(s,s1);
        }

        log_info(s);
        memmove(pTmp,pQuest,0xA0);
    }

    log_message("%*sQuest %02x -> +%02Xh(%2d) Called",Depth*4," ",
        pQuest->QuestID,event*4+0xA0,pqParam->eCallback);

    pTmp->pCallback[event](pQuest,pqParam);
    

    if(memcmp( pTmp,pQuest,0xA0))
    {
        char s[1000];
        char s1[100];
        DWORD *p1=(DWORD *)pQuest;
        DWORD *p2=(DWORD *)pTmp;

        lstrcpyn(s,"                                                   ",Depth*4+2);
        lstrcat(s,"Data Changed after :");
        for(int i=0;i<(0xA0>>2);i++)
        {
            if(*p1!=*p2)
            {
                wsprintf(s1,"+%02x = %08X was %08X ",i*4,*p1,*p2);
                lstrcat(s,s1);
            }
            p1++;p2++;
        }
        if(pQuest->eFilter != pTmp->eFilter )
        {
            wsprintf(s1," !!! eFilter = %08X was %08X ",i*4,pQuest->eFilter ,pTmp->eFilter );
            lstrcat(s,s1);
        }
        log_info(s);
        memmove(pTmp,pQuest,0xA0);
    }
    Depth--;
}

//=================================================================================
// Quest1 functions
//=================================================================================
#define QUEST1_NPC 0x94
#define QUEST1_LEVEL 8



//=================================================================================
// Quest1 pActiveFilter function
/*
6FC979A0   . 56             PUSH    ESI                                        ;  Quest 1 pActiveFilter (+E8)
6FC979A1   . 81FA 94000000  CMP     EDX,94 << NPC ID
6FC979A7   . 57             PUSH    EDI
6FC979A8   . 8BF1           MOV     ESI,ECX
6FC979AA   . 75 59          JNZ     SHORT D2Game.6FC97A05
6FC979AC   . 8B86 E0000000  MOV     EAX,[ESI+E0]
6FC979B2   . 8B7C24 10      MOV     EDI,[ESP+10]
6FC979B6   . 6A 00          PUSH    0
6FC979B8   . 50             PUSH    EAX
6FC979B9   . 57             PUSH    EDI
6FC979BA   . E8 553F0800    CALL    <JMP.&D2Common.#11107>                     ; if(GetBit(0))
6FC979BF   . 83F8 01        CMP     EAX,1                                      ;     return 0
6FC979C2   . 74 41          JE      SHORT D2Game.6FC97A05
6FC979C4   . 807E 09 01     CMP     BYTE PTR [ESI+9],1                         ; if(pQuest->bUnk09==1 && pQuest->bUnk0C==1)
6FC979C8   . 75 20          JNZ     SHORT D2Game.6FC979EA                      ; {
6FC979CA   . 807E 0C 01     CMP     BYTE PTR [ESI+C],1
6FC979CE   . 75 1A          JNZ     SHORT D2Game.6FC979EA

6FC979D0   . 8B8E E0000000  MOV     ECX,[ESI+E0]                               ;  pQuest->eFilter
6FC979D6   . 6A 01          PUSH    1
6FC979D8   . 51             PUSH    ECX
6FC979D9   . 57             PUSH    EDI                                        ; // *** test not needed, can only return 1 below if test fails here
6FC979DA   . E8 353F0800    CALL    <JMP.&D2Common.#11107>                     ;        if(!GetBit(1))
6FC979DF   . 85C0           TEST    EAX,EAX
6FC979E1   . 75 07          JNZ     SHORT D2Game.6FC979EA
6FC979E3   . 5F             POP     EDI
6FC979E4   . B0 01          MOV     AL,1                                       ;            return 1;
6FC979E6   . 5E             POP     ESI
6FC979E7   . C2 0C00        RETN    0C
6FC979EA   > 8B96 E0000000  MOV     EDX,[ESI+E0]                               ; }
6FC979F0   . 6A 01          PUSH    1
6FC979F2   . 52             PUSH    EDX
6FC979F3   . 57             PUSH    EDI
6FC979F4   . E8 1B3F0800    CALL    <JMP.&D2Common.#11107>                     ; if(GetBit(1))
6FC979F9   . 83F8 01        CMP     EAX,1
6FC979FC   . 75 07          JNZ     SHORT D2Game.6FC97A05
6FC979FE   . 5F             POP     EDI
6FC979FF   . 8AC0           MOV     AL,AL                                      ;     return 1
6FC97A01   . 5E             POP     ESI
6FC97A02   . C2 0C00        RETN    0C

6FC97A05   > 5F             POP     EDI                                        ; return 0;
6FC97A06   . 32C0           XOR     AL,AL
6FC97A08   . 5E             POP     ESI
6FC97A09   . C2 0C00        RETN    0C

*/


DWORD FASTCALL ActiveFilter(Quest *pQuest,DWORD NpcID,Unit* ptPlayer,DWORD *pHist,Unit* ptNPCUnit)
{
    if(NpcID!=QUEST1_NPC)
        return 0;

    if(GetBit(pHist,pQuest->eFilter,0))
        return 0;

    if(pQuest->bUnk09==1 && pQuest->bState==1)
        return 1;

    return (GetBit(pHist,pQuest->eFilter,1));
}



//=========================================================================
// Quest 1 - pSequenceFilter (+EC)

/*
6FC98330   . 56             PUSH    ESI
6FC98331   . 8BF1           MOV     ESI,ECX
6FC98333   . 57             PUSH    EDI
6FC98334   . 807E 0C 05     CMP     BYTE PTR [ESI+C],5                         ;  State 5 ?
6FC98338   . 74 0C          JE      SHORT D2Game.6FC98346
6FC9833A   . 8A46 09        MOV     AL,[ESI+9]                                 ;  pQuestInfo->bUnk09 (disabled ?) 
6FC9833D   . 84C0           TEST    AL,AL
6FC9833F   . 74 05          JE      SHORT D2Game.6FC98346
6FC98341   . 5F             POP     EDI
6FC98342   . B0 01          MOV     AL,1
6FC98344   . 5E             POP     ESI
6FC98345   . C3             RETN
6FC98346   > 8B56 10        MOV     EDX,[ESI+10]                               ;  pQuestInfo->nID
6FC98349   . 8B4E 04        MOV     ECX,[ESI+4]                                ;  ptGame
6FC9834C   . E8 3FB8FFFF    CALL    <D2Game.GetQuestInfo>
6FC98351   . 8BF8           MOV     EDI,EAX
6FC98353   . 85FF           TEST    EDI,EDI
6FC98355   . 74 3A          JE      SHORT D2Game.6FC98391
6FC98357   . 8B86 EC000000  MOV     EAX,[ESI+EC]                               ;  pQuestInfo->pSequence
6FC9835D   . 50             PUSH    EAX                                        ; /CodeAddress
6FC9835E   . FF15 107CD26F  CALL    [<&KERNEL32.IsBadCodePtr>]                 ; \IsBadCodePtr
6FC98364   . 85C0           TEST    EAX,EAX
6FC98366   . 74 1E          JE      SHORT D2Game.6FC98386                      ;  ??? check badPtr on this function addy :P stupid !
6FC98368   . 68 83020000    PUSH    283
6FC9836D   . 68 D044D36F    PUSH    D2Game.6FD344D0                            ;  ASCII "..\\D2Game/Quests/a1q1.cpp"
6FC98372   . 68 0C19D36F    PUSH    D2Game.6FD3190C                            ;  ASCII "pQuestInfo->pSequence"
6FC98377   . E8 72430800    CALL    <JMP.&Fog.#10023>
6FC9837C   . 83C4 0C        ADD     ESP,0C
6FC9837F   . 6A FF          PUSH    -1
6FC98381   . E8 67440800    CALL    D2Game.6FD1C7ED
6FC98386   > 8BCF           MOV     ECX,EDI
6FC98388   . FF97 EC000000  CALL    [EDI+EC]                                   ;  pQuestInfo->pSequence (next quest)
6FC9838E   . 5F             POP     EDI
6FC9838F   . 5E             POP     ESI
6FC98390   . C3             RETN
6FC98391   > 5F             POP     EDI
6FC98392   . 32C0           XOR     AL,AL
6FC98394   . 5E             POP     ESI
6FC98395   . C3             RETN
*/

DWORD FASTCALL SequenceFilter(Quest *pQuest)
{
    if(pQuest->bUnk09!=0 && pQuest->bState!=5)
        return 1;

    Quest* pNext=GetQuestInfo(pQuest->ptGame,pQuest->nID);

    if(pNext)
        return pNext->pSequence(pNext);

    return 0;
}



//=========================================================================
// Quest 1 - pCallback[13] (+D4)
/*
6FC983A0   . 8B42 0C        MOV     EAX,[EDX+C]                      ;  Quest 1 CB 13 (+D4)
6FC983A3   . 56             PUSH    ESI
6FC983A4   . 57             PUSH    EDI
6FC983A5   . 8B3A           MOV     EDI,[EDX]                        ;  ptGame
6FC983A7   . 50             PUSH    EAX                              ;  ptPlayerUnit
6FC983A8   . 8BF1           MOV     ESI,ECX                          ;  pQuest
6FC983AA   . E8 47320800    CALL    <JMP.&D2Common.#10424>           ;  GetPlayerData(pPlayer);
6FC983AF   . 8B96 E0000000  MOV     EDX,[ESI+E0]                     ;  pQuest->eFilter - pHistory index
6FC983B5   . 33C9           XOR     ECX,ECX
6FC983B7   . 8A4F 6D        MOV     CL,[EDI+6D]                      ;  ptGame->Diff
6FC983BA   . 6A 00          PUSH    0
6FC983BC   . 52             PUSH    EDX
6FC983BD   . 8B7C88 10      MOV     EDI,[EAX+ECX*4+10]               ;  pPlayerData->pQuestHistory[diff]
6FC983C1   . 57             PUSH    EDI
6FC983C2   . E8 4D350800    CALL    <JMP.&D2Common.#11107>
6FC983C7   . 85C0           TEST    EAX,EAX                          ;  if(GetBit(0))
6FC983C9   . 75 5C          JNZ     SHORT D2Game.6FC98427            ;      return
6FC983CB   . 8B86 E0000000  MOV     EAX,[ESI+E0]
6FC983D1   . 6A 0F          PUSH    0F
6FC983D3   . 50             PUSH    EAX
6FC983D4   . 57             PUSH    EDI
6FC983D5   . E8 3A350800    CALL    <JMP.&D2Common.#11107>
6FC983DA   . 85C0           TEST    EAX,EAX                          ;  if(GetBit(0Fh))
6FC983DC   . 75 49          JNZ     SHORT D2Game.6FC98427            ;      return
6FC983DE   . 6A 04          PUSH    4
6FC983E0   . 6A 01          PUSH    1                                ;  // hardcoded pHistory index 1 :(
6FC983E2   . 57             PUSH    EDI
6FC983E3   . E8 2C350800    CALL    <JMP.&D2Common.#11107>
6FC983E8   . 83F8 01        CMP     EAX,1                            ;  if (GetBit(4))
6FC983EB   . 75 0B          JNZ     SHORT D2Game.6FC983F8            ;  {
6FC983ED   . C646 0B 02     MOV     BYTE PTR [ESI+B],2               ;      pQuest->bUnk0B=2
6FC983F1   . C646 0C 03     MOV     BYTE PTR [ESI+C],3               ;      pQuest->bState=3
6FC983F5   . 5F             POP     EDI
6FC983F6   . 5E             POP     ESI                              ;      return
6FC983F7   . C3             RETN                                     ;  }
6FC983F8   > 6A 03          PUSH    3
6FC983FA   . 6A 01          PUSH    1                                ;  // hardcoded pHistory index 1 :(
6FC983FC   . 57             PUSH    EDI
6FC983FD   . E8 12350800    CALL    <JMP.&D2Common.#11107>
6FC98402   . 83F8 01        CMP     EAX,1                            ;  if(GetBit(3))
6FC98405   . 75 0A          JNZ     SHORT D2Game.6FC98411            ;  {
6FC98407   . C646 0C 03     MOV     BYTE PTR [ESI+C],3               ;      pQuest->bState=3
6FC9840B   . 8846 0B        MOV     [ESI+B],AL                       ;      pQuest->bUnk0B=1
6FC9840E   . 5F             POP     EDI
6FC9840F   . 5E             POP     ESI                              ;      return
6FC98410   . C3             RETN                                     ;  }
6FC98411   > 6A 02          PUSH    2
6FC98413   . 6A 01          PUSH    1                                ;  // hardcoded pHistory index 1 :(
6FC98415   . 57             PUSH    EDI
6FC98416   . E8 F9340800    CALL    <JMP.&D2Common.#11107>
6FC9841B   . 83F8 01        CMP     EAX,1                            ;  if(GetBit(2))
6FC9841E   . 75 07          JNZ     SHORT D2Game.6FC98427            ;  {
6FC98420   . C646 0C 02     MOV     BYTE PTR [ESI+C],2               ;      pQuest->bState=2
6FC98424   . 8846 0B        MOV     [ESI+B],AL                       ;      pQuest->bUnk0B=1
6FC98427   > 5F             POP     EDI                              ;  }
6FC98428   . 5E             POP     ESI                              ;  return
6FC98429   . C3             RETN
*/



void FASTCALL Q1D4(Quest *pQuest,QParam *pqParam) //Game Started
{
    D2Game* ptGame = pqParam->ptGame;
    Unit *ptPlayer= pqParam->ptPlayer;

    PlayerData* ptPlayerData=GetPlayerData(ptPlayer);
    DWORD diff=ptGame->Difficulty;
    DWORD *pHist=(DWORD *)ptPlayerData->ptQuest[diff];

    if(GetBit(pHist,pQuest->eFilter,0))
        return ;
    if(GetBit(pHist,pQuest->eFilter,0x0F))
        return ;

    if(GetBit(pHist,1,4))
    {
        pQuest->bUnk0B=2;
        pQuest->bState=3;
        return ;
    }
    if(GetBit(pHist,1,3))
    {
        pQuest->bUnk0B=1;
        pQuest->bState=3;
        return ;
    }
    if(GetBit(pHist,1,2))
    {
        pQuest->bUnk0B=1;
        pQuest->bState=2;
        return ;
    }
}


//=========================================================================
// Quest 1 - pCallback[0] (+A0)
/*
6FC97DC0   . 83EC 08        SUB     ESP,8                                      ;  Quest 1 function 0 (+A0) // NPC Selected
6FC97DC3   . 53             PUSH    EBX
6FC97DC4   . 8BDA           MOV     EBX,EDX
6FC97DC6   . 55             PUSH    EBP
6FC97DC7   . 56             PUSH    ESI
6FC97DC8   . 8B43 0C        MOV     EAX,[EBX+C]                                ;  ptPlayer
6FC97DCB   . 8BF1           MOV     ESI,ECX
6FC97DCD   . 57             PUSH    EDI
6FC97DCE   . 50             PUSH    EAX
6FC97DCF   . 8B7E 04        MOV     EDI,[ESI+4]
6FC97DD2   . E8 1F380800    CALL    <JMP.&D2Common.#10424>                     ;  getPlayerData
6FC97DD7   . 8B96 E0000000  MOV     EDX,[ESI+E0]                               ;  pQuest->eFilter
6FC97DDD   . 33C9           XOR     ECX,ECX
6FC97DDF   . 8A4F 6D        MOV     CL,[EDI+6D]
6FC97DE2   . 6A 00          PUSH    0                                          ;  check bit 0
6FC97DE4   . 52             PUSH    EDX
6FC97DE5   . 8B7C88 10      MOV     EDI,[EAX+ECX*4+10]                         ;  playerData->pHistory(diff)
6FC97DE9   . 57             PUSH    EDI
6FC97DEA   . E8 253B0800    CALL    <JMP.&D2Common.#11107>
6FC97DEF   . 8BE8           MOV     EBP,EAX
6FC97DF1   . 8B86 E0000000  MOV     EAX,[ESI+E0]
6FC97DF7   . 6A 0D          PUSH    0D                                         ;  check bit Dh
6FC97DF9   . 50             PUSH    EAX
6FC97DFA   . 57             PUSH    EDI
6FC97DFB   . E8 143B0800    CALL    <JMP.&D2Common.#11107>
6FC97E00   . 894424 14      MOV     [ESP+14],EAX
6FC97E04   . 8B43 08        MOV     EAX,[EBX+8]                                ;  pqParam->pNPCUnit
6FC97E07   . 85C0           TEST    EAX,EAX
6FC97E09   . 75 0A          JNZ     SHORT D2Game.6FC97E15
6FC97E0B   . C74424 10 FFFF>MOV     DWORD PTR [ESP+10],-1
6FC97E13   . EB 07          JMP     SHORT D2Game.6FC97E1C
6FC97E15   > 8B48 04        MOV     ECX,[EAX+4]                                ;  npcID
6FC97E18   . 894C24 10      MOV     [ESP+10],ECX

6FC97E1C   > 8B96 E0000000  MOV     EDX,[ESI+E0]
6FC97E22   . 6A 01          PUSH    1                                          ;  bit1
6FC97E24   . 52             PUSH    EDX                                        ;  eFilter
6FC97E25   . 57             PUSH    EDI                                        ;  pHistory
6FC97E26   . E8 E93A0800    CALL    <JMP.&D2Common.#11107>
6FC97E2B   . 85C0           TEST    EAX,EAX
6FC97E2D   . 74 19          JE      SHORT D2Game.6FC97E48                      ; if(GetBit(1)
6FC97E2F   . 8B4424 10      MOV     EAX,[ESP+10]                               ; {
6FC97E33   . 8B53 14        MOV     EDX,[EBX+14]
6FC97E36   . 6A 03          PUSH    3
6FC97E38   . 50             PUSH    EAX
6FC97E39   . 8BCE           MOV     ECX,ESI
6FC97E3B   . E8 20BFFFFF    CALL    D2Game.6FC93D60                            ;      PostNpcMsg(pQuest,pqParam->14h,npcID,3)
6FC97E40   . 5F             POP     EDI
6FC97E41   . 5E             POP     ESI
6FC97E42   . 5D             POP     EBP
6FC97E43   . 5B             POP     EBX
6FC97E44   . 83C4 08        ADD     ESP,8
6FC97E47   . C3             RETN                                               ; }

6FC97E48   > 8B43 0C        MOV     EAX,[EBX+C]                                ;  pqParam->ptPlayer
6FC97E4B   . 85C0           TEST    EAX,EAX
6FC97E4D   . 75 05          JNZ     SHORT D2Game.6FC97E54
6FC97E4F   . 83C8 FF        OR      EAX,FFFFFFFF
6FC97E52   . EB 03          JMP     SHORT D2Game.6FC97E57
6FC97E54   > 8B40 0C        MOV     EAX,[EAX+C]                                ;  ptPlayer->PlayerID
6FC97E57   > 8BD0           MOV     EDX,EAX
6FC97E59   . 8BCE           MOV     ECX,ESI                                    ;  pQuest
6FC97E5B   . E8 50EAFFFF    CALL    D2Game.6FC968B0                            ;  if(playerID in pQuest->1C[])
6FC97E60   . 83F8 01        CMP     EAX,1                                      ;  {
6FC97E63   . 75 19          JNZ     SHORT D2Game.6FC97E7E
6FC97E65   . 8B4C24 10      MOV     ECX,[ESP+10]
6FC97E69   . 8B53 14        MOV     EDX,[EBX+14]
6FC97E6C   . 6A 04          PUSH    4                                          ;  only difference from other call below
6FC97E6E   . 51             PUSH    ECX
6FC97E6F   . 8BCE           MOV     ECX,ESI
6FC97E71   . E8 EABEFFFF    CALL    D2Game.6FC93D60                            ;  PostNpcMsg(4)
6FC97E76   . 5F             POP     EDI
6FC97E77   . 5E             POP     ESI
6FC97E78   . 5D             POP     EBP
6FC97E79   . 5B             POP     EBX
6FC97E7A   . 83C4 08        ADD     ESP,8
6FC97E7D   . C3             RETN                                               ;  }
6FC97E7E   > 83FD 01        CMP     EBP,1                                      ;  if(pHistory->bit0)
6FC97E81   . 74 3C          JE      SHORT D2Game.6FC97EBF                      ;      return
6FC97E83   . 8A46 0C        MOV     AL,[ESI+C]
6FC97E86   . 3C 04          CMP     AL,4                                       ;  if(pQuest->0C >= 4) // byte value !
6FC97E88   . 72 08          JB      SHORT D2Game.6FC97E92                      ;  {
6FC97E8A   . 8B4C24 14      MOV     ECX,[ESP+14]
6FC97E8E   . 85C9           TEST    ECX,ECX                                    ;      if(!pHistory->bit0D)
6FC97E90   . 74 2D          JE      SHORT D2Game.6FC97EBF                      ;          return
6FC97E92   > 8A4E 09        MOV     CL,[ESI+9]                                 ;  }
6FC97E95   . 84C9           TEST    CL,CL                                      ;  if(!pQuest->09) // byte value !
6FC97E97   . 74 26          JE      SHORT D2Game.6FC97EBF                      ;      return
6FC97E99   . 25 FF000000    AND     EAX,0FF
6FC97E9E   . 8B0485 5825D36>MOV     EAX,[EAX*4+6FD32558]
6FC97EA5   . 83F8 FF        CMP     EAX,-1
6FC97EA8   . 74 15          JE      SHORT D2Game.6FC97EBF
6FC97EAA   . 83F8 08        CMP     EAX,8
6FC97EAD   . 73 10          JNB     SHORT D2Game.6FC97EBF
6FC97EAF   . 8B5424 10      MOV     EDX,[ESP+10]                               ;  npcID
6FC97EB3   . 50             PUSH    EAX                                        ;  2 - State to select ?
6FC97EB4   . 52             PUSH    EDX
6FC97EB5   . 8B53 14        MOV     EDX,[EBX+14]                               ;  pqParam->pTextHead
6FC97EB8   . 8BCE           MOV     ECX,ESI                                    ;  pQuest
6FC97EBA   . E8 A1BEFFFF    CALL    D2Game.6FC93D60                            ;  alloc(0C bytes) insert at pTextHead->pText
6FC97EBF   > 5F             POP     EDI
6FC97EC0   . 5E             POP     ESI
6FC97EC1   . 5D             POP     EBP
6FC97EC2   . 5B             POP     EBX
6FC97EC3   . 83C4 08        ADD     ESP,8
6FC97EC6   . C3             RETN

*/

void FASTCALL Q1A0(Quest *pQuest,QParam *pqParam) // NPC Selected
{
    D2Game* ptGame =    pqParam->ptGame;
    Unit*   ptPlayer =  pqParam->ptPlayer;
    Unit*   pNPCUnit =  pqParam->ptNPCUnit;

    DWORD   diff =  ptGame->Difficulty;

    PlayerData* ptPlayerData=GetPlayerData(ptPlayer);

    DWORD*  pHist=(DWORD *)ptPlayerData->ptQuest[diff];

    DWORD bit0=GetBit(pHist,pQuest->eFilter,0);

    DWORD bitD=GetBit(pHist,pQuest->eFilter,0x0D);

    DWORD npcID;
    DWORD playerID;

    if(pNPCUnit)
        npcID=pNPCUnit->nUnitId;
    else
        npcID=-1;

    if(GetBit(pHist,pQuest->eFilter,1))
    {
        PostNPCMsg(pQuest,pqParam->pTextHead,npcID,3);
        return;
    }

    if(ptPlayer)
        playerID=ptPlayer->nUnitUnid;
    else
        playerID=-1;

    if(QuestFindPlayer(pQuest,playerID)==1)
    {
        PostNPCMsg(pQuest,pqParam->pTextHead,npcID,4);
        return;
    }
    if(bit0==1)
        return;

    if(pQuest->bState>=4)
    {
        if(!bitD)
            return;
    }

    if(!pQuest->bUnk09)
        return;

    DWORD msgID=msgTbl[pQuest->bState];
    if(msgID!=-1 && msgID<8)
        PostNPCMsg(pQuest,pqParam->pTextHead,npcID,msgID);


}

//=========================================================================
// Quest 1 - pCallback[11] (+CC) - MsgPosted
/*
6FC97BA0   . 51             PUSH    ECX                                        ;  Quest 1 Callback 11(+CC)
6FC97BA1   . 53             PUSH    EBX
6FC97BA2   . 55             PUSH    EBP
6FC97BA3   . 56             PUSH    ESI
6FC97BA4   . 57             PUSH    EDI
6FC97BA5   . 8BF1           MOV     ESI,ECX                                    ;  pQuest
6FC97BA7   . 8BFA           MOV     EDI,EDX                                    ;  pqParams
6FC97BA9   . 8B46 18        MOV     EAX,[ESI+18]                               ;  pQuest->QuestBytes
6FC97BAC   . 66:817F 14 940>CMP     WORD PTR [EDI+14],94                       ;  if(NPC ID != Akara)
6FC97BB2   . 66:8B5F 18     MOV     BX,[EDI+18]                                ;   pqParam[18h] ? (strKey)
6FC97BB6   . 894424 10      MOV     [ESP+10],EAX                               ;   pQuest->QuestBytes
6FC97BBA   . 0F85 66010000  JNZ     D2Game.6FC97D26                            ;      return;
6FC97BC0   . 8B47 0C        MOV     EAX,[EDI+C]                                ;  ptPlayer
6FC97BC3   . 8B6E 04        MOV     EBP,[ESI+4]                                ;  ptGame
6FC97BC6   . 50             PUSH    EAX
6FC97BC7   . E8 2A3A0800    CALL    <JMP.&D2Common.#10424>                     ;  GetPlayerData
6FC97BCC   . 33C9           XOR     ECX,ECX
6FC97BCE   . 66:83FB 40     CMP     BX,40
6FC97BD2   . 8A4D 6D        MOV     CL,[EBP+6D]                                ;  ptGame->diff
6FC97BD5   . 8B6C88 10      MOV     EBP,[EAX+ECX*4+10]                         ;  pHistory[diff]
6FC97BD9   . 75 46          JNZ     SHORT D2Game.6FC97C21
6FC97BDB   . 8B5424 10      MOV     EDX,[ESP+10]                               ;  pQuest->QuestBytes
6FC97BDF   . 68 DD000000    PUSH    0DD
6FC97BE4   . 68 D044D36F    PUSH    D2Game.6FD344D0                            ;  ASCII "..\\D2Game/Quests/a1q1.cpp"
6FC97BE9   . 8BCE           MOV     ECX,ESI
6FC97BEB   . C682 86000000 >MOV     BYTE PTR [EDX+86],1
6FC97BF2   . BA 02000000    MOV     EDX,2
6FC97BF7   . E8 F4D0FFFF    CALL    D2Game.6FC94CF0                            ;  SetGameState(pQuest,2,FILE_,LINE_)

6FC97BFC   . 8B4E 04        MOV     ECX,[ESI+4]                                ; pQuest->ptGame
6FC97BFF   . 33D2           XOR     EDX,EDX                                    ; 0
6FC97C01   . 68 307DC96F    PUSH    D2Game.6FC97D30                            ; pfnIterate1
6FC97C06   . 6A 00          PUSH    0
6FC97C08   . E8 33500200    CALL    D2Game.6FCBCC40                            ; Iterate1(ptGame,0,0,pfnIterate1);
6FC97C0D   . 8B47 08        MOV     EAX,[EDI+8]                                ;  NPCUnit
6FC97C10   . 8B0F           MOV     ECX,[EDI]                                  ;  ptGame
6FC97C12   . 8B57 0C        MOV     EDX,[EDI+C]                                ;  ptPlayer
6FC97C15   . 50             PUSH    EAX                                        ; /Arg1
6FC97C16   . E8 F5F2FFFF    CALL    D2Game.6FC96F10                            ; \D2Game.6FC96F10 //DisplayNPCMessage
6FC97C1B   . 5F             POP     EDI
6FC97C1C   . 5E             POP     ESI
6FC97C1D   . 5D             POP     EBP
6FC97C1E   . 5B             POP     EBX
6FC97C1F   . 59             POP     ECX
6FC97C20   . C3             RETN

6FC97C21   > 66:83FB 4C     CMP     BX,4C
6FC97C25   . 0F85 FB000000  JNZ     D2Game.6FC97D26
6FC97C2B   . 8B8E E0000000  MOV     ECX,[ESI+E0]
6FC97C31   . 6A 01          PUSH    1
6FC97C33   . 51             PUSH    ECX
6FC97C34   . 55             PUSH    EBP
6FC97C35   . E8 DA3C0800    CALL    <JMP.&D2Common.#11107>
6FC97C3A   . 83F8 01        CMP     EAX,1                                    ; if(GetBit(1)!=1)
6FC97C3D   . 0F85 E3000000  JNZ     D2Game.6FC97D26                          ;    return

6FC97C43   . 8B96 E0000000  MOV     EDX,[ESI+E0]
6FC97C49   . 6A 0D          PUSH    0D
6FC97C4B   . 52             PUSH    EDX
6FC97C4C   . 55             PUSH    EBP
6FC97C4D   . E8 C23C0800    CALL    <JMP.&D2Common.#11107>
6FC97C52   . 33DB           XOR     EBX,EBX
6FC97C54   . 85C0           TEST    EAX,EAX                                    ; if(GetBit(0xD))
6FC97C56   . 74 6F          JE      SHORT D2Game.6FC97CC7                      ; {
6FC97C58   . 807E 0C 05     CMP     BYTE PTR [ESI+C],5                         ;     if(pQuest->bState!=5)
6FC97C5C   . 74 63          JE      SHORT D2Game.6FC97CC1                      ;     {
6FC97C5E   . 68 E9000000    PUSH    0E9
6FC97C63   . 68 D044D36F    PUSH    D2Game.6FD344D0                            ;  ASCII "..\\D2Game/Quests/a1q1.cpp"
6FC97C68   . BA 05000000    MOV     EDX,5
6FC97C6D   . 8BCE           MOV     ECX,ESI
6FC97C6F   . E8 7CD0FFFF    CALL    D2Game.6FC94CF0                            ;          SetGameState(pQuest,5,__FILE,__LINE
// assert (pSequence)
6FC97C74   . 8B86 EC000000  MOV     EAX,[ESI+EC]
6FC97C7A   . 50             PUSH    EAX                                        ; /CodeAddress
6FC97C7B   . FF15 107CD26F  CALL    [<&KERNEL32.IsBadCodePtr>]                 ; \IsBadCodePtr
6FC97C81   . 85C0           TEST    EAX,EAX
6FC97C83   . 74 1E          JE      SHORT D2Game.6FC97CA3
6FC97C85   . 68 EA000000    PUSH    0EA
6FC97C8A   . 68 D044D36F    PUSH    D2Game.6FD344D0                            ;  ASCII "..\\D2Game/Quests/a1q1.cpp"
6FC97C8F   . 68 0C19D36F    PUSH    D2Game.6FD3190C                            ;  ASCII "pQuestInfo->pSequence"
6FC97C94   . E8 554A0800    CALL    <JMP.&Fog.#10023>
6FC97C99   . 83C4 0C        ADD     ESP,0C
6FC97C9C   . 6A FF          PUSH    -1
6FC97C9E   . E8 4A4B0800    CALL    D2Game.6FD1C7ED

6FC97CA3   > 8BCE           MOV     ECX,ESI
6FC97CA5   . FF96 EC000000  CALL    [ESI+EC]                                   ;          pQuest->pSequence(pQuest);

6FC97CAB   . BA 0D000000    MOV     EDX,0D
6FC97CB0   . 8BCE           MOV     ECX,ESI
6FC97CB2   . 53             PUSH    EBX
6FC97CB3   . 68 307BC96F    PUSH    D2Game.6FC97B30
6FC97CB8   . 53             PUSH    EBX
6FC97CB9   . 885E 14        MOV     [ESI+14],BL                                ;         pQuest->bUnk14 = 0;
6FC97CBC   . E8 DFCFFFFF    CALL    D2Game.6FC94CA0                            ;         IterateFn2(pQuest,0x0D,0,pfnIterate2,0);
6FC97CC1   > 899E A8000000  MOV     [ESI+A8],EBX                               ;    }
                                                                               ;    pQuest->pCallback[2]=0;
6FC97CC7   > 8B8E E0000000  MOV     ECX,[ESI+E0]                               ; }
                                                                               ;
6FC97CCD   . 53             PUSH    EBX
6FC97CCE   . 51             PUSH    ECX
6FC97CCF   . 55             PUSH    EBP
6FC97CD0   . E8 EF3D0800    CALL    <JMP.&D2Common.#11108>                     ; SetBit(pHist,pQuest->eFilter,0) // Set bit 0 to 1
6FC97CD5   . 8B96 E0000000  MOV     EDX,[ESI+E0]
6FC97CDB   . 6A 01          PUSH    1
6FC97CDD   . 52             PUSH    EDX
6FC97CDE   . 55             PUSH    EBP
6FC97CDF   . E8 E63D0800    CALL    <JMP.&D2Common.#11109>                     ; ClearBit(pHist,pQuest->eFilter,1) // set bit 1 to 0
6FC97CE4   . 8B86 E0000000  MOV     EAX,[ESI+E0]
6FC97CEA   . 50             PUSH    EAX
6FC97CEB   . 55             PUSH    EBP
6FC97CEC   . E8 BF450800    CALL    <JMP.&D2Common.#11110>                     ; UpdateHist(pHist,pQuest->eFilter); //??

6FC97CF1   . 8B4F 0C        MOV     ECX,[EDI+C]                             
6FC97CF4   . 53             PUSH    EBX
6FC97CF5   . 6A 01          PUSH    1
6FC97CF7   . 6A 05          PUSH    5
6FC97CF9   . 51             PUSH    ECX
6FC97CFA   . E8 BB3B0800    CALL    <JMP.&D2Common.#10518>                     ; AddStat(ptPlayer,5,1,0); // add skill points for reward
6FC97CFF   . 8B47 0C        MOV     EAX,[EDI+C]
6FC97D02   . 3BC3           CMP     EAX,EBX
6FC97D04   . 75 05          JNZ     SHORT D2Game.6FC97D0B
6FC97D06   . 83C8 FF        OR      EAX,FFFFFFFF
6FC97D09   . EB 03          JMP     SHORT D2Game.6FC97D0E
6FC97D0B   > 8B40 0C        MOV     EAX,[EAX+C]
6FC97D0E   > 8D4E 1C        LEA     ECX,[ESI+1C]
6FC97D11   . 8BD0           MOV     EDX,EAX
6FC97D13   . E8 F8EAFFFF    CALL    D2Game.6FC96810

6FC97D18   . 8B57 08        MOV     EDX,[EDI+8]
6FC97D1B   . 8B0F           MOV     ECX,[EDI]
6FC97D1D   . 52             PUSH    EDX                                        ; /Arg1
6FC97D1E   . 8B57 0C        MOV     EDX,[EDI+C]                                ; |
6FC97D21   . E8 EAF1FFFF    CALL    D2Game.6FC96F10                            ; \D2Game.6FC96F10
6FC97D26   > 5F             POP     EDI
6FC97D27   . 5E             POP     ESI
6FC97D28   . 5D             POP     EBP
6FC97D29   . 5B             POP     EBX
6FC97D2A   . 59             POP     ECX
6FC97D2B   . C3             RETN
*/


void FASTCALL Q1CC(Quest *pQuest,QParam *pqParam) // msg played
{
    DWORD   npcID =     pqParam->dwNpcID; 

    if(npcID != QUEST1_NPC)
        return;

    D2Game* ptGame =    pqParam->ptGame;
    Unit*   ptPlayer =  pqParam->ptPlayer;
    Unit*   ptNPCUnit = pqParam->ptNPCUnit;
    WORD    strKey =    pqParam->wStrKey;
    DWORD   diff =      ptGame->Difficulty;
    PlayerData* ptPlayerData=GetPlayerData(ptPlayer);

    DWORD*  pHist=(DWORD *)ptPlayerData->ptQuest[diff];
    BYTE*   pQData= (BYTE *)pQuest->pQuestData;

    if(strKey==0x40)
    {
        pQData[0x86]=1;
        SetGameState(pQuest,2,__FILE__,__LINE__);

        IterateFn1(ptGame,0,0,pfnIterate1);
        
        DisplayNPCMsg(ptGame,ptPlayer,ptNPCUnit);
        return;
    }

    if(strKey==0x4C)
    {
        if(GetBit(pHist,pQuest->eFilter,1)!=1)
            return;

        if(GetBit(pHist,pQuest->eFilter,0x0D))
        {
            if(pQuest->bState!=5)
            {
                SetGameState(pQuest,5,__FILE__,__LINE__);
                pQuest->pSequence(pQuest);
                pQuest->bUnk14 = 0;
                IterateFn2(pQuest,0x0D,0,pfnIterate2,0); // last arg 0, no fn exec - just set pQuest->bUnk0B = 0x0D 
            }
            pQuest->pCallback[2]=0;
        }
        SetBit(pHist,pQuest->eFilter,0); // Set bit 0 to 1
        ClearBit(pHist,pQuest->eFilter,1); // set bit 1 to 0
        UpdateHist(pHist,pQuest->eFilter); //??

        D2CommonAddStat(ptPlayer,5,1,0); // add skill points for reward

        DisplayNPCMsg(ptGame,ptPlayer,ptNPCUnit);
    }
}


//=========================================================================
// Quest 1 - pCallback[2] (+A8) -  Leave NPC
/*
6FC97AE0   . 8B42 08        MOV     EAX,[EDX+8]                      ;  Quest 1 Callback 2 (+A8)
6FC97AE3   . 56             PUSH    ESI
6FC97AE4   . 85C0           TEST    EAX,EAX                          ; if(!ptNPCUnit)
6FC97AE6   . 57             PUSH    EDI                              ;     return
6FC97AE7   . 8BF1           MOV     ESI,ECX
6FC97AE9   . 74 3A          JE      SHORT D2Game.6FC97B25
6FC97AEB   . 66:8178 04 940>CMP     WORD PTR [EAX+4],94              ; if(ptNPCUnit->UnitID!=QUEST1_NPC)
6FC97AF1   . 75 32          JNZ     SHORT D2Game.6FC97B25            ;   return
6FC97AF3   . 8B7E 18        MOV     EDI,[ESI+18]                     ; pQuest->QuestData
6FC97AF6   . BA 01000000    MOV     EDX,1
6FC97AFB   . 3897 86000000  CMP     [EDI+86],DL                      ; if(QuestData+86 == 1)
6FC97B01   . 75 22          JNZ     SHORT D2Game.6FC97B25            ; {
6FC97B03   . 52             PUSH    EDX                              ;     1
6FC97B04   . 68 307BC96F    PUSH    D2Game.6FC97B30                  ;     pIterateFunc
6FC97B09   . 6A 00          PUSH    0                                ;     0
6FC97B0B   . C646 14 00     MOV     BYTE PTR [ESI+14],0              ;     pQuest->bUnk14=0
6FC97B0F   . E8 8CD1FFFF    CALL    D2Game.6FC94CA0                  ;     UpdatePlayers(pQuest,State,0,pfnIterate,1)
6FC97B14   . C687 86000000 >MOV     BYTE PTR [EDI+86],0              ;     QuestData+86 = 0
6FC97B1B   . C786 A8000000 >MOV     DWORD PTR [ESI+A8],0             ;     pQuest->pCallback(2)=0 !
6FC97B25   > 5F             POP     EDI                              ; }
6FC97B26   . 5E             POP     ESI
6FC97B27   . C3             RETN
*/


void FASTCALL Q1A8(Quest *pQuest,QParam *pqParam)
{
    Unit*   ptNPCUnit = pqParam->ptNPCUnit;

    if(!ptNPCUnit)
        return;

    if(ptNPCUnit->nUnitId!=QUEST1_NPC)
        return;

    BYTE*   pQData= (BYTE *)pQuest->pQuestData;

    if(pQData[0x86]!=1)
        return;

    pQuest->bUnk14=0;
    IterateFn2(pQuest,1,0,pfnIterate2,1); // UpdateClientQuests - Turn on +Quest btn ?

    pQData[0x86]=0;
    pQuest->pCallback[2]=0;
}

//=========================================================================
// Quest 1 - pCallback[3] (+AC) -  LevelChange

/*
6FC981E0   . 55             PUSH    EBP                              ;  Quest1 CB 3 - (+AC)
6FC981E1   . 56             PUSH    ESI
6FC981E2   . 57             PUSH    EDI
6FC981E3   . 8BFA           MOV     EDI,EDX
6FC981E5   . 8BF1           MOV     ESI,ECX
6FC981E7   . 8B4F 18        MOV     ECX,[EDI+18]                     ; pqParam+18 ? - LevelID
6FC981EA   . 8B47 14        MOV     EAX,[EDI+14]                     ; pqParam+14 ?
6FC981ED   . 8B6E 18        MOV     EBP,[ESI+18]                     ; pQData=pQuest->QuestData
6FC981F0   . 83F9 08        CMP     ECX,8                            ; if(eLevelID==8)
6FC981F3   . 0F85 80000000  JNZ     D2Game.6FC98279                  ; {
6FC981F9   . 8A46 09        MOV     AL,[ESI+9]                       ;
6FC981FC   . 84C0           TEST    AL,AL                            ;     if(pQuest->bUnk09)
6FC981FE   . 0F84 1D010000  JE      D2Game.6FC98321                  ;         return
6FC98204   . 8A46 0C        MOV     AL,[ESI+C]                       ;     
6FC98207   . 32C9           XOR     CL,CL                            ;     tmp=0;
6FC98209   . 3C 01          CMP     AL,1
6FC9820B   . 74 04          JE      SHORT D2Game.6FC98211            ;     if(pQuest->bState==1 ||
6FC9820D   . 3C 02          CMP     AL,2                             ;        pQuest->bState==2)
6FC9820F   . 75 18          JNZ     SHORT D2Game.6FC98229            ;     {
6FC98211   > 68 35020000    PUSH    235
6FC98216   . 68 D044D36F    PUSH    D2Game.6FD344D0                  ;  ASCII "..\\D2Game/Quests/a1q1.cpp"
6FC9821B   . BA 03000000    MOV     EDX,3
6FC98220   . 8BCE           MOV     ECX,ESI
6FC98222   . E8 C9CAFFFF    CALL    D2Game.6FC94CF0                  ;         SetGameState(pQuest,3,__FILE__,__LINE__);
6FC98227   . B1 01          MOV     CL,1                             ;         tmp=1;
                                                                     ;     }
6FC98229   > C685 85000000 >MOV     BYTE PTR [EBP+85],1              ;     pQData[0x85]=1;
6FC98230   . 8A46 0B        MOV     AL,[ESI+B]                       ;
6FC98233   . 3C 02          CMP     AL,2                             ;     if(pQuest->bUnk0B<2)
6FC98235   . 73 25          JNB     SHORT D2Game.6FC9825C            ;     {
6FC98237   . 6A 01          PUSH    1
6FC98239   . 68 307BC96F    PUSH    D2Game.6FC97B30
6FC9823E   . 6A 00          PUSH    0
6FC98240   . BA 02000000    MOV     EDX,2
6FC98245   . 8BCE           MOV     ECX,ESI
6FC98247   . C646 14 00     MOV     BYTE PTR [ESI+14],0                           pQuest->bUnk14=0;
6FC9824B   . E8 50CAFFFF    CALL    D2Game.6FC94CA0                  ;            IterateFn2(pQuest,2,0,pfnIterate2,1);
6FC98250   . C786 A8000000 >MOV     DWORD PTR [ESI+A8],0             ;            pQuest->pCallback[2]=0;
6FC9825A   . EB 08          JMP     SHORT D2Game.6FC98264            ;      }else{

6FC9825C   > 84C9           TEST    CL,CL                            ;          if(!tmp)
6FC9825E   . 0F84 BD000000  JE      D2Game.6FC98321                  ;              return;
6FC98264   > 8B4E 04        MOV     ECX,[ESI+4]                      ;      }
6FC98267   . 68 307DC96F    PUSH    D2Game.6FC97D30
6FC9826C   . 6A 00          PUSH    0
6FC9826E   . 33D2           XOR     EDX,EDX
6FC98270   . E8 CB490200    CALL    D2Game.6FCBCC40                  ;      IterateFn1(ptGame,0,0,pfnIterate1);
6FC98275   . 5F             POP     EDI                              ;      return
6FC98276   . 5E             POP     ESI
6FC98277   . 5D             POP     EBP
6FC98278   . C3             RETN

6FC98279   > 83F8 01        CMP     EAX,1                            ; } // endif level 8
6FC9827C   . 0F85 9F000000  JNZ     D2Game.6FC98321                  ; if(pqParam->dwUnk14!=1)
6FC98282   . 8BD7           MOV     EDX,EDI                          ;     return
6FC98284   . 8BCE           MOV     ECX,ESI
6FC98286   . E8 65E6FFFF    CALL    D2Game.6FC968F0                  ; QuestRemovePlayer(pQuest,pqParam);
6FC9828B   . 807E 0C 02     CMP     BYTE PTR [ESI+C],2               ; if(pQuest->bState!=2)
6FC9828F   . 0F85 8C000000  JNZ     D2Game.6FC98321                  ;     return;
6FC98295   . 8B47 0C        MOV     EAX,[EDI+C]                      ;
6FC98298   . 8B3F           MOV     EDI,[EDI]
6FC9829A   . 50             PUSH    EAX
6FC9829B   . E8 56330800    CALL    <JMP.&D2Common.#10424>           ; GetPlayerData(ptPlayer)
6FC982A0   . 8B96 E0000000  MOV     EDX,[ESI+E0]
6FC982A6   . 33C9           XOR     ECX,ECX
6FC982A8   . 8A4F 6D        MOV     CL,[EDI+6D]
6FC982AB   . 6A 00          PUSH    0
6FC982AD   . 52             PUSH    EDX
6FC982AE   . 8B7C88 10      MOV     EDI,[EAX+ECX*4+10]               ; GetHist(ptGame,ptPlayerData);
6FC982B2   . 57             PUSH    EDI
6FC982B3   . E8 5C360800    CALL    <JMP.&D2Common.#11107>           ;
6FC982B8   . 83F8 01        CMP     EAX,1                            ; if(GetBit(0))
6FC982BB   . 74 64          JE      SHORT D2Game.6FC98321            ;     return
6FC982BD   . 8B86 E0000000  MOV     EAX,[ESI+E0]
6FC982C3   . 6A 01          PUSH    1
6FC982C5   . 50             PUSH    EAX
6FC982C6   . 57             PUSH    EDI
6FC982C7   . E8 48360800    CALL    <JMP.&D2Common.#11107>
6FC982CC   . 83F8 01        CMP     EAX,1                            ; if(GetBit(1))
6FC982CF   . 74 50          JE      SHORT D2Game.6FC98321            ;     return
6FC982D1   . 68 51020000    PUSH    251
6FC982D6   . 68 D044D36F    PUSH    D2Game.6FD344D0                  ;  ASCII "..\\D2Game/Quests/a1q1.cpp"
6FC982DB   . BA 03000000    MOV     EDX,3
6FC982E0   . 8BCE           MOV     ECX,ESI
6FC982E2   . E8 09CAFFFF    CALL    D2Game.6FC94CF0                  ; SetGameState(pQuest,3,__FILE__,__LINE__);
6FC982E7   . 8B4E 04        MOV     ECX,[ESI+4]
6FC982EA   . 33D2           XOR     EDX,EDX
6FC982EC   . 68 307DC96F    PUSH    D2Game.6FC97D30                  ; pfnIterate1
6FC982F1   . 6A 00          PUSH    0
6FC982F3   . E8 48490200    CALL    D2Game.6FCBCC40                  ; IterateFn1(ptGame,0,0,pfnIterate1);
6FC982F8   . 807E 0B 01     CMP     BYTE PTR [ESI+B],1               ; if(pQuest->bUnk0B==1)
6FC982FC   . 74 23          JE      SHORT D2Game.6FC98321            ;     return
6FC982FE   . 6A 01          PUSH    1
6FC98300   . 68 307BC96F    PUSH    D2Game.6FC97B30
6FC98305   . 6A 00          PUSH    0
6FC98307   . BA 01000000    MOV     EDX,1
6FC9830C   . 8BCE           MOV     ECX,ESI
6FC9830E   . C646 14 00     MOV     BYTE PTR [ESI+14],0              ; pQuest->bUnk14=0;
6FC98312   . E8 89C9FFFF    CALL    D2Game.6FC94CA0                  ; IterateFn2(pQuest,1,0,pfnIterate2,1);
6FC98317   . C786 A8000000 >MOV     DWORD PTR [ESI+A8],0             ; pQuest->pCallback[2]=0;
6FC98321   > 5F             POP     EDI                              ; return
6FC98322   . 5E             POP     ESI
6FC98323   . 5D             POP     EBP
6FC98324   . C3             RETN

*/


void FASTCALL Q1AC(Quest *pQuest,QParam *pqParam) //LevelChanged
{
    DWORD   eLevel =        pqParam->eLevelID; 


    D2Game* ptGame =    pqParam->ptGame;
    Unit*   ptPlayer =  pqParam->ptPlayer;
//  Unit*   ptNPCUnit = pqParam->ptNPCUnit;
//  WORD    strKey =    pqParam->wStrKey;
    DWORD   diff =      ptGame->Difficulty;
    PlayerData* ptPlayerData=GetPlayerData(ptPlayer);

    DWORD*  pHist=(DWORD *)ptPlayerData->ptQuest[diff];
    BYTE*   pQData= (BYTE *)pQuest->pQuestData;

    
    if(eLevel==QUEST1_LEVEL)
    {

        if(!pQuest->bUnk09)
            return;

        BOOL tmp = 0;

        if(pQuest->bState==1 ||
           pQuest->bState==2)
        {
            SetGameState(pQuest,3,__FILE__,__LINE__);
            tmp=1;
        }
        pQData[0x85]=1;

        if(pQuest->bUnk0B<2)
        {
            pQuest->bUnk14=0;
            IterateFn2(pQuest,2,0,pfnIterate2,1);
            pQuest->pCallback[2]=0;
        }else if(!tmp)
            return;

        IterateFn1(ptGame,0,0,pfnIterate1);
        return;
    }
    if(pqParam->dwUnk14!=1)
        return;

    QuestRemovePlayer(pQuest,pqParam);

    if(pQuest->bState!=2)
        return;

    if(GetBit(pHist,pQuest->eFilter,0))
        return;
    if(GetBit(pHist,pQuest->eFilter,1))
        return;
    SetGameState(pQuest,3,__FILE__,__LINE__);

    IterateFn1(ptGame,0,0,pfnIterate1);

    if(pQuest->bUnk0B==1)
        return;

    pQuest->bUnk14=0;
    IterateFn2(pQuest,1,0,pfnIterate2,1);
    pQuest->pCallback[2]=0;
}

EDIT: code to long for box ???
continued next post.
Last edited by SVR on Sun Dec 19, 2004 6:06 pm, edited 1 time in total.

User avatar
SVR
Retired staff
Arch-Angel
Posts: 1449
Joined: Sat Nov 02, 2002 11:04 pm
Location: Texas
United States of America

Hand-picked

Re: Quest structures and functions

Post by SVR » Sun Dec 19, 2004 6:07 pm

A1Q1 code continued ...

Code: Select all



//=========================================================================
// Quest 1 - pCallback[8] (+C0) -  Monster killed
/*
6FC97ED0   . 51             PUSH    ECX                              ;  Quest 1 CB 8 (+C0) Monster killed
6FC97ED1   . 53             PUSH    EBX
6FC97ED2   . 55             PUSH    EBP
6FC97ED3   . 56             PUSH    ESI
6FC97ED4   . 57             PUSH    EDI
6FC97ED5   . 8BF9           MOV     EDI,ECX
6FC97ED7   . 8BF2           MOV     ESI,EDX
6FC97ED9   . 8A47 09        MOV     AL,[EDI+9]                       ;  if(pQuest->09h==0)
6FC97EDC   . 84C0           TEST    AL,AL                            ;      return
6FC97EDE   . 0F84 D3010000  JE      D2Game.6FC980B7
6FC97EE4   . 8B0E           MOV     ECX,[ESI]                        ;  ptGame
6FC97EE6   . 8B6F 18        MOV     EBP,[EDI+18]                     ;  pQuest->QuestBytes
6FC97EE9   . 81C1 F0000000  ADD     ECX,0F0
6FC97EEF   . BA 08000000    MOV     EDX,8                            ;  ptGame+F0 = RegionStats[0]
6FC97EF4   . E8 9700FDFF    CALL    D2Game.6FC67F90                  ;  GetptRegionStats(pRegionStats,8 (levelID))
6FC97EF9   . 85C0           TEST    EAX,EAX
6FC97EFB   . 894424 10      MOV     [ESP+10],EAX                     ;  ptRegionStats
6FC97EFF   . 75 1E          JNZ     SHORT D2Game.6FC97F1F
6FC97F01   . 68 C0010000    PUSH    1C0
6FC97F06   . 68 D044D36F    PUSH    D2Game.6FD344D0                  ;  ASCII "..\\D2Game/Quests/a1q1.cpp"
6FC97F0B   . 68 FC44D36F    PUSH    D2Game.6FD344FC                  ;  ASCII "ptRegionStats != NULL"
6FC97F10   . E8 D9470800    CALL    <JMP.&Fog.#10023>
6FC97F15   . 83C4 0C        ADD     ESP,0C
6FC97F18   . 6A FF          PUSH    -1
6FC97F1A   . E8 CE480800    CALL    D2Game.6FD1C7ED
6FC97F1F   > 8B88 CC020000  MOV     ECX,[EAX+2CC]                    ;  ptRegionStats->MonCount-ptRegionStats->MonKills
6FC97F25   . 8B90 D0020000  MOV     EDX,[EAX+2D0]
6FC97F2B   . 2BCA           SUB     ECX,EDX
6FC97F2D   . 898D 88000000  MOV     [EBP+88],ECX                     ;  monsters left
6FC97F33   . 8B46 0C        MOV     EAX,[ESI+C]                      ;  ptPlayer
6FC97F36   . 85C0           TEST    EAX,EAX
6FC97F38   . 74 19          JE      SHORT D2Game.6FC97F53
6FC97F3A   . 8B58 0C        MOV     EBX,[EAX+C]                      ;  ptPlayer->UID
6FC97F3D   . 8BCD           MOV     ECX,EBP                          ;  QuestBytes
6FC97F3F   . 8BD3           MOV     EDX,EBX
6FC97F41   . E8 3AE9FFFF    CALL    D2Game.6FC96880                  ;  find Player ID in QuestBytes->00h array
6FC97F46   . 85C0           TEST    EAX,EAX
6FC97F48   . 75 09          JNZ     SHORT D2Game.6FC97F53
6FC97F4A   . 8BD3           MOV     EDX,EBX                          ; playerUID
6FC97F4C   . 8BCD           MOV     ECX,EBP                          ; QData
6FC97F4E   . E8 BDE8FFFF    CALL    D2Game.6FC96810                  ; AddPlayerToQData(pQData,playeUID);
6FC97F53   > 6A 08          PUSH    8
6FC97F55   . 6A 08          PUSH    8
6FC97F57   . E8 08380800    CALL    <JMP.&D2Common.#10001>           ;  get act from level
6FC97F5C   . 8B16           MOV     EDX,[ESI]                        ;  ptGame
6FC97F5E   . 25 FF000000    AND     EAX,0FF
6FC97F63   . 8B8482 BC00000>MOV     EAX,[EDX+EAX*4+BC]               ;  ptGame->Acts[nAct]
6FC97F6A   . 50             PUSH    EAX
6FC97F6B   . E8 9A400800    CALL    <JMP.&D2Common.#10090>           ;  nRooms=#10090(ptAct,LevelID) getnRoomsVisited
6FC97F70   . 8B5424 10      MOV     EDX,[ESP+10]                     ;  ptRegionStats
6FC97F74   . 8B4A 04        MOV     ECX,[EDX+4]                      ;  ptRegionStats->NumRooms
6FC97F77   . 3BC1           CMP     EAX,ECX                          ; if(nRooms<=ptRegionStats->NumRooms)
6FC97F79   . 0F8F D7000000  JG      D2Game.6FC98056                  ; {
6FC97F7F   . 8B9A D0020000  MOV     EBX,[EDX+2D0]
6FC97F85   . 3B9A CC020000  CMP     EBX,[EDX+2CC]
6FC97F8B   . 0F85 C5000000  JNZ     D2Game.6FC98056                  ; if(!MonLeft)
6FC97F91   . 8B4F 04        MOV     ECX,[EDI+4]                      ;  pQuest->ptGame
6FC97F94   . 68 D3010000    PUSH    1D3
6FC97F99   . 68 D044D36F    PUSH    D2Game.6FD344D0                  ;  ASCII "..\\D2Game/Quests/a1q1.cpp"
6FC97F9E   . BA EC44D36F    MOV     EDX,D2Game.6FD344EC              ;  ASCII "Finished quest"
6FC97FA3   . E8 88CAFFFF    CALL    D2Game.6FC94A30                  ;  notify(ptGame,str,__FILE,__LINE);
6FC97FA8   . 33DB           XOR     EBX,EBX
6FC97FAA   . C685 84000000 >MOV     BYTE PTR [EBP+84],1              ;  QuestBytes->84h =1
6FC97FB1   . 68 D6010000    PUSH    1D6
6FC97FB6   . 68 D044D36F    PUSH    D2Game.6FD344D0                  ;  ASCII "..\\D2Game/Quests/a1q1.cpp"
6FC97FBB   . BA 04000000    MOV     EDX,4
6FC97FC0   . 8BCF           MOV     ECX,EDI
6FC97FC2   . 899F A8000000  MOV     [EDI+A8],EBX
6FC97FC8   . E8 23CDFFFF    CALL    D2Game.6FC94CF0                  ;  Change state to 4

6FC97FCD   . 8B97 E0000000  MOV     EDX,[EDI+E0]
6FC97FD3   . 899F C0000000  MOV     [EDI+C0],EBX
6FC97FD9   . 8B0E           MOV     ECX,[ESI]
6FC97FDB   . 6A 0D          PUSH    0D                               ; /Arg1 = 0000000D
6FC97FDD   . E8 DED6FFFF    CALL    D2Game.6FC956C0                  ; \6FC956C0(pqParam->ptGame,pQuest->eFilter,0x0D);
6FC97FE2   . 8B8F E0000000  MOV     ECX,[EDI+E0]
6FC97FE8   . 8BD5           MOV     EDX,EBP                          ; pQData
6FC97FEA   . 53             PUSH    EBX                              ; /Arg2
6FC97FEB   . 51             PUSH    ECX                              ; |Arg1
6FC97FEC   . 8B0E           MOV     ECX,[ESI]                        ; |
6FC97FEE   . E8 6DEDFFFF    CALL    D2Game.6FC96D60                  ; \D2Game.6FC96D60(pqParam->ptGame,pQData,pQuest->eFilter,0x00);
6FC97FF3   . 8B56 08        MOV     EDX,[ESI+8]
6FC97FF6   . 8B0E           MOV     ECX,[ESI]
6FC97FF8   . 68 6081C96F    PUSH    D2Game.6FC98160
6FC97FFD   . 52             PUSH    EDX
6FC97FFE   . 33D2           XOR     EDX,EDX


6FC98000   . E8 3B4C0200    CALL    D2Game.6FCBCC40                  ; IterateFn1(ptGame,0,pNPC??,68160)
6FC98005   . 8B46 08        MOV     EAX,[ESI+8]
6FC98008   . 8B0E           MOV     ECX,[ESI]
6FC9800A   . 68 C080C96F    PUSH    D2Game.6FC980C0
6FC9800F   . 50             PUSH    EAX
6FC98010   . 33D2           XOR     EDX,EDX
6FC98012   . E8 294C0200    CALL    D2Game.6FCBCC40                  ; IterateFn1(ptGame,0,pNPC??,680C0)
6FC98017   . 8B4E 08        MOV     ECX,[ESI+8]
6FC9801A   . 33D2           XOR     EDX,EDX
6FC9801C   . 68 2081C96F    PUSH    D2Game.6FC98120
6FC98021   . 51             PUSH    ECX
6FC98022   . 8B0E           MOV     ECX,[ESI]
6FC98024   . E8 174C0200    CALL    D2Game.6FCBCC40                  ; IterateFn1(ptGame,0,pNPC??,68120)
6FC98029   . 8B4F 04        MOV     ECX,[EDI+4]
6FC9802C   . 32D2           XOR     DL,DL
6FC9802E   . E8 4DEEFFFF    CALL    D2Game.6FC96E80                  ; 66E80(pQuest->ptGame,0)

6FC98033   . 389D 87000000  CMP     [EBP+87],BL                      ; if(pQData->bUnk87!=0)
6FC98039   . 75 7C          JNZ     SHORT D2Game.6FC980B7            ;     return
6FC9803B   . 6A 08          PUSH    8                                ;
6FC9803D   . BA B081C96F    MOV     EDX,D2Game.6FC981B0
6FC98042   . 8BCF           MOV     ECX,EDI
6FC98044   . C685 87000000 >MOV     BYTE PTR [EBP+87],1              ; pQData->bUnk87=1
6FC9804B   . E8 40C6FFFF    CALL    D2Game.6FC94690                  ; 64690(pQuest,681B0,8)
6FC98050   . 5F             POP     EDI                              ; return;
6FC98051   . 5E             POP     ESI
6FC98052   . 5D             POP     EBP
6FC98053   . 5B             POP     EBX
6FC98054   . 59             POP     ECX
6FC98055   . C3             RETN
                                                                     ; if(nRooms<=ptRegionStats->NumRooms)
6FC98056   > 3BC1           CMP     EAX,ECX                          ; 
6FC98058   . BA 05000000    MOV     EDX,5                            ; {
6FC9805D   . 7F 31          JG      SHORT D2Game.6FC98090
6FC9805F   . 3995 88000000  CMP     [EBP+88],EDX                     ;   5 monsters left ?
6FC98065   . 7F 29          JG      SHORT D2Game.6FC98090
6FC98067   . 6A 01          PUSH    1
6FC98069   . 68 307BC96F    PUSH    D2Game.6FC97B30
6FC9806E   . 6A 00          PUSH    0
6FC98070   . BA 04000000    MOV     EDX,4
6FC98075   . 8BCF           MOV     ECX,EDI
6FC98077   . C647 14 20     MOV     BYTE PTR [EDI+14],20             ;      pQuest->bUnk14=20h
6FC9807B   . E8 20CCFFFF    CALL    D2Game.6FC94CA0                  ;      UpdatePlayers(pQuest,4,0,pfnIterate,1)
6FC98080   . C787 A8000000 >MOV     DWORD PTR [EDI+A8],0             ;      pQuest->pCallback[2]=0;             
6FC9808A   . 5F             POP     EDI
6FC9808B   . 5E             POP     ESI
6FC9808C   . 5D             POP     EBP
6FC9808D   . 5B             POP     EBX
6FC9808E   . 59             POP     ECX
6FC9808F   . C3             RETN                                     ;  }
6FC98090   > 807F 0B 04     CMP     BYTE PTR [EDI+B],4               ;  if(pQuest->0B==4)
6FC98094   . 75 21          JNZ     SHORT D2Game.6FC980B7            ;  {
6FC98096   . 3995 88000000  CMP     [EBP+88],EDX
6FC9809C   . 7E 19          JLE     SHORT D2Game.6FC980B7
6FC9809E   . 6A 01          PUSH    1
6FC980A0   . 68 307BC96F    PUSH    D2Game.6FC97B30
6FC980A5   . 6A 00          PUSH    0
6FC980A7   . BA 04000000    MOV     EDX,4
6FC980AC   . 8BCF           MOV     ECX,EDI
6FC980AE   . C647 14 20     MOV     BYTE PTR [EDI+14],20             ;      pQuest->bUnk14=20h
6FC980B2   . E8 E9CBFFFF    CALL    D2Game.6FC94CA0                  ;      UpdatePlayers(pQuest,4,0,pfnIterate,1)
6FC980B7   > 5F             POP     EDI                              ;  }
6FC980B8   . 5E             POP     ESI
6FC980B9   . 5D             POP     EBP
6FC980BA   . 5B             POP     EBX
6FC980BB   . 59             POP     ECX
6FC980BC   . C3             RETN
*/


void FASTCALL Q1C0(Quest *pQuest,QParam *pqParam) // Monster killed
{
//  DWORD   eLevel =        pqParam->eLevelID; 


    D2Game* ptGame =    pqParam->ptGame;
    Unit*   ptPlayer =  pqParam->ptPlayer;
    Unit*   pNPCUnit =  pqParam->ptNPCUnit;
//  WORD    strKey =    pqParam->wStrKey;
    DWORD   diff =      ptGame->Difficulty;
    PlayerData* ptPlayerData=GetPlayerData(ptPlayer);

    DWORD*  pHist=(DWORD *)ptPlayerData->ptQuest[diff];
    Quest1Data* pQData= (Quest1Data *)pQuest->pQuestData;

    if(pQuest->bUnk09==0)
        return;

    RegionStat* ptRegionStats=ptGame->ptRegionStats[QUEST1_LEVEL];
    pQData->MonLeft=(ptRegionStats->MonCount-ptRegionStats->MonKills);

    if(ptPlayer)
    {
        DWORD playerUID=ptPlayer->nUnitUnid;
        if(!QDataFindPlayer(pQData,playerUID))
            QDataAddPlayer(pQData,playerUID);
    }

    Act *ptAct=ptGame->ptAct[GetActFromLevel(QUEST1_LEVEL)];
    DWORD nRooms=GetRoomsSeen(ptAct,QUEST1_LEVEL);
    if(nRooms<=ptRegionStats->nRooms)
    {
        if(!pQData->MonLeft)
        {
            Notify(ptGame,"Finished Quest",__FILE__,__LINE__);
            pQuest->pCallback[2]=0;
            SetGameState(pQuest,4,__FILE__,__LINE__);
            pQuest->pCallback[8]=0;

            D2Game_X656C0(ptGame,pQuest->eFilter,0x0D);
            D2Game_X66D60(ptGame,pQData,pQuest->eFilter,0x00);

            IterateFn1(ptGame,0,(DWORD)pNPCUnit,pfn_X68160);
            IterateFn1(ptGame,0,(DWORD)pNPCUnit,pfn_X680C0);
            IterateFn1(ptGame,0,(DWORD)pNPCUnit,pfn_X68120);
            D2Game_X66E80(ptGame,0);

            if(pQData->bUnk87!=0)
                return;
            pQData->bUnk87=1;
            IterateFn3(pQuest,pfn_X681B0,QUEST1_LEVEL);
            return;
        }
        if(pQData->MonLeft<=5)
        {
            pQuest->bUnk14=0x20;
            IterateFn2(pQuest,4,0,pfnIterate2,1); // sets Unk0B to 4 !
            pQuest->pCallback[2]=0;
            return;
        }
    }
    if(pQuest->bUnk0B==4)
    {
        pQuest->bUnk14=0x20;
        IterateFn2(pQuest,4,0,pfnIterate2,1);
    }
}


//=========================================================================
// Quest 1 - pCallback[10] (+C8) -  Exit Game
/*
6FCB2610   . 56             PUSH    ESI                              ;  Quest 1 CB 10 (+C8) - ExitGame
6FCB2611   . 57             PUSH    EDI
6FCB2612   . 8BFA           MOV     EDI,EDX
6FCB2614   . 8BF1           MOV     ESI,ECX
6FCB2616   . 8B47 0C        MOV     EAX,[EDI+C]                      ; ptPlayer
6FCB2619   . 85C0           TEST    EAX,EAX
6FCB261B   . 75 05          JNZ     SHORT D2Game.6FCB2622
6FCB261D   . 83C8 FF        OR      EAX,FFFFFFFF
6FCB2620   . EB 03          JMP     SHORT D2Game.6FCB2625
6FCB2622   > 8B40 0C        MOV     EAX,[EAX+C]
6FCB2625   > 8D4E 1C        LEA     ECX,[ESI+1C]                     ; pQuest->PlayerID
6FCB2628   . 8BD0           MOV     EDX,EAX
6FCB262A   . E8 1142FEFF    CALL    D2Game.6FC96840                  ; 
6FCB262F   . 8B47 0C        MOV     EAX,[EDI+C]                      ; ptPlayer
6FCB2632   . 8B4E 18        MOV     ECX,[ESI+18]                     ; pQData
6FCB2635   . 5F             POP     EDI
6FCB2636   . 5E             POP     ESI

6FCB2637   . 85C0           TEST    EAX,EAX
6FCB2639   . 75 0A          JNZ     SHORT D2Game.6FCB2645
6FCB263B   . 83C8 FF        OR      EAX,FFFFFFFF
6FCB263E   . 8BD0           MOV     EDX,EAX
6FCB2640   .^E9 FB41FEFF    JMP     D2Game.6FC96840
6FCB2645   > 8B40 0C        MOV     EAX,[EAX+C]
6FCB2648   . 8BD0           MOV     EDX,EAX
6FCB264A   .^E9 F141FEFF    JMP     D2Game.6FC96840

*/


void FASTCALL Q1C8(Quest *pQuest,QParam *pqParam)
{
    Unit*   ptPlayer =  pqParam->ptPlayer;

    DWORD playerUID;
    if(ptPlayer)
        playerUID=ptPlayer->nUnitUnid;
    else
        playerUID=-1;

    AllRemovePlayer(pQuest->dwUnk1C,playerUID);
    AllRemovePlayer(pQuest->pQuestData,playerUID);
}



//=================================================================================
// Quest1 Init function
/*
6FC97A10   . 56             PUSH    ESI                                           ;  InitQuest_a1q1
6FC97A11   . 8BF1           MOV     ESI,ECX
6FC97A13   . 57             PUSH    EDI
6FC97A14   . B9 0F000000    MOV     ECX,0F
6FC97A19   . 8D96 A0000000  LEA     EDX,[ESI+A0]
6FC97A1F   . 33C0           XOR     EAX,EAX
6FC97A21   . 8BFA           MOV     EDI,EDX
6FC97A23   . 50             PUSH    EAX
6FC97A24   . F3:AB          REP     STOS DWORD PTR ES:[EDI]
6FC97A26   . 8B46 04        MOV     EAX,[ESI+4]
6FC97A29   . C786 C8000000 >MOV     DWORD PTR [ESI+C8],D2Game.6FCB2610
6FC97A33   . C786 AC000000 >MOV     DWORD PTR [ESI+AC],D2Game.6FC981E0
6FC97A3D   . C786 C0000000 >MOV     DWORD PTR [ESI+C0],D2Game.6FC97ED0
6FC97A47   . C702 C07DC96F  MOV     DWORD PTR [EDX],D2Game.6FC97DC0
6FC97A4D   . C786 A8000000 >MOV     DWORD PTR [ESI+A8],D2Game.6FC97AE0
6FC97A57   . C786 CC000000 >MOV     DWORD PTR [ESI+CC],D2Game.6FC97BA0
6FC97A61   . C786 D4000000 >MOV     DWORD PTR [ESI+D4],D2Game.6FC983A0
6FC97A6B   . C786 DC000000 >MOV     DWORD PTR [ESI+DC],D2Game.6FD31F38
6FC97A75   . C646 0A 01     MOV     BYTE PTR [ESI+A],1
6FC97A79   . C646 0C 01     MOV     BYTE PTR [ESI+C],1
6FC97A7D   . 8B48 1C        MOV     ECX,[EAX+1C]
6FC97A80   . 68 CA020000    PUSH    2CA
6FC97A85   . 68 D044D36F    PUSH    D2Game.6FD344D0                               ;  ASCII "..\\D2Game/Quests/a1q1.cpp"
f6FC97A8A   . BA 8C000000    MOV     EDX,8C
6FC97A8F   . E8 4E4C0800    CALL    <JMP.&Fog.#10045>
6FC97A94   . 8BD0           MOV     EDX,EAX
6FC97A96   . B9 23000000    MOV     ECX,23
6FC97A9B   . 33C0           XOR     EAX,EAX
6FC97A9D   . 8BFA           MOV     EDI,EDX
6FC97A9F   . 8956 18        MOV     [ESI+18],EDX                                  ;  pQData
6FC97AA2   . C786 E0000000 >MOV     DWORD PTR [ESI+E0],1
6FC97AAC   . C786 E4000000 >MOV     DWORD PTR [ESI+E4],0
6FC97AB6   . C646 0D 04     MOV     BYTE PTR [ESI+D],4
6FC97ABA   . C786 E8000000 >MOV     DWORD PTR [ESI+E8],D2Game.6FC979A0
6FC97AC4   . C786 EC000000 >MOV     DWORD PTR [ESI+EC],D2Game.6FC98330
6FC97ACE   . C746 10 020000>MOV     DWORD PTR [ESI+10],2
6FC97AD5   . F3:AB          REP     STOS DWORD PTR ES:[EDI]
6FC97AD7   . 5F             POP     EDI
6FC97AD8   . 8BCA           MOV     ECX,EDX
6FC97ADA   . 5E             POP     ESI
6FC97ADB   .^E9 00EEFFFF    JMP     D2Game.6FC968E0                               ;  pQData->nPlayers=0

*/

void A1Q1Init(Quest *pQuest)
{
    memset(pQuest->pCallback,0,sizeof(pQuest->pCallback));

    pQuest->pCallback[0]=Q1A0;
    pQuest->pCallback[2]=Q1A8;
    pQuest->pCallback[3]=Q1AC;
    pQuest->pCallback[8]=Q1C0;
    pQuest->pCallback[10]=Q1C8;
    pQuest->pCallback[11]=Q1CC;
    pQuest->pCallback[13]=Q1D4;

    Quest1Data* pQData=(Quest1Data *)D2Malloc(pQuest->ptGame->MemoryPool,sizeof(Quest1Data),
        __FILE__,__LINE__,0);
    memset(pQData,0,sizeof(Quest1Data));

    pQuest->pQuestData=(QuestBytes *)pQData;

    pQuest->eFilter = 1;

    pQuest->pStatusFilter = 0;
    pQuest->pSequence=SequenceFilter;
    pQuest->pActiveFilter=ActiveFilter;

    pQuest->bUnk0A=1;
    pQuest->bState=1;
    pQuest->bUnk0D=4;
    pQuest->nID=2;

    pQuest->pdwUnkDC = (NPCInfo *)sNPCInfoA1Q1;

}



//=================================================================================
// Quest Callback functions

/*
void FASTCALL pfnA0(Quest *pQuest,QParam *pqParam)
{
    EventFunc(0,pQuest,pqParam);
}
void FASTCALL pfnA1(Quest *pQuest,QParam *pqParam)
{
     EventFunc(0x1,pQuest,pqParam);
}
void FASTCALL pfnA2(Quest *pQuest,QParam *pqParam)
{
     EventFunc(0x2,pQuest,pqParam);
}
void FASTCALL pfnA3(Quest *pQuest,QParam *pqParam)
{
     EventFunc(0x3,pQuest,pqParam);
}
void FASTCALL pfnA4(Quest *pQuest,QParam *pqParam)
{
     EventFunc(0x4,pQuest,pqParam);
}
void FASTCALL pfnA5(Quest *pQuest,QParam *pqParam)
{
     EventFunc(0x5,pQuest,pqParam);
}
void FASTCALL pfnA6(Quest *pQuest,QParam *pqParam)
{
     EventFunc(0x6,pQuest,pqParam);
}
void FASTCALL pfnA7(Quest *pQuest,QParam *pqParam)
{
     EventFunc(0x7,pQuest,pqParam);
}
void FASTCALL pfnA8(Quest *pQuest,QParam *pqParam)
{
     EventFunc(0x8,pQuest,pqParam);
}
void FASTCALL pfnA9(Quest *pQuest,QParam *pqParam)
{
     EventFunc(0x9,pQuest,pqParam);
}
void FASTCALL pfnAA(Quest *pQuest,QParam *pqParam)
{
     EventFunc(0xA,pQuest,pqParam);
}
void FASTCALL pfnAB(Quest *pQuest,QParam *pqParam)
{
     EventFunc(0xB,pQuest,pqParam);
}
void FASTCALL pfnAC(Quest *pQuest,QParam *pqParam)
{
     EventFunc(0xC,pQuest,pqParam);
}
void FASTCALL pfnAD(Quest *pQuest,QParam *pqParam)
{
     EventFunc(0xD,pQuest,pqParam);
}
void FASTCALL pfnAE(Quest *pQuest,QParam *pqParam)
{
     EventFunc(0xE,pQuest,pqParam);
}


QCB pfns[15] = {
    pfnA0,
    pfnA1,
    pfnA2,
    pfnA3,
    pfnA4,
    pfnA5,
    pfnA6,
    pfnA7,
    pfnA8,
    pfnA9,
    pfnAA,
    pfnAB,
    pfnAC,
    pfnAD,
    pfnAE
};

*/

void FASTCALL DoLog(Quest *pQuest)
{
    int i;

    Quests[pQuest->QuestID]=*pQuest;
    for(i=0;i<15;i++)
    {
        if(pQuest->pCallback[i])
        {
            pQuest->pCallback[i]=EventFunc;
        }
    }
}


NAKED LogQuest()
{
    __asm {
        CALL [EDI]
        MOV ECX,ESI
        CALL [DoLog]
        MOV     EAX,[ESP+0x14]
        retn
    }
}
//=======================================================================
// code edits - add code edits here
// {dwOffset, dwValue, FixupType =FT_NON,FT_REL or FT_FIX}


DECLARE_COMMON_MODS(0)
// D2Common edits here

END_MODS

DECLARE_CLIENT_MODS(0)
// D2Client edits here

END_MODS

DECLARE_GAME_MODS(2)
// D2Game edits here

/*
00063E5B   8BCE             MOV     ECX,ESI
00063E5D   FF17             CALL    [EDI]
00063E5F   8B4424 10        MOV     EAX,[ESP+10]

*/
{0x0063E5B,0xE890CE8B,FT_NON},
{0x0063E5F,(DWORD)LogQuest,FT_REL},

END_MODS

DECLARE_WIN_MODS(0)
// D2Win edits here

END_MODS

DECLARE_GFX_MODS(0)
// D2Gfx edits here

END_MODS

DECLARE_NET_MODS(0)
// D2Net edits here

END_MODS

DECLARE_LANG_MODS(0)
// D2Lang edits here

END_MODS

DECLARE_CMP_MODS(0)
// D2Cmp edits here

END_MODS

DECLARE_LAUNCH_MODS(0)
// D2Launch edits here

END_MODS
// stuct to pass to loader with pointers
DECLARE_DATA(ModData)

//=======================================================================
// dll init functions - setup your mod here

LPSTR pLogfilePath="Quest.log";

// exported Init function called when loading by D2Mod.dll

QUEST_API LPMODDATA STDCALL Init(LPSTR IniName)
{
    // Initialize any memory/resources here
    // use the ini file name passed to get settings
    char lLogFile[1024] = "";

    lstrcpy(lLogFile,IniName);
    char *p=strrchr(lLogFile,'\\');

    lstrcpy(p+1,pLogfilePath);
    set_logfile( lLogFile );


    // Setup Quest Init table for A1Q1.
    sgptQuestInit[1]=A1Q1;

    return &ModData;
}

// exported Release function called by when unloading
QUEST_API BOOL STDCALL Release()
{
    // release any memory/resources here
    return TRUE;
}

//=======================================================================

BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
                     )
{
    // According to MS: Don't call anything here that
    // may link in another call to LoadLibrary.
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        case DLL_PROCESS_DETACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
            break;
    }
    return TRUE;
}



User avatar
SVR
Retired staff
Arch-Angel
Posts: 1449
Joined: Sat Nov 02, 2002 11:04 pm
Location: Texas
United States of America

Hand-picked

Re: Quest structures and functions

Post by SVR » Sat Dec 25, 2004 5:17 pm

Some Client side quest related structs ...

Code: Select all


QuestMenu {
    DWORD NpcID,        // +00 Npc menu belongs to
    DWORD QuestID,      // +04 eFilter
    DWORD BitNumber,    // +08 bit in pHist to check
    DWORD BitValue,     // +0C exec func if bit is this
    Func* pfn           // +10 function to call to add/remove menu option(s)
};  //END               // +14

MenuDef {
    DWORD NpcID,        // +00 Npc menu belongs to
    DWORD count,        // +04 number of menu options
    WORD  strNDX[5],    // +08 array of str indices
    Func  pfn[5],       // +12 array of functions to call
    BYTE  enabled,      // +26 ?
};  // END              // +27



sgQuestMenuTable = 6FB7A518 (0DA518)

sgMenuDefTable = 6FB7BDE5 (0DBDE5)


Anya menu def...
6FB7C3AF  00 02 00 00 04 00 00 00 35 0D 44 0D 46 0D 00 00
6FB7C3BF  00 00 40 3C AF 6F 80 5B AF 6F 20 5D AF 6F 00 00 
6FB7C3CF  00 00 00 00 00 00 01  


Anya - add Personalize QuestMenu
6FB7A57C  00 02 00 00 26 00 00 00 01 00 00 00 01 00 00 00  
6FB7A58C  30 65 AF 6F 

6FAF6530   . C705 B3C3B76F >MOV     DWORD PTR [6FB7C3B3],5                          ;  Add personalize option to anya menu
6FAF653A   . C705 CDC3B76F >MOV     DWORD PTR [6FB7C3CD],D2Client.6FAF64A0
6FAF6544   . 66:C705 BDC3B7>MOV     WORD PTR [6FB7C3BD],58DD
6FAF654D   . B8 01000000    MOV     EAX,1
6FAF6552   . C3             RETN

The QuestMenu table is the method used to add additional menu options based on quest status.

The code scans through the list each time a menu is activated.
It calls 11107 to check the eFilter QuestRecord and calls the pfn if the test passes.

In the case of Anya's personalize option, it checks QuestRecord 26h bit 1 for 1.
If true, calls 6FAF6530 which adds the menu option into the MenuDef for Anya.

It will be very easy to load the tables from txt's if we overwrite the existing table and keep the same size.
The tables are referenced directly (not through a ptr) so completely replacing the tables will be difficult (but not impossible).

Myhrginoc has done some more exploration into the NPC menu structs & related code elsewhere.
Here is the code section that handles the QuestMenu's specifically...

Code: Select all

6FAF9A1A  |. BF 20A5B76F    MOV     EDI,D2Client.6FB7A520
6FAF9A1F  |. 8BD8           MOV     EBX,EAX
6FAF9A21  |> 8B4C24 10      /MOV     ECX,[ESP+10]
6FAF9A25  |. 8B47 F8        |MOV     EAX,[EDI-8]
6FAF9A28  |. 3BC1           |CMP     EAX,ECX                                        ;  if(npcId == pMenu->NPCId)
6FAF9A2A  |. 75 2E          |JNZ     SHORT D2Client.6FAF9A5A                        ;  {
6FAF9A2C  |. 8B47 04        |MOV     EAX,[EDI+4]
6FAF9A2F  |. 85C0           |TEST    EAX,EAX
6FAF9A31  |. 74 13          |JE      SHORT D2Client.6FAF9A46
6FAF9A33  |. 8B17           |MOV     EDX,[EDI]
6FAF9A35  |. 8B47 FC        |MOV     EAX,[EDI-4]
6FAF9A38  |. 52             |PUSH    EDX
6FAF9A39  |. 50             |PUSH    EAX
6FAF9A3A  |. 55             |PUSH    EBP
6FAF9A3B  |. E8 AC150700    |CALL    <JMP.&D2Common.#11107>
6FAF9A40  |. 85C0           |TEST    EAX,EAX
6FAF9A42  |. 74 16          |JE      SHORT D2Client.6FAF9A5A
6FAF9A44  |. EB 11          |JMP     SHORT D2Client.6FAF9A57
6FAF9A46  |> 8B0F           |MOV     ECX,[EDI]
6FAF9A48  |. 8B57 FC        |MOV     EDX,[EDI-4]
6FAF9A4B  |. 51             |PUSH    ECX
6FAF9A4C  |. 52             |PUSH    EDX
6FAF9A4D  |. 55             |PUSH    EBP
6FAF9A4E  |. E8 99150700    |CALL    <JMP.&D2Common.#11107>
6FAF9A53  |. 85C0           |TEST    EAX,EAX
6FAF9A55  |. 75 03          |JNZ     SHORT D2Client.6FAF9A5A
6FAF9A57  |> FF57 08        |CALL    [EDI+8]
6FAF9A5A  |> 83C7 14        |ADD     EDI,14                                         ;  }
6FAF9A5D  |. 4B             |DEC     EBX
6FAF9A5E  |.^75 C1          \JNZ     SHORT D2Client.6FAF9A21

The only modification needed here to use a custom tbl is the EDI load at the start.
The various pfn's are hardcoded to specific MenuDef records so they need to be replaced.

User avatar
SVR
Retired staff
Arch-Angel
Posts: 1449
Joined: Sat Nov 02, 2002 11:04 pm
Location: Texas
United States of America

Hand-picked

Post by SVR » Sat Jan 01, 2005 12:01 am

Here we go ... http://svr.d2mods.com/NPCMsg.zip

The first new txt for quests will be the NPCMsg.txt

It's basically the NPCMsg structure for each quest/ quest state.
The states are not numbered, should we add a column ?

These will be referenced by index in the Quests.txt file which will control the trigger events that switch from state to state.
Last edited by SVR on Thu Mar 19, 2009 9:03 pm, edited 1 time in total.

User avatar
Myhrginoc
Retired Admin
Cherub
Posts: 12100
Joined: Sat May 25, 2002 7:28 am
Location: Percussion U
United States of America

Hand-picked

Re: Quest structures and functions

Post by Myhrginoc » Sat Jan 01, 2005 3:18 am

Not all quests have all 16 states, so a state field would be a good idea. That way a record can get linked to a check/set/reset function by just extracting the quest and state fields from the appropriate record. This would be available in addition to any custom state handling functions.
Last edited by Myhrginoc on Sat Jan 01, 2005 3:20 am, edited 1 time in total.
Do the right thing. It will gratify some people and astonish the rest.
~ Mark Twain
Run Diablo II in any version for mods: tutorial
The Terms of Service!! Know them, abide by them, and enjoy the forums at peace.
The Beginner's Guide v1.4: (MS Word | PDF) || Mod Running Scripts || TFW: Awakening

User avatar
SVR
Retired staff
Arch-Angel
Posts: 1449
Joined: Sat Nov 02, 2002 11:04 pm
Location: Texas
United States of America

Hand-picked

Post by SVR » Sat Jan 01, 2005 4:43 am

ok :)

btw, the state here is not the same as we're used to thinking of from the QuestRecord bits (pHistory) used with d2common.#11107 etc.
It's the +0C byte in the pQuest struct.
And the Quest ID is not the pHist index, eFilter is.

In the txt, the state corresponds roughly to the sequence of the message relative to the first msg of the same quest.
So at A1Q1 state 5 (row 8), you have 3 messages available (A1Q1SuccessfulWarriv,A1Q1SuccessfulCharsi,A1Q1SuccessfulGheed).

In d2game, there is a small array at the end of each NPCMsg table that translates the state into a msg index.
Most are {-1,0,1,2,3,4...} for direct translation but some are switched up.
Probably so they could swap sub-quest order around easily.

There is no correspondence between the QuestRecord and the Quest state, each quest sets the QuestRecord as desired for it's current state.
It may remain unchanged for several Quest state changes then at state 5 gets bit 1 set (for reward pending).

We'll have to have a column in QuestStates.txt for the bit flags to set/reset.
Each row will correspond to a quest state and have a column for each possible trigger and action.
Will need some mechanism to tell which state results, for instance;
finishing quest 1 activates quest2 state 1 (Kashya speech) but going to Graveyard sets quest2 state 3 (Kill blood raven).

So far we're looking at 3 txt's...
Quests.txt - base quest info (pQuest struct initialization)
QuestStates.txt - sequence/trigger/action info (msg to show, QuestRecord bits etc)
NPCMsg - yep. (hmm, should we call this QuestMsg.txt? ;-)

Haven't looked into it yet but I'm sure we'll need QuestLog.txt to handle the quest log client side and an QuestMenu.txt for the special quest menu options.

User avatar
Joel
Moderator
Dominion
Posts: 6921
Joined: Mon May 27, 2002 7:19 am
Location: Orsay

Hand-picked

Post by Joel » Sun Jan 02, 2005 7:34 pm

So far we're looking at 3 txt's...
Quests.txt - base quest info (pQuest struct initialization)
QuestStates.txt - sequence/trigger/action info (msg to show, QuestRecord bits etc)
NPCMsg - yep. (hmm, should we call this QuestMsg.txt? ;-)
QuestMsg.txt

I assume reward are handled in QuestStates special code ?
"How much suffering, mortal, does it take before you lose your grace?"
Shadow Empire (coming soon) | forum

User avatar
SVR
Retired staff
Arch-Angel
Posts: 1449
Joined: Sat Nov 02, 2002 11:04 pm
Location: Texas
United States of America

Hand-picked

Post by SVR » Wed Jan 12, 2005 5:33 am

QuestMsg.txt it is :)

Yep, Rewards will go in the action columns of QuestStates.
Haven't had time to flushout any new ideas (just got my comp back up today from the move) but I'll try to get a rough format worked out this weekend.

User avatar
Joel
Moderator
Dominion
Posts: 6921
Joined: Mon May 27, 2002 7:19 am
Location: Orsay

Hand-picked

Post by Joel » Wed Jan 12, 2005 9:45 pm

good, can't wait to test this gem :D
"How much suffering, mortal, does it take before you lose your grace?"
Shadow Empire (coming soon) | forum

User avatar
Darque
Retired staff
Arch-Angel
Posts: 1052
Joined: Fri Aug 01, 2003 9:04 am
Location: Poplar Grove, IL

Post by Darque » Wed Jan 12, 2005 10:11 pm

good, can't wait to test this gem
Same here :mrgreen: Is there an ETA of when we can have a beta of QuestMsg.txt?
Is this system going to be somehow related to adding new NPCs to our mods?
Last edited by Darque on Wed Jan 12, 2005 10:13 pm, edited 1 time in total.
Diabolic Studios Web Site | Forum
THoC Beta v8.1Download | Feedback

Infinitum Web Site
Read The Forum Terms of Service

User avatar
SVR
Retired staff
Arch-Angel
Posts: 1449
Joined: Sat Nov 02, 2002 11:04 pm
Location: Texas
United States of America

Hand-picked

Post by SVR » Sat Jan 15, 2005 4:46 pm

No ETA ATM ;-)

Still mass quantities of research / coding.

I may be able to get a working QuestMsg.txt by Monday, but it will only reproduce the existing quests (changing the msgs to other NPCs should work though).

We will need QuestStates flushed out to get the game to actually trigger on new messages.

As far as new NPCs, the msg's should work on any Monster ID that is marked as NPC.
But I believe there is much more needed for fully functioning NPC's.

When finished, NPC's will be fully soft coded in the Quest system but it won't fix other NPC related code.
We'll save that for another project ;-)


EDIT:
More info on CallBack 8 (+C0) On Monster Killed.
The Quest column in levels.txt determines if the callback gets called.
If this column is filled, all monsters spawned will have the PQuest ptr stored into Unit+74h.
When they die, the game uses this ptr to call the callback.
Last edited by SVR on Tue May 31, 2005 12:42 am, edited 1 time in total.

User avatar
TimeHorse
Posts: 8
Joined: Fri Jan 06, 2006 8:40 pm

Re: Quest structures and functions

Post by TimeHorse » Sun Jan 22, 2006 5:23 pm

Are you still working on this project (I assume still 1.10 based)? Is there any way I can help?

User avatar
kingpin
Retired Admin
Cherub
Posts: 10954
Joined: Sat Jan 11, 2003 12:51 pm
Sweden

Hand-picked

Re: Quest structures and functions

Post by kingpin » Fri Mar 20, 2009 5:15 pm

SVR push page to top when you edit it :)

Did it for you this time.

User avatar
Necrolis
Senior Admin
Throne
Posts: 9125
Joined: Sat Mar 25, 2006 1:22 pm
Location: The Land of the Dead
South Africa

Hand-picked

Post by Necrolis » Fri Mar 20, 2009 7:36 pm

This bump has reminded me that i should post my structs and funcs, which are far more complete ;), hopefully tomorrow. So just for now i'll post a little know bug: the quest event timers use an incorrect number of frames per second, 20 instead of 25, just like the shrine event callback... Also quest data is a union of 0x29(that includes 6 quests for act 4, but the last 3 only get 5 bytes allocated) structs, most of which are a bad collection of tons of bools and guids. Finally the client side code is very small when it comes to quests(npcs are huge, unlike on the server), in total there are about 5 funcs, which just recieve, set and check the client side quest states(this doesn't include the quest log, which can actually lag the server :o)
Image
Netiquette, Do you USE it?!?! | Nefarius' Fixed TXT Files | Terms Of Service
Blackened | Day of Death | D2GFEx
"What was yours is mine. Your land, your people, and now your life." - Lim-Dul, the Necromancer
Judgement is Final, Death is Eternal

User avatar
Necrolis
Senior Admin
Throne
Posts: 9125
Joined: Sat Mar 25, 2006 1:22 pm
Location: The Land of the Dead
South Africa

Hand-picked

Post by Necrolis » Sat Mar 21, 2009 10:55 am

are you ready kids :P

Code: Select all

#define GETQUESTDATA(x,y) (x)y->pQuestDataEx

typedef void (__fastcall * QUESTINIT)    (D2QuestDataStrc*);
typedef void (__fastcall * QUESTCALLBACK)(D2QuestDataStrc*,D2QuestArgStrc*);
typedef int  (__fastcall * QUESTSTATUS)  (D2QuestDataStrc*,D2UnitStrc*,D2QuestFlagStrc*,D2QuestFlagStrc*,BYTE*);
typedef int  (__fastcall * QUESTUPDATE)  (D2GameStrc*,D2QuestDataStrc*);
typedef bool (__fastcall * QUESTACTIVE)  (D2QuestDataStrc*,int,D2UnitStrc*,D2QuestFlagStrc*,D2UnitStrc*);   
typedef int  (__fastcall * QUESTSEQ)     (D2QuestDataStrc*,D2UnitStrc*,D2QuestFlagStrc*,D2QuestFlagStrc*,BYTE*);

enum eD2QuestLogSubTypes
{
	QUESTLOG_DENOFEVIL			= 1,
	QUESTLOG_BLOODRAVEN			= 2,
	QUESTLOG_CAIN				= 4,
	QUESTLOG_DURIEL				= 13,
	QUESTLOG_HELLFORGE			= 23,
	QUESTLOG_BAAL				= 36
};

enum eD2QuestFilterEvents
{
    QUESTEVENT_STATUSUPDATE                     = 0,
    QUESTEVENT_NPCINIT                          = 1,
    QUESTEVENT_QUESTINIT                        = 2
};

enum eD2ScrollMenus
{
	SCROLLMENU_MESSAGE = 0,
	SCROLLMENU_MENU	   = 1,
	SCROLLMENU_NONE	   = 2
};

enum eD2QuestEvents
{
    QUESTEVENT_NPCACTIVATE                       = 0,
    QUESTEVENT_NPCDEACTIVATE                     = 2,
    QUESTEVENT_CHANGEDLEVEL                      = 3,
    QUESTEVENT_ITEMPICKEDUP                      = 4,
    QUESTEVENT_ITEMDROPPED                       = 5,
    QUESTEVENT_MONSTERKILLED                     = 8,
    QUESTEVENT_PLAYERDROPPEDWITHQUESTITEM        = 9,
    QUESTEVENT_PLAYERLEAVESGAME                  = 10,
    QUESTEVENT_SCROLLMESSAGE                     = 11,
    QUESTEVENT_PLAYERSTARTEDGAME                 = 13,
    QUESTEVENT_PLAYERJOINEDGAME                  = 14
};
//the others don't seem to be used...
enum eD2InteractStates
{
	INTERACT_NONE		= 0,
	INTERACT_TALKING	= 1,	//the menu is open
	INTERACT_TRADE		= 2,	//your trading
};

struct D2InteractChainStrc			//sizeof 0xC
{
    D2UnitStrc* pUnit;				//+00
    DWORD dwState;					//+04
    D2InteractChainStrc* pPrev;		//+08
};

struct D2InteractControlStrc
{
    D2InteractChainStrc* pLast;
    //...
};

struct D2NPCMessageListStrc					//sizeof 0x10
{
    WORD nMessageIndexes[MAX_NPCMESSAGES];	//+00
};

struct D2QuestArgStrc
{
    D2GameStrc* pGame;                      // +00
    int nEvent;                             // +04
    D2UnitStrc* pTarget;                    // +08
    D2UnitStrc* pPlayer;                    // +0C
    DWORD dw10;                             // +10

    union
    {
        struct
        {
            D2ScrollTextControlStrc* pTextControl;      // +14
            DWORD dw18;									// +18
        };

        struct
        {
            short nNPCNo;                   // +14
            WORD w3;                        // +16

            short nMessageIndex;            // +18
            WORD w4;                        // +1A
        };

        struct
        {
            int nOldLevel;                  // +14
            int nNewLevel;                  // +18
        };
    };
};

struct D2NPCMessageStrc						// sizeof 0x0C
{
    int nNPCNo;								// +00
    short nStringIndex;						// +04
	short nPad;								// +06
    BOOL nMenu;								// +08
};

struct D2NPCMessageTableStrc						//sizeof 0xC4
{
    D2NPCMessageStrc pMessages[MAX_NPCMESSAGESEX];	//+00
    int nMessages;									//+C0
};

struct D2QuestDataStrc                  // sizeof 0xF4
{
    int nQuestNo;                       // +00 - internal
    D2GameStrc* pGame;                  // +04
    char nActNo;                        // +08
    bool bNotIntro;                     // +09 - set to false for intro quests, it could also be for already completed...
    bool bActive;                       // +0A - confirmed
    FLAGS8 fLastState;                  // +0B - previous quest state
    FLAGS8 fState;                      // +0C - main quest state
    char nInitNo;                       // +0D
    WORD dw0E;							// +0E 
    int nSeqId;                         // +10 - nInitNo
    union
    {
        DWORD dwFlags;                      // +14
        FLAGS8 fFlags;                      // +14
    };
    void* pQuestDataEx;                 // +18 - union of 0x29 structs
    DWORD nPlayerGUID[32];              // +1C - players that have entered the quest zone
    WORD nPlayerCount;                  // +9C
    WORD dw9E;							// +9E 
    QUESTCALLBACK pfCallback[15];       // +A0
    D2NPCMessageTableStrc* pNPCMessages;// +DC
    int nQuest;                         // +E0 - index in quest flag bit array
    QUESTSTATUS pfStatusFilter;         // +E4
    QUESTACTIVE pfActiveFilter;         // +E8
    QUESTSEQ pfSeqFilter;               // +EC
    D2QuestDataStrc* pPrev;             // +F0
};

struct D2QuestTimerStrc							// sizeof 0x14
{
    QUESTUPDATE             pfUpdate;           //+00
    D2QuestDataStrc*        pQuest;             //+04
    DWORD                   dwTicks;            //+08
    DWORD                   dwTimeout;          //+0C
    D2QuestTimerStrc*       pNext;              //+10
};

struct D2QuestInitStrc                  // sizeof 0x18 - not pragma aligned
{
        QUESTINIT   pfInit;             // +00
        BYTE        nAct;               // +04
        BYTE        nPad[3];            // +05
        DWORD       nVersion;           // +08
        bool        bNoSetState;        // +0C - used by the sequences for quest init flags
        BYTE        nPad2[3];           // +0D
        int         nChainNo;           // +10 - quest data internal chain id
        DWORD       nQuestNo;           // +14 - pQuestData flag no 
};

struct D2QuestIntroInitStrc             // sizeof 0x8
{
        QUESTINIT   pfInit;             // +00
        BYTE        nAct;               // +04
        BYTE        nPad[3];            // +05
}; 

struct D2QuestInfoStrc                      //sizeof 0x24 - change to D2QuestControlStrc
{
    D2QuestDataStrc*    pLastQuest;         //+00
    BOOL                bExecuting;         //+04
    BOOL                bPickedSet;         //+08
    D2QuestFlagStrc*    pQuestFlags;        //+0C
    D2QuestTimerStrc*   pTimer;             //+10
    DWORD               dwTick;             //+14
    D2SeedStrc          pSeed;              //+18
    DWORD               nUnk[2];            //+20
};

struct D2QuestChainStrc                     //sizeof 0x8
{
    D2QuestDataStrc*    pQuestData;         //+00
    D2QuestChainStrc*   pNext;              //+04
};

#pragma pack()
ALOT of stuff is packet based, so it is omitted..

my not-up-to-date mapping of the 1.10 server quests.cpp module

Code: Select all

6FC93B60 to 6FC93B84 -> QuestFreeChainRecordEx();
6FC93B85 to 6FC93B8F -> Free Space
6FC93B90 to 6FC93BCF -> GetQuestDataEx();
6FC93BD0 to 6FC93C2F -> QuestAttachLevelChainRecord();
6FC93C32 to 6FC93C3F -> Free Space
6FC93C40 to 6FC93D54 -> QuestCreateChainRecordEx();
6FC93D57 to 6FC93D5F -> Free Space
6FC93D60 to 6FC93DBD -> QuestInitScrollTextChain();
6FC93DC0 to 6FC93FC1 -> QuestInitEx();
6FC93FC6 to 6FC93FCF -> Free Space
6FC93FD0 to 6FC94073 -> QuestFreeEx();
6FC94074 to 6FC9407F -> Free Space
6FC94080 to 6FC940A7 -> QuestGetGlobalSeed();
6FC940AC to 6FC940AF -> Free Space
6FC940B0 to 6FC9420F -> QuestParseKillEx();
6FC94210 to 6FC9427C -> QuestStatusCyclerEx();
6FC94281 to 6FC9428F -> Free Space
6FC94290 to 6FC942C3 -> QuestChangeLevelEx();
6FC942C6 to 6FC942CF -> Free Space
6FC942D0 to 6FC9437F -> QuestEventCallbackEx();
6FC94384 to 6FC9438F -> Free Space
6FC94390 to 6FC9449C -> QuestDroppedWithItemEx();
6FC944A1 to 6FC944AF -> Free Space
6FC944B0 to 6FC944EC -> QuestNPCActivateEx();
6FC944EF -> Free Space
6FC944F0 to 6FC9451B -> QuestNPCDeactivateEx();
6FC9451E to 6FC9451F -> Free Space
6FC94520 to 6FC94549 -> QuestItemPickedUpEx();
6FC9454C to 6FC9454F -> Free Space
6FC94550 to 6FC94579 -> QuestItemDroppedEx();
6FC9457C to 6FC9457F -> Free Space
6FC94580 to 6FC94686 -> QuestUpdaterEx();
6FC9468B to 6FC9468F -> Free Space
6FC94690 to 6FC94704 -> QuestCreateTimer();
6FC94707 to 6FC9470F -> Free Space
6FC94710 to 6FC947E8 -> QuestRefreshStatusEx();
6FC947EB to 6FC947EF -> Free Space
6FC947F0 to 6FC94A25 -> QuestStatusCallbackEx();
6FC94A26 to 6FC94A2F -> Free Space
6FC94A30 to 6FC94A40 -> QuestDebugOutputEx();
6FC94A43 to 6FC94A4F -> Free Space
6FC94A50 to 6FC94B14 -> QuestDeleteItemEx();
6FC94B17 to 6FC94B1F -> Free Space
6FC94B20 to 6FC94C98 -> QuestStatusCycler2Ex();
6FC94C9B to 6FC94C9F -> Free Space
6FC94CA0 to 6FC94CDF -> QuestUnitIterateEx();
6FC94CE4 to 6FC94CEF -> Free Space
6FC94CF0 to 6FC94D2E -> QuestStateDebugEx();
6FC94D31 to 6FC94D3F -> Free Space
6FC94D40 to 6FC94DA5 -> QuestNPCMessageEx();
6FC94DA8 to 6FC94DAF -> Free Space
6FC94DB0 to 6FC95358 -> QuestSequenceCyclerEx();
6FC9535B to 6FC9535F -> Free Space
D2Game.#10037 6FC95360 to 6FC95396 -> QuestCheckFirstPickedSet();
6FC95399 to 6FC9539F -> Free Space
D2Game.#10038 6FC953A0 to 6FC9541E -> QuestCheckNotIntroQuestEx();
6FC95421 to 6FC9542F -> Free Space
6FC95430 to 6FC95487 -> QuestSendCurrentFlagsEx();
6FC95488 to 6FC9548F -> Free Space
6FC95490 to 6FC956AF -> QuestActiveCyclerEx();
6FC956B4 to 6FC956BF -> Free Space
6FC956C0 to 6FC956FA -> QuestSetGlobalStateEx();
6FC956FD to 6FC956FF -> Free Space
6FC95700 to 6FC9573A -> QuestGetGlobalStateEx();
6FC9573D to 6FC9573F -> Free Space
6FC95740 to 6FC9578D -> QuestWarrivSpawnEx();
6FC95790 to 6FC957F7 -> QuestUseClueItemEx();
6FC957FA to 6FC957FF -> Free Space
6FC9585B to 6FC9585F -> Free Space
6FC95860 to 6FC95918 -> QuestObjectInitFNEx(); 1, 2 & 3
6FC95920 to 6FC95927 -> QuestDisableSequences(); 
6FC95928 to 6FC9592F -> Free Space
refrence if you need params for a quest func(not fully done)

Code: Select all

void __stdcall QuestCopyFlagsEx(D2QuestFlagStrc* pBuffer, BYTE* pFlagDest, size_t nSize, BOOL bFlipBits);
void __stdcall QuestCopyFlags(D2QuestFlagStrc* pBuffer, BYTE* pFlagDest, size_t nSize, BOOL bUnused = FALSE);
void __fastcall QuestEnableFlag(D2QuestFlagStrc* pQuestData, DWORD nQuest, DWORD nFlag);
void __fastcall QuestDisableFlag(D2QuestFlagStrc* pQuestData, DWORD nQuest, DWORD nFlag);
BOOL __fastcall QuestCheckFlag(D2QuestFlagStrc* pQuestData, DWORD nQuest, DWORD nFlag);

//Main
D2QuestDataStrc* __fastcall QuestGetDataEx(D2GameStrc* pGame, int nQuest);
D2SeedStrc* __fastcall QuestGetGlobalSeedEx(D2GameStrc* pGame);
void __fastcall QuestUpdaterEx(D2GameStrc* pGame);
void __fastcall QuestEventCallbackEx(D2QuestArgStrc* pArgs, bool bCheckActive, bool bCheckAct);
void __fastcall QuestStatusCallbackEx(D2GameStrc* pGame, D2UnitStrc* pPlayer);
void __fastcall QuestStatusCyclerEx(D2QuestArgStrc* pArgs, bool bForceActive);
void __fastcall QuestStatusCycler2Ex(D2GameStrc* pGame, D2UnitStrc* pPlayer, BYTE nQuestNo);
BOOL __fastcall QuestActiveCyclerEx(D2GameStrc* pGame, D2UnitStrc* pPlayer, D2UnitStrc* pNPC);
void __fastcall QuestInitEx(D2GameStrc* pGame);
void __fastcall QuestFreeEx(D2GameStrc* pGame);
BOOL __fastcall QuestCreateChainRecordEx(D2GameStrc* pGame, D2UnitStrc* pUnit, int nQuestID);
void __fastcall QuestFreeChainRecordEx(D2GameStrc* pGame, D2QuestChainStrc* pRecord);
void __fastcall QuestSendCurrentFlagsEx(D2GameStrc* pGame, D2ClientStrc* pClient);
void __fastcall QuestChangeLevelEx(D2GameStrc* pGame, int nOldLevel, int nNewLevel, D2UnitStrc* pPlayer);
void __fastcall QuestAttachLevelChainRecordEx(D2GameStrc* pGame, D2UnitStrc* pUnit, DRLGRoom* pRoom, BOOL bDebug);
void __fastcall QuestNPCActivateEx(D2ClientStrc* pClient, D2UnitStrc* pPlayer, D2UnitStrc* pNPC, D2ScrollTextControlStrc* pTextControl);
void __fastcall QuestDroppedWithItemEx(D2GameStrc* pGame, D2UnitStrc* pPlayer);
void __fastcall QuestNPCDeactivateEx(D2GameStrc* pGame, D2UnitStrc* pPlayer, D2UnitStrc* pNPC);
void __fastcall QuestItemPickedUpEx(D2GameStrc* pGame, D2UnitStrc* pPlayer, D2UnitStrc* pItem);
void __fastcall QuestItemDroppedEx(D2GameStrc* pGame, D2UnitStrc* pPlayer, D2UnitStrc* pItem);
void __fastcall QuestCreateTimerEx(D2QuestDataStrc* pQuest, QUESTUPDATE pfCallback, int nTicks);
void __fastcall QuestDebugOutputEx(D2GameStrc* pGame, char* szMessage, char* szFile, int nLine);
void __fastcall QuestInitScrollTextChainEx(D2QuestDataStrc* pQuest, D2ScrollTextControlStrc* pTextControl, int nNPCID, int nIndex);
void __fastcall QuestRefreshStatusEx(D2QuestDataStrc* pQuest,BYTE* pQuestList, D2QuestFlagStrc* pQuestFlags);
void __fastcall QuestUnitIterateEx(D2QuestDataStrc* pQuest, int nIterateState, void* pArg, UNITITERATE pfIterate, bool bIterate);
void __fastcall QuestSetMainStateEx(D2QuestDataStrc* pQuest, int nState, char* szFile, int nLine);
void __fastcall QuestNPCMessageEx(D2GameStrc* pGame, D2UnitStrc* pPlayer, DWORD nNPCGUID, short nMessage);
void __fastcall QuestSetGlobalStateEx(D2GameStrc* pGame, int nQuest, int nFlag);
void __fastcall QuestGetGlobalStateEx(D2GameStrc* pGame, int nQuest, int nFlag);
void __fastcall QuestWarrivSpawnEx(D2GameStrc* pGame, D2UnitStrc* pWarriv, int nWarrivID, int nXpos, int nYpos);
BOOL __stdcall QuestCheckNotIntroQuestEx(D2GameStrc* pGame, int nQuest);
void __fastcall QuestUseClueItemEx(D2GameStrc* pGame, D2UnitStrc* pPlayer, int nItemGUID);
void __fastcall QuestParseKillEx(D2GameStrc* pGame, D2UnitStrc* pDefender, D2UnitStrc* pAttacker);
void __stdcall QuestDisableSequenceCycler();

//Not Injected
void __fastcall QuestSendPlayerQuestsEx(D2ClientStrc* pClient, short nType, int nSubType, int nNPCGUID, D2QuestFlagStrc* fQuestFlags, BYTE nArg);
BOOL __stdcall QuestCheckPickedSetEx(D2GameStrc* pGame);
DWORD __stdcall QuestUpdateFlagsEx(D2QuestFlagStrc* pQuestFlags, int nQuestNo);
D2QuestFlagStrc* __stdcall CreateQuestFlagsEx(void* pMemPool);
void __stdcall FreeQuestFlagsEx(void* pMemPool, D2QuestFlagStrc* pQuestFlags);

//Not Finished
void __fastcall QuestSequenceCyclerEx(D2GameStrc* pGame, D2UnitStrc* pPlayer, BOOL bUnk);
void __fastcall QuestDeleteItemEx(D2GameStrc* pGame, D2UnitStrc* pPlayer, DWORD dwItemCode);

//Init FNs
void __fastcall QuestObjectInitFN1Ex(D2ObjectInitStrc* pArgs);
void __fastcall QuestObjectInitFN2Ex(D2ObjectInitStrc* pArgs);
void __fastcall QuestObjectInitFN3Ex(D2ObjectInitStrc* pArgs);
void __fastcall QuestObjectInitFN4Ex(D2ObjectInitStrc* pArgs);
all the client quests.cpp is packet related as well, but D2Client.#10002 etc is this

Code: Select all

BYTE __stdcall QUESTS_CheckQuestStatus(void* pMemPool, int nQuest)
{
	if(nQuest < 0 || nQuest > MAX_QUESTS)
		return VFLAG_QUEST_STARTED;

	return gnClientQuestsStatus[nQuest];
}
finally some of the quest data structs(also only partially started):
Act1

Code: Select all

/*=================================================================*/
/* Quest 1														   */
/*=================================================================*/

struct D2DenOfEvilQuestDataStrc		//sizeof 0x8C
{
	BYTE nUnk[135];			//+00
	int nMonstersLeft;		//+88
};

int __fastcall ACT1Q1_GetMonstersLeft(D2QuestDataStrc* pQuest);

/*=================================================================*/
/* Quest 2														   */
/*=================================================================*/

struct D2BloodRavenQuestDataStrc		//sizeof 0xC
{
	BYTE nUnk[12];			//+00
};

/*=================================================================*/
/* Quest 3														   */
/*=================================================================*/

struct D2MalusQuestDataStrc		//sizeof 0xA4
{
	BYTE nUnk2;				//+00
	bool bMalusTaken;		//+01
	WORD nUnk3;				//+02
	int nMalusGUID;			//+04
	BYTE nUnk4;				//+08
	bool bModeChanged;		//+09
	BYTE nUnk[142];			//+0A
	int nMalusMode;			//+98
	BYTE nUnk5[8];			//+9C
};

void __fastcall ACT1Q3_MalusModeChage(D2QuestDataStrc* pQuest, D2ObjectInitStrc* pArgs);

/*=================================================================*/
/* Quest 4														   */
/*=================================================================*/

struct D2CainQuestDataStrc		//sizeof 0x1BC
{
	WORD nStoneOrder[5];	//+00
	WORD nPad;				//+0A
	WORD nUnk;				//+0C
	BYTE nUnk1[38];			//+10
	int nCageGUID;			//+34
	BYTE nUnk5[16];			//+38
	bool bModeChanged;		//+48
	BYTE nUnk4;				//+49
	bool bStonesOrdered;	//+4A
	BYTE nUnk2[9];		    //+4B
	int nCageMode;			//+54
	BYTE nUnk3[59];			//+58
	bool bTristramTalk;		//+93
	BYTE nUnk6[296];		//+94
};

#define QUEST_CAIRNSTONES 5

void __fastcall ACT1Q4_RandomizeStoneOrder(D2QuestDataStrc* pQuest);
void __fastcall ACT1Q4_SendStoneOrder(D2GameStrc* pGame, D2UnitStrc* pPlayer);
void __fastcall ACT1Q4_CageModeChange(D2QuestDataStrc* pQuest, D2ObjectInitStrc* pArgs);
BOOL __fastcall ACT1Q4_CheckTristramInteract(D2GameStrc* pGame, int nNPC);	

/*=================================================================*/
/* Quest 5														   */
/*=================================================================*/

struct D2CountessQuestDataStrc	//sizeof 0x120
{
	BYTE nUnk[39];			//+00
	DWORD nBookGUID;		//+28
	BYTE  nUnk2[41];		//+2C
	bool bBookFound;		//+49
	BYTE nUnk3[208];		//+50
};

void __fastcall ACT1Q5_QuestSetBookGUID(D2GameStrc* pGame, D2QuestDataStrc* pQuest, D2UnitStrc* pUnit);

/*=================================================================*/
/* Quest 4														   */
/*=================================================================*/

struct D2AndarielQuestDataStrc		//sizeof 0x198
{
	BYTE nUnk[408];			//+00
};
Act 2

Code: Select all


/*=================================================================*/
/* Quest 3														   */
/*=================================================================*/

struct D2TainedSunQuestDataStrc		//sizeof 0xA0
{
	BYTE nUnk[2];			//+00
	bool bRewarded;			//+02
	BYTE nUnk4[2];			//+03
	bool bAlterDestroyed;	//+05
	BYTE nUnk3[3];			//+06
	int nAlterMode;			//+08
	int nAlterGUID;			//+0C
	BYTE nUnk2[143];		//+10
};

void __fastcall ACT2Q3_AlterModeChage(D2QuestDataStrc* pQuest, D2ObjectInitStrc* pArgs);

/*=================================================================*/
/* Quest 6														   */
/*=================================================================*/

struct D2DurielQuestDataStrc		//sizeof 0x68
{
	BYTE nUnk[52];			//+00
	int nStaffTomb;			//+34
	BYTE nUnk2[48];			//+38
};

void __fastcall ACT2Q6_SendStaffTomb(D2GameStrc* pGame, D2UnitStrc* pPlayer);

/*=================================================================*/
/* Quest 7														   */
/*=================================================================*/

struct D2Act2Quest7QuestDataStrc		//sizeof 0x1
{
	BYTE nUnk;				//+00
};

/*=================================================================*/
/* Quest 8														   */
/*=================================================================*/

struct D2Act2Quest8QuestDataStrc		//sizeof 0x2
{
	BYTE nUnk[2];			//+00
};
Act3

Code: Select all

=================================================================*/
/* Quest 1														   */
/*=================================================================*/

struct D2LamEsenQuestDataStrc		//sizeof 0xA0
{
	BYTE nUnk1[24];				//+00
	int nTomeStandMode;			//+18
	BYTE nUnk[136];				//+1C
};

void __fastcall ACT3Q1_TomeModeChange(D2QuestDataStrc* pQuest, D2ObjectInitStrc* pArgs);

/*=================================================================*/
/* Quest 3														   */
/*=================================================================*/

struct D2GidbinQuestDataStrc		//sizeof 0x30
{
	BYTE nUnk[48];					//+00
};

/*=================================================================*/
/* Quest 4														   */
/*=================================================================*/

struct D2GoldenBirdQuestDataStrc		//sizeof 0x1C
{
	BYTE nUnk;					//+00
	bool bGoldenBird;			//+01
	bool bJadeFigure;			//+02
	BYTE nUnk2;					//+03
	int nUnitGUID;				//+04
	BYTE nUnk1[20];				//+08
};

void __fastcall ACT3Q4_SetGoldenBirdState(D2QuestDataStrc* pQuest, D2UnitStrc* pUnit);
Act 5

Code: Select all

/*=================================================================*/
/* Quest 6														   */
/*=================================================================*/

struct D2BaalQuestDataStrc		//sizeof 0xA8
{
	BYTE nUnk1[133];				//+00
	bool bMinionsDefeated;			//+86
	BYTE nUnk[34];					//+87
};

bool __fastcall ACT5Q6_CheckMinionsDefeated(D2QuestDataStrc* pQuest);
void __fastcall ACT5Q6_SetMinionsDefeated(D2QuestDataStrc* pQuest);
void __fastcall ACT5Q6_SetMinionState(D2QuestDataStrc* pQuest, bool bState);
Image
Netiquette, Do you USE it?!?! | Nefarius' Fixed TXT Files | Terms Of Service
Blackened | Day of Death | D2GFEx
"What was yours is mine. Your land, your people, and now your life." - Lim-Dul, the Necromancer
Judgement is Final, Death is Eternal

User avatar
SVR
Retired staff
Arch-Angel
Posts: 1449
Joined: Sat Nov 02, 2002 11:04 pm
Location: Texas
United States of America

Hand-picked

Re: Quest structures and functions

Post by SVR » Mon Mar 23, 2009 2:56 pm

kingpin";p="412798" wrote:SVR push page to top when you edit it :)

Did it for you this time.
Eheh, I did a bunch of edit's friday to update links to my web pages. My ISP changed the URLs so all links broke.

Sorry for any confusion :)

Return to “Code Editing”