Skip to content

[MOD] EEex (v0.10.2-alpha)

1161719212249

Comments

  • OlvynChuruOlvynChuru Member Posts: 3,079
    Okay, so I tested EEex_ReadLString(EEex_GetActorShare(actorID) + 0x3596, 8) on an enemy while it was doing a SpellRES action, and it didn't get the RES. It only seems to work for party members.

    @Bubb Where or how is the info stored about the spell an actor is casting with SpellRES?
  • BubbBubb Member Posts: 1,005
    @OlvynChuru: This grabs the first string arg of the currently executing action:
    function B3PrintStringArg()
        local actorID = EEex_GetActorIDCursor()
        if actorID == 0x0 then return end
        local actionStringArg = EEex_ReadLString(EEex_ReadDword(EEex_GetActorShare(actorID) + 0x344), 8)
        Infinity_DisplayString("Current String: "..actionStringArg)
    end
    

    I don't believe the engine stores the current spell cast anywhere else. The action-overlay shown on party portraits uses a combination of checking the current action ID and the first string arg, so I assume that's the "correct" way to do it.

    Also, it appears the engine always fills the string arg, even when using the straight ID version of the spell actions, so I believe it is safe to directly poll the spell that way.
  • _Luke__Luke_ Member, Mobile Tester Posts: 1,535
    kjeron wrote: »
    OlvynChuru wrote: »
    This is already possible. The problem with Black Blade of Disaster is that effects with a higher timing override effects of the same opcode with a lower timing. Thus, the character's normal long sword proficiency effect (with timing = 9) overrides the Black Blade of Disaster equipped proficiency effect (with timing = 2). However, if you give the Black Blade of Disaster spell a proficiency effect granting 5 stats in long swords with timing = 10 (instant, limited, in ticks) with a duration 15 times the normal duration of the spell (15 ticks per second), it will grant the proficiency correctly.
    A normal second-based duration works as well. Either way you retain that higher proficiency if you change weapons until the spell expires, which still isn't correct.

    But you cannot change your weapon if you're currently equipped with a magically created weapon (opcode #111).... So what's the problem with this workaround?
  • kjeronkjeron Member Posts: 2,368
    edited June 2019
    Luke93 wrote: »
    But you cannot change your weapon if you're currently equipped with a magically created weapon (opcode #111).... So what's the problem with this workaround?
    Sure you can, just cast a one-use item creation spell, like shocking grasp.
  • BubbBubb Member Posts: 1,005
    @OlvynChuru: Great work! Merged :)
  • BubbBubb Member Posts: 1,005
    Random thought: Would an "Immunity to Effect" Opcode that calls a Lua function to determine immunity be helpful for anything?

    Basically an Opcode #101 that would be evaluated on every effect application based on the function's return value.
  • OlvynChuruOlvynChuru Member Posts: 3,079
    @Bubb Do you mean, an opcode that grants immunity to the specified effect if the Lua function returns true?

    What would be passed to the Lua function?
  • BubbBubb Member Posts: 1,005
    @OlvynChuru: I was thinking more general, as in it screens every effect applied to the target, but yes.

    The function would receive the creature data / actorID of the target creature + the data of the effect that is being screened. Return value of true would block the effect from applying, and false would, of course, allow its application.

    Since the whole opcode immunity / type immunity systems are so hardcoded I was wondering if lending the flexibility of a Lua function would do anything interesting.
  • OlvynChuruOlvynChuru Member Posts: 3,079
    @Bubb Sounds great! Go for it! :)
  • GrammarsaladGrammarsalad Member Posts: 2,582
    Just want to say that I'm super happy to see this development ( @OlvynChuru adding to the project). This is just awesome and really appreciated.

    Also, I'm a bit behind on the updates. I seem to recall that there was an opcode in the works that would execute a script directly. I think @Bubb said that it was so easy to do that it was surprising that there wasn't already an opcode for this. Has this been completed?
  • BubbBubb Member Posts: 1,005
    @Grammarsalad: Funny you would ask that; I was literally looking into that early today.

    I believe OlvynChuru first asked me if we could have a custom Opcode #298, which instead of being hardcoded to execute cut250a.bcs, could be used to execute any script of our choosing.

    I guess it depends on the goal of the script. Opcode #298 is actually much more complex than it first seems, it has several tasks:

    1) It stores party locations for use in returning from the pocket plane.
    2) Puts the engine into "cutscene" mode.
    3) Forces the protagonist to execute an uninterruptible script action 'StartCutScene("cut250a")'

    If the goal is simply starting a cutscene from an Opcode, I suppose it would be easy to rig Opcode #298 to allow custom scripts.

    The main problem with the "arbitrary script execution" idea is that script actions have to be executed in the context of a game object. Whether this be the area itself, or forcibly put upon a creature, (as in what cutscenes do with CutSceneId()). There's a lot to unpack here when I expected it to be simple - I'll see what I can come up with ;)

    PS: If these semi-rants get a little carried away, it's because they help me organize my thoughts on a problem.
  • GrammarsaladGrammarsalad Member Posts: 2,582
    edited June 2019
    Bubb wrote: »
    @Grammarsalad: Funny you would ask that; I was literally looking into that early today.

    I believe OlvynChuru first asked me if we could have a custom Opcode #298, which instead of being hardcoded to execute cut250a.bcs, could be used to execute any script of our choosing.

    I guess it depends on the goal of the script. Opcode #298 is actually much more complex than it first seems, it has several tasks:

    1) It stores party locations for use in returning from the pocket plane.
    2) Puts the engine into "cutscene" mode.
    3) Forces the protagonist to execute an uninterruptible script action 'StartCutScene("cut250a")'

    If the goal is simply starting a cutscene from an Opcode, I suppose it would be easy to rig Opcode #298 to allow custom scripts.

    The main problem with the "arbitrary script execution" idea is that script actions have to be executed in the context of a game object. Whether this be the area itself, or forcibly put upon a creature, (as in what cutscenes do with CutSceneId()). There's a lot to unpack here when I expected it to be simple - I'll see what I can come up with ;)

    PS: If these semi-rants get a little carried away, it's because they help me organize my thoughts on a problem.

    I was thinking of something more like 3--that is, just forcing some x to execute a script--but maybe:

    3a) Execute any arbitrary script (perhaps named in the Resource key)

    3b) Rather than the Protagonist having to execute the script action, perhaps with targeting taken from object.ids according to the value in a parameter or special? That is, the targeted object will execute the script?

    I don't know if it would be better as a new opcode or a modification of #298...
    Post edited by Grammarsalad on
  • CrevsDaakCrevsDaak Member Posts: 7,155
    I think it'll work best as a new opcode, without having it to do anything aside from telling the engine to start running a script using the target of the opcode as the active creature that will continue the task.
    I don't think filtering the target against an IDS is a good idea. We already have things that do that and they work well on their own. It'd be unnecessary complexity in my opinion.

    And as for the progress on the Mac version, I've been lagging on actual progress but I've done a lot of thinking on it, and I'm rather close to achieving something to run Lua scripts via the game's engine in the game's own memory space, while being able to load and use all of the game's own internal C/C++ functions. I've made no progress in being able to edit those functions (no reliable/secure way of hooking), so at this point I'm just sitting on a half-theoretical improved console.
  • OlvynChuruOlvynChuru Member Posts: 3,079
    @Bubb You have a new pull request!

    Six new functions:

    EEex_GetActorOverrideScript(actorID) => Returns the resref of the actor's override script.

    EEex_GetActorSpecificsScript(actorID) => Returns the resref of the actor's specifics script.

    EEex_GetActorClassScript(actorID) => Returns the resref of the actor's class script.

    EEex_GetActorRaceScript(actorID) => Returns the resref of the actor's race script.

    EEex_GetActorGeneralScript(actorID) => Returns the resref of the actor's general script.

    EEex_GetActorDefaultScript(actorID) => Returns the resref of the actor's default script.

    Also, I fixed a bug with EEex_GetActorAreaRes(actorID). That function previously would crash if it was called right when a game was loaded, because the actor didn't yet get assigned a pointer to the area. I made it so if that hasn't happened yet, it'll return "".
  • BubbBubb Member Posts: 1,005
    @OlvynChuru: Merged!
  • OlvynChuruOlvynChuru Member Posts: 3,079
    By the way, that idea for an opcode that runs a Lua function each time an effect would be applied sounds really useful! One idea I have for it is to use it to "link" creatures so that any effect that's applied to one will be applied to the other as well (to avoid an infinite loop, it would set a bit on that copied effect and only copy effects that don't have that bit set).
  • OlvynChuruOlvynChuru Member Posts: 3,079
    @Bubb Some questions:

    How would one determine with a Lua function whether an actor is currently moving to access a door, container, trap, or area transition? Where in the actor data is that stored?

    Is it possible to make an enemy controllable without charming it or changing its allegiance? I had an idea of changing Command so it actually lets you give the target a command (rather than just putting it to sleep). Where in the actor data does it store whether or not the actor is controllable? There seems to be something other than a creature's EA value that determines whether or not it can be controlled.
  • BubbBubb Member Posts: 1,005
    @OlvynChuru:
    OlvynChuru wrote: »
    How would one determine with a Lua function whether an actor is currently moving to access a door, container, trap, or area transition? Where in the actor data is that stored?

    It appears it isn't put into a special field. It's part of the action queue, (when you click to do one of those actions the engine lines up a MoveToPoint and the correct action). The only way to detect it after the fact would be to detect the correct sequence of actions in the queue. Also, it might be hard to distinguish certain actions if the "key" action can be executed by script, (like LeaveArea() in the case of an area transition).
    OlvynChuru wrote: »
    Is it possible to make an enemy controllable without charming it or changing its allegiance? I had an idea of changing Command so it actually lets you give the target a command (rather than just putting it to sleep). Where in the actor data does it store whether or not the actor is controllable? There seems to be something other than a creature's EA value that determines whether or not it can be controlled.

    There are master lists in the engine that control which control state the given creature is in. The categories are:
    1) Allies (green circle, controllable, limited functionality)
    2) Familiars (green circle, controllable, +area transition functionality)
    3) Overflow (limbo state for character-arbitration, I believe)
    4) Party (every permission under the sun)

    Now, don't get excited thinking you can shove any ol' creature into the "Party" slot. Party members have a lot of hardcoded mechanics to them that limits the total amount possible to 6, sadly.

    I'll post some code that works with these mechanisms tomorrow. :)
  • BubbBubb Member Posts: 1,005
    @OlvynChuru: Looks good; merged! Regarding the puppet-master ID, I believe you can also get this by reading Stat #138 using EEex_GetActorStat().
  • OlvynChuruOlvynChuru Member Posts: 3,079
    Bubb wrote: »
    @OlvynChuru: Looks good; merged! Regarding the puppet-master ID, I believe you can also get this by reading Stat #138 using EEex_GetActorStat().

    EEex_GetImageMasterID(actorID) also lets you get the actor ID of the image's master even if the image doesn't have an opcode 237 effect on it. There are some instances when I actually want to remove that effect: removing that effect from a Mislead image stops Mislead from granting the master permanent invisibility even after attacking. Even in such a case, this function still gets the master ID.
  • BubbBubb Member Posts: 1,005
    edited June 2019
    @OlvynChuru: You're right, of course; that's what I get for not reading the two lines of documentation literally right about the function where you explain that...

    Is it imperative that the EA value of the target doesn't get changed with your control request? It appears the engine has an internal "EA_CONTROLCUTOFF = 15" which is another validation layer on which creatures should be controllable. Any creature with an EA <= 15 + added to the allies master list can be ordered by the player, but I don't think you want the EA to change...

    I can hook into the EA check and override it in certain circumstances, if need be.
  • OlvynChuruOlvynChuru Member Posts: 3,079
    @Bubb It's okay, you don't need to go that far. If necessary, I could simply have Command give a 1-round charm effect or something, rather than that. I'm more interested in that opcode you proposed that screens each effect that would be applied to a creature. I'm really excited to start messing around with that!
  • BubbBubb Member Posts: 1,005
    @OlvynChuru: Then on that note I'll switch tasks :p

    Was thinking about that Opcode last night and thought of something: Should the screening process take place before or after other effect-immunity mechanisms? Should it "see" an effect that would otherwise be blocked by level, type, etc.?
  • OlvynChuruOlvynChuru Member Posts: 3,079
    edited June 2019
    I think it should see the effect before other effect-immunity mechanisms. That way, it's still possible to replicate the other way (by immediately returning false if the actor is immune to the effect). Whereas if it comes after other effect-immunity mechanisms, it's not possible to replicate the first way.
  • kjeronkjeron Member Posts: 2,368
    Bubb wrote: »
    Was thinking about that Opcode last night and thought of something: Should the screening process take place before or after other effect-immunity mechanisms? Should it "see" an effect that would otherwise be blocked by level, type, etc.?
    Something to keep in mind when adding this:
    https://support.baldursgate.com/issues/33047
    It was fixed in v2.5, but just in case it becomes relevant when adding the new immunity mechanic.
  • OlvynChuruOlvynChuru Member Posts: 3,079
    @Bubb I have a problem. It seems like my EEex_IterateActorEffects() function doesn't catch equipped effects of items. Are those stored somewhere different from an actor's other effects? How could I go through all of them?
  • BubbBubb Member Posts: 1,005
    @OlvynChuru: Node head for equipped effects is located at offset 0x3380. Should be the same process to iterate them.
  • BubbBubb Member Posts: 1,005
    edited June 2019
    I've just pushed some code that allows CStringList to be used as part of creature stats... (going to use this in the upcoming Screening Opcode). The changes made could have the potential to cause problems, so just a heads up to look for any new crashes / make sure the extended stats system still is working as expected.

    Edit: Looks like I left a direct-address reference in. The master branch might only work with BG2:EE until I fix that tomorrow; sorry! If you need to download from master in the meantime, use this link: here.
    Post edited by Bubb on
Sign In or Register to comment.