Jump to content

Bubb

Modders
  • Posts

    199
  • Joined

Posts posted by Bubb

  1. 7 hours ago, argent77 said:

    Interesting. How does condition 10 => Delay(Extra) work exactly? I could only make it work with Extra=1 (and Extra=-1 for some reason). Setting Extra=0 crashes the game. Any other value doesn't appear to work.

    The crash is a divide-by-zero in Delay(). The condition is pretty unreliable because two events need to line up for it to fire: the Delay() trigger needs to return true at the same time the contingency is checked, (every 101 ticks).

    1 hour ago, Luke said:

    Any relevant differences with respect to "Range()"...?

    As lynx says 🙂

    1 hour ago, Luke said:

    When you say [Extra | 1], do you mean BIT0=2^0=1 set to 1

    Yes

    1 hour ago, Luke said:

    So for instance, if you change Night Club +1 ("blun38.itm") to THAC0: +1, +2 at night dusk (i.e., set [Condition = 13] && [Extra = 1]), then you'll see "A contingency spell has been triggered" and the op232 effect will be removed (i.e., it'll trigger just once, even if I'm still equipping the club...). How can I avoid this issue...?

    That's the fun part — you can't. Extra acting as both flags and parameters for some Condition values is annoying.

    1 hour ago, Luke said:

    I tried applying it via op177 and I got mixed results, i.e.: the op232 effect does not terminate after first trigger but will also display "A contingency spell has been triggered" every time it triggers (but it shouldn't since "parameter3=0" 😕...)

    op177 is crazy in that it doesn't "add" the EFF to the creature, it merely wraps / executes it. So that's why op232 doesn't get removed when you apply it via op177, though there's still no way to stop the string from firing.

    1 hour ago, Luke said:

    Are you talking about STRREF_GUI_FEEDBACK_CONTINGENCY_TRIGGER...? If so, then how can strref be 0xF000F6...? According to BG2EE "enginest.2da", that should be strref 36936 (0x9048)...

    strrefs [0xF00000-0xF00537] index into enginest.2da, with the row being strref - 0xF00000. So, 0xF000F6 => row 246 => STRREF_GUI_FEEDBACK_CONTINGENCY_TRIGGER

    1 hour ago, Luke said:

    Could you clarify effect source vs. effect host...?

    Effect source = the creature that cast the spell

    Effect host = the creature the effect is attached to

    2 hours ago, Luke said:

    To clarify:

    • casting glow => Header @ 0x22...?
    • string => Header @ 0x8...?

    To clarify: range => Extended Header @ 0xE...?

    Yes

  2. Corrections:

    Parameter #1: Target:

    • 2 => NearestEnemyOf() *which evaluates as [EVILCUTOFF]
    • 3+ => [ANYONE]

    Parameter #2: Condition:

    • 1 => See(NearestEnemyOf()) *which evaluates as See([EVILCUTOFF])
    • 8 => PersonalSpaceDistance([ANYONE],4)
    • 9 => PersonalSpaceDistance([ANYONE],10)
    • 10 => Delay(Extra)
    • 13 => TimeOfDay(Extra)
    • 14 => PersonalSpaceDistance([ANYONE],Extra)
    • 15 => StateCheck(Myself,Extra)
    • 16 => Die()

     

    Additional flags:

    Special: Extra:

    [Condition = 0] && [Extra | 1] => Trigger even if the effect host was the source of the hit

    [Extra | 1] => When triggered, display strref 0xF000F6 ("A contingency spell has been triggered") as effect host and remove effect
    [Extra | 2] => Fire subspell as effect source instead of effect host
    [Extra | 4] => Suppress subspell casting glow and string
    [Extra | 8] => Suppress subspell range check

     

    Misc notes:

    1) [param3 != 0] acts as if [Extra | 1] was specified, (and maintains the portrait icon)

    2) New to v2.6, when:

    1. Starting Spell()
    2. And the resref being cast (first 7 letters) is a parent resource of an active op232 effect on the caster
    3. And the active op232 originated from op234 [param2=0/1] or was defined with [Extra | 1] or [param3 != 0]

           => Remove the active op232, (only remove one instance, even if more would have met the above)

  3. The animation INIs could use a good cleaning — many of them ended up with dead fields / the wrong fields all together. Not really important from a bug-fixing perspective, (if the fields are incorrect the engine falls back to hardcoded defaults), though it would make working with them easier.

  4. Here's something that's up for debate, quoting past me:

    Spoiler
    Quote

    Did you know that any attack a thief makes while invisible will ignore the target's AC dexterity bonus?

    To be exact, the attack must follow these rules:
    1) Attacker must have a backstab multiplier of 2 or higher
    2) Attacker must be STATE_INVISIBLE
    3) Attacker's weapon must not be RANGED

    In the following example:
    - Attacking character THAC0 = 17
    - Attacked character AC = 0, (dexterity contributing -4 bonus)

    The thief's attack only requires a total roll of 13 to hit, even though a roll of 17 is required normally:

    Spoiler

    549zfpevtj18.gif

     
    However, this mechanic isn't implemented properly...

    If the attacked character has a *penalty* to AC due to dexterity, they will be harder to hit while being attacked under the above conditions.


    In the following example:
    - Attacking character THAC0 = 17
    - Attacked character AC = 9, (dexterity contributing +5 penalty)

    The thief's attack should only require a total roll of 8 to hit, though a roll of 13 is now required:

    Spoiler

    plqslpksgbw3.gif

    Bug(?): Should backstabs (which invert a creature's AC Dex modifier) make a creature with a Dex penalty harder to hit?

  5. 1 minute ago, Luke said:

    In any case, modders should always use "TriggerOverride()", right...?

    TriggerOverride() is just a compiler-shortcut for NextTriggerObject() + WhateverTrigger(), so it really doesn't matter whether you write it out or use the shortcut. It's convenient shorthand, but it still suffers from the same bugs. But yes, since NextTriggerObject() isn't intended to be a standalone trigger, it's probably safer to use TriggerOverride().

  6. Quoting past me regarding NextTriggerObject() and how it breaks the trigger block it is in if it evaluates to an invalid creature:

    Spoiler
     19/2020 at 10:44 AM, Bubb said:

    It's easier just to show the flaw in the trigger block evaluation code.

    if (hasNextTriggerObjectOverride)
    {
        if (nextTriggerObjectOverride != NULL)
        {
            // do eval stuff
            hasNextTriggerObjectOverride = false
        }
    }
    else
    {
        // do eval stuff
    }

    NextTriggerObject sets both hasNextTriggerObjectOverride=true, and if it can't find a valid object, nextTriggerObjectOverride=NULL. Once this happens, the engine gets stuck in a bad state. Since nextTriggerObjectOverride is NULL it never clears hasNextTriggerObjectOverride, and it forever skips evaluating further triggers, (in the current trigger block).

    Note though, while it's not evaluating the triggers, it is still running through all of them and doing janitorial actions. Meaning - every non-OR'd trigger still sets the whole block's result to false, the active OR count still ticks down - another NextTriggerOverride has the potential of fixing the deadlock, etc.

    The only way a trigger block can still succeed after NextTriggerOverride breaks is:

    1. The NextTriggerOverride was in an OR block
    2. There are no subsequent triggers after the OR block
    3. A trigger in the OR block, that is positioned above the NextTriggerOverride, already evaluated to true

    or, further in the same OR block, another NextTriggerOverride evaluates to a valid creature, and fixes the bad state. Once that happens, the trigger block will start evaluating normally again from that point onward. So, I suppose, one way to prevent NextTriggerOverride from breaking everything would be something like this:

    IF
        OR(3)
            TriggerOverride(Player6,Global("B3WEIRD","LOCALS",1))
            TriggerOverride(Myself,False()) // This will fix the deadlock
            // Whatever trigger here
    THEN
  7. To quote the IESDP, here's another bug with event triggers, (they really are the worst):

    Quote

    NOTE: Triggers with values below 0x4000 use an alternate event-driven system, which is buggy even for IE standards. A negated event trigger returns true if there are pending events that have yet to be processed, and if any of these pending events fails to satisfy the unnegated trigger.

    You can see this effect by putting !OnCreation() on a creature. The block will fire every time the creature receives an event other than the one that is defined, such as taking damage — so not every time the triggers are checked after load as would be natural to expect.

  8. 3 hours ago, subtledoctor said:

    EDIT - just looking at the logic of the IESDP desription, it seems backwards: what it should be doing (and what ToBEx actually does?) is

    Spell NOT disrupted if (1d20 - 1) > Spell Level + Damage Taken

    Right, that's the intended formula, (with CHECK_MODE=1, Luck=0). The IESDP description I quoted is the observed behavior of the engine, not the intended behavior. I left out this note from the IESDP which I probably should of kept, I'll edit it in:

    Quote

    Bug: CHECK_MODE values greater than or equal to 1 are bugged (since all those inequality symbols should be < instead of >).
    As a result, you might want to stick with the default behavior (CHECK_MODE=0) until this bug is fixed.

    Inverting the last condition in the psedocode, (changing the > to <), aligns the behavior with ToBEx.

    EDIT — Ok, I've finished editing my previous post. Let me know if it still needs work.

  9. 11 hours ago, subtledoctor said:

    concentration checks should go on the list. 

    The IESDP lists the following behavior for CONCENTR.2DA's CHECK_MODE:

    Quote

    Known values for CHECK_MODE are:

    • 0 ⟶ Any damage a spellcaster takes will cause them to fail their spellcasting
    • 1 ⟶ Spell disrupted if (1d20 - 1) + Luck > Spell Level + Damage Taken
    • 2 ⟶ Spell disrupted if (1d20 - 1) + (Constitution / 2) - 5 > Spell Level + 15
    • 3 ⟶ Spell disrupted if (1d20 - 1) + Luck > Spell Level + 15
    • 4 ⟶ Spell disrupted if (1d20 - 1) + (Constitution / 2) - 5 > Spell Level + Damage Taken

    Bug: CHECK_MODE values greater than or equal to 1 are bugged (since all those inequality symbols should be < instead of >).
    As a result, you might want to stick with the default behavior (CHECK_MODE=0) until this bug is fixed.

    And here's some reversed pseudocode from the engine backing that up:

    Spoiler
    // CHECK_MODE bit0 = Use Luck instead of Constitution
    // CHECK_MODE bit1 = Use 15 instead of Damage Taken
    
    roll_modifier = nil
    
    if CHECK_MODE == 0 then
        // return 1 (Always disrupted)
    elseif bit0 == 1 then
        roll_modifier = luck
    else
        roll_modifier = math.floor(constitution / 2) - 5
    end
    
    roll = random_inclusive(0, 19) + roll_modifier
    bound_modifier = nil
    
    if bit1 == 1 then
        bound_modifier = 0xF
    else
        bound_modifier = damage_taken
    end
    
    success_higher_bound = spell_level + bound_modifier
    
    // BUG: This condition is flipped
    if roll > success_higher_bound then
        // return 1 (Disrupted)
    else
        // return 0 (Not disrupted)
    end

    The final condition is flipped — the behavior should be the following, based on TobEx:

    Do NOT disrupt if (1d20 - 1) + (Luck or Constitution/2) >= (Spell Level) + (Damage Taken or 15)

    not

    Disrupt if (1d20 - 1) + (Luck or Constitution/2) > (Spell Level) + (Damage Taken or 15)

    I.E. A greater Roll/Luck/Constitution should decrease the chance of disruption, not increase it.

  10. Casters can't be disrupted by damage if they are facing SSW(1), SWW(3), NWW(5), NNW(7), NNE(9), NEE(11), SEE(13), or SSE(15). This happens because the disruption code is only checked if the caster is in the correct orientation to face the target. SEQ_DAMAGE rounds a creature's orientation down to the nearest even direction, causing the caster to momentarily not face the target, making the check fail. Here's an example:

    Spoiler

    km4bjb2kar6x.gif

  11. Here's one I had fixed in old-EEex:

    Event triggers (IDS < 0x4000) "lock on" to a specific creature when using standard objects. For example:

    IF
        Global("B3TEST","GLOBAL",1)
        AttackedBy(PartySlot1,DEFAULT)
    THEN
        RESPONSE #100
            DisplayStringHead(Myself,1)  // No, I'm sorry, none of them sound familiar.
    END

    The AttackedBy() will only return true for whoever the party leader was during its first evaluation, even if you change the party order.

  12. 20 minutes ago, Lauriel said:

    Pardon me if this has been discussed elsewhere, but I thought it would fit here.  There is a state (I wouldn't know if it's a spell state or something hardwired in the engine that can't be fixed without @Bubb), but when someone is blind they can hide 100%.  I'm sure we'd all agree this can't possibly be intended behavior.  Is it possible, if it is hardwired, that we use a different 'state' on it?  I know next to nothing about spell states so...it's just an ask.

    That happens because the engine checks if the creature hiding can see any enemies, not if any enemies can see the creature. An exe patch could approximate a fix by changing that check to use the default range of 14, (instead of the current visual range, which is 2 when blinded).

  13. 5 hours ago, Luke said:

    206 can block SPL and EFF V2 files (provided that `Parent Resource Type` @ 0x90 is set to `1|Spell`; moreover, the `Resource` field of opcode #206 and the `Parent Resource` field @ 0x94 of the EFF file must be the same string). It will also fire a string in the combat log upon triggering (however, the string is not firing as of v2.6, regression @Galactygon @Bubb ...?)

    Yes, there's been a regression. op206's string is now bound by:

    if (!pEffect->m_sourceRes.IsValid() || (pImmunitySpell->m_error != 0xf00074 && pImmunitySpell->m_error != 0xf00080))
    {
        uint nNewFeedback = 0xffffffff;
        if (pImmunitySpell->m_error == 0xf00074) {
            nNewFeedback = 0xf00073;
        }
        pImmunitySpell->m_error = nNewFeedback;
    }

    Basically, the strref has to be 0xF00074 or 0xF00080 for the engine to display it.

  14. For CGameAnimationTypeMonsterQuadrant (0x1000-0x11FF), the engine disables shadows if:

    extend_direction == 0 && extend_direction_test != 8

    It does this by setting the shadow rgb at palette index (1) to 0x0 (if false_color == 0) or 0xFF00 (if false_color != 0). Looks like the animation renders correctly if you set extend_direction_test=8, so maybe that's a way to enable the shadow?

  15. AR4000 is hardcoded into the action that upgrades SoA to ToB, (MoveToExpansion). MoveToExpansion() is always called when entering ToB  if you're starting from SoA a script calls MoveToExpansion(), and if you're starting a new game the engine evaluates STARTARE.2DA, then runs MoveToExpansion(), (that's why you get the glitchy Elthan dialog).

    While hacky, your only option might be to have AR4000.BCS move the party somewhere else.

  16. What exactly are you trying to do? The configurable UI hotkeys are defined in BGEE.LUA by the keybindings table, though the ordinals all have hardcoded meanings and aren't really customizable. Basic custom hotkeys can be defined in UI.MENU by (ab)using a new menu definition. For example, add:

    menu
    {
    	name 'B3_HOTKEYS'
    	ignoreEsc
    	label
    	{
    		on M
    		action
    		"
    			Infinity_DisplayString('Hello from B3_HOTKEYS!')
    		"
    	}
    }

    and edit the 'START' menu's onOpen to push 'B3_HOTKEYS', (something like):

    menu
    {
    	name 'START'
    	align center center
    	ignoreesc
    	onOpen
    	"
    		Infinity_PushMenu('B3_HOTKEYS')

    What this does is add an invisible, non-closable menu that listens for the "M" key to be pressed, running whatever Lua code is in the label's action. The Lua environment has very little functionality to interact with the game engine though, so depending on what you want to do, it's probably impossible.

  17. If it helps any, scraping EE v2.6 results in the following section->key entries. I've left out some sections that the engine dynamically reads from since they aren't very interesting. Some of these keys are probably non-functional:

    Spoiler

    [Fonts]
    Zoom

    [Game Options]
    3E Thief Sneak Attack
    All Learn Spell Info
    Always Dither
    Area Effects Density
    Area Effects Refresh Probability
    Attack Sounds
    Audible Range
    Auto Pause Center
    Auto Pause State
    Automated 3D Animations
    Automated Attack Sounds
    Automated Diable Casting Glows
    Automated Disable Brightest
    Automated Disable VEFVidCells
    Automated Disable VVC Sounds
    Automated Faster Blur
    Automated Foot Steps
    Automated High Level Brighten
    Automated Limit Transparency
    Automated Low Performance
    Automated Mid Level Brighten
    Automated Speed Adjustment
    Automated Translucent Shadows
    Automated Very Low Performance
    Bored Timeout
    Cheats
    Classic Selection Circles
    Cleric Ranger Spells
    Color Circles
    Combat UI
    Command Sounds Frequency
    Confirm Dialog
    Critical Hit Screen Shake
    Difficulty Level
    Disable Casting Glows
    Disable Display Text
    Disable Foot Steps During Combat
    Disable Placed Sounds During Combat
    Disable Statics During Combat
    Disable VEFVidCells
    Disable VVC Sounds
    Duplicate Floating Text
    Effect Text Level
    Enable Fog
    Enhanced Path Finding
    Enhanced Path Search
    Environmental Audio
    Equipment Comparison
    Expire Trap Highlights
    Extra Combat Info
    Extra Feedback
    Faster Blur
    Filter Games
    Footsteps
    Force Dialog Pause
    Fully Disable Non Visible During Combat
    GUI Feedback Level
    Heal Party on Rest
    Hearth of Fury
    High Level Brighten
    Hotkeys On Tooltips
    HP Over Head
    Infravision
    Inventory Pause Warning
    Journal Popups
    Keyboard Scroll Speed
    Locator Feedback Level
    Low End Machine 2
    Low Mem Sounds 1
    Low Mem Sounds 2
    Maximum HP
    Memory Access
    Memory Level
    Message Box Top
    Mid Level Brighten
    Mouse Scroll Speed
    Nightmare Bonus Gold
    Nightmare Bonus XP
    No Difficulty Based XP Bonus
    One Time Popup
    Over Confirm Everything
    Pausing Map
    Quick Item Mapping
    Ranged Weapon Switching
    Render Actions
    Render Dynamic Search Map
    Render Explored Map
    Render Path
    Render Search Map
    Render Travel Regions
    Reverse Mouse Wheel Zoom
    Selection Sounds Frequency
    Show AOE
    Show Character HP
    Show Date On Pause
    Show Learnable Spells
    Show Message Box Hint
    Show Triggers On Tab
    Smart Radius
    Story Mode
    Subtitles
    Super Atomic Speed Fighting Action
    Suppress Extra Difficulty Damage
    Terrain Hugging
    Tiles Precache Percent
    Tutorial State
    Visual Range
    Weather
    WILD SURGE KEYS

    [GameSpy]
    Enabled
    Port

    [Graphics]
    Area Map Zoom
    Backend
    BGRA
    Greyscale On Pause
    Hardware Mouse Cursor
    Height
    Log Frame Times
    Postprocessing
    Redraw Entire Screen
    Render Frame Times
    Scale UI
    Shaders
    Show Black Space
    Sprite Blur Amount
    Use Character Highlights
    Use Nearest Neighbour Scaling
    Use Sprite Outlines
    Width
    Zoom Lock

    [Keyboard]
    Repeat Delay
    Repeat Rate

    [Language]
    Text

    [Mouse]
    Double Click Height
    Double Click Width

    [Multiplayer]
    AsyncEnumeration
    Client Timeout
    Default Permissions
    Disable Banters
    Enable Chat Menu
    Import Character
    Last Protocol Used
    Pausing Dialog
    Player Name
    Port
    Session Name

    [Program Options]
    Active Campaign
    Alternate SR Curve
    BMP Screensaves
    Brightness Correction
    Cloud Saves Enabled
    Cucumber
    Debug Mode
    Developer Mode
    Disable Cosmetic Attacks
    Disable Movies
    Disable Sound
    Display Subtitles
    Drop Capitals
    F FLOATTXT
    F NORMAL
    F REALMS
    F STONEBIG
    F STONESML
    F TOOLFONT
    Fake Touch UI
    First Frame Outline
    First Run
    Font Name
    Force Local IP
    Install Type
    Logging On
    Maximum Frame Rate
    Never Show Nuisance SOD
    New Gui
    Path Search Nodes
    Screen Position X
    Screen Position Y
    Short Pregen Description
    Sprite Mirror
    SR Curve Radius
    String Buffer
    Strref On
    Tooltips
    Translucent Shadows
    UI Edit Mode
    Use Mirror FX
    Volume Ambients
    Volume Movie
    Volume Music
    Volume SFX
    Volume Voices

    [Resolution]
    HeightDeath
    HeightDefault
    HeightMedium
    HeightPickContainer
    HeightPickMulti
    HeightPickSingle
    HeightSmall
    Left
    Top
    Width

    [Window]
    Full Screen
    h
    Maximized
    w
    x
    y

     

  18. No, I meant what I posted. EEex versions >= v0.9.0-alpha are for EE v2.6. The transition to a 64bit executable forced EEex to change significantly, which broke backwards compatibility with mods. So, if you want to play High Powers, which was designed for the "old" EEex, you have to downgrade to EE v2.5 and use the EEex version that goes along with it, as I linked.

  19. I'm not sure exactly what caused it, but I think Skie got interrupted executing:

    EscapeAreaMove("AR0700",3325,1240,SSW)

    (this is from a dialog)

    The engine uses opcode 186 to actually move the creature, and that effect got saved on her CRE entry in the SAV. On load the engine processes this opcode before the game has been fully initialized and it crashes. Edit: Or something like that; iirc when Skie attempts to move it crashes because she's not internally assigned to an area.

×
×
  • Create New...