Skip to content

[WIP] BG2 Radar Overlay. Need some help.

YunieYunie Member Posts: 44
Hello guys!

I've been working on this BG2 Radar Overlay thing lately and I wanted to share my progress.
The main idea of a mod is to allow the player to see all the stats of the enemies in the current encounter without searching for their stats on the wiki or through tools like Keeper.

A quick demo:
https://youtu.be/bOvNzC9CPFg

I was finding an inspiration in Mivsan_NT's videos like this one where he edits some overlays with monster stats describing its weaknesses. How cool would it be if there were a tool that actually shows it in game, right? Well, if it does not exist, why not to make it ourselves? :smiley:

There are some questions I need help with:
1. Where is it coded so that some monsters are only vulnerable to weapons to e.g. +4 weapons? Which files?
2. Same thing with Rakshasa - Where is it coded so that they are immune up to lvl 5 (7?) spells? Same with liches. I went through the .CRE files but found nothing about it.
3. The issue I showed in the demo - why Kobolds are referencing the hobgoblins' .CRE files and yet they look like kobolds? Is it some attached script magic?
4. Is there a way to identify a spell or effect which does not have a name associated with it? Like here lGjnUSD.jpgSPPR517a, SPPR717a, SPPR319a spells have no name. I believe they are added with SCS as it is the only mod I run these spell files are found in the override folder.

So, what do you guys think?
First post, btw :smile:

Comments

  • OlvynChuruOlvynChuru Member Posts: 3,079
    edited February 2021
    Great idea! :)

    I made a similar system in my spell mod. One component changes the spell Know Alignment into Identify Creature, which gives information about the target similar to what you did.

    u5mfa90nhsez.png

    But I don't think my plain text looks quite as good as your overlays.

    In order for your UI code to tell if the creature is immune to certain weapons, you will need to have EEex installed. EEex adds a lot of new functions for reading and changing creature info.

    Once you have EEex installed, here is approximately the code I used to generate that "Thrix can only be harmed by weapons of +2 or greater enchantment" message. This is regular Lua code, so you should be able to place it in UI.MENU just fine. You have my permission to use it in your mod and change it however you need.
    string_55357 = '<EXICNAME> cannot be harmed by any weapons'
    string_55358 = '<EXICNAME> can only be harmed by<EXICVAL1> weapons<EXICVAL2>'
    string_55359 = ' of +<EXICVAL3> or greater enchantment'
    weapon_effective_strings = {' non-magical', ' magical', ' non-silver', ' silver', ' silver or magical', ' one-handed', ' two-handed', ' non-cursed', ' cursed', ' non-cold iron', ' cold iron', }
    local maxEnchantment = -1
    local ineffectiveType = 0
    local ineffectiveString = ''
    local maxEnchantmentString = ''
    EEex_IterateActorEffects(currentID, function(eData)
    	local theopcode = EEex_ReadDword(eData + 0x10)
    	local theparameter1 = EEex_ReadDword(eData + 0x1C)
    	local theparameter2 = EEex_ReadDword(eData + 0x20)
    	if theopcode == 120 then
    		if theparameter2 == 0 and maxEnchantment < theparameter1 then
    			ineffectiveType = bit32.bor(ineffectiveType, 1)
    			maxEnchantment = theparameter1
    			maxEnchantmentString = string.gsub(string_55359, '<EXICVAL3>', maxEnchantment + 1)
    		elseif theparameter2 == 2 and maxEnchantment < 0 then
    			ineffectiveType = bit32.bor(ineffectiveType, 1)
    			maxEnchantment = 0
    			maxEnchantmentString = string.gsub(string_55359, '<EXICVAL3>', maxEnchantment + 1)
    		elseif theparameter2 ~= 0 and theparameter2 ~= 2 and bit32.band(ineffectiveType, 2 ^ theparameter2) == 0 then
    			ineffectiveType = bit32.bor(ineffectiveType, 2 ^ theparameter2)
    			if weapon_effective_strings[theparameter2] ~= nil then
    				if ineffectiveString == '' then
    					ineffectiveString = weapon_effective_strings[theparameter2]
    				else
    					ineffectiveString = ineffectiveString .. ',' .. weapon_effective_strings[theparameter2]
    				end
    			end
    		end
    	end
    end)
    local string = ''
    if ineffectiveType > 0 then
    	if bit32.band(ineffectiveType, 0x3) == 0x3 or bit32.band(ineffectiveType, 0x18) == 0x18 or bit32.band(ineffectiveType, 0xC0) == 0xC0 or bit32.band(ineffectiveType, 0x300) == 0x300 or bit32.band(ineffectiveType, 0xC00) == 0xC00 then
    		string = string.gsub(string_55357, '<EXICNAME>', EEex_GetActorName(currentID))
    		Infinity_DisplayString(string)
    	else
    		string = string.gsub(string.gsub(string.gsub(string_55358, '<EXICNAME>', EEex_GetActorName(currentID)), '<EXICVAL1>', ineffectiveString), '<EXICVAL2>', maxEnchantmentString)
    		Infinity_DisplayString(string)
    	end
    end
    
  • YunieYunie Member Posts: 44
    OlvynChuru wrote: »
    Great idea! :)

    I made a similar system in my spell mod. One component changes the spell Know Alignment into Identify Creature, which gives information about the target similar to what you did.
    u5mfa90nhsez.png

    But I don't think my plain text looks quite as good as your overlays.

    In order for your UI code to tell if the creature is immune to certain weapons, you will need to have EEex installed. EEex adds a lot of new functions for reading and changing creature info.

    Once you have EEex installed, here is approximately the code I used to generate that "Thrix can only be harmed by weapons of +2 or greater enchantment" message. This is regular Lua code, so you should be able to place it in UI.MENU just fine. You have my permission to use it in your mod and change it however you need.
    string_55357 = '<EXICNAME> cannot be harmed by any weapons'
    string_55358 = '<EXICNAME> can only be harmed by<EXICVAL1> weapons<EXICVAL2>'
    string_55359 = ' of +<EXICVAL3> or greater enchantment'
    weapon_effective_strings = {' non-magical', ' magical', ' non-silver', ' silver', ' silver or magical', ' one-handed', ' two-handed', ' non-cursed', ' cursed', ' non-cold iron', ' cold iron', }
    local maxEnchantment = -1
    local ineffectiveType = 0
    local ineffectiveString = ''
    local maxEnchantmentString = ''
    EEex_IterateActorEffects(currentID, function(eData)
    	local theopcode = EEex_ReadDword(eData + 0x10)
    	local theparameter1 = EEex_ReadDword(eData + 0x1C)
    	local theparameter2 = EEex_ReadDword(eData + 0x20)
    	if theopcode == 120 then
    		if theparameter2 == 0 and maxEnchantment < theparameter1 then
    			ineffectiveType = bit32.bor(ineffectiveType, 1)
    			maxEnchantment = theparameter1
    			maxEnchantmentString = string.gsub(string_55359, '<EXICVAL3>', maxEnchantment + 1)
    		elseif theparameter2 == 2 and maxEnchantment < 0 then
    			ineffectiveType = bit32.bor(ineffectiveType, 1)
    			maxEnchantment = 0
    			maxEnchantmentString = string.gsub(string_55359, '<EXICVAL3>', maxEnchantment + 1)
    		elseif theparameter2 ~= 0 and theparameter2 ~= 2 and bit32.band(ineffectiveType, 2 ^ theparameter2) == 0 then
    			ineffectiveType = bit32.bor(ineffectiveType, 2 ^ theparameter2)
    			if weapon_effective_strings[theparameter2] ~= nil then
    				if ineffectiveString == '' then
    					ineffectiveString = weapon_effective_strings[theparameter2]
    				else
    					ineffectiveString = ineffectiveString .. ',' .. weapon_effective_strings[theparameter2]
    				end
    			end
    		end
    	end
    end)
    local string = ''
    if ineffectiveType > 0 then
    	if bit32.band(ineffectiveType, 0x3) == 0x3 or bit32.band(ineffectiveType, 0x18) == 0x18 or bit32.band(ineffectiveType, 0xC0) == 0xC0 or bit32.band(ineffectiveType, 0x300) == 0x300 or bit32.band(ineffectiveType, 0xC00) == 0xC00 then
    		string = string.gsub(string_55357, '<EXICNAME>', EEex_GetActorName(currentID))
    		Infinity_DisplayString(string)
    	else
    		string = string.gsub(string.gsub(string.gsub(string_55358, '<EXICNAME>', EEex_GetActorName(currentID)), '<EXICVAL1>', ineffectiveString), '<EXICVAL2>', maxEnchantmentString)
    		Infinity_DisplayString(string)
    	end
    end
    

    Well thats interesting. I don't think I need EEex for this as the opcode you are using is 120 which is a non EEex opcode. But if we are to look for it with keeper, for example, it won't be present where it should be. It makes me think that these effects are applied at runtime.

    If it wasn't clear, my overlay does not use the game's UI system - it is a standalone thing. I was poking into the game process' memory blindly. But interestingly enough, this EEex thing has the description of the original datastructures which will allow for more runtime checks. At the moment, the only realtime info I use is the id, CRE reference, HP and coordinates. All the rest is parsed from archives.
  • jmerryjmerry Member Posts: 3,881
    Effects like weapon immunity, spell level immunities, and status effect immunities are generally done with non-droppable items.
    For example, a lich has the "Lich" and "Ring95" immunity items. "Lich" provides immunity to cold, lightning, spells up to level 5, nonmagical weapons, hold, sleep, paralysis, polymorph, disintegrate, and slay. "Ring95" provides standard undead/construct/ooze immunities - poison, stun, sleep, fear, hold, fatigue, petrification, confusion, berserk, charm, paralysis, slay, and level drain. And yes, many of these are redundantly covered by both items. That happens a lot.
    For another example, the rakshasa Adratha/Ihtafeer has the "RakRing" and "Immune2" items. "RakRing" provides immunity to spells up to level 7, while "Immune2" provides immunity to normal and +1 weapons. There's a standard naming scheme there; "ImmuneN" means that you need +N weapons to hit them. She also wears the periapt of proof against poison, for poison immunity - and that one's droppable, so the player gets to use it after they beat her.

    Some mods may change the exact effects of these items. For example, the "more consistent Breach" component of SCS changes items that grant immunity to level 5 spells to instead grant immunity to a specific list of spells at that level. Similarly, the SCS component that weakens insect spells adds immunity to those spells (or, actually, new subspells that carry the effects) to anything that grants normal weapon immunity, and also to undead creatures. That's what you were seeing with those spell immunities in the creature file - SPPR319 is Summon Insects, SPPR517 is Insect Plague, SPPR717 is Creeping Doom.

    You could go a long way with the creature files, but you also have to account for equipped items if you do that.
  • YunieYunie Member Posts: 44
    edited February 2021
    Thanks @jmerry!
    I did not now about the whole item thing.

    Thats being said, few days ago I've uploaded a new release of the overlay and now it shows spell level immunities and weapon immunities. Also, the hobgoblin \ kobold bug was fixed. It also now able to show the info windows by just right-clicking on a monster (no need to look for a monster in the enemy list anymore).
    What I did there was I dug into EEex's documentation and found original data structure descriptions which made possible to get this effects in runtime. The level 5 thing was actually weird for me as in my debugging Rakshasas were like immune to 1-4 and 6-7 spell levels (thanks SCS ). And for liches it says they are immune to 1-4 for the exact same reason. Now I know how to fix this.
    As for spells SPPR319 SPPR517 SPPR717, I saw their original names but the ones I was asking about ones having postfix 'a'. Obviously SCS modifies Insect spells and it looks like I could just look for description in the original spell.
  • YunieYunie Member Posts: 44
    Made a proper release thread here
Sign In or Register to comment.