Is it possible to add another extended stat to STATS.IDS to make it so spells cast by a character ignore a certain amount of magic resistance? It'd be nice to simulate Spell Penetration feats because its always a pain to go up against mobs with high MR.
English is not my mother-tongue, so there could be awkward expressions that I'm sorry.
---
vs Melissan
<Vanilla (2.5.16.6)> *I mean vanilla for no mod at all
She uses Stoneskin ▶ We use Breach ▶ and Greater-whirlwind ▶ Victory
<EEex.exe with no any other mod(even your Spell Menu)> still 2.5.16.6 of course
Breach doesn't work anywhere
(Chat log says it worked, though)
She gets no damage from any physical attack
and uses stoneskin again and again with very short term consistently
But!
If we use Pierce Magic(or Ruby Ray of Reversal, Khelben's Warding Whip or so),
chat log would say dispelled *twice*
and then our physical attacks could get to her own skin,
and she will not use stoneskin anymore
---
I'm sorry if what I've written is already known issue,
but I'm not fluent English user so that I couldn't read all the comments of this article
(slow reader)
Is it possible to add another extended stat to STATS.IDS to make it so spells cast by a character ignore a certain amount of magic resistance? It'd be nice to simulate Spell Penetration feats because its always a pain to go up against mobs with high MR.
while I don't know how to do it via global hook (if it's even possible) you could achieve it using following weidu patching on all castable spells, using Invoke Lua opcode:
and than create MYFUNC function that will alter magic resistance (EEex_ApplyEffectToActor, opcode 166, timing 0, duration 0) based on stat value. It will be called before other spell effects are applied, so I'm pretty sure the spell resistance change will affect them. You need to know Lua language and study EEEX code to write stuff like this tho.
Huh, looks like my message somehow disappeared so I'm reposting the message:
I have some questions regarding EEEX usage:
1. Does anyone know offset to read currently equipped launcher resource name? (or slot occupied by it) As a test I've adopted EEex_IterateActorItems code so that it returns equipped slot res:
local equippedWeaponSlot = EEex_ReadByte(share + 0xB1C, 0x0)
local invItemData = EEex_ReadDword(share + 0xA80 + equippedWeaponSlot * 4)
if invItemData > 0 then
local itemRES = EEex_ReadLString(invItemData + 0x8, 8)
print(itemRES)
end
but if the currently equipped weapon is launcher that requires ammo, the printed message is the ammo RES instead of launcher RES. I could iterate all 4 weapon slot and check if they are launchers but that wouldn't be accurate if there is more than 1 launcher of the same type equipped.
2. Is there a list of all possible action IDs returned by EEex_GetActionID?
3. Any way to hook a lua function that would trigger when cre has target set with intention to attack, but before making weapon attack rolls?
4. Is it possible to fool the engine into thinking that particular effect applied by EEEX comes from currently equipped item? I've tried something like this with timing = 2:
But this effect does not go away when the SW1H01 is unequipped. It's not saved directly on character when save file is viewed in Near Infinity, which leads me to believe that maybe it's some flag that is missing here or something like that.
3. Any way to hook a lua function that would trigger when cre has target set with intention to attack, but before making weapon attack rolls?
Yes.
EEex_AddActionHookGlobal("ONATTACK", function(actionData, creatureData)
local actionID = EEex_GetActionID(actionData)
local sourceID = EEex_ReadDword(creatureData + 0x34)
local targetID = EEex_ReadDword(actionData + 0x20)
if actionID == 3 or actionID == 94 or actionID == 98 or actionID == 105 or actionID == 134 then
-- Your code here
end
end)
This is even better implementation than I expected. Global hook like this opens so much modding opportunities when it comes to altering hardcoded gameplay mechanics. Thanks for the sample code!
regarding question 1. I wasn't able to find the exact offset to reliably read equipped launcher res, but there is another way to get the res name - iterating through effects applied to cre with timing = 2 (Instant/While equipped) and reading the sourceslot of these effects. Here is a function that returns both mainhand and offhand weapon res (stored in a table) in case someone else needs this (code adopted from @OlvynChuru's spells mod - gold mine when it comes to EEex lua scripts)
function getWeaponRes(id)
local t = {"", ""}
EEex_IterateActorEffects(id, function(eData)
if EEex_ReadDword(eData + 0x24) == 2 then --timing: Instant/While equipped
local sourceslot = EEex_ReadDword(eData + 0xA4)
if sourceslot == 10 or (sourceslot >= 35 and sourceslot <= 38) then --SLOT_FIST / SLOT_WEAPON
t[1] = EEex_ReadLString(eData + 0x94, 8)
elseif sourceslot == 9 then --SLOT_SHIELD
t[2] = EEex_ReadLString(eData + 0x94, 8)
end
end
end)
return t
end
To make it 100% reliable weidu patching all weapons with some dummy timing = 2 effect is needed (otherwise some weapons with 0 on equip effects won't be detectable).
edit: question 4 is no longer relevant because the same thing can be already achieved using opcode 407 (Cast spell on effect remove) with timing mode set to 2 (while equipped), which casts spell the moment item is unequipped.
bug report:
using EEex_2DALoad more than once crashes the engine. Example (tested by pasting in plain M_ file)
local xplevel = EEex_2DALoad("XPLEVEL")
local xpcap = EEex_2DALoad("XPCAP")
works fine when only 1 2da file is loaded.
feature request:
a way to disable hardcoded spell interruption on taking damage. Existing CONCENTR.2DA modes are not flexible enough (according to kjeron and Bubb, mode = 1 is also bugged, so the only option is either 100% chance or using static 15 in formula).
When disabled completely I think we would be able to write custom formula using existing EEex functionality (detecting damage opcode, interrupting spell as needed via forced hurt animation)
bug report:
using EEex_2DALoad more than once crashes the engine. Example (tested by pasting in plain M_ file)
local xplevel = EEex_2DALoad("XPLEVEL")
local xpcap = EEex_2DALoad("XPCAP")
works fine when only 1 2da file is loaded.
I've fixed this in master, thanks for reporting!
To everyone wondering why this project hasn't moved in a while: I'm currently waiting on v2.6's release before I continue my work. Anything I do now is effectively pointless, (as the 64-bit jump ensures that any hooks I write will be obsolete in the next executable), and I can't start work on v2.6 until the PDB is dropped with its full release.
Anything I do now is effectively pointless, (as the 64-bit jump ensures that any hooks I write will be obsolete in the next executable), and I can't start work on v2.6 until the PDB is dropped with its full release.
If there will be problems with porting EEex to v2.6, I will stay with 2.5 forever, that's for sure. It's incredible how much EEex opens up modding possibilities. btw. ignore the above feature request - it's already possible to make spell uninterruptable by hit thanks to EEex_AddScreenEffectsGlobal.
edit:
After some further testing having CONCENTR.2DA working opposite way to mode = 0 (none of the attacks interrupting spellcasting) would be preferable. Implementing it by blocking damage opcode via EEex_AddScreenEffectsGlobal requires to directly modify target's HP, display Damage string etc. (since opcode 12 can't be used for this purpose). Doing it this way would require taking tons of factors into account (rolling, flags, damage reduction, resistances etc.) So yeah, the original feature request is still valid.
question: does anyone knows roughly what EEex_GetDistanceIsometric value is an equivalent of 1 feet and default visual range?
The engine hardcodes 0x1C0 (or 448) as the default visual range. No idea what it considers a foot, though if there's a known value (in feet) for the visual range you should be able to figure it out.
The engine hardcodes 0x1C0 (or 448) as the default visual range. No idea what it considers a foot, though if there's a known value (in feet) for the visual range you should be able to figure it out.
Horizontal: 16 = 1 foot
Vertical: 12 = 1 foot
Default visual Range = 28 ft
I'm using screeneffects to look at income damage effects and then run some code. However if an actor has a mirror image effect, that damage sometimes actually gets cancelled as it doesn't really 'hit'. Is there anyway to check if if an effect is going to get through the mirror image or not?
I thought maybe I can just change the incoming effect flag to ignore mirror image and then replicate the effects of mirror image in my code, but that seems a bit overkill. any ideas?
I'm using screeneffects to look at income damage effects and then run some code. However if an actor has a mirror image effect, that damage sometimes actually gets cancelled as it doesn't really 'hit'. Is there anyway to check if if an effect is going to get through the mirror image or not?
I thought maybe I can just change the incoming effect flag to ignore mirror image and then replicate the effects of mirror image in my code, but that seems a bit overkill. any ideas?
Thanks
Not easily; ScreenEffects kicks in right after probabilities have been rolled, (and passed), but before all the other checks. The damage effect essentially forces a successful save, (even if it didn't allow one), when it hits a mirror image. I might co-opt the special field of ScreenEffects to allow you to specify where in the application process it intercepts the effect, (I.E. after probability, after save checks, etc.) - that should allow you to do what you want.
@Bubb Thanks, that would seem like a really useful solution. I'm sure it would expand the flexibility and usefulness of screeneffects for other uses too.
Whilst on the subject of saves and checks, is there currently a way to customise the saving throw rolls, or attack rolls? For example if you wanted to change the way saving throws worked or how attack rolls are calculated?
What I am thinking about is trying to implement different 'types' of bonuses. e.g. AC bonus but actors can have different types (natural armor/deflection etc.), some of which stack and some don't. (I would specify the 'type' in some bit of AC bonus opcode, then LUA can iterate and calculate the final AC as I want).
@nonlinearcoast, for saves and other things that are checked after opcode is screened (once a hit is successful and probability rolls passed) you should be able to modify that stuff directly during screening, for example: (untested but something like this should work)
function MYFUNC(originatingEffectData, effectData, creatureData)
local targetID = EEex_GetActorIDShare(creatureData)
local opcode = EEex_ReadDword(effectData + 0xC)
if opcode == 12 then --Damage
local saveBonus = EEex_ReadDword(effectData + 0x40)
EEex_WriteDword(effectData + 0x40, saveBonus + 10) --adding 10 to save throw bonus of this effect
end
end
I'm pretty sure that if you use EEex_ApplyEffectToActor with timing 0 and duration 0 to modify saving throws on targetID during screening, they will be taken into account too.
As for the AC and/or attack bonus, it's a bit more tricky since you need to apply it before attack rolls. So using EEex_AddActionHookGlobal and applying effects based the current action and various factors may be needed. It would be costly solution though, performance wise, I think, and it requires cleaning bonuses once they are no longer needed, so not sure if this is the right approach.
@logiteks yes that does work for saves that are checked after screening, and you can make things like a save vs opcode bonus, or save vs fire damage etc. So yes it is possible to have full control of saves by screening all effects and modifying the save bonuses as you see fit (or you can just screen and remove all saves then include custom code). I was just thinking it would be even better if we just had full access to replace/override the coding the engine uses when a save happens.
For attacks rolls I don’t really see an easy solution, as there is no effect to screen before the roll is successful. Maybe you can screen actions and when an actor attacks run all your own code to complete the attack, then cancel the engines attack? I assume you would need to code everything from running the attack animation etc, which seems like too much work...
or perhaps just detect an attack and apply bonuses to AC/thaco etc (which Is what I think you are suggesting?). But as you say you’d need to remove them again after the attack is done. Not sure if there are easy ways to apply bonuses just for the next attack for example. So in the case of attack rolls it really would be useful to be able to hook those and replace their coding.
about this timer visualization feature that was not implemented after all:
Could anyone please point to offsets (structure and names used in EEex docs) where this timer data is stored? (round, modal actions, spell intervals, spell cooldown, possibly attacks, although these were not mentioned in Bubb's post)
I can add new skills and these show up in game, but having issues in the level up screen. During char generation, i can add points to extended skills but not remove them again (the minus button does nothing). Also on level up screen there is a curious effect where adding or removing points from extended skills also increases/decreases a normal thief skill in the list above it (so the points are 'used' twice).
My skills with UI are not good enough to see where this is going wrong. Anyone else had this issue?
Okay, for anyone interested I've managed to fix it.
To fix the minus button not working for extended skills, I removed:
"chargen.levelingUp" from the below chunk, which stops the minus button working in the char gen screen but works okay in the level up screen. Im not sure what the rationale for that is but works fine with it removed for me.
if chargen.levelingUp and currentChargenThiefSkill > firstExtendedSkillIndex then
if chargen.thief_skill[currentChargenThiefSkill].value > extendedSkills[exIndex].base then
chargen.thief_skill[currentChargenThiefSkill].value = chargen.thief_skill[currentChargenThiefSkill].value - 1
extendedSkills[exIndex].value = extendedSkills[exIndex].value - 1
chargen.extraSkillPoints = chargen.extraSkillPoints + 1
createCharScreen:OnThiefSkillPlusMinusButtonClick(chargen.thief_skill[currentChargenThiefSkill].id, false)
displayExtendedSkills()
end
else
createCharScreen:OnThiefSkillPlusMinusButtonClick(chargen.thief_skill[currentChargenThiefSkill].id, false)
displayExtendedSkills()
end
Then to fix the wierd behaviour with two skills incrementing up, I revised the below:
(... = firstExtendedSkillIndex + i + 1) rather than -1
function displayExtendedSkills()
if firstExtendedSkillIndex ~= nil then
for i = 1, #extendedSkills, 1 do
local skill = extendedSkills[i]
chargen.thief_skill[firstExtendedSkillIndex + i] = {['description'] = skill.description, ['name'] = skill.name, ['value'] = skill.value, ['id'] = firstExtendedSkillIndex + i + 1}
end
end
end
Okay, for anyone interested I've managed to fix it.
To fix the minus button not working for extended skills, I removed:
"chargen.levelingUp" from the below chunk, which stops the minus button working in the char gen screen but works okay in the level up screen. Im not sure what the rationale for that is but works fine with it removed for me.
if chargen.levelingUp and currentChargenThiefSkill > firstExtendedSkillIndex then
if chargen.thief_skill[currentChargenThiefSkill].value > extendedSkills[exIndex].base then
chargen.thief_skill[currentChargenThiefSkill].value = chargen.thief_skill[currentChargenThiefSkill].value - 1
extendedSkills[exIndex].value = extendedSkills[exIndex].value - 1
chargen.extraSkillPoints = chargen.extraSkillPoints + 1
createCharScreen:OnThiefSkillPlusMinusButtonClick(chargen.thief_skill[currentChargenThiefSkill].id, false)
displayExtendedSkills()
end
else
createCharScreen:OnThiefSkillPlusMinusButtonClick(chargen.thief_skill[currentChargenThiefSkill].id, false)
displayExtendedSkills()
end
The reason that chargen.levelingUp condition is there is to prevent a worse bug. In certain games (like BG2EE), if you use the minus button to subtract from an extended skill during chargen, it decrements the skill without refunding the skill points. This doesn't happen during level up. It also doesn't happen in BG1EE, I don't think.
Anything I do now is effectively pointless, (as the 64-bit jump ensures that any hooks I write will be obsolete in the next executable), and I can't start work on v2.6 until the PDB is dropped with its full release.
If there will be problems with porting EEex to v2.6, I will stay with 2.5 forever, that's for sure. It's incredible how much EEex opens up modding possibilities.
100% agreed. I can no longer play this game without EEex. Would feel like going back to the stone age.
I reckon porting EEex to 64-bit will be a hell of a project in itself. Would it even be reasonable to do at all? I wonder what @Bubb's thoughts are on this with the 2.6 beta having been released now.
I'm planning on reimplementing everything I've done so far once v2.6 is released. It's just a waiting game at this point, since I need the symbols that come with a full release to start work on any 64-bit conversion.
Comments
---
vs Melissan
<Vanilla (2.5.16.6)> *I mean vanilla for no mod at all
She uses Stoneskin ▶ We use Breach ▶ and Greater-whirlwind ▶ Victory
<EEex.exe with no any other mod(even your Spell Menu)> still 2.5.16.6 of course
Breach doesn't work anywhere
(Chat log says it worked, though)
She gets no damage from any physical attack
and uses stoneskin again and again with very short term consistently
But!
If we use Pierce Magic(or Ruby Ray of Reversal, Khelben's Warding Whip or so),
chat log would say dispelled *twice*
and then our physical attacks could get to her own skin,
and she will not use stoneskin anymore
---
I'm sorry if what I've written is already known issue,
but I'm not fluent English user so that I couldn't read all the comments of this article
(slow reader)
Happy 2021
while I don't know how to do it via global hook (if it's even possible) you could achieve it using following weidu patching on all castable spells, using Invoke Lua opcode: and than create MYFUNC function that will alter magic resistance (EEex_ApplyEffectToActor, opcode 166, timing 0, duration 0) based on stat value. It will be called before other spell effects are applied, so I'm pretty sure the spell resistance change will affect them. You need to know Lua language and study EEEX code to write stuff like this tho.
I have some questions regarding EEEX usage:
1. Does anyone know offset to read currently equipped launcher resource name? (or slot occupied by it) As a test I've adopted EEex_IterateActorItems code so that it returns equipped slot res:
but if the currently equipped weapon is launcher that requires ammo, the printed message is the ammo RES instead of launcher RES. I could iterate all 4 weapon slot and check if they are launchers but that wouldn't be accurate if there is more than 1 launcher of the same type equipped.
2. Is there a list of all possible action IDs returned by EEex_GetActionID?
3. Any way to hook a lua function that would trigger when cre has target set with intention to attack, but before making weapon attack rolls?
4. Is it possible to fool the engine into thinking that particular effect applied by EEEX comes from currently equipped item? I've tried something like this with timing = 2: But this effect does not go away when the SW1H01 is unequipped. It's not saved directly on character when save file is viewed in Near Infinity, which leads me to believe that maybe it's some flag that is missing here or something like that.
Yes: there is ACTION.IDS.
Yes.
great.
edit: question 4 is no longer relevant because the same thing can be already achieved using opcode 407 (Cast spell on effect remove) with timing mode set to 2 (while equipped), which casts spell the moment item is unequipped.
using EEex_2DALoad more than once crashes the engine. Example (tested by pasting in plain M_ file) works fine when only 1 2da file is loaded.
feature request:
a way to disable hardcoded spell interruption on taking damage. Existing CONCENTR.2DA modes are not flexible enough (according to kjeron and Bubb, mode = 1 is also bugged, so the only option is either 100% chance or using static 15 in formula).
When disabled completely I think we would be able to write custom formula using existing EEex functionality (detecting damage opcode, interrupting spell as needed via forced hurt animation)
I've fixed this in master, thanks for reporting!
To everyone wondering why this project hasn't moved in a while: I'm currently waiting on v2.6's release before I continue my work. Anything I do now is effectively pointless, (as the 64-bit jump ensures that any hooks I write will be obsolete in the next executable), and I can't start work on v2.6 until the PDB is dropped with its full release.
Did I mention that EEex is awesome? :P
If there will be problems with porting EEex to v2.6, I will stay with 2.5 forever, that's for sure. It's incredible how much EEex opens up modding possibilities.
btw. ignore the above feature request - it's already possible to make spell uninterruptable by hit thanks to EEex_AddScreenEffectsGlobal.
edit:
After some further testing having CONCENTR.2DA working opposite way to mode = 0 (none of the attacks interrupting spellcasting) would be preferable. Implementing it by blocking damage opcode via EEex_AddScreenEffectsGlobal requires to directly modify target's HP, display Damage string etc. (since opcode 12 can't be used for this purpose). Doing it this way would require taking tons of factors into account (rolling, flags, damage reduction, resistances etc.) So yeah, the original feature request is still valid.
question: does anyone knows roughly what EEex_GetDistanceIsometric value is an equivalent of 1 feet and default visual range?
The engine hardcodes 0x1C0 (or 448) as the default visual range. No idea what it considers a foot, though if there's a known value (in feet) for the visual range you should be able to figure it out.
Vertical: 12 = 1 foot
Default visual Range = 28 ft
Thanks, it's been a while since I worked with this stuff, that rings a bell.
For Logitek: Note that EEex_GetDistanceIsometric() will consider 1 foot = 16, it uses the horizontal-scale.
I thought maybe I can just change the incoming effect flag to ignore mirror image and then replicate the effects of mirror image in my code, but that seems a bit overkill. any ideas?
Thanks
Not easily; ScreenEffects kicks in right after probabilities have been rolled, (and passed), but before all the other checks. The damage effect essentially forces a successful save, (even if it didn't allow one), when it hits a mirror image. I might co-opt the special field of ScreenEffects to allow you to specify where in the application process it intercepts the effect, (I.E. after probability, after save checks, etc.) - that should allow you to do what you want.
Whilst on the subject of saves and checks, is there currently a way to customise the saving throw rolls, or attack rolls? For example if you wanted to change the way saving throws worked or how attack rolls are calculated?
What I am thinking about is trying to implement different 'types' of bonuses. e.g. AC bonus but actors can have different types (natural armor/deflection etc.), some of which stack and some don't. (I would specify the 'type' in some bit of AC bonus opcode, then LUA can iterate and calculate the final AC as I want).
Thoughts very welcome.
Thanks,
As for the AC and/or attack bonus, it's a bit more tricky since you need to apply it before attack rolls. So using EEex_AddActionHookGlobal and applying effects based the current action and various factors may be needed. It would be costly solution though, performance wise, I think, and it requires cleaning bonuses once they are no longer needed, so not sure if this is the right approach.
For attacks rolls I don’t really see an easy solution, as there is no effect to screen before the roll is successful. Maybe you can screen actions and when an actor attacks run all your own code to complete the attack, then cancel the engines attack? I assume you would need to code everything from running the attack animation etc, which seems like too much work...
or perhaps just detect an attack and apply bonuses to AC/thaco etc (which Is what I think you are suggesting?). But as you say you’d need to remove them again after the attack is done. Not sure if there are easy ways to apply bonuses just for the next attack for example. So in the case of attack rolls it really would be useful to be able to hook those and replace their coding.
Could anyone please point to offsets (structure and names used in EEex docs) where this timer data is stored? (round, modal actions, spell intervals, spell cooldown, possibly attacks, although these were not mentioned in Bubb's post)
My best guess is to look at M__EEex.lua, B3_Timer.lua, and B3_Timer.menu.
Thanks. Indeed, there are even dedicated functions to read this data already:
EEex_GetActorModalTimer
EEex_GetActorSpellTimer
EEex_GetActorCastTimer
I can add new skills and these show up in game, but having issues in the level up screen. During char generation, i can add points to extended skills but not remove them again (the minus button does nothing). Also on level up screen there is a curious effect where adding or removing points from extended skills also increases/decreases a normal thief skill in the list above it (so the points are 'used' twice).
My skills with UI are not good enough to see where this is going wrong. Anyone else had this issue?
To fix the minus button not working for extended skills, I removed:
"chargen.levelingUp" from the below chunk, which stops the minus button working in the char gen screen but works okay in the level up screen. Im not sure what the rationale for that is but works fine with it removed for me.
Then to fix the wierd behaviour with two skills incrementing up, I revised the below:
(... = firstExtendedSkillIndex + i + 1) rather than -1
The reason that chargen.levelingUp condition is there is to prevent a worse bug. In certain games (like BG2EE), if you use the minus button to subtract from an extended skill during chargen, it decrements the skill without refunding the skill points. This doesn't happen during level up. It also doesn't happen in BG1EE, I don't think.
I reckon porting EEex to 64-bit will be a hell of a project in itself. Would it even be reasonable to do at all? I wonder what @Bubb's thoughts are on this with the 2.6 beta having been released now.