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

Metamagic class feat for Spellcasters #95

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
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
43 changes: 43 additions & 0 deletions cdtweaks/languages/english/metamagic.tra
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
@0 = "Quickened Spell"
@1 = "Quicken Spell"
@2 = "Quicken Spell

Quickened spells can be cast instantaneously, making them invulnerable to disruption."

@3 = "Empowered Spell"
@4 = "Empower Spell"
@5 = "Empower Spell

All variable, numeric effects of an empowered spell are increased by 50% (Empowerment does not increase spell duration)."

@6 = "Extended Spell"
@7 = "Extend Spell"
@8 = "Extend Spell

Extended spells have their duration doubled, lasting twice as long as normal."

@9 = "Maximized Spell"
@10 = "Maximize Spell"
@11 = "Maximize Spell

Maximized spells apply all variable numeric effects — including damage, number of targets, and so on — at their maximum values."

@12 = "Silenced Spell"
@13 = "Silent Spell"
@14 = "Silent Spell

Silent spells are cast without a verbal component."

@15 = "Stilled Spell"
@16 = "Still Spell"
@17 = "Still Spell

Still spells are cast without a somatic component. Any penalties incurred from casting in armor do not apply to a spell prepared with this feat."

@100 = "Metamagic: Spell Canceled: The targeted point is out of range."
@101 = "Metamagic: Spell Canceled: Spellcasting Disabled."
@102 = "Metamagic: Spell Canceled: Cannot target spells on invisible or sanctuaried creatures."
@103 = "Metamagic: Spell Canceled: The caster cannot cast spells while silenced."
@104 = "Metamagic: Spell Canceled: The caster does not meet the requirements for modifying the selected spell."
@105 = "Metamagic: Spell Canceled: The caster cannot perform more than one action per round."
@106 = "Metamagic: Spell Canceled: Spell Disrupted."
2 changes: 2 additions & 0 deletions cdtweaks/languages/english/weidu.tra
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,8 @@ The uninstall messages above are normal and expected.

@271000 = "Divine Grace / Dark Blessing feat for Paladins / Blackguards [Luke]"

@279000 = "Metamagic class feat for Spellcasters [Luke]"

/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\
/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\
///// \\\\\
Expand Down
43 changes: 43 additions & 0 deletions cdtweaks/languages/italian/metamagic.tra
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
@0 = "Incantesimo Rapido"
@1 = "Incantesimo Rapido"
@2 = "Incantesimo Rapido

Gli incantesimi rapidi possono essere lanciati istantaneamente, senza rischiare che vengano spezzati."

@3 = "Incantesimo Potenziato"
@4 = "Incantesimo Potenziato"
@5 = "Incantesimo Potenziato

Tutti gli effetti numerici di un incantesimo potenziato vengono aumentati del 50% (la durata dell'incantesimo non viene modificata)."

@6 = "Incantesimo Esteso"
@7 = "Incantesimo Esteso"
@8 = "Incantesimo Esteso

Gli incantesimi estesi hanno una durata doppia rispetto al normale."

@9 = "Incantesimo Massimizzato"
@10 = "Incantesimo Massimizzato"
@11 = "Incantesimo Massimizzato

Gli incantesimi massimizzati applicano i loro effetti numerici — danno, numero di bersagli, eccetera — al massimo del loro valore."

@12 = "Incantesimo Silenziato"
@13 = "Incantesimo Silenziato"
@14 = "Incantesimo Silenziato

Gli incantesimi silenziati non richiedono una componente verbale."

@15 = "Incantesimo Immobile"
@16 = "Incantesimo Immobile"
@17 = "Incantesimo Immobile

Gli incantesimi immobili non richiedono una componente somatica. Eventuali penalità dovute dal lancio in armatura non si applicano a un incantesimo preparato con questo talento."

@100 = "Metamagia: Incantesimo Annullato: Il punto selezionato è fuori portata."
@101 = "Metamagia: Incantesimo Annullato: Incantesimi Disabilitati."
@102 = "Metamagia: Incantesimo Annullato: Non puoi lanciare incantesimi su creature invisibili o sotto l'effetto dell'incantesimo Santuario."
@103 = "Metamagia: Incantesimo Annullato: L'incantatore non può lanciare incantesimi mentre è silenziato."
@104 = "Metamagia: Incantesimo Annullato: L'incantatore non soddisfa i requisiti per poter modificare l'incantesimo selezionato."
@105 = "Metamagia: Incantesimo Annullato: L'incantatore non può compiere più di un'azione per round."
@106 = "Metamagia: Incantesimo Annullato: Incantesimo Spezzato."
2 changes: 2 additions & 0 deletions cdtweaks/languages/italian/weidu.tra
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,8 @@ o rimpiazzata da - un'altra facente parte di uno dei mods installati.~

@271000 = "Grazia Divina / Benedizione Oscura per Paladini / Guardie Nere [Luke]"

@279000 = "Aggiungi talento di classe Metamagia per gli Incantatori [Luke]"

/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\
/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\
///// \\\\\
Expand Down
16 changes: 16 additions & 0 deletions cdtweaks/lib/comp_2790.tpa
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////
/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////
///// /////
///// Metamagic class feat for Spellcasters \\\\\
///// \\\\\
/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////
/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////

WITH_SCOPE BEGIN
INCLUDE "cdtweaks\luke\misc.tph"
//
INCLUDE "cdtweaks\lib\metamagic.tph"
WITH_TRA "cdtweaks\languages\english\metamagic.tra" "cdtweaks\languages\%LANGUAGE%\metamagic.tra" BEGIN
LAF "METAMAGIC" END
END
END
110 changes: 110 additions & 0 deletions cdtweaks/lib/metamagic.tph
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
DEFINE_ACTION_FUNCTION "METAMAGIC"
BEGIN
LAF "ADD_EXTENDED_STAT" INT_VAR "max" = 6 STR_VAR "identifier" = "CDTWEAKS_METAMAGIC" END
// apparently, op214 does not work when applied as an EFF V2.0...
/*WITH_SCOPE BEGIN
CREATE "eff" "cdmmagic"
COPY_EXISTING "cdmmagic.eff" "override"
WRITE_LONG 0x10 214 // 2da spell list
WRITE_LONG 0x20 3 // from lua
WRITE_ASCII 0x30 "GTSPMENU" #8 // lua function
BUT_ONLY
END*/
// auxiliary spell for PCs
WITH_SCOPE BEGIN
CREATE "spl" "cdspmenu"
COPY_EXISTING "cdspmenu.spl" "override"
/* Header */
WRITE_LONG NAME1 "-1"
WRITE_LONG UNIDENTIFIED_DESC "-1"
WRITE_LONG 0x18 BIT14 // ignore dead / wild magic
WRITE_SHORT 0x1C 4 // innate
WRITE_LONG 0x34 1 // level
/* Extended Header */
LPF "ADD_SPELL_HEADER" INT_VAR "target" = 5 "range" = 1000 END
/* Feature Block(s) */
LPF "ADD_SPELL_EFFECT" INT_VAR "opcode" = 214 "timing" = 1 "target" = 1 "parameter2" = 3 STR_VAR "resource" = "GTSPMENU" END // select spell (from lua)
BUT_ONLY
END
// auxiliary spell for Silent Spell
WITH_SCOPE BEGIN
CREATE "spl" "cd#mmsil"
COPY_EXISTING "cd#mmsil.spl" "override"
/* Header */
WRITE_LONG NAME1 "-1"
WRITE_LONG UNIDENTIFIED_DESC "-1"
WRITE_LONG 0x18 BIT14 // ignore dead / wild magic
WRITE_SHORT 0x1C 4 // innate
WRITE_LONG 0x34 1 // level
/* Extended Header */
LPF "ADD_SPELL_HEADER" INT_VAR "target" = 5 "range" = 1000 END
/* Feature Block(s) */
LPF "ADD_SPELL_EFFECT" INT_VAR "opcode" = 402 "target" = 1 STR_VAR "resource" = "GTSILSPL" END // invoke lua
BUT_ONLY
END
//
WITH_SCOPE BEGIN
ACTION_DEFINE_ASSOCIATIVE_ARRAY "cdtweaks_metamagic" BEGIN
// bam file / spl resref, bam file (icon), tra ref (icon), tra ref (name), tra_ref (desc) => stat value
"cdmtmqck", "cdmtmgc1", 0, 1, 2 => 1
"cdmtmemp", "cdmtmgc2", 3, 4, 5 => 2
"cdmtmext", "cdmtmgc3", 6, 7, 8 => 3
"cdmtmmax", "cdmtmgc4", 9, 10, 11 => 4
"cdmtmsil", "cdmtmgc5", 12, 13, 14 => 5
"cdmtmstl", "cdmtmgc6", 15, 16, 17 => 6
END
//
ACTION_PHP_EACH "cdtweaks_metamagic" AS "key" => "value" BEGIN
COPY "cdtweaks\luke\bam\metamagic\%key%.bam" "override" "cdtweaks\luke\bam\metamagic\%key_1%.bam" "override"
LAF "ADD_STATDESC_ENTRY" INT_VAR "description" = RESOLVE_STR_REF ((AT "%key_2%")) STR_VAR "bam_file" = "%key_1%" RET "index" END
//
CREATE "spl" "%key%"
COPY_EXISTING "%key%.spl" "override"
/* Header */
WRITE_LONG NAME1 RESOLVE_STR_REF ((AT "%key_3%"))
WRITE_LONG UNIDENTIFIED_DESC RESOLVE_STR_REF ((AT "%key_4%"))
WRITE_ASCII 0x10 "CAS_M02" #8 // casting sound
WRITE_SHORT 0x1C 4 // innate
WRITE_LONG 0x34 1 // level
WRITE_ASCII 0x3A "%key%" #8 // icon
WRITE_SHORT 0x22 34 // casting animation: swirl white
/* Extended Header */
LPF "ADD_SPELL_HEADER" INT_VAR "target" = 7 "range" = 30 STR_VAR "icon" = "%key%" END
/* Feature Block(s) */
LPF "ADD_SPELL_EFFECT" INT_VAR "opcode" = 321 "timing" = 1 "target" = 1 STR_VAR "resource" = "%DEST_RES%" END // refresh without stacking
PATCH_FOR_EACH "res" IN "cdmtmqck" "cdmtmemp" "cdmtmext" "cdmtmmax" "cdmtmsil" "cdmtmstl" BEGIN
PATCH_IF ("%res%" STRING_COMPARE_CASE "%DEST_RES%") BEGIN
LPF "CLONE_EFFECT" INT_VAR "match_opcode" = 321 "multi_match" = 1 STR_VAR "resource" = "%res%" "insert" = "below" END
END
END
LPF "ADD_SPELL_EFFECT" INT_VAR "opcode" = 401 "timing" = 1 "target" = 1 "parameter2" = 1 "parameter1" = "%value%" "special" = IDS_OF_SYMBOL ("stats" "CDTWEAKS_METAMAGIC") END // Set CDTWEAKS_METAMAGIC to "%value%"
LPF "ADD_SPELL_EFFECT" INT_VAR "opcode" = 142 "timing" = 1 "target" = 1 "parameter2" = "%index%" END // portrait icon
LPF "ADD_SPELL_EFFECT" INT_VAR "opcode" = 326 "timing" = 1 "target" = 1 "parameter2" = 102 "parameter1" = IDS_OF_SYMBOL ("EA" "PC") STR_VAR "resource" = "CDSPMENU" END // apply spl => EA=PC
// castable at will \\
LPF "ADD_SPELL_CFEFFECT" INT_VAR "insert_point" = "-1" "opcode" = 172 "target" = 1 "timing" = 1 STR_VAR "resource" = "%DEST_RES%" END
LPF "ADD_SPELL_CFEFFECT" INT_VAR "insert_point" = "-1" "opcode" = 171 "target" = 1 "timing" = 1 STR_VAR "resource" = "%DEST_RES%" END
BUT_ONLY
END
END
// Listeners
LAF "APPEND_LUA_FUNCTION" STR_VAR "description" = "Listeners" "sourceFileSpec" = "cdtweaks\luke\lua\metamagic\gain.lua" "destRes" = "m_gtlstn" END // run 'func' each time a sprite has finished evaluating its effects
LAF "APPEND_LUA_FUNCTION" STR_VAR "description" = "Listeners" "sourceFileSpec" = "cdtweaks\luke\lua\metamagic\utility.lua" "destRes" = "m_gtmmgc" END
//
WITH_SCOPE BEGIN
OUTER_SET "strref_OutOfRange" = RESOLVE_STR_REF (@100)
OUTER_SET "strref_SpellcastingDisabled" = RESOLVE_STR_REF (@101)
OUTER_SET "strref_InvisibleOrSanctuaried" = RESOLVE_STR_REF (@102)
OUTER_SET "strref_CasterSilenced" = RESOLVE_STR_REF (@103)
OUTER_SET "strref_CasterDoesNotMeetRequirements" = RESOLVE_STR_REF (@104)
OUTER_SET "strref_OncePerRound" = RESOLVE_STR_REF (@105)
OUTER_SET "strref_SpellDisrupted" = RESOLVE_STR_REF (@106)
//
LAF "APPEND_LUA_FUNCTION" STR_VAR "description" = "Listeners" "sourceFileSpec" = "cdtweaks\luke\lua\metamagic\usage.lua" "destRes" = "m_gtmmgc" END
END
//
LAF "APPEND_LUA_FUNCTION" STR_VAR "description" = "Functions to be invoked via op408" "sourceFileSpec" = "cdtweaks\luke\lua\metamagic\projectile_mutator.lua" "destRes" = "m_gt#408" END
//
ACTION_IF !(FILE_EXISTS_IN_GAME "m_gttbls.lua") BEGIN
COPY "cdtweaks\luke\lua\m_gttbls.lua" "override"
END
END
Binary file added cdtweaks/luke/bam/metamagic/cdmtmemp.bam
Binary file not shown.
Binary file added cdtweaks/luke/bam/metamagic/cdmtmext.bam
Binary file not shown.
Binary file added cdtweaks/luke/bam/metamagic/cdmtmgc1.bam
Binary file not shown.
Binary file added cdtweaks/luke/bam/metamagic/cdmtmgc2.bam
Binary file not shown.
Binary file added cdtweaks/luke/bam/metamagic/cdmtmgc3.bam
Binary file not shown.
Binary file added cdtweaks/luke/bam/metamagic/cdmtmgc4.bam
Binary file not shown.
Binary file added cdtweaks/luke/bam/metamagic/cdmtmgc5.bam
Binary file not shown.
Binary file added cdtweaks/luke/bam/metamagic/cdmtmgc6.bam
Binary file not shown.
Binary file added cdtweaks/luke/bam/metamagic/cdmtmmax.bam
Binary file not shown.
Binary file added cdtweaks/luke/bam/metamagic/cdmtmqck.bam
Binary file not shown.
Binary file added cdtweaks/luke/bam/metamagic/cdmtmsil.bam
Binary file not shown.
Binary file added cdtweaks/luke/bam/metamagic/cdmtmstl.bam
Binary file not shown.
48 changes: 48 additions & 0 deletions cdtweaks/luke/lua/metamagic/214.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
-- cdtweaks, metamagic feat for spellcasters: display a list of spells that can be modified --

function GTSPMENU(CGameEffect, CGameSprite)
if CGameSprite:getLocalInt("cdtweaksSpontaneousCast") > 0 then
CGameSprite:setLocalInt("cdtweaksSpontaneousCast", 0) -- cannot be used in conjunction with the Cleric Spontaneous Cast feat
end
--
local metamagicReq = {4, 2, 1, 3, 1, 1} -- quicken (+4), empower (+2), extend (+1), maximize (+3), silent (+1), still (+1)
local metamagicType = EEex_Sprite_GetStat(CGameSprite, GT_Resource_SymbolToIDS["stats"]["CDTWEAKS_METAMAGIC"])
--
return EEex_Actionbar_GetOp214ButtonDataItr(EEex_Utility_SelectItr(3, EEex_Utility_FilterItr(
EEex_Utility_ChainItrs(
CGameSprite:getKnownPriestSpellsWithAbilityIterator(1, 7 - metamagicReq[metamagicType]),
CGameSprite:getKnownMageSpellsWithAbilityIterator(1, 9 - metamagicReq[metamagicType])
),
function(spellLevel, knownSpellIndex, spellResRef, spellHeader, spellAbility)
--unless I'm missing something, op145 (Disable Spellcasting) / op38 (Silence) make ``HaveSpell()`` return ``false``...?
--local isSpellMemorized = EEex_Trigger_ParseConditionalString(string.format('HaveSpellRES("%s")', spellResRef))
local isSpellMemorized = false
local spellLevelMemListArray
--
if spellHeader.itemType == 1 then -- Wizard
spellLevelMemListArray = CGameSprite.m_memorizedSpellsMage
elseif spellHeader.itemType == 2 then -- Priest
spellLevelMemListArray = CGameSprite.m_memorizedSpellsPriest
end
--
local memList = spellLevelMemListArray:getReference(spellHeader.spellLevel - 1) -- count starts from 0 (that is why ``-1``)
EEex_Utility_IterateCPtrList(memList, function(memInstance)
local memInstanceResref = memInstance.m_spellId:get()
if memInstanceResref == spellResRef then
local memFlags = memInstance.m_flags
if EEex_IsBitSet(memFlags, 0x0) then -- make sure it is memorized
isSpellMemorized = true
return true
end
end
end)
--
if isSpellMemorized then
-- effects that don't go through the projectile, like Target = Self, won't mutated by the op408
if not GT_Metamagic_SelfBuff(spellHeader, spellAbility, metamagicType) then
return true
end
end
end
)))
end
66 changes: 66 additions & 0 deletions cdtweaks/luke/lua/metamagic/gain.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
-- cdtweaks: NWN-ish Metamagic feat for spellcasters --

EEex_Opcode_AddListsResolvedListener(function(sprite)
-- internal function that grants the actual feat
local gain = function()
-- Mark the creature as 'feat granted'
sprite:setLocalInt("cdtweaksMetamagic", 1)
--
local metamagicRes = {"CDMTMQCK", "CDMTMEMP", "CDMTMEXT", "CDMTMMAX", "CDMTMSIL", "CDMTMSTL"}
for _, v in ipairs(metamagicRes) do
sprite:applyEffect({
["effectID"] = 172, -- Remove spell
["durationType"] = 1,
["res"] = v,
["sourceID"] = sprite.m_id,
["sourceTarget"] = sprite.m_id,
})
sprite:applyEffect({
["effectID"] = 171, -- Give spell
["durationType"] = 1,
["res"] = v,
["sourceID"] = sprite.m_id,
["sourceTarget"] = sprite.m_id,
})
end
end
-- Check creature's class / flags
local spriteClassStr = GT_Resource_IDSToSymbol["class"][sprite.m_typeAI.m_Class]
--
local spriteFlags = sprite.m_baseStats.m_flags
-- 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 spriteLevel1 = sprite.m_derivedStats.m_nLevel1
local spriteLevel2 = sprite.m_derivedStats.m_nLevel2
-- Check if spellcaster class -- single/multi/(complete)dual
local gainAbility = spriteClassStr == "MAGE" or spriteClassStr == "CLERIC" or spriteClassStr == "DRUID" or spriteClassStr == "FIGHTER_MAGE_THIEF" or spriteClassStr == "FIGHTER_MAGE_CLERIC" or spriteClassStr == "CLERIC_MAGE"
or (spriteClassStr == "FIGHTER_MAGE" and (EEex_IsBitUnset(spriteFlags, 0x4) or spriteLevel1 > spriteLevel2))
or (spriteClassStr == "FIGHTER_CLERIC" and (EEex_IsBitUnset(spriteFlags, 0x5) or spriteLevel1 > spriteLevel2))
or (spriteClassStr == "MAGE_THIEF" and (EEex_IsBitUnset(spriteFlags, 0x4) or spriteLevel2 > spriteLevel1))
or (spriteClassStr == "CLERIC_THIEF" and (EEex_IsBitUnset(spriteFlags, 0x5) or spriteLevel2 > spriteLevel1))
or (spriteClassStr == "FIGHTER_DRUID" and (EEex_IsBitUnset(spriteFlags, 0x7) or spriteLevel1 > spriteLevel2))
or (spriteClassStr == "CLERIC_RANGER" and (EEex_IsBitUnset(spriteFlags, 0x5) or spriteLevel2 > spriteLevel1))
--
if sprite:getLocalInt("cdtweaksMetamagic") == 0 then
if gainAbility then
gain()
end
else
if gainAbility then
-- do nothing
else
-- Mark the creature as 'feat removed'
sprite:setLocalInt("cdtweaksMetamagic", 0)
--
local metamagicRes = {"CDMTMQCK", "CDMTMEMP", "CDMTMEXT", "CDMTMMAX", "CDMTMSIL", "CDMTMSTL"}
for _, v in ipairs(metamagicRes) do
sprite:applyEffect({
["effectID"] = 172, -- Remove spell
["durationType"] = 1,
["res"] = v,
["sourceID"] = sprite.m_id,
["sourceTarget"] = sprite.m_id,
})
end
end
end
end)
Loading