Closed obligaron closed 3 weeks ago
Hold that thought. I seem to have some uniques vanishing before I can reach them. Also it appears to be generating like vanilla. Let me test further.
Okay, so I was able to get some item drops, but the vanished from the ground before I could pick them up:
(Note: All the other uniques I've encountered so far have not vanished)
This was played in Nightmare, so some of these could legally drop even in vanilla. What it looks like to me is that the uidOffset
being utilized is causing the items to be in an invalid state for "impossible items", although there are items that don't require uidOffset
at all to be functional, although those items are vanishing as well. So far it does appear that I'm getting mostly "vanilla" style drops (last valid uid), and the ones that aren't last valid uid vanish.
How do you generate the uniques?
I tried to generate Bramble and Bleeder with spawnUnique
command and they work (single player hellfire; can pick them up from ground and place them there again).
How do you generate the uniques? I tried to generate Bramble and Bleeder with
spawnUnique
command and they work (single player hellfire; can pick them up from ground and place them there again).
I'm forcing all monsters to take on unique monster status, then spamming chain lightning around the dungeon. I personally don't trust the debug command
With this PR logic:
https://github.com/user-attachments/assets/65e5cf78-5aec-45b1-aa75-77427f51070f
Current master logic:
https://github.com/user-attachments/assets/1ea15998-18c1-4409-b30d-099601569b24
Debug modifications: items.cpp L3381:
} else if (true || monster.isUnique() || dropsSpecialTreasure) {
L3415
int uper = 15;
L3237
item._iIdentified = true;
Thanks @kphoenix137 🙂 I think I found the issue. The item position wasn't set correctly when the unique is (re)created.
This didn't matter in the debug commands, because the position was set after the TryRandomUniqueItem
.
Thanks @kphoenix137 🙂 I think I found the issue. The item position wasn't set correctly when the unique is (re)created.
Thats funny. I remember having the same exact problem when coding this in the first place. I just forgot what the problem was until you mentioned that
You left your debug code in.
Bad news, nearly all the uniques I picked up morph in vanilla (Specifically most of the ones I was picking up were previously on the "vanish" list, so those are the ones being recreated). Are you utilizing uidOffset
for more than just the "impossible" uniques? If so, that will certainly break reverse compatibility. uidOffset
was introduced as a workaround to allow the impossibles to exist in DevilutionX without morphing on regeneration, and allow them to be moved to vanilla where they would inevitably morph, but still change back into what they were found as when returning to DevilutionX.
The code for selecting a target level was introduced in order to create reverse compatible uniques, as we would first select the uid we want, and then brute force search using target item data to find that unique, that doesn't exactly match the source of the drop, but it successfully allows us to create stable uniques that won't morph in vanilla. The idea was to take advantage of the fact vanilla always selects the last applicable uid, so we'd select a target level that makes sure that the desired uid is the last in the list.
I just tested forcing all monsters as unique monsters and forced the War Hammer as the selected idx for every drop, and I was able to successfully get Schaefer's Hammer to drop in the latest master. Maybe there's something wrong with the debug command for generating these uniques?
Edit: Back again, I got an infinite loop in Single Player Hellfire. I'll see what happens in Single Player Diablo
Edit: No infinite loop in SP Diablo
Edit: Infinite loop in SP and MP Hellfire. Looking into it.
INFO: SetupAllItems: iblvl: 34
INFO: CheckUnique: (Schaefer's Hammer) Checking if lvl (34) is at least UIMinLvl (16)
INFO: CheckUnique: Added uid 52 to valid uniques vector
INFO: CheckUnique: Added uid 102 to valid uniques vector
INFO: CheckUnique: Selected uid: 102
INFO: TryRandomUniqueItem: Made it past return. Item is Unique, and item is a not a quest unique
INFO: TryRandomUniqueItem: Item._iUid (should be non-zero): 102
INFO: TryRandomUniqueItem: Item._iCreateInfo & CF_UNIQUE: 512
INFO: TryRandomUniqueItem: Item._iMiscId: 0
INFO: TryRandomUniqueItem: Item._iMagical: 2
INFO: TryRandomUniqueItem: We selected new uid: 102
INFO: TryRandomUniqueItem: The new uid matches the old one, returning.
Here, the vanilla algorithm picked Thunderclap, and then TryRandomUniqueItem()
also picked Thunderclap, so it didn't need to regenerate.
INFO: SetupAllItems: iblvl: 31
INFO: CheckUnique: (Schaefer's Hammer) Checking if lvl (31) is at least UIMinLvl (16)
INFO: CheckUnique: Added uid 52 to valid uniques vector
INFO: CheckUnique: Added uid 102 to valid uniques vector
INFO: CheckUnique: Selected uid: 102
Vanilla algorithm decides again that it's a Thunderclap.
INFO: TryRandomUniqueItem: Made it past return. Item is Unique, and item is a not a quest unique
INFO: TryRandomUniqueItem: Item._iUid (should be non-zero): 102
INFO: TryRandomUniqueItem: Item._iCreateInfo & CF_UNIQUE: 512
INFO: TryRandomUniqueItem: Item._iMiscId: 0
INFO: TryRandomUniqueItem: Item._iMagical: 2
INFO: TryRandomUniqueItem: We selected new uid: 52
INFO: TryRandomUniqueItem: Potential uper adjustment. uper is: 15
INFO: TryRandomUniqueItem: uidOffset: 0
At this point, TryRandomUniqueItem()
decides instead of Thunderclap, we're going to drop Schaefer's Hammer, with no uidOffset
and keeping the item at uper15
.
INFO: TryRandomUniqueItem: Forcing item generation until we find a match.
INFO: SetupAllItems: iblvl: 16
INFO: TryRandomUniqueItem: item._iUid 0 is not equal to desired uid 52, roll again
INFO: TryRandomUniqueItem: Forcing item generation until we find a match.
INFO: SetupAllItems: iblvl: 16
INFO: TryRandomUniqueItem: item._iUid 0 is not equal to desired uid 52, roll again
INFO: TryRandomUniqueItem: Forcing item generation until we find a match.
This repeats for a bit as it's selecting non-uniques.
INFO: SetupAllItems: iblvl: 16
INFO: CheckUnique: (Schaefer's Hammer) Checking if lvl (16) is at least UIMinLvl (16)
INFO: CheckUnique: Added uid 52 to valid uniques vector
INFO: CheckUnique: Added uid 102 to valid uniques vector
INFO: CheckUnique: Selected uid: 102
We finally roll a unique, but CheckUnique()
selects Thunderclap. Both uniques are available. Since the lvl
being passed is 16, naturally this should mean it would pick Schaefer's Hammer, however Hellfire added uniques that made the table no longer chronological, so instead of picking Schaefer's Hammer with a min level of 16, it picks Thunderclap with a min level of 13.
INFO: TryRandomUniqueItem: item._iUid 102 is not equal to desired uid 52, roll again
INFO: TryRandomUniqueItem: Forcing item generation until we find a match.
INFO: SetupAllItems: iblvl: 16
INFO: TryRandomUniqueItem: item._iUid 0 is not equal to desired uid 52, roll again
INFO: TryRandomUniqueItem: Forcing item generation until we find a match.
INFO: SetupAllItems: iblvl: 16
INFO: TryRandomUniqueItem: item._iUid 0 is not equal to desired uid 52, roll again
INFO: TryRandomUniqueItem: Forcing item generation until we find a match.
INFO: SetupAllItems: iblvl: 16
INFO: CheckUnique: (Schaefer's Hammer) Checking if lvl (16) is at least UIMinLvl (16)
INFO: CheckUnique: Added uid 52 to valid uniques vector
INFO: CheckUnique: Added uid 102 to valid uniques vector
INFO: CheckUnique: Selected uid: 102
As expected, it continues on and on like this. I truly didn't anticipate this happening as I was testing in Diablo. Maybe you already explained this, but the original post seems overly technical for my understanding. :(
Nice work, infinite loops are terrible
I tried to create some debug items for testing (with
dev.items.spawn
) and sometimes I encountered an infinite loop inTryRandomUniqueItem
.After some debugging I found out that it is trying to generate one of the following unqiues
The problem can be reproduced with debug lua commands
dev.items.spawnUnique
command and one of the above uniques.The cause was that the
uid
offset was not always calculated correctly. There are two reasons why this can happenCheckUnique
andTryRandomUniqueItem
can be different because the check againstUIMinLvl
is different (CheckUnique
calculates it withGetItemBLevel
andTryRandomUniqueItem
has a custom implementation). So the calculated offset can be different or the desired unique can't be generated byCheckUnique
.UIMinLvl
is == and not >= as inCheckUnique
.All this can only happen for some specific uniques or item levels, so the problem is not so easy to get in normal gameplay.
This PR
SetupAllItems
doesCheckUnique
CheckUnique
remains the same