Skip to content

Can I get ID of currently active character?

SparrowJacekSparrowJacek Member Posts: 17
Hello,

I'd been working on my modified Stealth ability, but was never satisfied with the fact, that it was a Special Ability, and not something you could trigger via Stealth button from the main Actionbar. Today I've found a potential solution to my problem, that doesn't even require EEEx, however due to my lack of experience with UI modding, I am not sure how to finalize my implementation.

I can overwrite the original action from
action "buttonArray:OnLButtonPressed(X)"
to
action "C:Eval('ReallyForceSpell(Myself,MY_STEALTH)', CHARACTER_ID)"
and it works well, but I need a way to dynamically pass that "CHARACTER_ID" value, which IESDP describes as:
CHARACTER_ID is an optional numeric parameter specifies the active party member for the script action.
The parameter expects the portrait slot index, starting with 0 for the first party member.

Do you know if such index is possible to get instantly, or if it can be somehow mapped from some other data?

PS. this CHARACTER_ID is an optional parameter, but its value is NOT current character ID if I don't provide it at all, so getting rid of it is not the solution.



Thank you very much for your help!

Comments

  • TSoTTSoT Member Posts: 6
    Guess you're working on the same thing as I am. Here's what I've found
    First, you can use
    Infinity_GetSelectedCharacterName()
    
    or
    characters[id].name
    
    to get the name of the selected character.

    Second, you can use
    Infinity_GetPortraitTooltip(i)
    
    (i = 0~5, being the CHARACTER_ID) to get the PortraitTooltip of a specific character. The PortraitTooltip is like this:
    name
    CurrentHP/MaxHP
    
    I think we're pretty close.
  • SparrowJacekSparrowJacek Member Posts: 17
    Hey @TSoT

    You came to the same solution I had at some point. However thanks to @Pecca I managed to get what I think is a better and more reliable solution, although far more complicated.

    Here's my main function:
    function getCurrentCharPortraitId()
      local idMapping = {}
      Infinity_UpdateLuaStats()
      local curCharId = id
      local i = 0
      while ( i < Infinity_GetNumCharacters()) do
        Infinity_OnPortraitLClick(i)
        Infinity_UpdateLuaStats()
        idMapping[id] = i
        i = i + 1
      end
      Infinity_OnPortraitLClick(idMapping[curCharId])
      return idMapping[curCharId]
    end
    
    This ensures that we get the proper ID, no matter if there are characters with the same name for example. But the problem here is that this code actually clicks through all portraits, which makes the characters say their standard selection phrases. To avoid that I've implemented functions that deactivate those selection sounds and bring them back to the original state right after the selection is finished:
    function getSelectionSoundsValues()
        panelID = 7
        soundOptions = {}
        soundOptions[5] = Infinity_GetOption(5, panelID)
        i = 58
        while (i < 61) do
          optionVal = Infinity_GetOption(i, panelID)
          if optionVal == 1 then
            soundOptions[i] = optionVal
          end
          i = i + 1
        end
        return soundOptions
    end
    
    function setOptionsValues(valuesTable, panel)
      for k, v in pairs(valuesTable) do
        Infinity_ChangeOption(k, v, panel)
      end   
    end
    
    function silentlyGetCurrentCharPortraitId()
      initialSoundOptions = getSelectionSoundsValues()
      noSoundOptions = {[5]=0, [60]=1} 
      setOptionsValues(noSoundOptions, 7)
      currentCharPortraitId = getCurrentCharPortraitId()
      setOptionsValues(initialSoundOptions, 7)
      return currentCharPortraitId
    end
    

    And finally here's the usage of this code in practice:
    button
    	{
    		area 353 1 52 52
    		actionBar 5
    		enabled "buttonArray:GetButtonEnabled(5)"
    		tooltip lua "actionBarTooltip[5]"
    		action "
    				if buttonArray:GetButtonType(5) == 11 then
                  				charPortraitId = silentlyGetCurrentCharPortraitId()
                  				command = 'ReallyForceSpellRES(' .. stealth_spell .. ', Myself)'
                  				C:Eval(command, charPortraitId)
                			else
                  				buttonArray:OnLButtonPressed(5)
                			end"
    		actionAlt "buttonArray:OnRButtonPressed(5)"
    	}
    
  • TSoTTSoT Member Posts: 6
    @SparrowJacek
    Yes, your method is definitely better. However I don't understand how does the C:Eval part works. If I get it right, charPortraitId is a number 0~5, so the C:Eval part should be like:
    C:Eval('ReallyForceSpellRES(SPCLXXX, Myself)', 1)
    
    How does this work? I tested several commands like this using the console and nothing happened.
  • SparrowJacekSparrowJacek Member Posts: 17
    Ahhh, sorry. I forgot to mention one thing, you need quotation marks for spell resource, so something like:
    C:Eval('ReallyForceSpellRES("SPCLXXX", Myself)', 1)
    

    This works well. I have to warn you though - using Stealth from F5 (or any other) keyboard shortcut will still trigger standard Stealth behavior.
  • TSoTTSoT Member Posts: 6
    Yeah I already knew the keyboard thing. Thanks anyway.
    Now I have another problem: how do I type "SPCLXXX" into this code? It seems that the program recognize anything inside "" as some token. I tried [""] and \"\", didn't work.
  • SparrowJacekSparrowJacek Member Posts: 17
    @TSoT I am not sure why is that, but there's a relatively simple solution. For readability I declared the following variable at the start of UI.MENU file:
    spell_default_string = 'ReallyForceSpellRES("RESOURCE", Myself)'
    
    and then I used it like that inside "action"
    command = spell_default_string:gsub('RESOURCE', 'SPWI408')
    
  • TSoTTSoT Member Posts: 6
    Genius...
    Okay, here comes another another problem. I've been trying to create my own Hide in Plain Sight ability. So far the best approach I come up with is to apply a spell that temporarily sets the character's visual range to 0 before stealth. It worked somehow, but it didn't work well.
    If I do this by
    command = 'command = ApplySpell(Myself, TheSpell)'
    C:Eval(command,charPortraitId)
    buttonArray:OnLButtonPressed(5)
    
    after I press the Stealth button it becomes gray immediately and the character stops stealth.

    Or I can do this by
    command = 'command = ApplySpell(Myself, TheSpell)'
    C:Eval(command,charPortraitId)
    C:Eval('ActionOverride(Myself,Hide())',charPortraitId)
    
    Everything seems right after I press the button. HOWEVER I can press the button anytime, even when it's gray, and it still functions.
  • SparrowJacekSparrowJacek Member Posts: 17
    Everything seems right after I press the button. HOWEVER I can press the button anytime, even when it's gray, and it still functions.
    Yes, I don't think there's anything we can do about it. Sadly using "Disable button X" opcode doesn't change the BAM frame, it just greys it out. That's why I just make my spell, that is cast by Stealth button, give immunity to itself for some time and I give it no name, so players don't get any feedback from pressing the button. That's the best I could do right now.
  • TSoTTSoT Member Posts: 6
    Everything seems right after I press the button. HOWEVER I can press the button anytime, even when it's gray, and it still functions.
    Yes, I don't think there's anything we can do about it. Sadly using "Disable button X" opcode doesn't change the BAM frame, it just greys it out. That's why I just make my spell, that is cast by Stealth button, give immunity to itself for some time and I give it no name, so players don't get any feedback from pressing the button. That's the best I could do right now.

    What's your design of the spell? It seems quite complicated to make such a spell. I can think of a way to check the Stealth points (stats 27 I think), maybe day/night and indoor/outside too, but I don't know how to check whether the character stands in light or shadow.
  • SparrowJacekSparrowJacek Member Posts: 17
    edited July 2023
    The spell is very complicated even though I dropped some elements, like day/night (though it can be implemented, just adds another layer of complexity). There is no way to check for shadows, at least to my knowledge. But I made like that, because I needed much more freedom for the effects that it grants, as I am reworking the whole Thieving Skills to work slightly differently (for example I change "Move Silently" to something that is not related to hiding).

    If you just want to have Hide in Plain Sight and use standard Hide() action, then I am sure it can be done rather easily, though preventing players from clicking the button until they get their desired result is a bit more complicated, it would require giving your spell that blinds you something else, that could be accessed through UI.
    TSoT wrote: »
    command = 'command = ApplySpell(Myself, TheSpell)'
    C:Eval(command,charPortraitId)
    buttonArray:OnLButtonPressed(5)
    
    after I press the Stealth button it becomes gray immediately and the character stops stealth.
    Firstly, you need to change
    buttonArray:OnLButtonPressed(5)
    
    to
    C:Eval('Hide()', charPortraitId)
    
    So the action function should look something like that:
    action "
    
    				if buttonArray:GetButtonType(5) == 11 then
                  				charPortraitId = silentlyGetCurrentCharPortraitId()
                  				command = spell_default_string:gsub('RESOURCE', 'SPWI958')
                          
                  				C:Eval(command, charPortraitId)
                  				C:Eval('Hide()', charPortraitId)
                			else
                  				buttonArray:OnLButtonPressed(5)
                			end
                			"
    		actionAlt "buttonArray:OnRButtonPressed(5)"
    
    As from my observations buttonArray:OnLButtonPressed(5) action is fired before the spell from Eval.
    And secondly, you need to make sure that your spell, which blinds your character:
    1. Targets self
    2. Is non-hostile and doesn't break sanctuary/invisibility (flags at offset 0x18)
    3. Applies blind only for 1 game tick (using timing mode=10)

    That way the blindness shouldn't be problematic and you should get proper order of actions, firstly the spell and right after that HiS attempt. Here's the result (the planetar is hostile). And ofc it would be better to create a spell that has no name and only applies blind, to not clutter the log.
    yKReuBe.png
  • TSoTTSoT Member Posts: 6
    @SparrowJacek Thanks again. Your advice helped a lot.
    3. Applies blind only for 1 game tick (using timing mode=10)
    At least in my game, the duration should be no less than 2 game tick, and it must be done via opcode #262 instead of #74.

    I found a way to make sure that players can only trigger sneak once per round. I used Infinity_GetClockTicks() to time the time between two clicks.
    if ButtonTimer[id] == nil
    then
    	ButtonTimer[id] = Infinity_GetClockTicks()
    	C:Eval('Hide()',charPortraitId)
    elseif Infinity_GetClockTicks()-ButtonTimer[id] >= 6000
    then
    	ButtonTimer[id] = Infinity_GetClockTicks()
    	C:Eval('Hide()',charPortraitId)
    end
    
  • SparrowJacekSparrowJacek Member Posts: 17
    @TSoT That's really clever :) if you aren't plannig to allow players to use HiS more often than once every 6 seconds, then I think you have everything you need!
Sign In or Register to comment.