[MOD] EEex (v0.10.2-alpha)
Bubb
Member Posts: 1,005
Overview
EEex is an executable extender for Beamdog's Enhanced Edition of the Infinity Engine. Its goal is to externalize certain parts of the engine to grant modders a greater degree of control over otherwise hardcoded mechanics.
EEex's core component does not make any gameplay changes itself – it merely enables other mods to do so. The installer provides additional components that make gameplay changes.
Compatibility
Operating systems:
- Windows — Yes (native)
- Linux — Proton / Wine with the appropriate option toggled in InfinityLoader.ini
- MacOS — Wine? (untested)
- BG:EE v2.6.6.0, BG2:EE v2.6.6.0, and IWD:EE v2.6.6.0 — EEex versions ≥ v0.9.0-alpha
- BG:EE v2.5.17.0, BG2:EE v2.5.16.6, and IWD:EE v2.5.17.0 — EEex versions < v0.9.0-alpha
Download
The latest EEex version can be downloaded from the Releases page. The installer is located under the collapsible "Assets" menu.
A WeiDU installer has been intentionally omitted from the master branch to prevent it from being accidentally installed, as it contains work-in-progress features and is not guaranteed to be stable.
Installation
EEex is distributed as a Gibberlings3 installer. After running the setup file, simply point it to your game directory and click "Install".
- Older versions of EEex are distributed without the Gibberlings3 installer. Extract the archive's contents into your game directory and run the setup file to install.
Stability
While crashes are extremely rare, they may still occur. If you encounter a crash, or a bug with EEex, please report the issue to EEex’s GitHub Issues page or EEex’s thread on the Beamdog Forums.
When reporting, please:
- Upload:
- WeiDU.log — This is in your game directory.
- A save that exhibits the issue — Saves are found in C:\Users\[user name]\Documents\[game folder]\save. Zip the entire save folder.
- (If applicable) The generated crash .dmp — This is usually found in C:\Users\[user name]\Documents\Infinity Engine - Enhanced Edition\crash.
- WeiDU.log — This is in your game directory.
- And:
- Provide a series of steps that reproduce the issue.
- Provide a series of steps that reproduce the issue.
How EEex Works
EEex uses a loader program to modify the game's executable after it has been placed into memory. The exact modifications depend on the version of EEex, and any installed mods that make use of EEex's capabilities.
Due to EEex's use of in-memory patching, antivirus solutions might flag InfinityLoader.exe / EEex.exe as a virus. This is a false positive.
Please note: The following links are NOT intended to be used for installing EEex. The loader programs are bundled with EEex and are automatically installed alongside it.
- InfinityLoader — EEex versions ≥ v0.9.0-alpha
- EEexLoader (thanks mrfearless!) — EEex versions < v0.9.0-alpha
Documentation
EEex makes extensive use of the EE Lua environment, with most of its functionality implemented as Lua code. Features include new Lua functions, opcodes, scripting actions, triggers, and objects. Please see the EEex Documentation for an overview of EEex's features.
The above documentation is a work in progress. If you wish to contribute, visit the contributing page for details.
Post edited by Bubb on
37
Comments
If possible, I would like to have some more information about Saving Throws in the combat log (bonuses, penalties and displaying the roll no matter if you failed or succeeded). It is painful to know if a modded effect is working or not because requires tons of trial-and-error to make a poor estimative (as you can see in a discussion here)
- Get value of characters stats (from STATS.IDS)
- Get is set of characters spellstate (from SPLSTATE.IDS)
- Get characters EA/GENERAL/CLASS/RACE/ALIGNMENT/GENDER/ALIGNMENT IDS index (as opposed to their current string references).
- Get Memorized spell table of characters.
- Get Known/memorized spell tables for innate abilities.
- MemorizeSpell(level,"resref"), as opposed to the current MemorizeSpell(level,index), same for Unmemorize.
Infinity_DoString(chunk) - Runs whatever LUA code is embedded in the given string. Self-modifying code anyone?
I was wondering if I could take my LUA endeavors further and integrate them into BCS scripts. And well, I did just that.
The above is a BCS script that prints "No, I'm sorry, none of them sound familiar." to the log if the player has their cursor over the second portrait slot.
Oh, this is awesome!
Edit:
I'm not sure about psionics-- you would need to ask @subtledoctor about that-- but I would be so super happy with @kjeron 's list. So +1 to that
a) My plan is to make these modifications on every binary that isn't locked-down. So, Windows, Mac, and Linux. The mobile versions cannot be modified to the extent I am doing. I am currently using the Windows binary as my base, and I will port over my changes to the other binaries when I am done.
b) Mostly everything. My original purpose of this thread was to outline new LUA functions, (those that pertain to the UI), but I have recently been branching out into other behaviors of the engine. For example, in my last post I showed that I extended the LUA environment into the scripting environment, basically allowing for dynamic scripts; almost like ToBEx's Assign() and Eval() scripting triggers / actions, but more powerful. I'm not completely done with this concept, but I'm getting there. As for new opcodes, I might be able to create some that mirrors other parts of the engine, such as variable getting and usage, yes.
New triggers / actions (they are implemented as both):
Bubb_LUA(S:Chunk*) - Runs some LUA code provided by the given string. The trigger version of this checks the "trigger" LUA boolean after running the chunk, and succeeds if it is set to true.
Bubb_StoreObjectStat(S:Variable*,O:Object*,I:Stat*STATS) - Stores the provided stat from the given object in the defined LUA variable.
Bubb_StoreGlobal(S:Variable*,S:Global*) - Stores the given global into the defined LUA variable.
Bubb_StoreLocal(S:Variable*,S:Local*) - Stores the given local from the provided object into the defined LUA variable.
And perhaps the most important change of them all is the ability to dynamically override triggers / action parameters at runtime. This is accomplished by setting special LUA variables, here's an example of how it works:
The above script brings the XP value of the script owner to the exact value of Player1's XP.
I believe this system is good already, but I wanted to ask you all a question: is there any other triggers / actions you can think of that would help expand it even further? Any other information types you would like to be able to store... etc.
Edit: Oh, and I haven't ditched all of your UI suggestions. I'll implement them after I've finished my scripting stuff, (which I'm almost done with, btw).
Also, totally out of left field: spell exclusion flags:
http://gibberlings3.net/forums/index.php?showtopic=28382
Now, bit 14 excludes trueclass bards/sorcerers/mages. Unfortunately, unlike with mage kits, it also excludes bard kits. Would it be at all possible to make it only exclude trueclass bards? That one thing would allow me to create a mod with bard exclusive spells, which I think would be fun. Also, is it possible to make those exclusions that don't work, work?
Just figured I'd ask
I realize that the EE engine on the whole is not nearly as prone to it but I'm still a little concerned that we might have the old stutter bugs again when a lot of these things are being called at once.
well, you've requested this trigger in your previous post:
it's not really needed because this is already possible with Bubb_LUA (Bubb, correct me if I'm wrong). For example this OR section: can be rewritten like this to work as you requested: trigger = false means that the Bubb_Lua will return false and end the block. For easier understanding here is the above Bubb_Lua trigger code with formatting instead of 1 line (it's normal lua code) Alternatively, if you're doing lots of checks like this in your scripts you can for example prepare M_*.lua file with pre-made function that will do above mentioned stuff and just call it like this instead of repeating the same code over and over: This is just an example, far more complicated stuff can be done with access to lua from within BCS.
@MoonWolf: I am watching performance very closely to make sure my new functions don't impact the game. You are correct in that I am currently having the engine compile and run the LUA chunk every time it executes it; not very efficient, but the engine actually does this itself in several places as well. To test performance I spawned 500 creatures running my script, and I felt no noticeable slowdowns:
If problems do occur with arbitrary LUA execution I can easily have a script shove all the LUA code into a M_*.LUA file, and change my Bubb_LUA() function to execute already compiled functions. This would mirror exactly how the GUI code works, so the game will run just as fast as it would without my functions in this scenario.
@swit: You are correct that an exclusive OR can be replicated with my Bubb_LUA() function, but I believe this would only work IF the triggers you are working with deal with fields that you can store. I believe @Grammarsalad wanted any combination of triggers to be dealt with in this manner. In this situation, I think a dedicated exclusive or is the only way. I'll look into it, but I don't know if I can accomplish that, as it has to do with changing how scripts are fundamentally processed.
[Block 1]
OR(3)
A
-B
-C
...
[Block 2]
OR(3)
-A
B
-C
...
[Block 3]
OR(3)
-A
-B
C
Heh, it adds up
Edit: I'm really interested in the exclusion flag stuff, though...
The implementation you see in the XP example is a special trigger / action which could technically be used by the LUA environment by calling
C:Eval('Bubb_StoreObjectStat("xp",Player1,STATS.XP)')
The problem with trying to use Eval is that any concept of the "currently selected party member" is nonexistent.The good news is that I have all of the internal shenanigans to do with fetching stats done, so all I have to do is implement a LUA-side function which uses a Creature ID instead of a Object IDS value. This way, you could pass currentID into the function and get the stats of the selected party member. It would work like this:
local xp = Infinity_GetStat(currentID, STATS.XP)
Also, I've been working on some more scripting stuff:
The above script shows how objects can be stored and referred to using the LUA environment. The script doesn't really do anything other than having the script owner pick a random target once and then move to that object for all of eternity.
I believe this recreates IWD2's SetMyTarget action and MyTarget object.
@kjeron, while this is extremly impressive peace of code (like damn, I’m saving it right away - I’m sure something here will be useful for me in future) but the example XP matching code is something that can be written in a minute by anyone, while your opcode solution is a riddle that few people in the entire modding scene would be able to solve Let's say I'd like to match 75% of player1 XP for party members XP adjustment. With the above posted Bubb_LUA example code it would be a matter of adding: Doing the same with opcodes would be another riddle.
@Bubb, absolutely fantastic.
*shivers*