I'm happy to see you are still around as well. The couple of small mods I put out in the day ended up being as slick as they were in large part to your help. The EE games certainly offer a lot of expanded modding capabilities that we all wanted for years back in the day. There's been some incredibly creative stuff created in the last decade since I have been away, so I'm jumping back in and sampling the best. My plan is to get a stable install and then go through the saga with some friends "book club" style. Play a chapter/set of quests and then discuss.
It seems like right when a character finishes casting the spell, the data for the spell gets copied to somewhere else. Could you make hook that gives us the offset where the spell's data got copied to so that we could modify it?
It has to do with the method of effect delivery. Spells that use projectiles first copy and shuffle along their effects to a projectile instance, then that projectile "delivers" the effect copies to anything it hits. The problem arises with non-projectile delivery, where the engine directly adds the effects to targeted actors. I'm looking into how to capture all these circumstances under one hook mechanism.
I'd be fine with a hook like this even if for now it only worked with spells that use projectiles. I could still do things with this like change projectile area of effect size or projectile speed.
There's a new opcode in master, #408 (ProjectileMutator)! This effect works similar to Opcode #403 (ScreenEffects), in that the resource field of the opcode points to a Lua object. For example, say you attach an Opcode #408 with the resource field "B3FIRE" to a creature, the object template looks like this:
(Note: You put this in UI.MENU or a M_*.lua file, and the name must be completely uppercase)
The three functions do as follows:
typeMutator => projectileType is the IDS value of a projectile about to be created. The return value of this function, (if you supply one), overrides the projectileType to what you supplied. Note that once the projectile type is overridden, no further typeMutators are called.
projectileMutator => projectileData is one of the CProjectile structures, as outlined here. This function allows you to mutate the raw projectile fields however you wish. Returning true from this function prevents further projectileMutators from being called.
effectMutator => effectData is a CGameEffect structure. This function is called when an effect is being added to the projectile, and allows you to mutate the raw effect fields. Returning true from this function blocks the effect from being added to the projectile, and stops further effectMutators from being called, (on this CGameEffect instance).
originatingEffectData is the CGameEffect structure of the source Opcode #408 effect.
creatureData is the CGameSprite structure of the projectile's source creature.
Note that these hooks are only called in regards to a sprite's Spell() and ForceSpell() actions, and only pertain to the effects delivered via projectiles.
@OlvynChuru: All the above is experimental, if you have any suggestions on how to improve this system I can add more functionality.
EXCHARGE (for use with opcode 402) - Adds charges to an item in the character's inventory.
EXMODAOE (for use with opcode 408) - Increases the area of effect of your spells.
(Unfortunately, it doesn't make the area of effect indicator bigger; I don't know how to change that.)
There's a problem with EXMODAOE: it changes the area of effect regardless of whether the projectile actually has an area of effect. This causes problems when it's used with single-target spells. How can I check whether the projectile has an area of effect?
Ok, well, figuring out the type of the projectile was harder than I thought. The engine doesn't store any type identifier on the projectiles, so I had to go crazy and manually find / match the projectile vftables. All of this is packaged up into EEex_IsProjectileOfType(), used like so:
if EEex_IsProjectileOfType(projectileData, EEex_ProjectileType.CProjectileArea) then
end
The EEex_ProjectileType constants match up with the internal projectile names, though some are unavailable due to not really existing in the engine anymore, (these are commented out in EEex_Pro.lua).
Also, the Lua object template signature has changed:
Note that the newly added UPDATE_AOE allows you to detect when the engine is previewing a projectile in order to display the AoE marker. If you limit your projectile mutations based on castings, make sure you don't let this mode count as a cast, (but still do the mutation so the engine displays your modifications).
@OlvynChuru: Since I altered the Lua object template your pull request is now a little borked, once that is updated to match master I'll merge it, (also, I preemptively implemented your edit in EEex_Pro.lua, so it is no longer needed in the PR). I figured breaking the PR now and fixing it before merge was better than merging and immediately breaking it. Thanks for contributing, as always
Is there any way to find the RESref of a weapon where the damage is from the base weapon and not added by an effect?
I know I can use EEex_DemandResData(parent_res, x) but it seems base weapon damage doesn't write a parent_res to its effects. Any workaround you clever guys have come up with?
@nonlinearcoast: Turns out that info isn't really kept-track of by the engine. I could do an EEex hook that puts something like "EEEX_DAM" into the parent resource field, and then fills Resource2 and Resource3 with the weapon resref and the launcher resref respectively - if that is sufficient and doesn't break some engine behavior somewhere.
@Bubb Thanks for your input that sounds like a good solution - What I'm trying to do is look up the flags on the weapon for example 'Silver/Iron' etc. and use that in the code for things like overcoming damage reduction.
@nonlinearcoast: Alright, those fields are now filled in master! Example of how you access them via a Screen Effects opcode:
function B3EFF(originatingEffectData, effectData, creatureData)
local m_sourceRes = EEex_ReadLString(effectData + 0x90, 8)
if m_sourceRes == "EEEX_DAM" then
local curWeaponIn = EEex_ReadLString(effectData + 0x6C, 8)
local pLauncher = EEex_ReadLString(effectData + 0x74, 8)
print("curWeaponIn: '"..curWeaponIn.."'")
print("pLauncher: '"..pLauncher.."'")
end
end
While I'm asking silly questions of people who know the game engine way better than I do:
Is there any way to get information out of the CHARGEN screen? (Either with or without EEEx.) Presumably setting a global doesn't work because the game hasn't started; can one write to the game ini and then read it back in once the game starts?
@DavidW: What sort of info do you want to get out of the chargen screen? Some of it is accessible via the normal Lua environment, (since the engine has to display it). And interestingly, the GLOBAL's system actually does work in the chargen screen - if you set one via a C:SetGlobal() call, it appears ingame.
EEex can grab additional values via memory reads, if it turns out you can't get to the info normally.
Globals actually work?! I was sufficiently confident they wouldn't, I didn't think to check. (Is that with EEEx, or in the unmodded engine?)
What I'd ideally like (thinking this through further) is a way to attach a stat or splstate to a character on character creation, though there's probably something I can do through GLOBAL.
I've been working on tweaking the spell system for the past couple of days - EEex now allows traps / doors to be targetable via spells. With the addition of some new trap-manipulating functions, and Invoke Lua now functioning on triggers/doors/containers, it's possible to create a spell that disarms traps!
For example, an Invoke Lua (Opcode 402) utilizing the following function can disarm any trap normally removable by a thief, (all the prerequisites to disarming are defined separately, so you can remove one/more of them if you wish):
function B3TRAP(effect, object)
local objectID = EEex_GetActorIDShare(object)
local isSprite = EEex_IsSprite(objectID, true)
local isActive = EEex_IsTrapActive(objectID)
local isDetectable = (not EEex_IsTrapFlaggedNondetectable(objectID)) and EEex_GetTrapDetectDifficulty(objectID) ~= 100
local isDetected = EEex_IsTrapDetected(objectID)
local isDisarmable = EEex_GetTrapDisarmDifficulty(objectID) ~= 100
if isSprite then
Infinity_DisplayString("Tis' but a scratch!")
return
end
if not isActive then
Infinity_DisplayString("Object not trapped")
return
end
if not isDetectable then
Infinity_DisplayString("Trap warded against scrying")
return
end
if not isDetected then
Infinity_DisplayString("Trap concealed")
return
end
if not isDisarmable then
Infinity_DisplayString("Trap warded against spells")
return
end
EEex_DisarmTrap(objectID)
Infinity_DisplayString("Trap disarmed")
end
@Bubb I sent you a pull request! I made it possible to modify the projectiles of items and ranged weapons, rather than just spells.
Here's a Gesen Bow which fires lightning bolts that deal the damage of the equipped arrows (normally ranged weapons use the projectile of the ammo, not the launcher, but in this case the bow overrides the arrow's projectile with a lightning bolt).
I've been working on tweaking the spell system for the past couple of days - EEex now allows traps / doors to be targetable via spells. With the addition of some new trap-manipulating functions, and Invoke Lua now functioning on triggers/doors/containers, it's possible to create a spell that disarms traps!
For example, an Invoke Lua (Opcode 402) utilizing the following function can disarm any trap normally removable by a thief, (all the prerequisites to disarming are defined separately, so you can remove one/more of them if you wish):
This is amazing! I have longed for this option for years. No more deadweight thiefs on my team! Yay!
Unfortunately, I can't get Bupp's new disarm trap lua to work. I have copied his B3Trap code into a lua file, placed it in override and now have linked it as Olvyn suggested. I was trying to add this onto the Knock spell. I have attached the files below if anyone can point me to what I am doing wrong.
@bubb do you now anything about enabling 8-9 priest spells? My ui knowledge is limited but it seems simply extending the existing code in ui.menu and adding some buttons isn’t enough to get it to display these. Is there some hard coded limitation to the functions there?
When an enemy goes invisible, all visual animations attached to it (via opcode #215, #204 and the like) should go invisible as well (and come back when it's no longer invisible). Note that this can already be done nowadays, but it's somewhat complicated / tricky...
A new Spell() Action that can be used to cast a spell at a location offset from the target object point, i.e.: Spell(O:Target*,P:Offset*,I:Spell*Spell)
Yes, I was referring to your implementation (the only one available so far...? Actually, it's probably impossible to implement it in a different way... Anyway, it's so much needed...)
@Luke93:
for (1) SCS does this already (perhaps that's what you're referring to?) But yes, it's complicated (and requires buy-in from the scripts).
Currently that behavior is hardcoded into the minor globe, shield globe, and iirc the sanctuary opcodes if you leave the .vvc resource field blank. The animations disappear if the opponent disappears invisible and then resume playing when the opponent reappears even if 1/2 invisible with improved invsibility. The animations do not disappear at all if a party member playing them becomes invisible. It might be possible do outsource any persistant animations to external (repeating) effects/spells and then block them from playing while an opponent is invisible but it's a hacky solution at best.
Imho it would make the most sense to have a .vvc flag possibly at 0x18 that hides the associating .bam while the target (if not allied) is completely invisible.
@Bubb Would it ever be possible to make an opcode for EEex that can suppress sound and string effects without having to target a specific opcode, eff, vvc etc that they are used in but simply the specific sound resource key or string number. Essentially I want to block the engine from using specific sounds or strings in specific situations.
@Bubb can you add/edit some opcode so that it can set the .cre-ature files original class -flags ? I am talking about the files offset's 0x000c bits 3, 4, 5, 6, 7 and 8.
@TheImp: EEex can set any field, given you know where it is in memory. Throw the following function into an M_*.lua file and put that in the override folder. Invoke it from a spell via the following:
function B3OCLASS(effectData, creatureData)
local param1 = EEex_ReadDword(effectData + 0x18)
local param2 = EEex_ReadDword(effectData + 0x1C)
local creFlags = EEex_ReadDword(creatureData + 0x424)
if param2 == 0 then
EEex_WriteDword(creatureData + 0x424, EEex_UnsetMask(creFlags, param1))
elseif param2 == 1 then
EEex_WriteDword(creatureData + 0x424, EEex_SetMask(creFlags, param1))
end
end
Technically this can set any bit in the creature flags, given you use the correct mask.
Note that the memory offset might change in future versions. But with v2.6 being a complete shift from 32bit to 64bit, EEex will have to be rebuilt from the ground up anyway.
Comments
I'd be fine with a hook like this even if for now it only worked with spells that use projectiles. I could still do things with this like change projectile area of effect size or projectile speed.
(Note: You put this in UI.MENU or a M_*.lua file, and the name must be completely uppercase)
The three functions do as follows:
originatingEffectData is the CGameEffect structure of the source Opcode #408 effect.
creatureData is the CGameSprite structure of the projectile's source creature.
Note that these hooks are only called in regards to a sprite's Spell() and ForceSpell() actions, and only pertain to the effects delivered via projectiles.
@OlvynChuru: All the above is experimental, if you have any suggestions on how to improve this system I can add more functionality.
New functions:
EXCHARGE (for use with opcode 402) - Adds charges to an item in the character's inventory.
EXMODAOE (for use with opcode 408) - Increases the area of effect of your spells.
(Unfortunately, it doesn't make the area of effect indicator bigger; I don't know how to change that.)
There's a problem with EXMODAOE: it changes the area of effect regardless of whether the projectile actually has an area of effect. This causes problems when it's used with single-target spells. How can I check whether the projectile has an area of effect?
The EEex_ProjectileType constants match up with the internal projectile names, though some are unavailable due to not really existing in the engine anymore, (these are commented out in EEex_Pro.lua).
Also, the Lua object template signature has changed:
The projectileData in effectMutator is new, and each function now has a source parameter. The source parameter is one of these constants:
Note that the newly added UPDATE_AOE allows you to detect when the engine is previewing a projectile in order to display the AoE marker. If you limit your projectile mutations based on castings, make sure you don't let this mode count as a cast, (but still do the mutation so the engine displays your modifications).
@OlvynChuru: Since I altered the Lua object template your pull request is now a little borked, once that is updated to match master I'll merge it, (also, I preemptively implemented your edit in EEex_Pro.lua, so it is no longer needed in the PR). I figured breaking the PR now and fixing it before merge was better than merging and immediately breaking it. Thanks for contributing, as always
I know I can use EEex_DemandResData(parent_res, x) but it seems base weapon damage doesn't write a parent_res to its effects. Any workaround you clever guys have come up with?
Is there any way to get information out of the CHARGEN screen? (Either with or without EEEx.) Presumably setting a global doesn't work because the game hasn't started; can one write to the game ini and then read it back in once the game starts?
EEex can grab additional values via memory reads, if it turns out you can't get to the info normally.
What I'd ideally like (thinking this through further) is a way to attach a stat or splstate to a character on character creation, though there's probably something I can do through GLOBAL.
For example, an Invoke Lua (Opcode 402) utilizing the following function can disarm any trap normally removable by a thief, (all the prerequisites to disarming are defined separately, so you can remove one/more of them if you wish):
Here's a Gesen Bow which fires lightning bolts that deal the damage of the equipped arrows (normally ranged weapons use the projectile of the ammo, not the launcher, but in this case the bow overrides the arrow's projectile with a lightning bolt).
This is amazing! I have longed for this option for years. No more deadweight thiefs on my team! Yay!
Unfortunately, I can't get Bupp's new disarm trap lua to work. I have copied his B3Trap code into a lua file, placed it in override and now have linked it as Olvyn suggested. I was trying to add this onto the Knock spell. I have attached the files below if anyone can point me to what I am doing wrong.
A couple of other ideas:
for (1) SCS does this already (perhaps that's what you're referring to?) But yes, it's complicated (and requires buy-in from the scripts).
Yes, I was referring to your implementation (the only one available so far...? Actually, it's probably impossible to implement it in a different way... Anyway, it's so much needed...)
The possibility to use "ANIMATE = specified value" with opcode #318, #324 and #326.
Currently that behavior is hardcoded into the minor globe, shield globe, and iirc the sanctuary opcodes if you leave the .vvc resource field blank. The animations disappear if the opponent disappears invisible and then resume playing when the opponent reappears even if 1/2 invisible with improved invsibility. The animations do not disappear at all if a party member playing them becomes invisible. It might be possible do outsource any persistant animations to external (repeating) effects/spells and then block them from playing while an opponent is invisible but it's a hacky solution at best.
Imho it would make the most sense to have a .vvc flag possibly at 0x18 that hides the associating .bam while the target (if not allied) is completely invisible.
Opcode -> 402 (Invoke Lua)
Parameter1 -> Original Class mask
Parameter2 -> Mode (0 = unset, 1 = set)
Resource -> B3OCLASS (capitalization matters)
Technically this can set any bit in the creature flags, given you use the correct mask.
Note that the memory offset might change in future versions. But with v2.6 being a complete shift from 32bit to 64bit, EEex will have to be rebuilt from the ground up anyway.