#Loading and Showing a Custom celfile.
1 messages · Page 1 of 1 (latest)
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);
pretty much tankyou this is exactly what i was looking for.
gonna show some results soon :d
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.
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);
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
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
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.
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 ?
progress so far
Any clue, why im not being able to hide 4digits items on the mh?, its says BLOCKED but shows anyway.
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;
THANK YOU, it was exactly that!
sadly, now i jsut realize i can hide only 4 code items XD ffs
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
Will check that thank You!
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;
Didn't I already send exactly this in a different thread?
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
I been using this method to load some details when You hoover the mouse over some monsters but i encountered an issue with the memory if i hoover the mouse in that unit, the memory stats going up until the Game crashes, hoy do You guys fixed the memory issues?
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
Thx! that fixed the issue!.
Any pointing direction on how to make the exp shrines with a cellno custom? Instead of the regular one?
@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?
@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);
}
thank you, i work with this, ty
looks awesome, any updates?
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
This does look awesome.
finally found the error, this is the progress on the new UI so far
Menu of Colectibles
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.
but, what im confuse is ,should i put the LONG WINAP inside the module of my new window? in another place? isnt matter?
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)
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.
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
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
}
// ...
}
i need to add inside the d2handelrs right?
if (Drawing::NEWMODULE::LeftClick(false, mouseX, mouseY))
or, inside of my new module too ?
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)
thats exactly whats happening xd
lol right, that's what blockEvent is for
i see
would be correct paste that entire module class inside my newmodule.h ?
how man this is intense
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
gonna check that one for reference!
you'll remove the virtual part once it's inside your class
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.
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
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-
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)
ohhh isee
the thing is, since the new module inst a part of the namespace Drawing, im not being able to add it
if it's not, you can call it using whatever namespace it belongs to (or no namespace at all)
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
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
}
// ...
}

🎉
@marble elm you guys got issues with the d2gl and new maps crashing on enter?
I'm asking bc i'm having exactly the same issue and still trying to fix it
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?
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
};
Allright it's different from d2moo too, gonnna test with this one.
Do You have the struct for shrines bye any chance? I'm trying to make shrines with different icons depending of their ncode
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
THX, amazing as usual, i got an idea on how to do it but i'm not being able to complete it, any advise?
what issue are you having?
well, im not sure why i cannot paste my code, so i need ot send a pic
my actual problem is, im missing the part where i say the code, this is a EXP shrine, then draw with this cellNo
that dword shrines its the ID from every shrine objects, now i need something like IF (preset ->dwtxtfileno == 2 && ncode = 0x04 (expshrine)) do {...}
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
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;
}
}
Cellno 310 it's used in the maximap.dc6 that controles all the graphics for the minimap
Son leyes Say that i puta a bp there, how exactly know what's in there?
right but just setting your variable cellNo doesn't do anything by itself, how are you telling the minimap to draw this cellNo?
Also, from your code you are getting the Player Unit and checking the pUnit->pObjectData?
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
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;
}
}```
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
Just yo clarify i'm doing this just to learn stuff not plan to use this in any oficial server whatsoever.
yeah I understand, it automatically removes posts though
im afraid that wont work, bc pobjectdata isnt a member of presetunit
in my head its simple but i dont know how to put it in code zzz
what's the struct of PresetUnit* in this case?
struct PresetUnit {
DWORD _1; //0x00
DWORD dwTxtFileNo; //0x04
DWORD dwPosX; //0x08
PresetUnit* pPresetNext; //0x0C
DWORD _3; //0x10
DWORD dwType; //0x14
DWORD dwPosY; //0x18
};
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
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
#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
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
yeah, each room has a linked list of all the units in that room
im afraid, it doesnt work
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));
}
}
}
maybe its bc i dont have any proom1 in room1
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
}
thats absolutly right, i been trying to learn how to debug my own code but im missing the part where i need to link my actual c code with the assamble shown in the debugger
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.
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.
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