This is exceedingly helpful. Thank you! I'll do some experimenting with this in DFU to see if the behavior matches classic.Elenwel wrote: ↑Mon May 11, 2020 8:30 amCode: Select all
//a = [6 * ([[[zeroInAllMyTests * 2 + itemDataLocation] + 0028706C] + 00292454]) + charInfoLocation + 0000009D] playerSkill = pPlayerData->skills[gMagicSchoolsToSkills[gSpellTypeToMagicSchool[pSpell->types[i].major]]].level //b = [itemDataLocation + 000E] * [002A5E3C] b = pSpell->duration * gSpellModifier->field_0 x = ((110 - playerSkill) * b) / 100
Where is item condition stored in memory?
- numidium3rd
- Posts: 188
- Joined: Sun Mar 25, 2018 12:34 am
- Location: United States
Re: Where is item condition stored in memory?
Nexus DFU Mods:
https://www.nexusmods.com/daggerfalluni ... user+files
Mod source repo:
https://github.com/numidium/dfu-mods
https://www.nexusmods.com/daggerfalluni ... user+files
Mod source repo:
https://github.com/numidium/dfu-mods
-
- Posts: 7
- Joined: Tue Jan 01, 2019 5:14 pm
Re: Where is item condition stored in memory?
Ok, I think I found the equip/unequip item handlers. And it seems to be bugged in Daggerfall classic.
Long story short, the spell cost (calculated using my previous post function) for the last "on held" effect is reduced from item's health when equipping. The cost is calculated using current player stats.
Longer version : a given item can have up to 10 enchantment, most of the time (on stroke, or when time is passing) each enchantment will reduce item's health (for example 10pt per stroke per enchantment with "cast strikes"). Except for equipping item callback (zipped code below):
This code is iterating over the 10 possible effects, and is applying every "on held" effect . But cost is calculated using pLastHeldSpellEffect which is overwritten for each effects... So, for an item with multiple "on held" effect, only last one is used for cost calculation. I don't know if this is the intended behaviour, it may be more coherent to reduce item health by each effect cost.
Interkarma: it should be noted that this function use current player stats for spell cost, item maker uses a dummy player with every skills at 50 for calculating cost (*10).
Once more, it's my current understanding of Daggerfall, it should be validated by in-game experimenting
Long story short, the spell cost (calculated using my previous post function) for the last "on held" effect is reduced from item's health when equipping. The cost is calculated using current player stats.
Longer version : a given item can have up to 10 enchantment, most of the time (on stroke, or when time is passing) each enchantment will reduce item's health (for example 10pt per stroke per enchantment with "cast strikes"). Except for equipping item callback (zipped code below):
Code: Select all
SC_OBJECT_DATA *__fastcall SC_inven::96CCF_EquipMagicItem(SC_OBJECT *pItemObj, char inventorySlot)
{
unsigned __int16 effectType; // ax
signed int cost; // eax
signed int effectIdx; // [esp+Ch] [ebp-24h]
SC_OBJECT_DATA *pItem; // [esp+14h] [ebp-1Ch]
SC_OBJECT *pNewSpellObj; // [esp+18h] [ebp-18h]
ELE_SpellsSTDEntry *pLastHeldSpellEffect; // [esp+1Ch] [ebp-14h]
effectIdx = 0;
pItem = &pItemObj.u;
pLastHeldSpellEffect = 0;
while ( effectIdx < 10 )
{
if ( pItem->type2_item.magics[effectIdx].effectType == -1 )
break;
effectType = pItem->type2_item.magics[effectIdx].effectType;
if ( effectType == CAST_HELD )
{
for (int i = 0; gFile_Spells_std[i].SpellID != pItem->type2_item.magics[effectIdx].spellID; ++i )
;
pNewSpellObj = SC_Object::InsertNewObject(gObjRoot, 0, sizeof(ELE_SpellsSTDEntry));
pNewSpellObj->h.type = SC_OBJECT_TYPE_SPELL;
pNewSpellObj->h.field_15 = 3;
SC_jmem::memcpy(&pNewSpellObj->u, &gFile_Spells_std[i], sizeof(ELE_SpellsSTDEntry), "inven.c" , 2092, 4);
pLastHeldSpellEffect = &pNewSpellObj->u;
pNewSpellObj->u.type9_spell.Icon = inventorySlot - 56;
for (int j = 0; j < 3; ++j )
{
if ( pLastHeldSpellEffect->types[j].major != 255 )
{
pLastHeldSpellEffect->duration[j].base = -1;
pLastHeldSpellEffect->duration[j].x = 0;
pLastHeldSpellEffect->duration[j].div = 0;
}
}
ELE_SPELL::ApplyEffect_qqq(pNewSpellObj, &pNewSpellObj->u);
}
}
//...
effectIdx++;
}
if ( !effectIdx )
return 0;
if ( !pLastHeldSpellEffect )
return effectIdx;
cost = SC_spells::_3A0C0_GetCost_qqq(pLastHeldSpellEffect, gPlayerData);
return SC_OBJECT_TYPE2::ReduceEnchantment(pItemObj, cost);
}
Interkarma: it should be noted that this function use current player stats for spell cost, item maker uses a dummy player with every skills at 50 for calculating cost (*10).
Once more, it's my current understanding of Daggerfall, it should be validated by in-game experimenting
- numidium3rd
- Posts: 188
- Joined: Sun Mar 25, 2018 12:34 am
- Location: United States
Re: Where is item condition stored in memory?
More progress. We were on the right track saying that b = pSpell->duration * gSpellModifier->field_0 but we also need to add something to that to make it the actual b variable for my original formula.
To Add:
[ebp-001C] = [002A5E40] * ([spellData + 20] + [spellData + 21]) / 2 + (([spellData + 22] + [spellData + 23]) / 2) * ([002A5E42] / [spellData + 24])
For the Amulet of The Orc Lord this ends up being 6E. We then need to add this to EAX which is set to the value below:
ax = [spellData + 000E] * [002A5E3C] = F9
F9 + 6E = 67 (only taking into account least significant byte)
Next step is to figure out what those spellData indices point to and where to find them.
To Add:
[ebp-001C] = [002A5E40] * ([spellData + 20] + [spellData + 21]) / 2 + (([spellData + 22] + [spellData + 23]) / 2) * ([002A5E42] / [spellData + 24])
For the Amulet of The Orc Lord this ends up being 6E. We then need to add this to EAX which is set to the value below:
ax = [spellData + 000E] * [002A5E3C] = F9
F9 + 6E = 67 (only taking into account least significant byte)
Next step is to figure out what those spellData indices point to and where to find them.
Nexus DFU Mods:
https://www.nexusmods.com/daggerfalluni ... user+files
Mod source repo:
https://github.com/numidium/dfu-mods
https://www.nexusmods.com/daggerfalluni ... user+files
Mod source repo:
https://github.com/numidium/dfu-mods
- Ferital
- Posts: 282
- Joined: Thu Apr 05, 2018 8:01 am
Re: Where is item condition stored in memory?
There are 51 spell effects in classic, all of which have a "cost type" taken from the following map:
Then to know the spell cost (which is also the amount of damage the item holding such effect takes when equipped, as guessed by Elenwel), you get 7 functions depending on this cost type.
The one you tried to reverse is probably this one:
Code: Select all
1, 2, 3, 4, 5, 4, 4, 2, 1, 6
5, 6, 1, 3, 3, 3, 1, 4, 2, 1
1, 1, 1, 3, 3, 3, 3, 3, 3, 1
3, 3, 1, 1, 1, 2, 2, 1, 1, 1
1, 1, 3, 4, 1, 2, 2, 2, 2, 2
2
The one you tried to reverse is probably this one:
Code: Select all
((g_current_spell->magnitude[g_current_spell_type_id].level_base + g_current_spell->magnitude[g_current_spell_type_id].level_high) / 2
/ g_current_spell->magnitude[g_current_spell_type_id].per_level * unknown_var_1 +
unknown_var_2 * ((g_current_spell->magnitude[g_current_spell_type_id].base_high + g_current_spell->magnitude[g_current_spell_type_id].base_low) / 2));
- Ferital
- Posts: 282
- Joined: Thu Apr 05, 2018 8:01 am
Re: Where is item condition stored in memory?
The map I mentioned in my previous post wouldn't be complete without the list of spell effects, so here it is:
So, as you can see, some effect slots are unused and are probably a leftover of early stages of Daggerfall development.
Edit: looking at DFU code, it seems that Allofich already reverse engineered all of these spell cost computation functions years ago. Numidium, look at FormulaHelper.getCostFromSettings, you'll find the list of 7 functions (packed into one) I was talking about above.
Code: Select all
char *g_spell_effect_names[51] =
{
"Paralyze",
"Continuous Damage",
"Create Item",
"Cure",
"Damage",
"Disintegrate",
"Dispel",
"Drain",
"Elemental Resistance",
"Fortify Attribute",
"Heal",
"Transfer",
"Soul Trap",
"Invisibility",
"Levitate",
"Light",
"Lock",
"Open",
"Regenerate",
"Silence",
"Spell Absorption",
"Spell Reflection",
"Spell Resistance",
"Chameleon",
"Shadow",
"Slowfall",
"Free Action",
"Jumping",
"Climbing",
NULL,
"Water Breathing",
"Water Walking",
NULL,
"Pacify",
"Charm",
"Shield",
NULL,
NULL,
NULL,
"Detect",
"Identify",
NULL,
NULL,
"Teleport",
"Comprehend Languages",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
Edit: looking at DFU code, it seems that Allofich already reverse engineered all of these spell cost computation functions years ago. Numidium, look at FormulaHelper.getCostFromSettings, you'll find the list of 7 functions (packed into one) I was talking about above.
- Ferital
- Posts: 282
- Joined: Thu Apr 05, 2018 8:01 am
Re: Where is item condition stored in memory?
I submitted a PR.
- numidium3rd
- Posts: 188
- Joined: Sun Mar 25, 2018 12:34 am
- Location: United States
Re: Where is item condition stored in memory?
Thanks, Ferital! I was so focused on analyzing the assembly that I didn't think to see if we already had code written. Allofich proves to be a reversing wizard as always.
Nexus DFU Mods:
https://www.nexusmods.com/daggerfalluni ... user+files
Mod source repo:
https://github.com/numidium/dfu-mods
https://www.nexusmods.com/daggerfalluni ... user+files
Mod source repo:
https://github.com/numidium/dfu-mods
- Interkarma
- Posts: 7253
- Joined: Sun Mar 22, 2015 1:51 am
Re: Where is item condition stored in memory?
This is wonderful work everyone. Congratulations all on the progress!