Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New component: Overwhelming/Devastating Critical #113

Merged
merged 8 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@0 = "Overwhelming Critical"
@1 = "Unaffected by effects from Overwhelming Critical"
@2 = "Devastating Critical"
@3 = "Unaffected by effects from Devastating Critical"
3 changes: 3 additions & 0 deletions cdtweaks/languages/english/weidu.tra
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,8 @@ Use Baldur.lua options: a7_interval_ini

@619000 = "Dirty Fighting class feat for Chaotic-aligned Rogues [Luke (EEex)]"

@620000 = "Overwhelming/Devastating Critical class feat for Trueclass Fighters [Luke]"

@621000 = "Self Concealment class feat for Monks [Luke (EEex)]"

@622000 = "Parry Mode kit feat for Blades e Swashbucklers [Luke (EEex)]"
Expand All @@ -853,3 +855,4 @@ Use Baldur.lua options: a7_interval_ini
@625000 = "Nature Sense class feat for Druids [Luke (EEex)]"

@626000 = "Uncanny Dodge class feat for Barbarians and Rogues [Luke (EEex)]"

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@0 = "Colpo Critico Possente"
@1 = "Non soggetto agli effetti di Colpo Critico Possente"
@2 = "Colpo Critico Devastante"
@3 = "Non soggetto agli effetti di Colpo Critico Devastante"
3 changes: 3 additions & 0 deletions cdtweaks/languages/italian/weidu.tra
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,8 @@ Usa opzioni di Baldur.lua: a7_interval_ini

@619000 = "Aggiungi talento di classe Combattimento Scorretto per i Ladri di allineamento Caotico [Luke (EEex)]"

@620000 = "Aggiungi talento di classe Colpo Critico Possente/Devastante per i Guerrieri [Luke]"

@621000 = "Aggiungi talento di classe Auto-Occultamento per i Monaci [Luke (EEex)]"

@622000 = "Aggiungi talento di classe Modalità Parata per Bardi Lama e Rodomonti [Luke (EEex)]"
Expand All @@ -764,3 +766,4 @@ Usa opzioni di Baldur.lua: a7_interval_ini
@625000 = "Aggiungi talento di classe Senso della Natura per i Druidi [Luke (EEex)]"

@626000 = "Aggiungi talento di classe Schivata Prodigiosa per i Barbari e i Ladri [Luke (EEex)]"

18 changes: 18 additions & 0 deletions cdtweaks/lib/comp_6200.tpa
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\////\\\\////
/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\////\\\\////
///// \\\\\////\\\\////
///// Overwhelming/Devastating Critical class feat for Trueclass Fighters \\\\\
///// \\\\\////\\\\////
/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\////\\\\////
/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\////\\\\////

WITH_SCOPE BEGIN
INCLUDE "cdtweaks\luke\misc.tph"
INCLUDE "cdtweaks\ardanis\functions.tph"
//
INCLUDE "cdtweaks\lib\overwhelming_devastating_critical.tph"
//
WITH_TRA "cdtweaks\languages\english\overwhelming_devastating_critical.tra" "cdtweaks\languages\%LANGUAGE%\overwhelming_devastating_critical.tra" BEGIN
LAF "OVERWHELMING_DEVASTATING_CRITICAL" END
END
END
57 changes: 57 additions & 0 deletions cdtweaks/lib/overwhelming_devastating_critical.tph
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
DEFINE_ACTION_FUNCTION "OVERWHELMING_DEVASTATING_CRITICAL"
BEGIN
LAF "GT_ADD_SPELL"
INT_VAR
"level" = 1
"type" = 4
STR_VAR
"idsName" = "TRUECLASS_FIGHTER_CRITICAL"
RET
"TRUECLASS_FIGHTER_CRITICAL" = "resName"
END
//
WITH_SCOPE BEGIN
ACTION_TO_LOWER "TRUECLASS_FIGHTER_CRITICAL"
//
CREATE "eff" "%TRUECLASS_FIGHTER_CRITICAL%b"
COPY_EXISTING "%TRUECLASS_FIGHTER_CRITICAL%b.eff" "override"
WRITE_LONG 0x10 341 // opcode: critical hit effect
WRITE_LONG 0x20 1 // mode: by this weapon only
WRITE_SHORT 0x2C 100 // prob1
WRITE_ASCII 0x30 "%TRUECLASS_FIGHTER_CRITICAL%b" #8 // SPL resref
BUT_ONLY
//
CREATE "spl" "%TRUECLASS_FIGHTER_CRITICAL%b"
COPY_EXISTING "%TRUECLASS_FIGHTER_CRITICAL%b.spl" "override"
WRITE_LONG NAME1 "-1"
WRITE_LONG UNIDENTIFIED_DESC "-1"
WRITE_LONG 0x18 (BIT9 BOR BIT10 BOR BIT14 BOR BIT25) // break sanctuary/invisibility, ignore dead/wild magic, castable when silenced
WRITE_LONG 0x1C 4 // type: innate
WRITE_LONG 0x34 1 // level
WRITE_ASCII 0x3A "SPCL905B" #8 // icon
//
LPF ~ADD_SPELL_HEADER~ STR_VAR "icon" = "SPCL905B" END
LPF ~ADD_SPELL_HEADER~ INT_VAR "required_level" = 30 STR_VAR "icon" = "SPCL905B" END
//
LPF "ADD_SPELL_EFFECT" INT_VAR "header" = 1 "opcode" = 402 "target" = 2 "parameter1" = 1 STR_VAR "resource" = "%DEST_RES%" END // invoke lua
LPF "ADD_SPELL_EFFECT" INT_VAR "header" = 2 "opcode" = 402 "target" = 2 "parameter1" = 2 STR_VAR "resource" = "%DEST_RES%" END // invoke lua
BUT_ONLY_IF_IT_CHANGES
END
// Listener: run 'func' each time a sprite has finished evaluating its effects
WITH_SCOPE BEGIN
OUTER_SET "feedback_strref_overwhelming_crit_hit" = RESOLVE_STR_REF (@0)
OUTER_SET "feedback_strref_overwhelming_crit_immune" = RESOLVE_STR_REF (@1)
OUTER_SET "feedback_strref_devastating_crit_hit" = RESOLVE_STR_REF (@2)
OUTER_SET "feedback_strref_devastating_crit_immune" = RESOLVE_STR_REF (@3)
//
LAF "APPEND_LUA_FUNCTION" STR_VAR "description" = "Class/Kit Abilities" "sourceFileSpec" = "cdtweaks\luke\lua\class\overwhelming_devastating_critical.lua" "destRes" = "m_gtspcl" END
END
//
ACTION_IF !(FILE_EXISTS_IN_GAME "m_gttbls.lua") BEGIN
COPY "cdtweaks\luke\lua\m_gttbls.lua" "override"
END
//
ACTION_IF !(FILE_EXISTS_IN_GAME "gtabmod.2da") BEGIN
COPY "cdtweaks\luke\2da\gtabmod.2da" "override"
END
END
207 changes: 207 additions & 0 deletions cdtweaks/luke/lua/class/overwhelming_devastating_critical.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
--[[
+---------------------------------------------------------------------------------------+
| cdtweaks, NWN-ish Overwhelming/Devastating Critical class feat for Trueclass Fighters |
+---------------------------------------------------------------------------------------+
--]]

-- Apply ability --

EEex_Opcode_AddListsResolvedListener(function(sprite)
-- Sanity check
if not EEex_GameObject_IsSprite(sprite) then
return
end
-- internal function that applies the actual feat
local apply = function(mainHandResRef)
-- Update tracking var
sprite:setLocalString("gtTrueFighterCriticalMainHand", mainHandResRef)
-- Mark the creature as 'feat applied'
sprite:setLocalInt("gtTrueFighterCritical", 1)
--
sprite:applyEffect({
["effectID"] = 321, -- Remove effects by resource
["res"] = "%TRUECLASS_FIGHTER_CRITICAL%",
["sourceID"] = sprite.m_id,
["sourceTarget"] = sprite.m_id,
})
sprite:applyEffect({
["effectID"] = 182, -- Use EFF file while ITM is equipped
["durationType"] = 9,
["res"] = string.upper(mainHandResRef), -- ITM
["m_res2"] = "%TRUECLASS_FIGHTER_CRITICAL%B", -- EFF
["m_sourceRes"] = "%TRUECLASS_FIGHTER_CRITICAL%",
["sourceID"] = sprite.m_id,
["sourceTarget"] = sprite.m_id,
})
end
-- Check creature's equipment / class / pips
local equipment = sprite.m_equipment -- CGameSpriteEquipment
local selectedWeapon = equipment.m_items:get(equipment.m_selectedWeapon) -- CItem
local selectedWeaponHeader = selectedWeapon.pRes.pHeader -- Item_Header_st
local selectedWeaponResRef = selectedWeapon.pRes.resref:get()
--
local selectedWeaponProficiencyType = selectedWeaponHeader.proficiencyType
-- get launcher if needed
local launcher = sprite:getLauncher(selectedWeapon:getAbility(equipment.m_selectedWeaponAbility)) -- CItem
if launcher ~= nil then
local pHeader = launcher.pRes.pHeader -- Item_Header_st
selectedWeaponProficiencyType = pHeader.proficiencyType
end
--
local spriteClassStr = GT_Resource_IDSToSymbol["class"][sprite.m_typeAI.m_Class]
-- since ``EEex_Opcode_AddListsResolvedListener`` is running after the effect lists have been evaluated, ``m_bonusStats`` has already been added to ``m_derivedStats`` by the engine
local spriteKitStr = sprite.m_derivedStats.m_nKit == 0 and "TRUECLASS" or GT_Resource_IDSToSymbol["kit"][sprite.m_derivedStats.m_nKit]
--
local grandmastery = EEex_Trigger_ParseConditionalString(string.format("ProficiencyGT(Myself,%d,4)", selectedWeaponProficiencyType))
--
local applyAbility = spriteClassStr == "FIGHTER" and (spriteKitStr == "TRUECLASS" or spriteKitStr == "MAGESCHOOL_GENERALIST") and grandmastery:evalConditionalAsAIBase(sprite)
--
if sprite:getLocalInt("gtTrueFighterCritical") == 0 then
if applyAbility then
apply(selectedWeaponResRef)
end
else
if applyAbility then
-- Check if weapon resref has changed since the last application
if selectedWeaponResRef ~= sprite:getLocalString("gtTrueFighterCriticalMainHand") then
apply(selectedWeaponResRef)
end
else
-- Mark the creature as 'feat removed'
sprite:setLocalInt("gtTrueFighterCritical", 0)
--
sprite:applyEffect({
["effectID"] = 321, -- Remove effects by resource
["res"] = "%TRUECLASS_FIGHTER_CRITICAL%",
["sourceID"] = sprite.m_id,
["sourceTarget"] = sprite.m_id,
})
end
end
--
grandmastery:free()
end)

-- Core function --

function %TRUECLASS_FIGHTER_CRITICAL%(CGameEffect, CGameSprite)
local sourceSprite = EEex_GameObject_Get(CGameEffect.m_sourceId)
--
local equipment = sourceSprite.m_equipment -- CGameSpriteEquipment
local selectedWeapon = equipment.m_items:get(equipment.m_selectedWeapon) -- CItem
local selectedWeaponHeader = selectedWeapon.pRes.pHeader -- Item_Header_st
local selectedWeaponAbility = EEex_Resource_GetItemAbility(selectedWeaponHeader, equipment.m_selectedWeaponAbility) -- Item_ability_st
--
if CGameEffect.m_effectAmount == 1 then
-- Overwhelming Critical
local immunityToDamage = EEex_Trigger_ParseConditionalString("EEex_IsImmuneToOpcode(Myself,12)")
--
local targetActiveStats = EEex_Sprite_GetActiveStats(CGameSprite)
--
local itmAbilityDamageTypeToIDS = {
[0] = 0x0, -- none (crushing)
[1] = 0x10, -- piercing
[2] = 0x0, -- crushing
[3] = 0x100, -- slashing
[4] = 0x80, -- missile
[5] = 0x800, -- non-lethal
[6] = targetActiveStats.m_nResistPiercing > targetActiveStats.m_nResistCrushing and 0x0 or 0x10, -- piercing/crushing (better)
[7] = targetActiveStats.m_nResistPiercing > targetActiveStats.m_nResistSlashing and 0x100 or 0x10, -- piercing/slashing (better)
[8] = targetActiveStats.m_nResistCrushing > targetActiveStats.m_nResistSlashing and 0x0 or 0x100, -- slashing/crushing (worse)
}
--
if itmAbilityDamageTypeToIDS[selectedWeaponAbility.damageType] then -- sanity check
if not immunityToDamage:evalConditionalAsAIBase(CGameSprite) then
EEex_GameObject_ApplyEffect(sourceSprite,
{
["effectID"] = 139, -- Display string
["effectAmount"] = %feedback_strref_overwhelming_crit_hit%,
["m_sourceRes"] = CGameEffect.m_sourceRes:get(),
["m_sourceType"] = CGameEffect.m_sourceType,
["sourceID"] = sourceSprite.m_id,
["sourceTarget"] = sourceSprite.m_id,
})
--
EEex_GameObject_ApplyEffect(CGameSprite,
{
["effectID"] = 12, -- Damage
["dwFlags"] = itmAbilityDamageTypeToIDS[selectedWeaponAbility.damageType] * 0x10000, -- mode: normal
["numDice"] = 2,
["diceSize"] = 6,
["m_sourceRes"] = CGameEffect.m_sourceRes:get(),
["m_sourceType"] = CGameEffect.m_sourceType,
["sourceID"] = CGameEffect.m_sourceId,
["sourceTarget"] = CGameEffect.m_sourceTarget,
})
else
EEex_GameObject_ApplyEffect(CGameSprite,
{
["effectID"] = 139, -- Display string
["effectAmount"] = %feedback_strref_overwhelming_crit_immune%,
["m_sourceRes"] = CGameEffect.m_sourceRes:get(),
["m_sourceType"] = CGameEffect.m_sourceType,
["sourceID"] = CGameEffect.m_sourceId,
["sourceTarget"] = CGameEffect.m_sourceTarget,
})
end
end
--
immunityToDamage:free()
elseif CGameEffect.m_effectAmount == 2 then
-- Devastating Critical
local sourceActiveStats = EEex_Sprite_GetActiveStats(sourceSprite)
--
local gtabmod = GT_Resource_2DA["gtabmod"]
--
local savebonus = tonumber(gtabmod[string.format("%s", sourceActiveStats.m_nSTR)]["BONUS"])
if selectedWeaponAbility.type == 2 then -- if ranged, make it scale with Dexterity
savebonus = tonumber(gtabmod[string.format("%s", sourceActiveStats.m_nDEX)]["BONUS"])
end
--
local immunityToKillTarget = EEex_Trigger_ParseConditionalString("EEex_IsImmuneToOpcode(Myself,13)")
--
if not immunityToKillTarget:evalConditionalAsAIBase(CGameSprite) then
EEex_GameObject_ApplyEffect(sourceSprite,
{
["effectID"] = 139, -- Display string
["effectAmount"] = %feedback_strref_devastating_crit_hit%,
["m_sourceRes"] = CGameEffect.m_sourceRes:get(),
["m_sourceType"] = CGameEffect.m_sourceType,
["sourceID"] = sourceSprite.m_id,
["sourceTarget"] = sourceSprite.m_id,
})
--
local effectCodes = {
{["op"] = 0xD7, ["tmg"] = 1, ["res"] = "SPBOLTGL"}, -- feedback vfx
{["op"] = 0xD, ["tmg"] = 4, ["p2"] = 0x4} -- kill target (normal death)
}
--
for _, attributes in ipairs(effectCodes) do
CGameSprite:applyEffect({
["effectID"] = attributes["op"] or EEex_Error("opcode number not specified"),
["dwFlags"] = attributes["p2"] or 0,
["durationType"] = attributes["tmg"] or 0,
["res"] = attributes["res"] or "",
["savingThrow"] = 0x4, -- save vs. death
["saveMod"] = -1 * savebonus,
["m_sourceRes"] = CGameEffect.m_sourceRes:get(),
["m_sourceType"] = CGameEffect.m_sourceType,
["sourceID"] = CGameEffect.m_sourceId,
["sourceTarget"] = CGameEffect.m_sourceTarget,
})
end
else
EEex_GameObject_ApplyEffect(CGameSprite,
{
["effectID"] = 139, -- Display string
["effectAmount"] = %feedback_strref_devastating_crit_immune%,
["m_sourceRes"] = CGameEffect.m_sourceRes:get(),
["m_sourceType"] = CGameEffect.m_sourceType,
["sourceID"] = CGameEffect.m_sourceId,
["sourceTarget"] = CGameEffect.m_sourceTarget,
})
end
--
immunityToKillTarget:free()
end
end
Loading