#Loading and Showing a Custom celfile.

1 messages · Page 1 of 1 (latest)

tawny zealot
#

Pretty much tittle, im just want to load a red circle in the middle of the screen thats it.
where do i start? where can get some examples of it.

marble elm
#

there's a few steps involved in this, you need to create a dc6 file of your image that you want to display (you'll have to google this or go to phrozen keep website to get tools for it) and you'll need to put it into the correct folder in your mpq or data folder, the load the image into a cel file in memory, set up some parameters for drawing, then draw the image. I'll show you an example from pd2 for drawing the quick cast icons:

ASMPTR(D2CLIENT, LoadDC6Image, 0x2B420);
FUNCPTR(D2GFX, DrawShiftedImage, void __stdcall, (GfxData* pData, int nXpos, int nYpos, DWORD dwGamma, int nDrawMode, int nGlobalPaletteShift), -10019)

__declspec(naked) CellFile* __stdcall D2CLIENT_LoadDC6Image_STUB(char* szFilePath, int param_2)
{
    __asm
    {
        mov EAX, [esp + 4]                                   // szFilePath
        push[esp + 8]                                       // param_2
        call D2CLIENT_LoadDC6Image                              // 0x2B420
        retn 8
    }
}

struct GfxData
{
    int nFrame;                  //0x00
    DWORD __04[10];               //0x04
    char* szName;               //0x2C
    int nMaxFrames;               //0x30
    CellFile* pCellFile;      //0x34
    DWORD __38;                  //0x38
    CellFile* pCurrentCell;   //0x3C
    int nDirection;               //0x40
    DWORD __44;                  //0x44
};

GfxData stQCGfxData = {};
stQCGfxData.pCellFile = D2CLIENT_LoadDC6Image_STUB((char*)"DATA\\GLOBAL\\ui\\SPELLS\\skillicons_mini", 0);
stQCGfxData.nFrame = 0;

int nQCBtnX = *p_D2CLIENT_ScreenSizeX - QC_ICON_OFFSET_X - nNumQuickCastSkills * nBuffIconWidth;
int nQCBtnY = *p_D2CLIENT_ScreenSizeY - QC_ICON_OFFSET_Y;

D2GFX_DrawShiftedImage(&stQCGfxData, nQCBtnX, nQCBtnY, -1, 5, 0);
tawny zealot
#

pretty much tankyou this is exactly what i was looking for.
gonna show some results soon :d

tawny zealot
#

WIP but, we are getting there

#

any chance of remembering where do we asign the font used in the HoverObject_Interception, seen to be the font6 but im not finding the place to change it.

marble elm
#

wow looks really good, I like it. The font is just preset fonts for each "size" but you can change the font size before you draw your text like:

FUNCPTR(D2WIN, SetFont, DWORD __fastcall, (DWORD dwSize), -10184, -10047)

enum D2Fonts
{
    FONT_6BORDERED = 0,
    FONT_8 = 1,
        // ...
        // I have used any other font sizes, so I haven't bothered mapping the rest out
};

D2WIN_SetFont(FONT_6BORDERED);
D2WIN_DrawText(...);
// reset font to what it was before
D2WIN_SetFont(FONT_8);
tawny zealot
#

im hving a tiny bit of a situation here, i need to filter the texthook well not the texthook, only the call only to show withthe hcldx of andariel, instead of globally

marble elm
#

not sure I understand what you mean, you mean the picture with the face?

#

if you have the unit that you're hovering, you would just check if (pUnit->dwTxtFileNo == MONID_ANDARIEL) and you'd get andariel's ID from monstats.txt

tawny zealot
#

Yea i think thats was i looking for. Tytyty

#

I'm a bit confused tho. I just created a new font named font55, places in the proper latín font folder, and i did enumerate it, but when i use it on the draw, it crashes, i'm not sure why yet, gonnna share the code when reach home.

Edit, still crashes but, im just going to use the vanilla ones.

tawny zealot
#

What could use to difference between difficulties, my goal is show the monster lvl on the screen, but, i need a way to difference beween difficulties since the lvls changes.

#

maybe D2CLIENT_GetDifficulty ?

tawny zealot
#

progress so far

tawny zealot
#

Any clue, why im not being able to hide 4digits items on the mh?, its says BLOCKED but shows anyway.

sullen kindle
#

Looks like a bug in BH/Modules/ItemMover/ItemMover.cpp.
I think item->code[3] = 0; here should actually be item->code[4] = 0;

for (std::size_t i = 0; i < 4; i++) {
    item->code[i] = static_cast<char>(reader.read(8));
}
item->code[3] = 0;
tawny zealot
tawny zealot
sullen kindle
#

Ok I think I see what's going on now. Normally the 4th character for 3 digit codes is a space, so the old code was changing that to a 0. I think what you may need to do is check to see if the 4th character is a space and if it is then set it to 0.

You could probably replicate how it's done in CreateUnitItemInfo():

uInfo->itemCode[0] = code[0];
uInfo->itemCode[1] = code[1] != ' ' ? code[1] : 0;
uInfo->itemCode[2] = code[2] != ' ' ? code[2] : 0;
uInfo->itemCode[3] = code[3] != ' ' ? code[3] : 0;
uInfo->itemCode[4] = 0;

Also, you may have to update ItemCodeCondition in ItemDisplay.cpp if you haven't already. I think currently it only supports 3 digit codes

tawny zealot
#

Will check that thank You!

tawny zealot
#

gonna leave this here, maybe someone needs it too. this fixed the issue.
item->code[0] = item->code[0]; item->code[1] = item->code[1] != ' ' ? item->code[1] : 0; item->code[2] = item->code[2] != ' ' ? item->code[2] : 0; item->code[3] = item->code[3] != ' ' ? item->code[3] : 0; item->code[4] = 0;

marble elm
#

Didn't I already send exactly this in a different thread?

tawny zealot
#

Nop, back then, i was having issues trying to make the mh show items with 4 digits, now, without this i wasnt able to hide the [blocked] 4 digits items

tawny zealot
marble elm
#

you should only call D2CLIENT_LoadDC6Image_STUB((char*)"DATA\\GLOBAL\\ui\\SPELLS\\skillicons_mini", 0); once

#

so call it once, save it to a variable and then use that variable from then on

tawny zealot
#

Thx! that fixed the issue!.

tawny zealot
#

Any pointing direction on how to make the exp shrines with a cellno custom? Instead of the regular one?

tawny zealot
#

@marble elm sorry to Tag You, i need to ask something, i want to do something simple, just on mouse right click in X position (doesnt matter where actually), shows a texthook. Any advise on where could read about it?

marble elm
#

@tawny zealot sorry, I've been away for a while so I haven't been checking up on these. But if you want to intercept mouse clicks it's a little bit complicated. You need to intercept the GameWindowEvent like this:

FUNCPTR(D2GFX, GetHwnd, HWND __stdcall, (void), -10048, -10007)

    // Do this somewhere near game start
    WNDPROC OldWNDPROC = (WNDPROC)GetWindowLong(D2GFX_GetHwnd(), GWL_WNDPROC);
    SetWindowLong(D2GFX_GetHwnd(), GWL_WNDPROC, (LONG)D2Client_GameWindowEvent);

then inside your new GameWindowEvent function, you need to call the only window proc event at the end (if you don't do anything, so the game functions normally)

VARPTR(D2CLIENT, MouseX, DWORD, 0x11B828, 0x11C950)
VARPTR(D2CLIENT, MouseY, DWORD, 0x11B824, 0x11C94C)

LONG WINAPI D2Client_GameWindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  int mouseX = (*p_D2CLIENT_MouseX);
  int mouseY = (*p_D2CLIENT_MouseY);
  // Do your custom mouse even things here
  // The mouse even that happened is inside uMsg
  switch (uMsg)
  {
    case WM_LBUTTONDOWN:
      // Do something on left mouse button down
      break;
  }
  
  // If you didn't do your custom things, call the old window event (for normal game mouse things)
  return (LONG)CallWindowProcA(OldWNDPROC, hWnd, uMsg, wParam, lParam);
}
tawny zealot
#

thank you, i work with this, ty

tight haven
tawny zealot
#

well im currently working in a new custom menu, but im having a crash in the code, and im not so sure where is it, just figuring a way to find the error xd

tawny zealot
#

finally found the error, this is the progress on the new UI so far

tawny zealot
# marble elm <@193886085871304704> sorry, I've been away for a while so I haven't been checki...

so, since im using the bh source code, more that a half of this its already done, im missing one part, that would be the case WM_LBUTTOMDOWN, im having doubts on whats going inside there, for example:

void Colec::OnRightClick(bool up, int x, int y, bool* block)
{
if (WM_LBUTTOMDOWN)

if (mouseX > posstartx && mouseX < posstartx + 40 && mouseY > possstartyM - 42 && mouseY < possstartyM && Toggles["Pergamino Nephalem"].state == false)
{
Toggles["Pergamino Nephalem"].state = true;
}
}
}

that opens a new custom menu, but im not so sure whats the thing going inside the case.

tawny zealot
#

but, what im confuse is ,should i put the LONG WINAP inside the module of my new window? in another place? isnt matter?

marble elm
#

WM_LBUTTONDOWN is just a constant, you need to check it against the uMsg from the window event to see if you actually pressed that button

#

like if (uMsg == WM_LBUTTONDOWN)

tawny zealot
#

Right but, where goes the entire if? Bc using the bh src, it's inside of d2helpers do i need to insert the entire if there? Or in My new module where is all the coding for the new menú is? There is My confusión.

tawny zealot
#

this is so confusing, but as a side note, i want to THANK YOU, iknow you are ultra bussywith the server and the crashes, i salute you for your kindness

marble elm
#

ah I see what you're saying, yeah BH already has this stuff mapped out so you'll want to put your new module click handling function inside D2Handlers.cpp at LONG WINAPI GameWindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
you can see in here, each module has a OnLeftClick function or OnRightClick function and it gets called automatically from the BH::moduleManager

LONG WINAPI GameWindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {

    bool blockEvent = false;
    int mouseX = (*p_D2CLIENT_MouseX);
    int mouseY = (*p_D2CLIENT_MouseY);

    if (uMsg == WM_LBUTTONDOWN) { // <---------------- Here they check left mouse button down
        if (Drawing::Hook::LeftClick(false, mouseX, mouseY))
            blockEvent = true;
        if (Drawing::UI::LeftClick(false, mouseX, mouseY))
            blockEvent = true;
        if (Drawing::StatsDisplay::Click(false, mouseX, mouseY))
            blockEvent = true;
                if (Drawing::NEWMODULE::LeftClick(false, mouseX, mouseY)) // <---- Add this to your new module to check if the coordinates are inside your new UI
                        blockEvent = true;
        __raise BH::moduleManager->OnLeftClick(false, mouseX, mouseY, &blockEvent);  // <--- This calls OnLeftClick for all modules that have the function
    }
// ...
}
tawny zealot
marble elm
#

both, your new module needs to define all the virtual functions in the module class:

class Module {
    private:
        friend class ModuleManager;

        string name;
        bool active;

        void Load();
        void Unload();

    public:
        Module(string name);
        virtual ~Module();

        string GetName() { return name; };
        bool IsActive() { return active; };

        // Module Events
        virtual void OnLoad() {};
        virtual void OnUnload() {};

        virtual void LoadConfig() {};
        virtual void MpqLoaded() {};

        virtual void OnLoop() {};

        // Game Events
        virtual void OnGameJoin() {}
        virtual void OnGameExit() {};

        // Drawing Events
        virtual void OnDraw() {};
        virtual void OnAutomapDraw() {};
        virtual void OnOOGDraw() {};

        virtual void OnLeftClick(bool up, int x, int y, bool* block) {};
        virtual void OnRightClick(bool up, int x, int y, bool* block) {};
        virtual void OnKey(bool up, BYTE key, LPARAM lParam, bool* block) {};

        virtual void OnChatPacketRecv(BYTE* packet, bool* block) {};
        virtual void OnRealmPacketRecv(BYTE* packet, bool* block) {};
        virtual void OnGamePacketRecv(BYTE* packet, bool* block) {};

        __event void UserInput(const wchar_t* msg, bool fromGame, bool* block);
        virtual void OnUserInput(const wchar_t* msg, bool fromGame, bool* block) {};
        virtual void OnChatMsg(const char* user, const char* msg, bool fromGame, bool* block) {};
};
#

if you don't plan on using all of these, you can just have it return

#

that check is to see if your UI should block further mouse events (like if your UI is above the inventory, you don't want it to pick up items as you click on your UI)

tawny zealot
marble elm
#

lol right, that's what blockEvent is for

tawny zealot
#

i see

#

would be correct paste that entire module class inside my newmodule.h ?

#

how man this is intense

marble elm
#

well do you have your new module set up to inherit from this module class? you can look at ItemMover.h to see an example

#

ItemMover is a Module class, it doesn't define all of these virtual function but the ones that it wants to use

tawny zealot
#

gonna check that one for reference!

marble elm
#

you'll remove the virtual part once it's inside your class

tawny zealot
#
 class Colec : public Module {

    private:
        CellFile* Codex;
        CellFile* Scroll;
        CellFile* Colec1;
        CellFile* Blur1;
        int showColec1;
        int showPerg;
        BOOL pergaOpen;
        vector<wchar_t*> colNames;
        vector<wchar_t*> col2Names;
        vector<wchar_t*> escNames;
        vector<wchar_t*> clockNames;
        vector<wchar_t*> pergaNames;
        vector<wchar_t*> codexNames;

    public:
        static map<std::string, Toggle> Toggles;
        Colec() : Module("Colec") {};

        void OnLoad();
        void LoadConfig();
        void OnKey(bool up, BYTE key, LPARAM lParam, bool* block);
        void OnGameJoin();
        void OnLeftClick(bool up, int x, int y, bool* block);
        void OnDraw();
        void OnOOGDraw();

        void DrawPopup2(wchar_t* colNames, int x, int y);
        void DrawPopup3(wchar_t* colNames, int x, int y);
};

just addedding the missing one should do the work i think.

marble elm
#

yep, looks good. Then inside your OnLeftClick function, you should set the block value like this:

void Colec::OnLeftClick(bool up, int x, int y, bool* block)
{
  if (mouseX > posstartx && mouseX < posstartx + 40 && mouseY > possstartyM - 42 && mouseY < possstartyM && Toggles["Pergamino Nephalem"].state == false)
  {
    Toggles["Pergamino Nephalem"].state = true;
    *block = true;
  }
}
#

or, you can create a new OnClick function and set blockEvent like some of the other modules do

tawny zealot
#

so after a billion of tries, somehow now its working, but im not so sure why, bc, its bloquing perfectly all but, i didnt put any inside the

LONG WINAPI GameWindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{}

im not happy tbh, bc doesnt cout if i dont know why is it working xD, i do actually put the block event-

marble elm
#

if you put *block = true; inside your OnLeftClick function, then it will work without adding anything to the GameWindowEvent but it won't work for overlapping new UIs

#

so if the BH menu is on top of your new UI, the block may not apply correctly (it depends on the order of the modules)

tawny zealot
#

ohhh isee

#

the thing is, since the new module inst a part of the namespace Drawing, im not being able to add it

marble elm
#

if it's not, you can call it using whatever namespace it belongs to (or no namespace at all)

tawny zealot
#

for some weird reason im not being able to addit,

        if (Colec::OnLeftClick(true, mouseX, mouseY, &blockEvent))
            blockEvent = true;
#

sorry for the monkey lenguage but

marble elm
#

you have to create a new function, call it OnClick and make it static:

// Colec.h
 class Colec : public Module {

    private:
        CellFile* Codex;
        CellFile* Scroll;
        CellFile* Colec1;
        CellFile* Blur1;
        int showColec1;
        int showPerg;
        BOOL pergaOpen;
        vector<wchar_t*> colNames;
        vector<wchar_t*> col2Names;
        vector<wchar_t*> escNames;
        vector<wchar_t*> clockNames;
        vector<wchar_t*> pergaNames;
        vector<wchar_t*> codexNames;

    public:
        static map<std::string, Toggle> Toggles;
        Colec() : Module("Colec") {};

        void OnLoad();
        void LoadConfig();
        void OnKey(bool up, BYTE key, LPARAM lParam, bool* block);
        void OnGameJoin();
        void OnLeftClick(bool up, int x, int y, bool* block);
        void OnDraw();
        void OnOOGDraw();

        void DrawPopup2(wchar_t* colNames, int x, int y);
        void DrawPopup3(wchar_t* colNames, int x, int y);

        static bool OnClick(bool up, int x, int y);
};

// Colec.cpp
bool Colec::OnClick(bool up, int x, int y)
{
  if (x > posstartx && x < posstartx + 40 && y > possstartyM - 42 && y < possstartyM)
  {
    return true;
  }

  return false;
}

void Colec::OnLeftClick(bool up, int x, int y, bool* block)
{
  if (x > posstartx && x < posstartx + 40 && y > possstartyM - 42 && y < possstartyM)
  {
    // This toggles from true -> false and false -> true
    Toggles["Pergamino Nephalem"].state = !Toggles["Pergamino Nephalem"].state;
    *block = true;
  }
}

// D2Handlers.cpp
LONG WINAPI GameWindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {

    bool blockEvent = false;
    int mouseX = (*p_D2CLIENT_MouseX);
    int mouseY = (*p_D2CLIENT_MouseY);

    if (uMsg == WM_LBUTTONDOWN) { // <---------------- Here they check left mouse button down
        if (Drawing::Hook::LeftClick(false, mouseX, mouseY))
            blockEvent = true;
        if (Drawing::UI::LeftClick(false, mouseX, mouseY))
            blockEvent = true;
        if (Drawing::StatsDisplay::Click(false, mouseX, mouseY))
            blockEvent = true;
        if (Colec::OnClick(false, mouseX, mouseY)) // <---- Add this to your new module to check if the coordinates are inside your new UI
            blockEvent = true;
        __raise BH::moduleManager->OnLeftClick(false, mouseX, mouseY, &blockEvent);  // <--- This calls OnLeftClick for all modules that have the function
    }
// ...
}
tawny zealot
tawny zealot
#

your wisdom is disgusting 🥰

#

(it works, ty!)

marble elm
#

🎉

tawny zealot
#

@marble elm you guys got issues with the d2gl and new maps crashing on enter?

tawny zealot
#

I'm asking bc i'm having exactly the same issue and still trying to fix it

tawny zealot
#
    for (PresetUnit* preset = room->pPreset; preset; preset = preset->pPresetNext)
    {
        int cellNo = -1;
            if (cellNo == -1 && preset->dwTxtFileNo <= 572)
            {
                ObjectTxt* pObj = D2COMMON_GetObjectTxt(preset->dwTxtFileNo);
                if (pObj)
                    cellNo = pObj->nAutoMap; this part crash my code
            }
}
#

i may have the ObjectTxt struct wrong?

marble elm
#

this is what we have for objecttxt:

struct ObjectTxt
{
    CHAR szName[0x40];                //0x00
    WCHAR wszName[0x40];            //0x40
    CHAR szAnimationToken[3];        //0xC0
    BYTE nSpawnMax;                    //0xC3
    BYTE nSelectable[8];            //0xC4
    DWORD nTrapProb;                //0xCC
    DWORD dwSizeX;                    //0xD0
    DWORD dwSizeY;                    //0xD4
    //BYTE dwFrameCnt[8];            //0xD8    This is what would make sense, however;
    BYTE _1;                        //0xD8    While debugging, dwFrameCnt seems shifted over one byte down. Last frameCnt is one byte less
    DWORD dwFrameCnt[7];            //0xD9 
    BYTE dwFrameCnt7[3];            //0xF5    Last frameCnt is one byte less
    WORD wFrameDelta[8];            //0xF8
    BYTE nCycleAnim[8];                //0x108
    BYTE nLit[8];                    //0x110
    BYTE nBlocksLight[8];            //0x118
    BYTE nHasCollision[8];            //0x120
    BYTE nIsAttackable;                //0x128 -- probably not, see nAttackable
    BYTE nStart[8];                    //0x129
    BYTE nOrderFlag[8];                //0x131
    BYTE nEnvEffect;                //0x139
    BYTE nIsDoor;                    //0x13A
    BYTE nBlocksVis;                //0x13B
    BYTE nOrientation;                //0x13C
    BYTE nTrans;                    //0x13D
    BYTE nPreOperate;                //0x13E
    BYTE nMode[8];                    //0x13F
    BYTE _2;                        //0x147
    DWORD dwXOffset;                //0x148
    DWORD dwYOffset;                //0x14C
    BYTE nDraw;                        //0x150
    BYTE nLayers[16];                //0x151
    BYTE nTotalLayers;                //0x161
    BYTE nXSpace;                    //0x161
    BYTE nAttackable;                //0x162 -- not nYSpace like D2MOO says
    BYTE nRed;                        //0x163
    BYTE nGreen;                    //0x164
    BYTE nBlue;                        //0x165
    BYTE nSubclass;                    //0x166
    int dwNameOffset;                //0x167
    BYTE _3;                        //0x16B
    BYTE nMonsterOK;                //0x16C
    BYTE nOperateRange;                //0x16D
    BYTE nShrineFunction;            //0x16E
    BYTE nAct;                        //0x16F
    BYTE _4;                        //0x170
    BYTE nLockable;                    //0x171
    BYTE nRestore;                    //0x172
    BYTE nRestoreVirgins;            //0x173
    BYTE nSync;                        //0x174
    BYTE nGore;                        //0x175
    BYTE _5;                        //0x176
    DWORD dwParm[8];                //0x177
    BYTE nTgtFX;                    //0x197
    BYTE nTgtFY;                    //0x198
    BYTE nTgtBX;                    //0x199
    BYTE nTgtBY;                    //0x19A
    BYTE nDamage;                    //0x19B
    BYTE nCollisionSubst;            //0x19C
    BYTE _6[2];                        //0x19D
    int dwLeft;                        //0x1A0
    int dwTop;                        //0x1A4
    DWORD dwWidth;                    //0x1A8
    DWORD dwHeight;                    //0x1AC
    BYTE nBeta;                        //0x1B0
    BYTE nInitFn;                    //0x1B1
    BYTE nClientFn;                    //0x1B2
    BYTE nOperateFn;                //0x1B3
    BYTE _7;                        //0x1B4
    BYTE nOverlay;                    //0x1B5
    BYTE nBlockMissile;                //0x1B6
    BYTE nDrawUnder;                //0x1B7
    DWORD dwOpenWarp;                //0x1B8
    DWORD dwAutoMap;                //0x1BC
};
tawny zealot
marble elm
#

shrines are just objects, I have a small struct for objectdata:

struct ObjectData {
    ObjectTxt* pTxt;                //0x00
    union {
        BYTE Type;                    //0x04 (0x0F would be a Exp Shrine)
        BYTE nTrapState;            // Spike trap uses this to keep track of state
        struct {
            BYTE _1 : 7;
            BYTE ChestLocked : 1;
        };
        BYTE nPortalLevel;
    };
    DWORD _2[3];                    //0x08
    DWORD PortalFlags;                // 0x14
    DWORD dwX;                        // 0x18
    DWORD dwY;                        // 0x1C
    DWORD _3[2];                    // 0x20
    char szOwner[0x10];                //0x28
};
#

the byte at 0x04 changes depending on the type of object

#

for shrines, it is the type of the shrine

tawny zealot
marble elm
#

what issue are you having?

tawny zealot
#

well, im not sure why i cannot paste my code, so i need ot send a pic

tawny zealot
tawny zealot
#

that dword shrines its the ID from every shrine objects, now i need something like IF (preset ->dwtxtfileno == 2 && ncode = 0x04 (expshrine)) do {...}

tawny zealot
#

Current one

DWORD Shrines[] = { 2,77,81,83,84,85,86,93,96,97,109,116,120,123,124,133,134,135,136,150,151,164,165,166,167,168,170,172,     173,184,190,191,197,199,200,201,202,206,226,231,232,236,249,260,262,263,264,265,275,276,277,278,279,280,281,282,299,300,301,302,303,319,320,325,343,344,361,414,415,421,422,423,427,428,472,428,472,473,479,483,484,488,491,492,495,497,499,503,509,512,520,521,522 };
UnitAny* pUnit = D2CLIENT_GetPlayerUnit();
for (DWORD S = 0; S < 90; S++)
{
if (preset->dwTxtFileNo == Shrines[S] && pUnit->pObjectData->Type != 0x0F)
cellNo = 310;
if (preset->dwTxtFileNo == Shrines[S] && pUnit->pObjectData->Type == 0x0F)
cellNo = 1501;
}```
#

managed to show all the shrines, but fails on show different the ones of the exp

marble elm
#

I would use a map or set for this, it's a little easier instead of looping over the whole list:

#include <set>

std::set<int> Shrines = { 2,77,81,83,84,85,86,93,96,97,109,116,120,123,124,133,134,135,136,150,151,164,165,166,167,168,170,172,     173,184,190,191,197,199,200,201,202,206,226,231,232,236,249,260,262,263,264,265,275,276,277,278,279,280,281,282,299,300,301,302,303,319,320,325,343,344,361,414,415,421,422,423,427,428,472,428,472,473,479,483,484,488,491,492,495,497,499,503,509,512,520,521,522 };

UnitAny* pUnit = D2CLIENT_GetPlayerUnit();

if (Shrines.find(preset->dwTxtFileNo) != Shrines.end())
{
  if (pUnit->pObjectData->Type != 0x0F) // Set a breakpoint here and check pUnit->pObjectData->Type to see what's actually in it
  {
    cellNo = 310; // Where are these numbers used?
  }
  else
  {
    cellNo = 1501;
  }
}
tawny zealot
#

Son leyes Say that i puta a bp there, how exactly know what's in there?

marble elm
#

you need the object, not the player

#

so, change this: pUnit->pObjectData->Type to preset->pObjectData->Type

#

and once you hit a breakpoint in visual studio, you can just hover your mouse over the name of the variable (preset in this case) and it will show you all the values in that object. You just just go to pObjectData, click the arrow and look inside there

tawny zealot
#

Well i forgot to share the other half of the code what a dumb

#
void Mapinfo::RevealRoom(Room2* room) {
    
    for (PresetUnit* preset = room->pPreset; preset; preset = preset->pPresetNext)
        {
        int cellNo = -1;

        if (preset->dwType == UNIT_OBJECT)
        {
            DWORD Shrines[] = { 2,77,81,83,84,85,86,93,96,97,109,116,120,123,124,133,134,135,136,150,151,164,165,166,167,168,170,172,
                173,184,190,191,197,199,200,201,202,206,226,231,232,236,249,260,262,263,264,265,275,276,277,278,279,280,281,282,299,300,
                301,302,303,319,320,325,343,344,361,414,415,421,422,423,427,428,472,428,472,473,479,483,484,488,491,492,495,497,499,503,
                509,512,520,521,522 };

            UnitAny* pUnit = D2CLIENT_GetPlayerUnit();

            for (DWORD S = 0; S < 90; S++)
            {
                if (preset->dwTxtFileNo == Shrines[S] && pUnit->pObjectData->Type != 0x0F)
                    cellNo = 310;
                if (preset->dwTxtFileNo == Shrines[S] && pUnit->pObjectData->Type == 0x0F)
                    cellNo = 1501;
                if (preset->dwTxtFileNo == Shrines[S] && pUnit->pObjectData->Type == 0x0E)
                    cellNo = 1499;
                if (preset->dwTxtFileNo == Shrines[S] && pUnit->pObjectData->Type == 0x0D)
                    cellNo = 1498;
                if (preset->dwTxtFileNo == Shrines[S] && pUnit->pObjectData->Type == 0x0C)
                    cellNo = 1497;
                if (preset->dwTxtFileNo == Shrines[S] && pUnit->pObjectData->Type == 0x0B)
                    cellNo = 1496;
                if (preset->dwTxtFileNo == Shrines[S] && pUnit->pObjectData->Type == 0x0A)
                    cellNo = 1495;
            }
        }```
marble elm
#

yeah, we don't allow the word maphack in this discord, so you'll have to change it

#

but I still don't quite see where the cellNo is actually used and you don't need the player unit at all here, remove UnitAny* pUnit = D2CLIENT_GetPlayerUnit(); and change everywhere that says pUnit to preset

tawny zealot
#

Just yo clarify i'm doing this just to learn stuff not plan to use this in any oficial server whatsoever.

marble elm
#

yeah I understand, it automatically removes posts though

tawny zealot
#

in my head its simple but i dont know how to put it in code zzz

marble elm
#

what's the struct of PresetUnit* in this case?

tawny zealot
#

struct PresetUnit {
DWORD _1; //0x00
DWORD dwTxtFileNo; //0x04
DWORD dwPosX; //0x08
PresetUnit* pPresetNext; //0x0C
DWORD _3; //0x10
DWORD dwType; //0x14
DWORD dwPosY; //0x18
};

marble elm
#

why are you using Room2* instead of Room1*?

#

I see, you're iterating over preset objects in the room. Well you can just iterate over all units in the room and check if it's an object of shrine type, you don't have to limit it to just presets

tawny zealot
#

okey

#

when i ask in the pk, necrolis said, that, shrine info is stored here pUnit->pObjectData(+0x14)->nShrine(+4)

#

thats why i was trying to compare it with the punit

#

but 100% sure im missing something

marble elm
#
#include <set>

// Keep this outside the function otherwise you waste time allocating it every call of the function
const std::set<int> Shrines = { 2,77,81,83,84,85,86,93,96,97,109,116,120,123,124,133,134,135,136,150,151,164,165,166,167,168,170,172,     173,184,190,191,197,199,200,201,202,206,226,231,232,236,249,260,262,263,264,265,275,276,277,278,279,280,281,282,299,300,301,302,303,319,320,325,343,344,361,414,415,421,422,423,427,428,472,428,472,473,479,483,484,488,491,492,495,497,499,503,509,512,520,521,522 };

void Mapinfo::RevealRoom(Room2* room)
{    
    Room1* pRoom = room->pRoom1;
    if (pRoom == NULL)
         return;

    for (UnitAny* pUnit = pRoom->pUnitFirst; pUnit; pUnit = pUnit->pRoomNext)
    {
        int cellNo = -1;
        if (pUnit->dwType == UNIT_OBJECT && pUnit->pObjectData != NULL && Shrines.find(pUnit->dwTxtFileNo) != Shrines.end())
        {
            switch(pUnit->pObjectData->Type)
            {
                case 0x0F:
                    cellNo = 1501;
                    break;
                case 0x0E:
                    cellNo = 1499;
                    break;
                case 0x0D:
                    cellNo = 1498;
                    break;
                case 0x0C:
                    cellNo = 1497;
                    break;
                case 0x0B:
                    cellNo = 1496;
                    break;
                case 0x0A:
                    cellNo = 1495;
                    break;
                default:
                    cellNo = 310;
                    break;
            }
        }

        // ... Continue and draw cellNo
    }
#

the issue was that you were checking the player unit, but the player doesn't have pObjectData because it's not an object. You can only check pObjectData on UNIT_OBJECT type units, so get all the units in the room instead of just the presets, then check if it's an object and it's one of the shrine objects, then draw the minimap icon

tawny zealot
#

Okey gonnna try it right when get home i think i get the general concepto that the important

#

Basically we are iterating from the first unit to the next room

marble elm
#

yeah, each room has a linked list of all the units in that room

tawny zealot
#
void MapInfo::RevealRoom(Room2* room) {
    //Grabs all the preset units in room.
    
    Room1* pRoom = room->pRoom1;
    if (pRoom == NULL)
    return;

    for (UnitAny* pUnit = pRoom->pUnitFirst; pUnit; pUnit = pUnit->pRoomNext)
    {
                PresetUnit* preset = room->pPreset; preset; 

                int cellNo = -1;
                if (pUnit->dwType == UNIT_OBJECT && pUnit->pObjectData != NULL && Shrines.find(pUnit->dwTxtFileNo) != Shrines.end())
                {
                    switch (pUnit->pObjectData->Type)
                    {
                    case 0x0F:
                        cellNo = 1501;
                        break;
                    case 0x0E:
                        cellNo = 1499;
                        break;
                    case 0x0D:
                        cellNo = 1498;
                        break;
                    case 0x0C:
                        cellNo = 1497;
                        break;
                    case 0x0B:
                        cellNo = 1496;
                        break;
                    case 0x0A:
                        cellNo = 1495;
                        break;
                    default:
                        cellNo = 310;
                        break;
                    }
                }

                //Draw the cell if wanted.
                if ((cellNo > 0) && (cellNo < 1700))
                {
                    AutomapCell* cell = D2CLIENT_NewAutomapCell();

                    cell->nCellNo = cellNo;
                    int x = (preset->dwPosX + (room->dwPosX * 5));
                    int y = (preset->dwPosY + (room->dwPosY * 5));
                    cell->xPixel = (((x - y) * 16) / 10) + 1;
                    cell->yPixel = (((y + x) * 8) / 10) - 3;

                    D2CLIENT_AddAutomapCell(cell, &((*p_D2CLIENT_AutomapLayer)->pObjects));
                }
    }

}
tawny zealot
#

maybe its bc i dont have any proom1 in room1

marble elm
#

you'll have to learn to create breakpoints and step through it to see where it's going wrong

#

most likely the issue is that you're not getting the preset object anymore

#

you'll need to put the previous loop back in at the top:

void Mapinfo::RevealRoom(Room2* room)
{    
    Room1* pRoom = room->pRoom1;
    if (pRoom == NULL)
         return;
for (PresetUnit* preset = room->pPreset; preset; preset = preset->pPresetNext)
{
    // Now we have the correct info inside preset

    for (UnitAny* pUnit = pRoom->pUnitFirst; pUnit; pUnit = pUnit->pRoomNext)
    {
        int cellNo = -1;
        // Check if this is the same object as the preset
        if (pUnit->dwTxtFileNo == preset->dwTxtFileNo && pUnit->dwType == UNIT_OBJECT && pUnit->pObjectData != NULL && Shrines.find(pUnit->dwTxtFileNo) != Shrines.end())
        {
            switch(pUnit->pObjectData->Type)
            {
                case 0x0F:
                    cellNo = 1501;
                    break;
                case 0x0E:
                    cellNo = 1499;
                    break;
                case 0x0D:
                    cellNo = 1498;
                    break;
                case 0x0C:
                    cellNo = 1497;
                    break;
                case 0x0B:
                    cellNo = 1496;
                    break;
                case 0x0A:
                    cellNo = 1495;
                    break;
                default:
                    cellNo = 310;
                    break;
            }
        }

        // if you found one of the shrine objects, break out of this loop
        if (cellNo != -1)
            break;
    }
    // ... Continue and draw cellNo
}
tawny zealot
#

it still doesnt work, it stop drawing any kind of shrine, but HEY enough is enough, i learned lots of things today, but its clear as daylight that im not ready for this yet!.
so dont worry! , eventually when i be more used to this will go back for a payback.
Pretty much thx as always.

tawny zealot
#

hey @marble elm i been wondering, is it too insane hard a piece of code to increase the lvltypes by 1 ? my knowledge its basic for now so, if its too deep gonna skip it for the mean time.

marble elm
#

it's a bit complicated but you have to intercept the call at d2common+0x6CEF4 as well as the ranges in the loop at d2common+0x6CFF3