Giter Club home page Giter Club logo

d2moo's People

Contributors

ajtos avatar audriusbutkevicius avatar eezstreet avatar eugenekolo avatar galaxyhaxz avatar iamtrial avatar lectem avatar mw95 avatar necrolis avatar nooperation avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

d2moo's Issues

typos

//D2Game.0x6FCD0840
int32_t __fastcall AITTACTICS_WalkCloseToUnit(D2GameStrc* pGame, D2UnitStrc* pUnit, int32_t nMaxDistance);
//D2Game.0x6FCD0D00
int32_t __fastcall AITTACTICS_RunCloseToTargetUnit(D2GameStrc* pGame, D2UnitStrc* pUnit, D2UnitStrc* pTarget, int32_t nMaxDistance);

spelling error with double T

//D2Common.0x6FDB9C10 (#10166)
D2ActiveRoomStrc* __stdcall PATH_GetRoom(D2DynamicPathStrc* pDynamicPath);
//D2Common.0x6FDB9C10 (#11265)
int __stdcall STATLIST_GetSkillId(D2StatListStrc* pStatList);

duplicate address listed

[D2Game] Error PLRTRADE_HandleCubeInteraction

if (pCubeMainTxtRecord->bOp = 0)-> the variable (bOpConditionsFulfilled) is always = false.
while the function below has the condition variable (bOpConditionsFulfilled >= 1)

repair

....
switch (pCubeMainTxtRecord->nOp)
{
     ...
     default:
       break;
}

to

....
switch (pCubeMainTxtRecord->nOp)
{
     ...
    default:
    {
          bOpConditionsFulfilled = true;
          break;
    }
}

DRLGPRESET_ParseDS1File does not control MonPresetId properly

From Conqueror on the PK discord:

There are more bugs I discovered in 3 DRLG functions
//D2Common.0x6FD85A10
DRLGPRESET_ParseDS1File
Doesn't control MonPreset ID properly
The Objects I rewrote it completely to implement Extended Object in my Mod, this works fine, still adding more than 150 objects per act. So for this part the function is perfect.
In MonPresets the error is that it does not correctly calculate the ID for SuperUniques, NPC and Champion / Unique monsters

Bugs already present in the original game

The original game as a lot of bugs, and we need to decide what to do about it.
Since D2MOO's main objective is to implement the vanilla game, we must keep the code of those bugs, and document them.
We could probably still fix them, and use a macro for such code to enable/disable fixes.

I'm adding a new Label for bugs reported that might have an impact to modders, but were already present in the original game, thus not due to D2MOO.

Such bugs must be both labeled and reference this issue.

//D2Game.0x6FC90010

Need to capture pNextItem before removing i (Current item) otherwise INVENTORY_GetNextItem(i) will return nullptr and terminate the loop prematurely

	D2ClientStrc* pClient = SUNIT_GetClient(pUnit);
	if (pUnit->pInventory)
	{
		D2UnitStrc* pNext = nullptr;
		for (D2UnitStrc* i = D2COMMON_INVENTORY_GetFirstItem(pUnit->pInventory); i; i = pNext)
		{
			D2UnitStrc* pCheckedItem = D2COMMON_INVENTORY_UnitIsItem(i);
			pNext = D2COMMON_INVENTORY_GetNextItem(i);
			if (pCheckedItem && ITEMS_GetInvPage(pCheckedItem) == INVPAGE_CUBE)
			{
				D2COMMON_ITEMS_SetItemCell(pCheckedItem, INVPAGE_CUBE);
				D2GAME_SCMD_UpdateItem(pClient, pUnit, pCheckedItem, ITEMCMDFLAG_DELETE);
				D2GAME_ITEMMOD_RemoveItem(pGame, pUnit, pCheckedItem, 0);
			}
		}
	}

DRLGPRESET_BuildPresetArea and DRLGWARP_GetWaypointRoomExFromLevel broken

From Conqueror on the PK discord:

//D2Common.0x6FD87760
DRLGPRESET_BuildPresetArea

//D2Common.0x6FD78C10
DRLGWARP_GetWaypointRoomExFromLevel

These 2 functions generate kernel errors
I do not have more details on these functions.

21:45:52.872 Address Frame Module Return Stack
21:45:52.872 75ec1c3b 0019f908 KERNEL32.DLL KERNEL32!InterlockedDecrement+000B [omap]
21:45:52.872 6f647faf 00000000 D2Common.dll D2Common!Ordinal10022+1AAF [omap]
21:45:52.872 Stack walk error: 299
21:45:52.872 2 frames dumped

UNITS_SetAnimStartFrame frame error

D2Common.0x6FDBE510 (#10349)
This function doesn't correctly calculate the frame count for Players, Monsters & objects.

  • Stops Objects animations.
  • Apparently on monsters it works fine, but with too low movement speed.
  • Players receive constant blinks in animations and running speed equals walking speed.

The error log periodically marks this on each frame (which is obviously an error in that sense):

(ptCelContext->dwCel < ptGfxBlock->bNumCelsPerDir)
Couldn't get cel handle in for:(null) TYPE:0 CLASS:1 MODE:3 FILE:C:\projects\D2\head\Diablo2\Source\D2CMP\Src\SpriteCache.cpp LINE:1833
(ptCelContext->dwCel < ptGfxBlock->bNumCelsPerDir)
(ptCelContext->dwCel < ptGfxBlock->bNumCelsPerDir)
[...]
...

MONSTER_InitializeStatsAndSkills

void __fastcall MONSTER_InitializeStatsAndSkills(D2GameStrc* pGame, D2ActiveRoomStrc* pRoom, D2UnitStrc* pUnit, D2MonRegDataStrc* pMonRegData)
{
    if (!pUnit || pUnit->dwUnitType != UNIT_PLAYER || !pUnit->pMonsterData || !pUnit->pMonsterData->pMonstatsTxt)
    {
        return;
    }

should be UNIT_MONSTER

Improve the debugger !

Currently the debugger only displays a bit of information related to the current game and units animation data.

Any suggestion / work on the debugger would be more than welcome, and I believe a great way to discover the codebase.
The idea is that we should be able to use the debugger to help with the reverse engineering, but also for modders !

image

Ideas

  • "Cheat codes"
    • Invincible character
    • Spawn any unit
      • SuperUnique monster by name✅ / id
      • Normal monster by name✅ / id
      • unique item by name
    • Kill any unit
  • Stats debugger
  • Item debugger
  • Units
    • Display class name
    • Path visualization
    • ...
  • Collision visualization
  • Warp level###
  • reset quests state and waypoints
  • TreasureClass and loot emulation
  • mouse target info panel

Long(er) term

  • Support multi-game (server)
  • Hook into D2Gfx / D2Client to display things directly in-game
    • Or simply create a transparent window on top of the game ?
  • Better detection of the game display window by loading the handle from D2Client or D2Gfx (Currently filters using the window name "Diablo II". Won't work with mods

[D2Game] Error SUNIT_CreatePresetUnit

This function is missing UNIT_TILE and caves don't seem to work.
Origin:

else if (nUnitType == UNIT_ITEM)
{
   ...
}

Fix:

else if (nUnitType == UNIT_ITEM)
{
   ...
}
else if ((nUnitType - 1) > 0)
{
    pUnit = SUNIT_AllocUnitData(nUnitType, nClassId, nX, nY, pGame, pRoom, 1, nMode, 0);
}

`MONSTERREGION_InitializeAll` Uses Incorrect Sizes

The following 2 calls to memcpy in MONSTERREGION_InitializeAll use the incorrect size, as the monster id's are a uint16_t but memcpy takes a byte count:

memcpy(monsterIds, pLevelsTxtRecord->wNMon, pLevelsTxtRecord->nNumNMon);

memcpy(monsterIds, pLevelsTxtRecord->wMon, pLevelsTxtRecord->nNumNormMon);

these calls should be pLevelsTxtRecord->nNumNMon * sizeof(uint16_t) and pLevelsTxtRecord->nNumNormMon * sizeof(uint16_t) respectively, else std::copy should be used in place of memcpy.

Additionally the default array initializer of monsterIds does not exist in the assembly code (apart from being redundant due to the memcpy). the compiler should elide this, but there may be scenarios where a pointless memset may be done.

MISSILE_SetDamageStats: Burn length might override fire length

This was already present in the original game (#14 ).

MISSILE_SetDamageStats ( D2Common.0x6FDBB2E0 (#11218)) will try to set the STAT_FIRELENGTH stat twice:

STATLIST_SetUnitStat(pMissile, STAT_FIRELENGTH, pMissileDamageData->nFireLength, 0);

STATLIST_SetUnitStat(pMissile, STAT_FIRELENGTH, pMissileDamageData->nBurnLength, 0);

In practice it most likely never happens though, as it is only an issue if both nFireLength and nBurnLength are not 0, since otherwise STATLIST_SetUnitStat does not actually set the value.

SUnitEvent.h structs members need cleaning/renaming

Events timers (from Event.h) and SUnitEvent.h used to be mixed up.
This needs cleaning up and clarification.

I think historically there were confusions between D2EventTimerStrc (which uses the somewhat badly named D2C_UnitEventCallbackTypes) and D2UnitEventStrc which says uint8_t nTimerType; //0x00 D2C_UnitEventCallbackTypes but actually should be using D2C_EventTypes according to what I'm seeing (D2Game.0x6FD156A0 calls SUNITEVENT_AllocTimer and sets nTimerType to a value from D2ItemStatCostTxt::wItemEvent from D2SkillsTxt::wAuraEvent which are both indices into events.txt...

events.txt:

event	*desc
hitbymissile	hit by a missile
damagedinmelee	damaged in melee
damagedbymissile	damaged by missile
attackedinmelee	melee attack atttempt
doactive	do active state skill
domeleedamage	do damage in melee
domissiledamage	do missile damage
domeleeattack	do melee attack
domissileattack	do missile attack
kill	killed something
killed	killed by something
absorbdamage	dealt damage
levelup	gain a level

This confirms SUNITEVENT_EventFunc_Handler event is an id from events.Txt (thus D2C_EventTypes)...

Would you be ok with fixing the comment and name of the field in D2UnitEventStrc ? And also documenting value 13 with above information from necrolis.

Originally posted by @Lectem in #129 (comment)

          @Lectem TBH the whole of SUnitEvent.h has poor naming since nothing about it relates to timers (its all trigger based). Might be best to shift that renaming to its own pull request.

Originally posted by @Necrolis in #129 (comment)

I checked the latest changes with StatList, I have bad news

The errors remain exactly the same.

This was added that LoadItemStatCost does not read the OPs 0/8/9/10/11 that correspond to the same OPs that fail with the STATLIST_UpdateUnitNewStat function and STATLIST_InsertStatModOrFail

(As a side note, LoadISC from the pre-bulk changes version worked fine (it's the one I'm using))

Sorry for my slowness, I still haven't finished rearranging all my code, it's very long and I didn't check even half of the D2Moo functions (corresponding to ItemsFunctions, DRLG, Path and Missiles).
The good news is that I have made several fixes to the Path and Skills functions to make them work much better (and in some cases, to work directly).

And with Statlist I can't fix those errors.

I also don't have enough time to repair all the maximum details, but I would like to get it since it is very useful to handle all the statlists to try to expand them a bit with some custom code (in addition to expanding the ISC OPs which are also very useful)

DRLGPRESET_SpawnHardcodedPresetUnits is broken

Flavie (and probably other harcoded units) is currently misplaced. She is spawned near the Den of Evil.
While waiting for a fix, D2Moo's DRLGPRESET_SpawnHardcodedPresetUnits has been detoured to call the original D2Common.dll function in c324972.

DUNGEON_ChangeClientRoom has been marked as broken since it calls DRLGPRESET_SpawnHardcodedPresetUnits, but once it is fixed we can put it back as checked.

[D2GAME] Error sub_6FC84D40

i am testing with function sub_6FC84D40, to send a notice to join the room, and it error like this

Screenshot007.jpg

I fixed it like this

int i = 0;
while (pPacket5A->szText[i])
{
if (++i >= 16)
return;
}
GAME_SendPacketToAllConnectedClients(pGame, j_D2GAME_SendPacket0x5A_6FC3DEC0, pPacket5A);

the following functions work perfectly:

GAME_SendPacketToAllConnectedClients
j_D2GAME_SendPacket0x5A_6FC3DEC0
D2GAME_PACKETS_SendPacket0x5A_6FC3DEC0

D2Game.0x6FCBB6C0

D2UnitStrc* __fastcall SUNIT_AllocUnitData(int32_t nUnitType, int32_t nClassId, int32_t nX, int32_t nY, D2GameStrc* pGame, D2ActiveRoomStrc* pRoom, char a7, int32_t nMode, DWORD a3)
{
...
  switch (nUnitGUID)
    {
    case UNIT_PLAYER:
    {
    ...
}

should be nUnitType

Use `const char*` as param to FnLeaveGame as it is indeed a null terminated string.

          Although `D2CharacterPreviewInfoStrc* pPreviewInfoStrc` is correct from a memory layout, the parameter is a `const char*` as the data is treated as a variable length text string with a nil terminator (you can also see this from the func that fills it out in pClient).

The experience args are also likely a spanned uint64_t rather than two uint32_t's, though @Lectem will likely need to weigh in if we want to aggregate them back to uint64_t or not.

Originally posted by @Necrolis in #157 (comment)

Clarify how to integrate D2Moo for modders

Right now I did not choose the way we want to support mods for the D2Moo integration.
In the long term, I think the best option is to use a fork of D2Moo, but it's not necessarily simple to migrate from an existing mod (for example using D2Template) as you would have 2 different patching methods.

In my opinion this should be a discussion with other modders / community, so I listed a few ideas, their advantages and drawbacks in the following table.

Please give your opinion!

D2MOO as Method Integration with existing mod Ease of update Can share includes Compatible with D2Template Can easily change implementation of an ordinal Mod redistribution Can contribute/share easily (bugs) Is own project bugged or is it D2Moo?
subproject Configure CMake to use your own patch files and .def medium easy only unpatched +/- yes, but only from the original dll pov D2Moo launcher + .dlls yes easy to know
subproject Import D2MOO .dlls and patch the functions you want easy easy only unpatched yes yes, but only from the original dll pov Whatever you already had yes easy to know
subproject Link D2Moo statically medium easy only unpatched yes yes, but only from the original dll pov Whatever you already had yes mostly easy to know
fork Fork D2Moo and work directly on it needs some transition (start by importing dll?) easy, might need resolve all +/- yes D2Moo launcher + .dlls (OR simply .dlls if we use forwarding DLLs for unpatched ordinals) yes just need to change branch
source Copy paste (please don't) "easy" but leads to lot of issues hard copy-paste... yes depends on your project patching facilities ? no no

sub_6FCFEDD0

int32_t __fastcall sub_6FCFEDD0(D2GameStrc* pGame, D2UnitStrc* pUnit, D2UnitStrc* pTarget, int32_t nTimeout, int32_t nSkillId, int32_t nSkillLevel)
{
   ...
    const int32_t nRange = SKILLS_GetProgressiveSkillMissileId(pUnit, nSkillId);
    if (nRange <= 0)
    {
        return 1;
    }

    D2MissileStrc missileParams = {};
    missileParams.nMissile = nRange;
    missileParams.nRange = nTimeout;```

Naming for nRange seems out of place

Choose better names for D2RoomStrc and D2RoomExStrc

Currently those names are not very practical.

Information

Possible names

For D2RoomExStrc:

  • D2RoomStrc
    • Could cause confusion with previous names
    • Probably the name used by the original code since an assert string gave the following: !DrlgTestClient (DrlgRoomGetDrlg (ptRoom)), and Drlg is the prefix for all functions related to DRLG.
  • D2DrlgRoomStrc
  • D2StaticRoomStrc
  • ?

For D2RoomStrc:

  • D2UnitRoomStrc
  • D2ActiveRoomStrc
  • D2TiledRoomStrc
  • D2DynamicRoomStrc
  • ?

News

D2Common.0x6FDB64A0 and LoadItemStatCost Now they seem to work properly, however the STATLIST_InsertStatModOrFail_6FDB7690 and D2Common.0x6FDB5830 functions are not working properly.

STATLIST_InsertStatModOrFail_6FDB7690 it's wrong in some ISC, example: Cold Skill Damage %
D2Common.0x6FDB5830 it has a problem with the Auras. The auras of nearby friendly units do not share it with other friendly units (disabling it works perfectly).

Error Check TYPE_ITEM in Func (ITEMS_GetRunesTxtRecordFromItem)

Error:

if (pItem->dwUnitType != UNIT_ITEM || !pItem->pItemData || pItem->pItemData->dwQualityNo < ITEMQUAL_MAGIC || pItem->pItemData->dwQualityNo > ITEMQUAL_TEMPERED)

Fix:

if (pItem->dwUnitType == UNIT_ITEM || pItem->pItemData || pItem->pItemData->dwQualityNo < ITEMQUAL_MAGIC || pItem->pItemData->dwQualityNo > ITEMQUAL_TEMPERED)

Minion monster AI Param `att1/att2?` not working

This was already present in the original game (#14 ).

As can be seen in void __fastcall AITHINK_Fn116_Minion(D2GameStrc* pGame, D2UnitStrc* pUnit, D2AiTickParamStrc* pAiTickParam), the param used to determine if attack 1 or 2 is used is 1 instead of 4 as described by MonAi.txt.

It was confirmed this bug was present in the original game in v1.10f.

See the code here :

if ((ITEMS_RollRandomNumber(&pUnit->pSeed) % 100) >= pAiTickParam->pMonstatsTxt->wAiParam[1][pGame->nDifficulty])
{
AITACTICS_ChangeModeAndTargetUnit(pGame, pUnit, MONMODE_ATTACK1, pTarget);
}
else
{
AITACTICS_ChangeModeAndTargetUnit(pGame, pUnit, MONMODE_ATTACK2, pTarget);
}

Result of the massive testing of StatList Functions

I did an exhaustive verification of all the code related to StatList (basically because my intention is to expand some sections, but before doing so I must verify that all the vanilla code works perfectly).

The testing was carried out in 3 phases:

  • I checked all the functions together in the same HOOK (Exactly the same from the D2Moo code). The result was bad (obviously) since there were endless errors when starting a game. After this result I started the second phase.
  • I checked the code from the latest D2Moo update (with the massive changes) and compared it to the code I used previously (with the first version). In this phase I rechecked all the code with its changes, and the result was exactly the same. Negative, only this time I noticed corrected functions and functions with helpers (which did not exist before). For this reason, when exposing phase 3 of my tests, I included a partial result when using the Helper functions, but I am not 100% sure of their correct operation, however 50% of them work well at first glance. And regarding the rest of the functions, I did a third test to analyze them one by one.
  • In this phase, I did an individual test and changed the syntax of some functions (for my convenience, but the result is theoretically the same). This phase is the most extensive and consists of trying to repair what I can and report everything I got (with my corrections).
    Here I got the following result:

  • 29 Wrong Functions (Short functions that depend on these long functions I don't consider "wrong" because they are literally the same function.)

  • 77 functions that work fine (within my limited, but extreme tests).

  • A function I could not verify in any way that I have no idea where or when it is executed (in D2Game and D2Client).

Also, I proposed some names for the unknown functions and some changes to other functions (although the latter is not important). And, Most of the improvements, I made were control for IFs (if you use pUnit->pStatListEx I added if pUnit and other things like that). I did this because some functions just fixed like this

I clarify that there are also functions that internally call the functions that "do not work", this is because I did a complete stability test, I added vanilla PTRS in each function and I was verifying one by one (its structure, loops and other characteristics). That is why there are functions that "work". This is an attempt to isolate the error.

And one last clarification regarding the Names: I mean "new name" with respect to the first name they had, some I updated them with the Name you gave them and others I invented them.

Work Functions:

  1. 10470 - STATLIST_AllocStatList
  2. 10471 - STATLIST_GetOwnerType (The result if pUnit is NULL is 6) (In D2Game it is common to use these values)
  3. 10472 - STATLIST_GetOwnerGUID (The result if pUnit is NULL is -1 not 0) (In D2Game it is common to use these values)
  4. 11304 - STATLIST_GetBaseStatsCount
  5. 11305 - STATLIST_GetFullStatsCountFromUnit
  6. 10478 - STATLIST_SetState
  7. 10479 - STATLIST_GetState
  8. 10528 - STATLIST_SetExpireFrame
  9. 10529 - STATLIST_GetExpireFrame
  10. 10467 - STATLIST_GetStatFromStatListByStatID
  11. 11264 - STATLIST_SetSkillId
  12. 11265 - STATLIST_GetSkillId
  13. 11266 - STATLIST_SetSkillLevel
  14. 11267 - STATLIST_GetSkillLevel
  15. 10534 - (NEW NAME) STATLIST_SetFlag100Unknown
  16. 10530 - STATLIST_CheckStatlistFlagDMGRed
  17. 10477 - STATLIST_SetStatRemoveCallback
  18. 10482 - STATLIST_GetStatListFromFlag (Improvement FLAG change (nFlag & pStatList->dwFlags) to (pStatList->dwFlags & nFlag))
  19. 10481 - STATLIST_GetStatListFromUnitAndFlag (Improvement control IF FLAG change (nFlag & pStatList->dwFlags) to (pStatList->dwFlags & nFlag))
  20. 10484 - STATLIST_GetStatListFromUnitStateAndFlag (Improvement control if and Flags change (nFlag & pStatList->dwFlags) to (pStatList->dwFlags & nFlag))
  21. 10535 - STATLIST_GetOwner (Corrected flag (pUnit->pStatListEx->dwFlags & STATLIST_DYNAMIC) to ~(pUnit->pStatListEx->dwFlags & STATLIST_DYNAMIC)) - In this function you can notice the error if it is patched as it is extracted from D2Moo, the Weapons do not add the damage to the character until you position the cursor over it, with this improvement this is corrected.
  22. 10512 - (New Name) STATLIST_FindStatAndCallBack (In this function I was especially careful with the order of the control if, otherwise it did not work - if (pUnit1 && pUnit1->pStatListEx && STATLIST_IsExtended(pUnit1->pStatListEx) && !STATLIST_FindStatFromArrayID(&pUnit1->pStatListEx->ModStats, nStatId << 16))
  23. 10511 - STATLIST_FreeModStats
  24. 11268 - STATLIST_GetFullStatsDataFromUnit
  25. 11243 - STATLIST_GetBaseStatsData
  26. 10519 - 10520 - STATLIST_GetUnitStatUnsigned - STATLIST_GetUnitStatSigned (Theoretically they are exactly the same functions. Since Blizzard uses 10519 to extract 80% of the Game Stats (including those that can have negative numbers like Resistances in Hell), I removed the return uint32_t and left it int (the old type) because int casts as uint32_t, int32_t , int8_t int16_t, uint8_t and uint16_t alike. Without this change these functions do not work properly).
  27. 10532 - (New Name) STATLIST_GetStatUnsigned_Layer0
  28. 11248 - (New Name) STATLIST_GetUnitStatInLayer0Control
  29. 10564 - 10565 - 10566 - 10567 - 10568 - 10569 - 10570 - 10571 - 10572 - STATLIST_GetMaxLifeFromUnit - STATLIST_GetMaxManaFromUnit - STATLIST_GetMaxStaminaFromUnit - STATLIST_GetMaxDurabilityFromUnit - STATLIST_GetMaxDamageFromUnit - STATLIST_GetMinDamageFromUnit - STATLIST_GetMaxThrowDamageFromUnit - STATLIST_GetMinThrowDamageFromUnit - STATLIST_GetDefenseFromUnit (these depend solely on 11248)
  30. 10466 - STATLIST_GetStatValue
  31. 11269 - STATLIST_CopyStatsToBuffer
  32. 11270 - (New Name) STATLIST_CheckAndCopyStatToBufferEX (Improvement Control IF)
  33. 10480 - STATLIST_GetStatListFromUnitAndState (Improvement control IF)
  34. 10563 - STATLIST_AreUnitsAligned (Improvement control IF)
  35. 10562 - STATLIST_GetUnitAlignment
  36. 10521 - STATLIST_GetUnitBaseStat (Improvement Control IF)
  37. 10522 - STATLIST_GetUnitStatBonus (Improvement Control IF and Simplify)
  38. 10527 - STATLIST_FreeStatListEx (Improvement Control IF)
  39. 10526 - STATLIST_AllocStatListEx (Improvement Control IF)
  40. 10515 - (New Name) STATLIST_CleanStatesAndStatListEx (Improvement control IF and Clean)
  41. 10485 - STATLIST_FreeStatList
  42. 10469 - (New Name) STATLIST_Delete (Improvement control IF and Clean)
  43. 10516 - (New Name) STATLIST_UpdateStatListsExpiration (Improvement Control IF)
  44. 10483 - STATLIST_GetStatListFromUnitStateOrFlag (Improvement Control IF)
  45. 0x6FDB8A90 - (New Name) STATLIST_STATES_GetStatFlags
  46. 0x6FDB8AC0 - (New Name) STATLIST_STATES_GetListGfxFlags
  47. 0x6FDB6300 - STATLIST_FindStatIndex (Improvement Control IF)
  48. 0x6FDB6920 - STATLIST_FindStatFromArrayID (Improvement Control IF)
  49. 0x6FDB8190 - (New Name) STATLIST_GetStatListExFromState
  50. 0x6FDB63E0 - (New Name) STATLIST_GetTotalStat (Improvement Control IF)
  51. 0x6FDB7050 - (New Name) STATLIST_FreeStatListImpl (Improvement control IF)
  52. 0x6FDB6A30 - (New Name) STATLIST_RemoveStat
  53. 0x6FDB9C50 - (New Name) STATLIST_CopyStatToBufferEX
  54. 0x6FDB6340 - (New Name) STATLIST_GetBaseStatVal (Improvement Control IF)
  55. 0x6FDB8900 - (New Name) STATLIST_STATES_ToggleState (Improvement Control IF and Clean)

Functions that exclusively depend on wrong functions to Work:

  1. 10531 - STATLIS_GoSetStatInStatListLayer0 (depends on 10463 STATLIST_SetStat)
  2. 11295 - STATLIST_SetBaseStat2 (depends on 11294 STATLIST_SetBaseStat)
  3. 11273 - (New Name) STATLIST_CheckAndGetNewValueFromStatID (Improvement control IF and depends on 0x6FDB5830 STATLIST_GetNewValueFromStatID)
  4. 10514 - (New Name) STATLIST_ClampStaminaManaHP (depends on 10464 STATLIST_AddStat)
  5. 10524 - STATLIST_ExpireUnitStatlist (depends on 0x6FDB6E30 STATLIST_ExpireStatListEx)
  6. 10574 - (New Name) STATLIST_SetOrRemoveStatListFromStateID (Improvement control IF and Clean, and depends on 0x6FDB6E30 STATLIST_ExpireStatListEx and 10475 STATLIST_AddPostStatToStatList)
  7. 10474 - STATLIST_ExpireStatList (depends on 0x6FDB6E30 STATLIST_ExpireStatListEx)
  8. 0x6FDB7690 - (New Name) STATLIST_InsertStatModOrFail (depends on 0x6FDB6970 STATLIST_InsertStatOrFail)

WRONG functions:

  1. 10513 - (New Name) STATLIST_FindStatAndCallBackEx
  2. 10523 - STATLIST_MergeStatLists
  3. 11274 - (New Name) STATLIST_MergeStatListTRUE
  4. 11275 - (New Name) STATLIST_MergeStatListFALSE
  5. 10463 - STATLIST_SetStat
  6. 11294 - (New Name) STATLIST_SetBaseStat
  7. 10517 - STATLIST_SetUnitStat
  8. 10573 - STATLIST_MergeBaseStats
  9. 10464 - STATLIST_AddStat (Improvement Clean)
  10. 10465 - STATLIST_SetStatIfListIsValid
  11. 10518 - STATLIST_AddUnitStat
  12. 10468 - STATLIST_RemoveAllStats
  13. 10475 - (New Name) STATLIST_AddPostStatToStatList
  14. 10525 - (New Name) STATLIST_MergeStatListFromUnitToUnit
  15. 0x6FDB6970 - (New Name) STATLIST_InsertStatOrFail
  16. 0x6FDB6C10 - (New Name) STATLIST_SetStatProcess
  17. 0x6FDB6E30 - (New Name) STATLIST_ExpireStatListEx
  18. 0x6FDB5830 - (New Name) STATLIST_GetNewValueFromStatID (The error you have is curious. Make Missiles Invisible)
  19. 0x6FDB6AB0 - (New Name) STATLIST_UpdateUnitStat (Wrong DIV BY ZERO)
  20. 0x6FDB64A0 - (New Name) STATLIST_UpdateUnitNewStat

HELPER Functions:

Work Functions:

  1. STATLIST_IsExtended - Improvement Control IF
  2. BITMANIP_SetBitsValueForMask
  3. STATLIST_StatListExCast - Improvement Control IF
  4. STATLIST_ApplyMinValue - Improvement Control IF and __fastcall
  5. STATLIST_GetStatUnsigned
  6. STATLIST_CopyStatsData - Improvement Control IF
  7. STATLIST_ClampStat - Improvement Control IF (depends on 10464 STATLIST_AddStat)

Wrong Functions:

  1. STATLIST_FindStatInsertionIndex
  2. STATLIST_InsertStat
  3. STATLIST_GetOrInsertStat
  4. STATLIST_NotifyUnitOfStatValueChange
  5. STATLIST_SetUnitStatNewValue
  6. ComputeStatPercentage
  7. STATLIST_MergeStatListWithBool

[D2Game] Error AIGENERAL_GetMinionOwner

This function also requires that pAiControl is nullptr or not

//D2Game.0x6FCCF320
...
if (pAiControl->pGame)

To

//D2Game.0x6FCCF320
...
if (pAiControl && pAiControl->pGame)

padding in D2QuestDataStrc / D2QuestGUIDStrc

struct D2QuestDataStrc
{...
D2QuestGUIDStrc tPlayerGUIDs; //0x1C
uint16_t dw9E; //0x9E

struct D2QuestGUIDStrc //sizeof 0x84
{
uint32_t nPlayerGUIDs[32]; //0x00 - players that have entered the quest zone
uint16_t nPlayerCount; //0x80
uint8_t pad0x82[2]; //0x82
};

If the padding of 2 bytes is in D2QuestGUIDStrc and the size 0x84, then it shouldn't also be in D2QuestData, right?

Finish (re)naming D2Net functions

D2Net_10006
Should probably be SERVER_Send or SERVER_SendToClient. (if option n°2, then rename CLIENT_Send to CLIENT_SendToServer)

Here are names from @Necrolis we may want to get inspiration from

D2Net.#10000 - InitSystem
D2Net.#10001 - ShutdownSystem
D2Net.#10002 - Wait
D2Net.#10003 - CreateQServer
D2Net.#10004 - CloseQServer
D2Net.#10005 - SendToServer
D2Net.#10006 - SendToClient
D2Net.#10007 - RecvFromBufferClt
D2Net.#10008 - RecvFromHiPriorityBufferClt
D2Net.#10009 - <Missing?????> -> assuming this is RecvFromLoPriorityBufferClt
D2Net.#10010 - RecvFromBufferSrv
D2Net.#10011 - RecvFromHiPriorityBufferSrv
D2Net.#10012 - RecvFromLoPriorityBufferSrv
D2Net.#10013 - GetSocketName
D2Net.#10014 - GetIPAddress
D2Net.#10015 - CreateConnection
D2Net.#10016 - CloseConnection
D2Net.#10017 - Connect
D2Net.#10018 - SetQServerData(0xBD8)
D2Net.#10019 - SetConnectionCallback
D2Net.#10020 - SetConnectionData
D2Net.#10021 - GetConnectionData
D2Net.#10022 - Wait(0xBD4)
D2Net.#10023 - SetQServerMode
D2Net.#10024 - GetLastWSAError
D2Net.#10025 - CreatePollThread
D2Net.#10026 - SetMaxClients
D2Net.#10027 - GetMaxClients
D2Net.#10028 - Return
D2Net.#10029 - Return
D2Net.#10030 - GetClientPacketSize
D2Net.#10031 - GetServerPacketSize
D2Net.#10032 - CloseAllConnections
D2Net.#10033 - GetQServerIP
D2Net.#10034
D2Net.#10035
D2Net.#10036
D2Net.#10037
D2Net.#10038
D2Net.#10039 - GetSendFunction
D2Net.#10040 - GetDataFunction

Originally posted by @Lectem in #96 (comment)

UNITS_GetFrameBonus & D2COMMON_11013_ConvertMode - Synchronization issues

//D2Common.0x6FDC0FC0 (#10436)
int __stdcall UNITS_GetFrameBonus(D2UnitStrc* pUnit)

This feature has timing issues in "hth" mode when the character dies.

Verified with Sorceress.

The head of the Sorceress goes "crazy" and does not show the proper location as she walks after dying.

Possibly it is related to an internal function, I have not verified exactly which one, although my suspicions point to this other:

//D2Common.0x6FDB30A0 (#11013)
void __stdcall D2COMMON_11013_ConvertMode(D2UnitStrc* pUnit, int* pType, int* pClass, int* pMode, char* szFile, int nLine)

[D2Game] Error D2GAME_MERCS_EquipItem_6FC88D10

When we use the sword in mercenaries act 3, the item will disappear because the function only checks the item on the right hand side, not on the left hand side, and so are the other entries it seems to work only in the right hand.

Origin:

uint8_t nTargetBodyLoc = nBodyLoc1;
if (pMerc->dwClassId == MONSTER_ACT3HIRE && ITEMS_CheckItemTypeId(pItem, ITEMTYPE_SHIELD))
{
        pExchangeItem = pTempItem;
        nTargetBodyLoc = nBodyLoc2;
}

Fix:

uint8_t nTargetBodyLoc = nBodyLoc1;
if (pMerc->dwClassId == MONSTER_ACT3HIRE && ITEMS_CheckItemTypeId(pItem, ITEMTYPE_SHIELD))
{
        pExchangeItem = pTempItem;
        nTargetBodyLoc = nBodyLoc2;
}
else
{
        nBodyLoc2 = nTargetBodyLoc;
}

D2Game.0x6FC6FF10

void __fastcall D2GAME_BOSSES_AssignUMod_6FC6FF10(D2GameStrc* pGame, D2UnitStrc* pUnit, int32_t nUMod, int32_t bUnique)
{
...
for (int32_t i = 0; i < 9; ++i)
    {
        if (!pUMods[i])
        {
            pUMods[i] = nUMod;
            sub_6FC6F670(pUnit, nUMod, bUnique);
        }
    }

should be a break or return after the call, otherwise it fills the entire list with the same UMod

CLIENTS_RemoveClientFromListWithId may remove from wrong list

From @Necrolis

At

pClientToRemove = CLIENTS_RemoveClientFromListWithId(&pGame->pClientList, nClientIdToRemove);
, we call CLIENTS_RemoveClientFromListWithId which removes from the the "global" static linked list instead of the game's linked list (D2ClientStrc+0x4AC instead of D2ClientStrc+0x4A8).

The helper function should be told what list to remove from.

Game List Unlink
0533B927 . 8B90 A8040000 MOV EDX,DWORD PTR DS:[EAX+4A8]
Global List Unlink
0533B89D . 8B86 AC040000 MOV EAX,DWORD PTR DS:[ESI+4AC]
Global List Unlink (By Name)
0533B9CA . 8B82 B0040000 MOV EAX,DWORD PTR DS:[EDX+4B0]

We might want to rename D2ClientStrc+0x4AC or D2ClientStrc+0x4A8 to avoid confusion in the future.

D2Game.0x6FC55360

In

D2UnitStrc* __fastcall MISSILES_CreateMissileFromParams(D2GameStrc* pGame, D2MissileStrc* missileParams)
{
...
    if (missileParams->dwFlags & 0x1000)
    {
        STATLIST_SetUnitStat(pMissile, STAT_TOHIT, missileParams->nAttBonus, 0);
    }
...
}

should be STAT_ITEM_TOHIT_PERCENT

image

Document and chose naming convention for coordinates

There are many types of coordinates systems with various granularity.

  • Cartesion Vs Iso
  • Room, Tile, Subtile, World position, Path
  • GUI coordinates
  • Render data coordinates
  • ...

It would be great to have a clear and concise documentation of what those are, what functions let us convert from one to another, and chose a naming convention for variables / struct members so that we can easily identify what type of coordinates we are talking about.

It might even be interesting to use typedefs (or even strongly typed structs?) to replace the plain int / D2CoordStrc.

A first draft of the documentation is available here https://github.com/ThePhrozenKeep/D2MOO/blob/master/doc/Coordinates.md.

Function (D2Common_STATES_ToggleState_6FDB8900) Not Working

  • i tested the function it doesnt work and you error 2 is
    gdwBitMasks[nState % 32] -> gdwBitMasks[nState % 31]

bool bStateMaskDisguise = pStatesTxtRecord->dwStateFlags & gdwBitMasks[STATEMASK_DISGUISE];
Fix
bool bStateMaskNoSend = pStatesTxtRecord->dwStateFlags & gdwBitMasks[STATEMASK_NOSEND];

  • i fixed the code my way and it works
    Link

D2Game.0x6FCF3000

few edits that I think are correct looking at the ASM

int32_t __fastcall AIUTIL_CanUnitSwitchAi(D2UnitStrc* pUnit, int32_t nSpecialState)
{
	if (!pUnit || pUnit->dwUnitType != UNIT_MONSTER || nSpecialState > 19 || STATES_CheckState(pUnit, STATE_UNINTERRUPTABLE) || !UNITS_CanSwitchAI(pUnit->dwClassId))
	{
		return 0;
	}

	if (!(pUnit->dwFlags & UNITFLAG_CANBEATTACKED) && !SUNIT_IsDead(pUnit))
	{
		return 0;
	}

	if (MONSTERUNIQUE_CheckMonTypeFlag(pUnit, MONTYPEFLAG_UNIQUE | MONTYPEFLAG_SUPERUNIQUE))
	{
		return 0;
	}

	if (nSpecialState == 19)
	{
		return 1;
	}

	return AITHINK_CanUnitSwitchAi(pUnit, MONSTERMODE_GetMonStatsTxtRecord(pUnit->dwClassId), nSpecialState, 1);
}```

Error in 0x6FDAA1E0 (#10186)

This bug was discovered by Silvermane#7177 in PK.

Can someone clarify if I'm just having a brain fart or if the == UNIT_PLAYER should be != UNIT_PLAYER

//D2Common.0x6FDAA1E0 (#10186)
void __stdcall PATH_ResetToPreviousType(D2DynamicPathStrc* pDynamicPath)
{
    D2_ASSERT(pDynamicPath->pUnit);

    if (pDynamicPath->dwFlags & 0x8000)
    {
        pDynamicPath->dwVelocity = pDynamicPath->unk0x80;
    }

    if (pDynamicPath->pUnit->dwUnitType == UNIT_PLAYER)
    {
        if (pDynamicPath->dwFlags & 0x2000)
        {
            PATH_SetType(pDynamicPath, pDynamicPath->dwPrevPathType);
        }
    }
    else
    {
        PATH_SetType(pDynamicPath, 7);
    }
}
6F66A21A   8339 00          CMP DWORD PTR DS:[ECX],0
6F66A21D   5E               POP ESI
6F66A21E   75 0B            JNZ SHORT D2Common.6F66A22B
6F66A220   6A 07            PUSH 7
6F66A222   50               PUSH EAX
6F66A223   E8 B8FEFFFF      CALL D2Common.6F66A0E0
6F66A228   C2 0400          RETN 4
6F66A22B   F6C6 20          TEST DH,20
6F66A22E   74 0A            JE SHORT D2Common.6F66A23A
6F66A230   8B48 40          MOV ECX,DWORD PTR DS:[EAX+40]
6F66A233   51               PUSH ECX
6F66A234   50               PUSH EAX
6F66A235   E8 A6FEFFFF      CALL D2Common.6F66A0E0
6F66A23A   C2 0400          RETN 4

From me, I found more errors in the Internal Subfunction, Apparently they misplaced the variables (or I had them wrong in that version, maybe).

My Solution

//D2Common.0x6FDAA1E0 (#10186) - WRONG D2Moo
// != UNIT_PLAYER Corrected
void __stdcall PATH_ResetToPreviousType(D2DynamicPathStrc* pDynamicPath)
{
	if (pDynamicPath && pDynamicPath->pUnit)
	{
		if (pDynamicPath->dwFlags & PATHFLAG_SETSPEED)
		{
			pDynamicPath->dwVelocity = pDynamicPath->dwPrevVelocity;
		}

		if (pDynamicPath->pUnit->dwUnitType != UNIT_PLAYER)
		{
			if (pDynamicPath->dwFlags & PATHFLAG_SETTYPE)
			{
				PATH_SetType(pDynamicPath, pDynamicPath->dwPrevPathType);
			}
		}
		else
		{
			PATH_SetType(pDynamicPath, 7);
		}
	}
}
//D2Common.0x6FDAA0E0 (#10185) - WRONG D2Moo
//nValueType corrected
void __stdcall PATH_SetType(D2DynamicPathStrc* pDynamicPath, int nPathType)
{
	int nFlag = 0;

	D2_ASSERT(!(pDynamicPath->pUnit && pDynamicPath->pUnit->dwUnitType == UNIT_PLAYER && nPathType == PATHTYPE_TOWARD));

	nFlag = dword_6FDD2088[nPathType];
	if (nFlag & PATHFLAG_SETTYPE && !(pDynamicPath->dwFlags & PATHFLAG_KEEPTYPE))
	{
		pDynamicPath->dwPrevPathType = pDynamicPath->dwPathType;
	}

	if (nFlag & PATHFLAG_SETSPEED && !(pDynamicPath->dwFlags & PATHFLAG_KEEPSPEED))
	{
		pDynamicPath->dwPrevVelocity = pDynamicPath->dwVelocity;
	}

	pDynamicPath->dwPathType = nPathType;
	pDynamicPath->dwFlags = nFlag | pDynamicPath->dwFlags & 0xFFF800FF;
	/*pDynamicPath->PathPoints[0].X = word_6FDD20D0[2 * nPathType]; <<<<<<<<<<<<<< error
	pDynamicPath->PathPoints[0].Y = word_6FDD20D0[2 * nPathType + 1];*/
	pDynamicPath->nValueType = gnValueTypes[nPathType];

	D2_ASSERT(pDynamicPath->dwPrevPathType != PATHTYPE_KNOCKBACK_CLIENT);
	D2_ASSERT(pDynamicPath->dwPrevPathType != PATHTYPE_KNOCKBACK_SERVER);

	D2_ASSERT((nPathType != PATHTYPE_MISSILE) || (pDynamicPath->nDistMax < MAXPATHLEN));
}
//6FDD20D0
static int32_t gnValueTypes[] =
{
	0,
	0,
	0,
	0,
	0,
	2,
	-2,
	0,
	0,
	0,
	0,
	0,
	-4,
	0,
	0,
	0,
	0,
	0
};

//D2Game.0x6FC6EC10

   for (int32_t i = 0; i < 2; ++i)
    {
        const int16_t nExcludeMonType = pMonUModTxtRecord->wExclude[i];
        if (pUnit && pUnit->dwUnitType == UNIT_MONSTER)
        {
            D2MonStatsTxt* pMonStatsTxtRecord = MONSTERMODE_GetMonStatsTxtRecord(pUnit->dwClassId);
            if (pMonStatsTxtRecord && pMonStatsTxtRecord->wMonType >= 0 && pMonStatsTxtRecord->wMonType < sgptDataTables->nMonTypeTxtRecordCount
                && nExcludeMonType >= 0 && nExcludeMonType < sgptDataTables->nMonTypeTxtRecordCount
                && sgptDataTables->pMonTypeNest[(pMonStatsTxtRecord->wMonType >> 5) + nExcludeMonType * sgptDataTables->nMonTypeIndex] & gdwBitMasks[pMonStatsTxtRecord->wMonType & 0x1F])
            {
                return 0;
            }
        }
    }
This looks like inlined SUNITDMG_CheckMonType
        for (int32_t i = 0; i < 2; ++i)
    {
        const int16_t nExcludeMonType = pMonUModTxtRecord->wExclude[i];
        if (pUnit && pUnit->dwUnitType == UNIT_MONSTER)
        {
            D2MonStatsTxt* pMonStatsTxtRecord = MONSTERMODE_GetMonStatsTxtRecord(pUnit->dwClassId);
            if (SUNITDMG_CheckMonType(nExcludeMonType, pMonStatsTxtRecord->wMonType);
            {
                return 0;
            }
        }
    }
or
    D2MonStatsTxt* pMonStatsTxtRecord = MONSTERMODE_GetMonStatsTxtRecord(pUnit->dwClassId);
	if (pMonStatsTxtRecord)
	{
		if (SUNITDMG_CheckMonType(pMonUModTxtRecord->wExclude[0], pMonStatsTxtRecord->wMonType) || SUNITDMG_CheckMonType(pMonUModTxtRecord->wExclude[1], pMonStatsTxtRecord->wMonType))
			return;
	}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.