Skip to content

General mod Questions thread

17810121370

Comments

  • kjeronkjeron Member Posts: 2,367
    @chimeric
    For some reason the ADD_ITEM_EFFECT requires specifying the ability type for weapons:
    LPF	ADD_ITEM_EFFECT
    	INT_VAR
    		type = 1 // Melee
    		opcode = 328
    		target = 2
    		timing = 0
    		duration = 1
    		parameter2 = 366
    		special = 1
    END
    You can also skip all those prof/cat/string checks, the only check you need to make here is field 0x74. If it is not '1', its not a weapon, and specifying the type will further restrict it to melee weapons. Just make sure the item file is at least that large (FILE_SIZE > 0x73) before reading it.
  • chimericchimeric Member Posts: 1,163
    edited September 2017
    Okay, that worked completely fine with weapons. The next step is to patch damaging spells the same way. I'm targeting the OFFENSIVEDAMAGE type. Don't know what the minimum file size is with spells, but that doesn't really matter. Here:
    COPY_EXISTING_REGEXP	~.*\.spl~ override
    	READ_BYTE 0x27 type
    	PATCH_IF	(type = 10) THEN BEGIN
    		LPF	ADD_SPELL_EFFECT
    			INT_VAR
    				
    				opcode = 328
    				target = 2
    				timing = 0
    				duration = 1
    				parameter2 = 366
    				special = 1
    		END
    	
    	END
    BUT_ONLY
    Somehow this doesn't quite work. The effect gets added alright, but the spell state is not recognized, because "special = 1" (IWD 2 mode) doesn't go through. Any idea why?
  • kjeronkjeron Member Posts: 2,367
    edited September 2017
    chimeric said:

    Somehow this doesn't quite work. The effect gets added alright, but the spell state is not recognized, because "special = 1" (IWD 2 mode) doesn't go through. Any idea why?

    Outdated version of weidu maybe? IIRC there was either a typo in that function or just complete lack of support preventing it from accepting values for the special field at one point. I think at least as far back as v2.39.
  • chimericchimeric Member Posts: 1,163
    edited September 2017
    But it applied the patching properly with weapons. Not with the spells, for some reason. In one case, it recognizes the Special field, as it did also for that global equipped effect you gave me the code for. But with this ADD_SPELL_EFFECT, not. I'll try to find an update for Weidu. If there isn't one, I can still try to bypass this problem and add a Cast Spell to everything instead, and that spell will have a custom spell state application I'll enter by hand.

    I'm having some other issues with custom states. Apparently they don't behave properly in all situations for high position numbers. The last "official" position in SPLSTATE.IDS (BG:EE without SoD) is 255, CYNICISM_EQUIPPED. I don't know how high one can go after this and get proper responses with CheckSpellState. My "equipped" states for weapons, what I've asked the code for a week ago, range from 348 to 363, rather high to avoid clashing with anything. And scripts notice them, but weirdly... For example:
    CheckSpellState(LastSummonerOf(Myself),CLUBSTAFF_EQUIPPED)
    
    returns true if a club or a staff is equipped and selected, and returns false if I deselect it, just as intended. But If I ask about another of these states, one that is off at the moment, like so:

    CheckSpellState(LastSummonerOf(Myself),WAKI_EQUIPPED)
    then this also returns true. Official states don't do this, so I tried planting WAKI_EQUIPPED in the empty stretch of the file between 126, WIZARD_SHIELD, and 249, PRONE. And then the trigger stopped returning true when the state was not on. This means I'm going to have to fit my states in the working range of SPLSTATE.IDS, and I don't know, as I said, how far above 255 I can go there, or, if I stick my states in the gap, the positions may well be occupied by extensions or other mods. An alternative would be to draw on the official and irrelevant spell states instead of my own "equipped" states. For example, I could make wakizashis apply constantly HOPELESSNESS or EYE_OF_THE_MIND. Would that have any side effects? The spell state HELD doesn't actually hold a character, does it?
  • kjeronkjeron Member Posts: 2,367
    SPLSTATE values 256 - 6271 (with a few gaps) are reserved by the engine, set by variuos STATES, STATS, current BARDSONG, and EXE version. Setting them manually has so far no known effect (with 2 visual-only exceptions). You can read more here: https://forums.beamdog.com/discussion/comment/901480/#Comment_901480

    You should always use a dynamically generated SPLSTATE for custom entries, determined at installation from those available, not preset values. There is no reason to "reserve" SPLSTATEs.

    Only a few of the SPLSTATES 0-255 have hardcoded features, the rest are arbitrary, among them:
    DISEASED
    PRONE
    EVASION
    SUPPRESS_HP_INFO
    DOESNT_AWAKEN_ON_DAMAGE
    maybe SNEAK_ATTACK_IMMUNITY
    (I made a more complete list is on G3, but I don't remember offhand).
  • chimericchimeric Member Posts: 1,163
    Okay. I think people have told me how to dynamically generate entries for IDS files, but I don't know where that information is now. Care to explain?
  • chimericchimeric Member Posts: 1,163
    Okay, and how about factoring Strength into damage of spell attacks simulating strikes? You've said it's possible. Did you mean a series of Apply Effects List - > STR > X, or is there some more congenial method?
  • kjeronkjeron Member Posts: 2,367
    edited September 2017
    Add Strength Equality check to SPLPROT.2da:
    DEFINE_ACTION_FUNCTION	ADD_SPLPROT	INT_VAR	stat = 0	value = 0	relation = 0	RET	new_row		BEGIN
    //	[0:<=][1:=][2:<][3:>][4:>=][5:!=][6:b<=][7:b>=][8:b=][9:b!=][10:b>][11:b<]
    	ACTION_IF	(stat > 278)	BEGIN	WARN	~SPLPROT.2DA - Stat out of Bounds.~	END
    	ACTION_IF	((stat != 259) AND (stat != 260) AND (relation > 11))	BEGIN	WARN	~SPLPROT.2DA - Relation out of Bounds.~	END
    	COPY_EXISTING ~SPLPROT.2DA~	~override~
    		COUNT_2DA_COLS	cols
    		COUNT_2DA_ROWS cols rows
    		TEXT_SPRINT check_row ~~
    		TEXT_SPRINT check_stat ~~
    		SET new_row = 0
    		FOR(current_row = 0; current_row < rows; ++current_row)	BEGIN	READ_2DA_ENTRY current_row 1 cols check_stat
    			PATCH_IF (~%check_stat%~ STRING_EQUAL ~%stat%~)	BEGIN	READ_2DA_ENTRY current_row 2 cols check_value
    				PATCH_IF (~%check_value%~ STRING_EQUAL ~%value%~)	BEGIN	READ_2DA_ENTRY current_row 3 cols check_relation
    					PATCH_IF (~%check_relation%~ STRING_EQUAL ~%relation%~)	BEGIN	new_row = current_row	current_row = rows	END
    				END
    			END
    		END
    		PATCH_IF (new_row = 0)	BEGIN
    			FOR(current_row = 0; current_row < rows; ++current_row)	BEGIN	READ_2DA_ENTRY current_row 1 cols check_stat
    				PATCH_IF (~%check_stat%~ STRING_EQUAL ~*~)	BEGIN	new_row = current_row	current_row = rows	END
    			END
    			PATCH_IF (new_row = 0)	BEGIN	new_row = rows	INSERT_2DA_ROW rows cols ~%new_row%		   %stat%		   %value%		   %relation%~
    			END	ELSE	BEGIN	SET_2DA_ENTRY new_row 1 cols ~%stat%~	SET_2DA_ENTRY new_row 2 cols ~%value%~	SET_2DA_ENTRY new_row 3 cols ~%relation%~	END
    		END
    	BUT_ONLY
    END
    LAF	ADD_SPLPROT INT_VAR	stat = 36 value = ~-1~ relation = 1	RET STR_CHECK = new_row	END
    
    • For instant-area-effect spells only:
      AoE spell (One ability header, min level[1]):
      <pre class="CodeBlock"><code>Opcode 146 (Cast Spell)
      Target: 2 (Preset Target)
      Parameter2: 1 (Instantly (Caster level))
      Resource: (CheckSpell)
    • For AoE's this is a subspell, for single-target spells this is the spell itself:
      CheckSpell (One ability header, min level[1], projectile[1]):
      // For each Strength value:
      Opcode 326 (Apply Effects List)
      Target: 9 (Original Caster)
      Parameter1: (Strength value)
      Parameter2: STR_CHECK
      Resource: (MidSpell for this STR value)
      
    • For each STR value:
      MidSpell (Ability header for every caster level [1 - CAP], projectile[1]):
      // One for each ability header:
      Opcode 146 (Cast Spell)
      Target: 1 (Self)
      Parameter1: (level entry in Subspell for this Caster Level & STR value combination)
      Parameter2: 2 (Cast Instantly (at level))
      Resource: Subspell for this STR value
    • Subspell (Ability header for every Caster Level + STR value combination needed):
      Projectile: [spell projectile] for single-target or [None(1)] for AoE's (actual spell effects)
    Explanation about casting level:
    Lets say this spell scales level 1-30.
    The Midspell for STR=1 will have abilities with minlevel 1-30. The casting level of their 146 effects will be 1-30. The effects for STR=1 will occupy ability levels 1-30 of (Subspell).
    The Midspell for STR=2 will have abilities with minlevel 1-30. The casting level of their 146 effects will be 31-60. The effects for STR=2 will occupy ability level 31-60 of (Subspell).
    The Midspell for STR=2 will have abilities with minlevel 1-30. The casting level of their 146 effects will be 61-90. The effects for STR=2 will occupy ability level 61-90 of (Subspell).
    etc...
    This way, all effects originate from the same resource, making it simple to prevent stacking or provide immunity with opcodes 206/321.
    Note: Normal spellcasting has a caster level limit of 255, this does not apply when you specify the casting level through opcodes.
    

    For repeating area-effect spells (i.e. cloudkill), its complicated. You can use the above, but it will require the caster remain in the same area as the spell, and it will use the current STR score every time it triggers, rather than the STR score at the time of casting. Technically it also does this for instant-area-effect spells, but normally its not a noticeable issue with them. It can be done properly, but I haven't formulated the process yet.

    I don't recommend attempting to factor in Extra Strength, it should be doable, but the payoff won't likely be worth the effort.

    For anyone interested in 3E mechanics, this can be used to overhaul the spell system to incorporate a casters ability-score modifier to affect saving throws for their spells. Though you may be better off rebuilding the spells from scratch than trying to patch what exists.
  • chimericchimeric Member Posts: 1,163
    edited September 2017
    This is terribly complicated. The part about adding a new option to SPLTPROT.2DA can be used as written? That file is what Apply Effects List uses, yes?

    As for the rest... I don't think I need it. I don't want to patch all spells that do damage. I just need my attack-simulating spells to include a series of A.E.L. effects reflecting the caster's stats. I know how to do that, Self-targeted A.E.L.s inside a Living actor spell take the measure of the caster and send the extra damage to Living actor. It's just a lot of work to nest those checks when there is no straight = for a stat.

    By the way, you've told me how to patch spells by their casting time, but the offset is going to be different for every ability, and there is no telling how many there will be. Might be just one, might be one for every casting level. If I try to read them without knowing the length of the file... scatch that, they all have the same casting time in the spells that we know. And I can read the first one.
  • kjeronkjeron Member Posts: 2,367
    chimeric said:

    This is terribly complicated. The part about adding a new option to SPLTPROT.2DA can be used as written? That file is what Apply Effects List uses, yes?

    Yes.
    chimeric said:

    As for the rest... I don't think I need it. I don't want to patch all spells that do damage. I just need my attack-simulating spells to include a series of A.E.L. effects reflecting the caster's stats. I know how to do that, Self-targeted A.E.L.s inside a Living actor spell take the measure of the caster and send the extra damage to Living actor.

    As long as it works under the circumstances (which the simpler the spell is the more likely it should work). The extra redirect become necessary as the spell becomes more complex, as the game starts to lose track of targets the further you go down that rabbit hole.
  • chimericchimeric Member Posts: 1,163
    And the new line for SPLPROT won't throw off my existing spell effects with that opcode, will it?
  • kjeronkjeron Member Posts: 2,367
    chimeric said:

    And the new line for SPLPROT won't throw off my existing spell effects with that opcode, will it?

    It shouldn't affect anything that is already in the file. It either adds the new entry to the end, or replaces one of the empty rows if it detects one, maintaining row count for existing entries.
  • chimericchimeric Member Posts: 1,163
    edited September 2017
    So how is this addition going to work on others' machines? First I tell Weidu to patch their file as you wrote, then I'll copy over my A.E.L.s with the =, the spells I created after I ran the code on my setup, and it should show for them as it does for me?

    All right. I think I'm in the home run with this. By the way, my Near Infinity doesn't want to recognize WILDMAGE in KIT.IDS. It sees the entry and I can set it, but it shows as "Unknown" - only with a number. May not be a problem, but is this a permanent feature of NI, or is it because I'm using an old version? This version has some convenient features later removed, but it might have a few left-over bugs.
  • kjeronkjeron Member Posts: 2,367
    edited September 2017
    The value will not be the same on different installs, since their is no way to know what entries they will already be using. It's fine for pre-existing entries(no-one should be changing those), but not custom ones.

    First you patch their SPLPROT.2DA, adding an entry for STR=.
    Then you either patch your files using the value it returns when you copy them to the override. Or, if you've already create the files with the AEL effect on your side, you can also just write down the offset(s) the STR= value is at in the spell, and write over just that offset after adding the entry to SPLPROT.2da.
    DEFINE_ACTION_FUNCTION	ADD_SPLPROT (code) END
    LAF	ADD_SPLPROT INT_VAR	stat = 36 value = ~-1~ relation = 1	RET STR_CHECK = new_row	END
    // Then:
    	COPY	~myfile.spl~ override
    		SAY NAME1	@###
    		LPF	ADD_SPELL_EFFECT
    			INT_VAR
    				opcode = 326
    				target = 2
    				timing = 0
    				duration = 1
    				parameter2 = STR_CHECK
    				special = 1
    		END
    // Or if the effect is already there:
    	COPY	~myfile.spl~ override
    		SAY NAME1	@###
    		WRITE_LONG	0xa2 STR_CHECK	// use the offset for the parameter2 of the AEL effects.
    		WRITE_LONG	0xd2 STR_CHECK	// use the offset for the parameter2 of the AEL effects.
    		WRITE_LONG	0x102 STR_CHECK	// use the offset for the parameter2 of the AEL effects.
    

    I don't know anything about that WILDMAGE issue, but your assumption is likely.
    Post edited by kjeron on
  • chimericchimeric Member Posts: 1,163
    edited September 2017
    Okay... one thing after another. But thanks a lot, I appreciate it. Here I was patching my SPLs, going by the casting time of their first spell ability, and I got an installation error from a Winter Wolf pelt SPL. Don't ask. It's called CDDETECT.SPL. That one doesn't have any abilities or global effects, but it's there. I guess I'm going to need a way to select only files with at least one spell ability.
    INT_VAR
    				opcode = 328
    				target = 2
    				timing = 0
    				duration = 1
    				parameter2 = STR_CHECK
    				special = 1
    		END
    You mean 326, right? 328 is Set Spell State, that was a different issue. Here it should be 326 pointing to the Strength stat as a filter...
    Post edited by chimeric on
  • PheosicsPheosics Member Posts: 16
    Is it possible to create sub-races the same way class kits work? You select a race and then have the option to select a sub-race?
  • kjeronkjeron Member Posts: 2,367
    edited September 2017
    chimeric said:

    You mean 326, right? 328 is Set Spell State, that was a different issue. Here it should be 326 pointing to the Strength stat as a filter...

    Yes, typo, sorry, fixed.
  • The user and all related content has been deleted.
  • chimericchimeric Member Posts: 1,163
    How about just more races, then?
  • kjeronkjeron Member Posts: 2,367
    You can populate RACE.ids and RACETEXT.2da(SODRACE.2da) up to values 255, any race that has all fields in both will show up at chargen. RACEFEAT.2da controls infravision, however, no other race-based files accept other race entries, instead they use the same values as Humans or the default (except Tiefling, which is treated as an Elf). Most of it is only a problem during Chargen, which can be worked around by modding the UI.
  • chimericchimeric Member Posts: 1,163
    edited September 2017
    So how about this?
    chimeric said:

    I guess I'm going to need a way to select only files with at least one spell ability.

    Also I'd like to take advantage of exceptional strength. If it's not terribly difficult - for you, I mean. What what I need to change in your = code for that? Also would there be a doubling-up of bonuses for Str 18, regular, and exceptional Strength, if used on a fighter?

    Post edited by chimeric on
  • kjeronkjeron Member Posts: 2,367
    edited September 2017
    chimeric said:

    I guess I'm going to need a way to select only files with at least one spell ability.

    READ_SHORT	0x68	ab_num
    PATCH_IF	ab_num > 0	BEGIN
    chimeric said:

    Also I'd like to take advantage of exceptional strength. If it's not terribly difficult - for you, I mean. What what I need to change in your = code for that? Also would there be a doubling-up of bonuses for Str 18, regular, and exceptional Strength, if used on a fighter?

    You will need all of these setup checks added to SPLPROT:
    LAF	ADD_SPLPROT INT_VAR	stat = 36 value = ~-1~ relation = 1	RET STR_CHECK = new_row END	// Strength = 'Parameter1'
    LAF	ADD_SPLPROT INT_VAR	stat = 36 value = 18 relation = 5	RET STR_N18 = new_row END	// Strength != 18
    
    LAF	ADD_SPLPROT INT_VAR	stat = 37 value = 0 relation = 5	RET EXSTR_N0 = new_row END	// EXStrength != 0
    
    LAF	ADD_SPLPROT INT_VAR	stat = 37 value = 1 relation = 3	RET EXSTR_L1 = new_row END	// EXStrength < 1
    LAF	ADD_SPLPROT INT_VAR	stat = 37 value = 50 relation = 2	RET EXSTR_G50 = new_row END	// EXStrength > 50
    LAF	ADD_SPLPROT INT_VAR	stat = 0x103 value = EXSTR_L1 relation = EXSTR_G50	RET STR_1_50 = new_row END
    	// EXStrength  != 1-50
    
    LAF	ADD_SPLPROT INT_VAR	stat = 37 value = 51 relation = 3	RET EXSTR_L51 = new_row END	// EXStrength < 50
    LAF	ADD_SPLPROT INT_VAR	stat = 37 value = 75 relation = 2	RET EXSTR_G75 = new_row END	// EXStrength > 76
    LAF	ADD_SPLPROT INT_VAR	stat = 0x103 value = EXSTR_L51 relation = EXSTR_G75	RET STR_51_75 = new_row END
    	// EXStrength  != 51-75
    
    LAF	ADD_SPLPROT INT_VAR	stat = 37 value = 76 relation = 3	RET EXSTR_L76 = new_row END	// EXStrength < 76
    LAF	ADD_SPLPROT INT_VAR	stat = 37 value = 90 relation = 2	RET EXSTR_G90 = new_row END	// EXStrength > 90
    LAF	ADD_SPLPROT INT_VAR	stat = 0x103 value = EXSTR_L76 relation = EXSTR_G90	RET STR_76_90 = new_row END
    	// EXStrength  != 76-90
    
    LAF	ADD_SPLPROT INT_VAR	stat = 37 value = 91 relation = 3	RET EXSTR_L91 = new_row END	// EXStrength < 91
    LAF	ADD_SPLPROT INT_VAR	stat = 37 value = 99 relation = 2	RET EXSTR_G99 = new_row END	// EXStrength > 100
    LAF	ADD_SPLPROT INT_VAR	stat = 0x103 value = EXSTR_L91 relation = EXSTR_G99	RET STR_91_99 = new_row END
    	// EXStrength  != 91-99
    
    LAF	ADD_SPLPROT INT_VAR	stat = 37 value = 100 relation = 5	RET EXSTR_N100 = new_row END	// EXStrength != 100
    
    LAF	ADD_SPLPROT INT_VAR	stat = 0x104 value = STR_N18 relation = EXSTR_N0 RET STR_CHECK_18 = new_row END
    	// Strength  = 18/0 (not ((not STR18) or (not EX00)))
    
    LAF	ADD_SPLPROT INT_VAR	stat = 0x104 value = STR_N18 relation = STR_1_50 RET STR_CHECK_18_1_50 = new_row END
    	// Strength  = 18/1 - 18/50
    
    LAF	ADD_SPLPROT INT_VAR	stat = 0x104 value = STR_N18 relation = STR_51_75 RET STR_CHECK_18_51_75 = new_row END
    	// Strength  = 18/51 - 18/75
    
    LAF	ADD_SPLPROT INT_VAR	stat = 0x104 value = STR_N18 relation = STR_76_90 RET STR_CHECK_18_76_90 = new_row END
    	// Strength  = 18/76 - 18/90
    
    LAF	ADD_SPLPROT INT_VAR	stat = 0x104 value = STR_N18 relation = STR_76_90 RET STR_CHECK_18_91_99 = new_row END
    	// Strength  = 18/90 - 18/99
    
    LAF	ADD_SPLPROT INT_VAR	stat = 0x104 value = STR_N18 relation = STR_100	RET STR_CHECK_18_100 = new_row END
    	// Strength  = 18/100
    

    Add the A.E.L. checks for Strength values 1-17 and 19-25 as previous using %STR_CHECK%, then add an A.E.L. for each of these checks (Parameter1 will be irrelevant for these):
    %STR_CHECK_18%		// Strength  = 18 or 18/0
    %STR_CHECK_18_1_50%	// Strength  = 18/1 - 18/50
    %STR_CHECK_18_51_75%	// Strength  = 18/51 - 18/75
    %STR_CHECK_18_76_90%	// Strength  = 18/76 - 18/90
    %STR_CHECK_18_91_99%	// Strength  = 18/91 - 18/99
    %STR_CHECK_18_100%	// Strength  = 18/100
    It will ignore exceptional strength if Strength isn't 18, so there will only be a single match.

    If you have altered the Exceptional Strength ranges, you will have to alter/add/remove some of these check s ranges accordingly.
  • chimericchimeric Member Posts: 1,163
    Okay, thanks. I haven't altered anything yet. Now, I think, I have enough information for my patching of spells and the Str bonus.
  • chimericchimeric Member Posts: 1,163
    Another question, to any takers. I'm giving some special abilities to classes, and I want to emulate the Shadowdancer, how he can hide in plain sight. What are the mechanics of that?
  • semiticgoddesssemiticgoddess Member Posts: 14,903
    @chimeric: Hide in Plain Sight is currently hardcoded; only the Shadowdancer kit can do it. But any thief can hide in plain sight or set traps in the middle of combat provided that he or she is blinded. Unfortunately, a blind thief still suffers all of the normal penalties and restrictions of blindness.
  • chimericchimeric Member Posts: 1,163
    edited September 2017
    Oho-ho! That's perfect. I'm still going to look at the shadowdancer, but the trick with blindness (to expire in 1 second) should be it. (How is it that one is not seen when one just can't see anybody else? Is this a sort of "nah-nah-nah, looking through my fingers" sort of logic? If Hide is normally prevented by being Seen, then does blindness also makes one not Seen?)
  • kjeronkjeron Member Posts: 2,367
    chimeric said:

    Oho-ho! That's perfect. I'm still going to look at the shadowdancer, but the trick with blindness (to expire in 1 second) should be it. (How is it that one is not seen when one just can't see anybody else? Is this a sort of "nah-nah-nah, looking through my fingers" sort of logic? If Hide is normally prevented by being Seen, then does blindness also makes one not Seen?)

    Stealth is blocked by being able to see enemies while not already invisible.
    You can be blind or they can be invisible(to you). It might even work if they have opcode 101 against you.
    You can also use opcode 262(visual range bonus) in place of Blindness to avoid complications with immunities.
  • chimericchimeric Member Posts: 1,163
    Okay. Sounds like the way to go. I'm going to have to use a hot key for another part of this mod... I can't get them to work, except what's set inside the game options. I assign a trigger to an unoccupied letter for my scripts, I free a letter in the options for my scripts, and there is still no response.
  • kjeronkjeron Member Posts: 2,367
    chimeric said:

    Okay. Sounds like the way to go. I'm going to have to use a hot key for another part of this mod... I can't get them to work, except what's set inside the game options. I assign a trigger to an unoccupied letter for my scripts, I free a letter in the options for my scripts, and there is still no response.

    IIRC only A-Z work with the HotKey() trigger.
Sign In or Register to comment.