diff --git a/cdtweaks/languages/english/revised_archer.tra b/cdtweaks/languages/english/revised_archer.tra index 5b0d6cf..d339dd8 100644 --- a/cdtweaks/languages/english/revised_archer.tra +++ b/cdtweaks/languages/english/revised_archer.tra @@ -3,13 +3,12 @@ Advantages: – +1 to hit and damage rolls with bows every 3 levels. – May achieve Grand Mastery (5 slots) in longbows and shortbows. -– May use the Called Shot ability (when wielding a bow) once per day. Gains one use at level 4 and an additional use every 4 levels thereafter. +– May use the Called Shot ability (when wielding a bow). -CALLED SHOT: All successful ranged attacks within the next 10 seconds have the following cumulative effects besides normal damage, according to the level of the Archer: - 4th level: -1 penalty to target's THAC0. - 8th level: -1 penalty to target's Saving Throws vs. Spell. - 12th level: -1 penalty to the target's Strength score. - 16th level: +2 bonus to damage roll. +CALLED SHOT: Grants the ability to make a potentially disabling attack against an opponent's arms or legs. + Called shots are made at a -4 thac0 penalty. + A successful called shot against the legs reduces the opponent's movement rate by 20% and gives them a -2 cumulative penalty to their dexterity. + A successful called shot against the arms applies a cumulative -2 penalty to the creature's attack rolls. – May benefit from the Point Blank Shot passive trait. @@ -18,4 +17,25 @@ POINT BLANK SHOT: When wielding a bow, the Archer negates the -8 thac0 penalty f Disadvantages: – May only wear leather, studded leather, and hide armor. – May only become Proficient (one slot) with melee weapons. -– May not use the Charm Animal ability." \ No newline at end of file +– May not use the Charm Animal ability." + +@1 = "Called Shot (Arms)" +@2 = "Called Shot (Arms) + +A successful called shot against the arms applies a cumulative -2 thac0 penalty." + +@3 = "Called Shot (Legs)" +@4 = "Called Shot (Legs) + +A successful called shot against the legs reduces the opponent's movement rate by 20% and gives them a -2 cumulative penalty to their dexterity." + +@100 = "The selected target is out of range" +@101 = "THAC0 Modification" +@102 = "Dexterity Modification" +@103 = "The character must wield a bow in order to use this ability" + +@200 = "The character cannot perform more than one action per round" + +@300 = "The character cannot use this ability while shooting AoE missiles" + +@400 = "Unaffected by effects from Called Shot" \ No newline at end of file diff --git a/cdtweaks/languages/english/weidu.tra b/cdtweaks/languages/english/weidu.tra index 06daf4e..fcb2703 100644 --- a/cdtweaks/languages/english/weidu.tra +++ b/cdtweaks/languages/english/weidu.tra @@ -463,8 +463,6 @@ The uninstall messages above are normal and expected. @267000 = "Dual-Wield feat for Rangers [Luke]" -@268000 = ~"Force" the Archer kit to use bows [Luke]~ - @269000 = "NWN-ish Armor vs. Dexterity [Luke]" @270000 = "Defensive Roll feat for Thieves [Luke]" @@ -802,3 +800,13 @@ Use Baldur.lua options: a7_interval_ini @504000 = ~Allow Yeslick to Use Axes~ @505000 = ~Ensure Shar-Teel Doesn't Die in the Original Challenge~ + +/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ +/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ +///// \\\\\ +///// NWN-ish feats collection \\\\\ +///// \\\\\ +/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ +/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ + +@600170 = ~"Force" the Archer kit to use bows [Luke]~ \ No newline at end of file diff --git a/cdtweaks/languages/italian/revised_archer.tra b/cdtweaks/languages/italian/revised_archer.tra index 53e00c6..09293bf 100644 --- a/cdtweaks/languages/italian/revised_archer.tra +++ b/cdtweaks/languages/italian/revised_archer.tra @@ -3,14 +3,12 @@ Vantaggi: - Bonus pari a +1 ai tiri per colpire e al danno con qualsiasi arco ogni 3 livelli. - Può diventare Gran Maestro (cinque punti) con l'arco lungo e l'arco corto. -- Può utilizzare l'abilità Colpo Mirato (quando impugna un arco) una volta al giorno (inizia al 4° livello con un uso e ne ottiene uno addizionale ogni 4 livelli). +- Può utilizzare l'abilità Colpo Mirato (quando impugna un arco). -COLPO MIRATO: Tutti gli attacchi a distanza messi a segno nei successivi 10 secondi, oltre ad infliggere un normale danno, hanno i seguenti effetti cumulativi a seconda del livello: - -4° Livello: Penalità pari a -1 al THAC0 del bersaglio. -8° Livello: Penalità pari a -1 ai tiri-salvezza contro Incantesimi del bersaglio. -12° Livello: Penalità pari a -1 alla Forza del bersaglio. -16° Livello: Bonus pari a +2 al danno. +COLPO MIRATO: Conferisce la capacità di effettuare un attacco potenzialmente incapacitante contro le braccia o le gambe di un avversario. + I Colpi Mirati vengono effettuati con una penalità di -4 ai tiri per colpire. + Un Colpo Mirato contro le gambe riduce la velocità di movimento dell'avversario del 20% e gli applica una penalità cumulativa di -2 alla destrezza. + Un Colpo Mirato contro le braccia applica una penalità cumulativa di -2 ai tiri per colpire dell'avversario. - Può beneficiare dell'abilità passiva Tiro Ravvicinato. @@ -19,4 +17,25 @@ TIRO RAVVICINATO: Quando l'arciere impugna un arco, può negare la penalità di Svantaggi: - Può indossare soltanto armature di cuoio, armature di cuoio borchiato e armature di pelle. - Può diventare soltanto Competente (un punto) nelle armi da mischia. -- Non può utilizzare l'abilità Charmare Animali." \ No newline at end of file +- Non può utilizzare l'abilità Charmare Animali." + +@1 = "Colpo Mirato (Braccia)" +@2 = "Colpo Mirato (Braccia) + +Un Colpo Mirato contro le braccia applica una penalità cumulativa di -2 ai tiri per colpire dell'avversario." + +@3 = "Colpo Mirato (Gambe)" +@4 = "Colpo Mirato (Gambe) + +Un Colpo Mirato contro le gambe riduce la velocità di movimento dell'avversario del 20% e gli applica una penalità cumulativa di -2 alla destrezza." + +@100 = "Il bersaglio selezionato è fuori portata" +@101 = "Modificatore al THAC0" +@102 = "Modificatore alla Destrezza" +@103 = "Il personaggio deve impugnare un arco per poter utilizzare questa abilità" + +@200 = "Il personaggio non può compiere più di un'azione per round" + +@300 = "Il personaggio non può utilizzare questa abilità mentre sta scoccando frecce con effetto ad area" + +@400 = "Non soggetto agli effetti di Colpo Mirato" \ No newline at end of file diff --git a/cdtweaks/languages/italian/weidu.tra b/cdtweaks/languages/italian/weidu.tra index 738ea95..75d8c6a 100644 --- a/cdtweaks/languages/italian/weidu.tra +++ b/cdtweaks/languages/italian/weidu.tra @@ -718,3 +718,13 @@ Usa opzioni di Baldur.lua: a7_interval_ini @504000 = ~Permettere a Yeslick di usare le asce~ @505000 = ~Assicura che Shar-Teel non muoia nella sfida iniziale~ + +/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ +/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ +///// \\\\\ +///// Raccolta di talenti in stile NWN \\\\\ +///// \\\\\ +/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ +/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ + +@600170 = ~"Costringi" l'Arciere ad utilizzare gli archi [Luke]~ \ No newline at end of file diff --git a/cdtweaks/lib/comp_2680.tpa b/cdtweaks/lib/comp_2680.tpa index d47a86f..9f14a14 100644 --- a/cdtweaks/lib/comp_2680.tpa +++ b/cdtweaks/lib/comp_2680.tpa @@ -9,6 +9,8 @@ WITH_SCOPE BEGIN INCLUDE "cdtweaks\ardanis\functions.tph" INCLUDE "cdtweaks\lib\revised_archer.tph" + INCLUDE "cdtweaks\luke\misc.tph" + // WITH_TRA "cdtweaks\languages\english\revised_archer.tra" "cdtweaks\languages\%LANGUAGE%\revised_archer.tra" BEGIN LAF "REVISED_ARCHER" END END diff --git a/cdtweaks/lib/revised_archer.tph b/cdtweaks/lib/revised_archer.tph index ec69a90..e00e4bb 100644 --- a/cdtweaks/lib/revised_archer.tph +++ b/cdtweaks/lib/revised_archer.tph @@ -1,15 +1,28 @@ DEFINE_ACTION_FUNCTION "REVISED_ARCHER" BEGIN - WITH_SCOPE BEGIN - LAF "APPEND_LUA_FUNCTION" STR_VAR "description" = "Functions to be invoked via op402" "sourceFileSpec" = "cdtweaks\luke\lua\revised_archer_402.lua" "destRes" = "m_gt#402" END - // Listener: run 'func' each time a sprite has finished evaluating its effects - LAF "APPEND_LUA_FUNCTION" STR_VAR "description" = "Listeners" "sourceFileSpec" = "cdtweaks\luke\lua\revised_archer_listener.lua" "destRes" = "m_gtlstn" END - // - ACTION_IF !(FILE_EXISTS_IN_GAME "m_gttbls.lua") BEGIN - COPY "cdtweaks\luke\lua\m_gttbls.lua" "override" - END + LAF "GT_ADD_SPELL" + INT_VAR + "level" = 1 + "type" = 4 + "preferredSlot" = 21 + STR_VAR + "idsName" = "ARCHER_CALLED_SHOT" + RET + "ARCHER_CALLED_SHOT" = "resName" + END + LAF "GT_ADD_SPELL" + INT_VAR + "level" = 1 + "type" = 4 + "preferredSlot" = 22 + STR_VAR + "idsName" = "ARCHER_KIT_BONUS" + RET + "ARCHER_KIT_BONUS" = "resName" END // + //LAF "ADD_EXTENDED_STAT" INT_VAR "max" = 25 STR_VAR "identifier" = "GT_IGNORE_ACTION_ADD_SPRITE_STARTED_ACTION_LISTENER" END + // update description WITH_SCOPE BEGIN OUTER_SET "new_desc" = RESOLVE_STR_REF (@0) COPY_EXISTING "kitlist.2da" "override" @@ -25,14 +38,15 @@ BEGIN END END BUT_ONLY - // + // remove passive trait + Called Shot from CLAB file WITH_SCOPE BEGIN COPY_EXISTING "%clabfile%.2da" "override" PATCH_IF (GAME_IS "bgee bg2ee eet") BEGIN - REPLACE_TEXTUALLY CASE_INSENSITIVE EXACT_MATCH "AP_SPCL122" "****" + REPLACE_TEXTUALLY CASE_INSENSITIVE EXACT_MATCH "AP_SPCL122" "****" // Missile thac0/damage bonus END ELSE BEGIN - REPLACE_TEXTUALLY CASE_INSENSITIVE EXACT_MATCH "AP_SPCL124" "****" + REPLACE_TEXTUALLY CASE_INSENSITIVE EXACT_MATCH "AP_SPCL124" "****" // Missile thac0/damage bonus END + REPLACE_TEXTUALLY CASE_INSENSITIVE EXACT_MATCH "GA_SPCL121" "****" // Called Shot // formatting PRETTY_PRINT_2DA BUT_ONLY @@ -52,55 +66,50 @@ BEGIN BUT_ONLY END END - // Called Shot (bows only!) + // Called Shot (bows only!): Split it into two (arms/legs) WITH_SCOPE BEGIN - WITH_SCOPE BEGIN - COPY_EXISTING "spcl121.spl" "override\cdcl121.spl" - WRITE_LONG NAME1 "-1" // blank name - WRITE_LONG NAME2 "-1" - WRITE_LONG UNIDENTIFIED_DESC "-1" // blank description - WRITE_LONG DESC "-1" - WRITE_SHORT 0x1C 4 // innate - WRITE_LONG 0x34 1 // level - // - LPF "DELETE_EFFECT" END // fresh start - // - LPF "ADD_SPELL_EFFECT" INT_VAR "opcode" = 54 "target" = 2 "parameter1" = "-1" "duration" = 20 END // Base THAC0 bonus - // - LPF "ADD_SPELL_EFFECT" INT_VAR "header" = 2 "opcode" = 37 "target" = 2 "parameter1" = "-1" "duration" = 15 END // Save vs. spell bonus - LPF "ADD_SPELL_EFFECT" INT_VAR "header" = 3 "opcode" = 37 "target" = 2 "parameter1" = "-1" "duration" = 15 END // Save vs. spell bonus - LPF "ADD_SPELL_EFFECT" INT_VAR "header" = 4 "opcode" = 37 "target" = 2 "parameter1" = "-1" "duration" = 15 END // Save vs. spell bonus - // - LPF "ADD_SPELL_EFFECT" INT_VAR "header" = 3 "opcode" = 44 "target" = 2 "parameter1" = "-1" "duration" = 10 END // Strength bonus - LPF "ADD_SPELL_EFFECT" INT_VAR "header" = 4 "opcode" = 44 "target" = 2 "parameter1" = "-1" "duration" = 10 END // Strength bonus - // - LPF "ADD_SPELL_EFFECT" INT_VAR "header" = 4 "opcode" = 12 "target" = 2 "parameter1" = 2 "parameter2" = IDS_OF_SYMBOL ("dmgtype" "missile") "timing" = 1 END // +2 (missile) Damage - BUT_ONLY - END + ACTION_TO_LOWER "ARCHER_CALLED_SHOT" + // Arms + CREATE "spl" "%ARCHER_CALLED_SHOT%b" + COPY_EXISTING "%ARCHER_CALLED_SHOT%b.spl" "override" + WRITE_LONG NAME1 RESOLVE_STR_REF (@1) + WRITE_LONG NAME2 "-1" + WRITE_LONG UNIDENTIFIED_DESC RESOLVE_STR_REF (@2) + WRITE_LONG DESC "-1" + WRITE_LONG 0x18 (BIT14 BOR BIT25) // ignore dead/wild magic, castable when silenced + WRITE_SHORT 0x1C 4 // innate + WRITE_LONG 0x34 1 // level + WRITE_ASCII 0x3A "%DEST_RES%" #8 // icon + // + LPF "ADD_SPELL_HEADER" INT_VAR "range" = 30 STR_VAR "icon" = "%DEST_RES%" END + // + LPF "ADD_SPELL_EFFECT" INT_VAR "opcode" = 402 "target" = 2 "parameter1" = 1 STR_VAR "resource" = "%ARCHER_CALLED_SHOT%" END // Invoke lua + BUT_ONLY + // Legs + COPY_EXISTING "%ARCHER_CALLED_SHOT%b.spl" "override\%ARCHER_CALLED_SHOT%c.spl" + WRITE_LONG NAME1 RESOLVE_STR_REF (@3) + WRITE_LONG UNIDENTIFIED_DESC RESOLVE_STR_REF (@4) + WRITE_ASCII 0x3A "%DEST_RES%" #8 // icon + // + LPF "ALTER_SPELL_HEADER" STR_VAR "icon" = "%DEST_RES%" END + // + LPF "ALTER_SPELL_EFFECT" INT_VAR "match_opcode" = 402 "parameter1" = 2 END + BUT_ONLY + // EFFs + CREATE "eff" "%ARCHER_CALLED_SHOT%b" + COPY_EXISTING "%ARCHER_CALLED_SHOT%b.eff" "override" + WRITE_LONG 0x10 146 // Cast spell + WRITE_LONG 0x14 2 // Projectile target + WRITE_LONG 0x20 1 // Mode: instant/ignore level + WRITE_SHORT 0x2C 100 // prob1 + WRITE_ASCII 0x30 "%DEST_RES%" #8 // spl file + BUT_ONLY // - WITH_SCOPE BEGIN - COPY_EXISTING "spcl121.spl" "override" - GET_OFFSET_ARRAY "ab_array" SPL_V10_HEADERS - PHP_EACH "ab_array" AS "ab_ind" => "ab_off" BEGIN - PATCH_IF SHORT_AT ("%ab_off%" + 0x10) > 1 BEGIN - WRITE_BYTE "%ab_off%" 0xFF // mark for later deletion - END - END - LPF "DELETE_SPELL_HEADER" INT_VAR "header_type" = 0xFF END // enable deletion - // - LPF "ALTER_EFFECT" INT_VAR "match_opcode" = 249 STR_VAR "resource" = "cdcl121" END // Ranged hit effect - BUT_ONLY - END + COPY_EXISTING "%ARCHER_CALLED_SHOT%b.eff" "override\%ARCHER_CALLED_SHOT%c.eff" + WRITE_ASCII 0x30 "%DEST_RES%" #8 // spl file + BUT_ONLY // - WITH_SCOPE BEGIN - CREATE "eff" "cdcl121" - COPY_EXISTING "cdcl121.eff" "override" - WRITE_LONG 0x10 402 // Invoke Lua - WRITE_LONG 0x14 1 // Self - WRITE_SHORT 0x2C 100 // prob1 - WRITE_ASCII 0x30 "GTCLDSHT" #8 // Lua function - BUT_ONLY - END + COPY "cdtweaks\luke\bam\kit\archer\called_shot_arms.bam" "override\%ARCHER_CALLED_SHOT%b.bam" "cdtweaks\luke\bam\kit\archer\called_shot_legs.bam" "override\%ARCHER_CALLED_SHOT%c.bam" END // Point Blank Shot (bows only!) WITH_SCOPE BEGIN @@ -117,4 +126,22 @@ BEGIN PRETTY_PRINT_2DA BUT_ONLY END + // lua + WITH_SCOPE BEGIN + OUTER_SET "feedback_strref_thac0_mod" = RESOLVE_STR_REF (@101) + OUTER_SET "feedback_strref_dex_mod" = RESOLVE_STR_REF (@102) + OUTER_SET "feedback_strref_bow_only" = RESOLVE_STR_REF (@103) + OUTER_SET "feedback_strref_aura_free" = RESOLVE_STR_REF (@200) + OUTER_SET "feedback_strref_AoE" = RESOLVE_STR_REF (@300) + // + LAF "APPEND_LUA_FUNCTION" STR_VAR "description" = "Class/Kit Abilities" "sourceFileSpec" = "cdtweaks\luke\lua\kit\archer\passive_traits.lua" "destRes" = "m_gtspcl" END + // Listener: run 'func' each time a sprite has finished evaluating its effects + LAF "APPEND_LUA_FUNCTION" STR_VAR "description" = "Class/Kit Abilities" "sourceFileSpec" = "cdtweaks\luke\lua\kit\archer\called_shot.lua" "destRes" = "m_gtspcl" END + END + // + LAF "APPEND_LUA_FUNCTION" STR_VAR "description" = "Utility Functions / Listeners" "sourceFileSpec" = "cdtweaks\luke\lua\utility\effect_check.lua" "destRes" = "m_gtutil" END + // + ACTION_IF !(FILE_EXISTS_IN_GAME "m_gttbls.lua") BEGIN + COPY "cdtweaks\luke\lua\m_gttbls.lua" "override" + END END \ No newline at end of file diff --git a/cdtweaks/luke/bam/kit/archer/called_shot_arms.bam b/cdtweaks/luke/bam/kit/archer/called_shot_arms.bam new file mode 100644 index 0000000..2faa50a Binary files /dev/null and b/cdtweaks/luke/bam/kit/archer/called_shot_arms.bam differ diff --git a/cdtweaks/luke/bam/kit/archer/called_shot_legs.bam b/cdtweaks/luke/bam/kit/archer/called_shot_legs.bam new file mode 100644 index 0000000..c744f97 Binary files /dev/null and b/cdtweaks/luke/bam/kit/archer/called_shot_legs.bam differ diff --git a/cdtweaks/luke/lua/kit/archer/called_shot.lua b/cdtweaks/luke/lua/kit/archer/called_shot.lua new file mode 100644 index 0000000..75d5413 --- /dev/null +++ b/cdtweaks/luke/lua/kit/archer/called_shot.lua @@ -0,0 +1,404 @@ +--[[ ++--------------------------------------------+ +| cdtweaks, Revised Archer Kit (Called Shot) | ++--------------------------------------------+ +--]] + +-- NWN-ish Called Shot ability. Creatures with no arms -- + +local cdtweaks_CalledShot_NoArms = { + {"WEAPON"}, -- GENERAL.IDS + {"DOG", "WOLF", "ANKHEG", "BASILISK", "CARRIONCRAWLER", "SPIDER", "WYVERN", "SLIME", "BEHOLDER", "DEMILICH", "BEETLE", "BIRD", "WILL-O-WISP"}, -- RACE.IDS + {"WOLF_WORG", "ELEMENTAL_AIR", "WIZARD_EYE"}, -- CLASS.IDS + -- ANIMATE.IDS + { + "DOOM_GUARD", "DOOM_GUARD_LARGER", + "SNAKE", "DANCING_SWORD", "BLOB_MIST_CREATURE", "HAKEASHAR", "NISHRUU", "SNAKE_WATER", + "BOAR_ARCTIC", "BOAR_WILD", "BONEBAT", "WATER_WEIRD" + }, +} + +-- NWN-ish Called Shot ability. Creatures with no legs -- + +local cdtweaks_CalledShot_NoLegs = { + {"WEAPON"}, -- GENERAL.IDS + {"ANKHEG", "WYVERN", "SLIME", "BEHOLDER", "MEPHIT", "IMP", "YUANTI", "DEMILICH", "FEYR", "SALAMANDER", "BIRD", "WILL-O-WISP"}, -- RACE.IDS + {"MEPHIT", "ELEMENTAL_AIR", "WIZARD_EYE"}, -- CLASS.IDS + -- ANIMATE.IDS + { + "DOOM_GUARD", "DOOM_GUARD_LARGER", + "IMP", "SNAKE", "DANCING_SWORD", "MIST_CREATURE", "BLOB_MIST_CREATURE", "HAKEASHAR", "NISHRUU", "SNAKE_WATER", + "LEMURE", "BONEBAT", "SHADOW_SMALL", "SHADOW_LARGE", "WATER_WEIRD" + }, +} + +-- NWN-ish Called Shot ability (main) -- + +function %ARCHER_CALLED_SHOT%(CGameEffect, CGameSprite) + local sourceSprite = EEex_GameObject_Get(CGameEffect.m_sourceId) -- CGameSprite + local sourceActiveStats = EEex_Sprite_GetActiveStats(sourceSprite) + -- Check creature's equipment + local equipment = sourceSprite.m_equipment + local selectedWeapon = equipment.m_items:get(equipment.m_selectedWeapon) + local selectedWeaponHeader = selectedWeapon.pRes.pHeader + local selectedWeaponTypeStr = GT_Resource_IDSToSymbol["itemcat"][selectedWeaponHeader.itemType] + -- Get level + local sourceLevel = sourceActiveStats.m_nLevel1 + if sourceSprite.m_typeAI.m_Class == 18 then -- CLERIC_RANGER + sourceLevel = sourceActiveStats.m_nLevel2 + end + -- + local savebonus = math.floor((sourceLevel - 1) / 4) -- +1 every 4 levels, starting at 0 + if savebonus > 7 then + savebonus = 7 -- cap at 7 + end + -- + local stats = GT_Resource_SymbolToIDS["stats"] + -- + local targetGeneralStr = GT_Resource_IDSToSymbol["general"][CGameSprite.m_typeAI.m_General] + local targetRaceStr = GT_Resource_IDSToSymbol["race"][CGameSprite.m_typeAI.m_Race] + local targetClassStr = GT_Resource_IDSToSymbol["class"][CGameSprite.m_typeAI.m_Class] + local targetAnimateStr = GT_Resource_IDSToSymbol["animate"][CGameSprite.m_animation.m_animation.m_animationID] + -- + local targetIDS = {targetGeneralStr, targetRaceStr, targetClassStr, targetAnimateStr} + -- Bow with arrows equipped || bow with unlimited ammo equipped + if selectedWeaponTypeStr == "ARROW" or selectedWeaponTypeStr == "BOW" then + if CGameEffect.m_effectAmount == 1 then + -- Called Shot (Arms): -2 thac0 penalty + local found = false + -- + do + for index, symbolList in ipairs(cdtweaks_CalledShot_NoArms) do + for _, symbol in ipairs(symbolList) do + if targetIDS[index] == symbol then + found = true + break + end + end + end + end + -- + if not found then + local effectCodes = { + {["op"] = 54, ["p1"] = -2, ["dur"] = 24}, -- base thac0 bonus + {["op"] = 139, ["p1"] = %feedback_strref_thac0_mod%} -- feedback string + } + -- + for _, attributes in ipairs(effectCodes) do + CGameSprite:applyEffect({ + ["effectID"] = attributes["op"] or EEex_Error("opcode number not specified"), + ["effectAmount"] = attributes["p1"] or 0, + ["duration"] = attributes["dur"] or 0, + ["savingThrow"] = 0x2, -- save vs. breath + ["saveMod"] = -1 * savebonus, + ["m_sourceRes"] = "%ARCHER_CALLED_SHOT%B", + ["m_sourceType"] = 1, + ["sourceID"] = CGameEffect.m_sourceId, + ["sourceTarget"] = CGameEffect.m_sourceTarget, + }) + end + else + CGameSprite:applyEffect({ + ["effectID"] = 324, -- immunity to resource and message + ["res"] = CGameEffect.m_sourceRes:get(), + ["sourceID"] = CGameEffect.m_sourceId, + ["sourceTarget"] = CGameEffect.m_sourceTarget, + }) + end + elseif CGameEffect.m_effectAmount == 2 then + -- Called Shot (Legs): -2 dex penalty, 20% movement rate penalty + local found = false + -- + do + for index, symbolList in ipairs(cdtweaks_CalledShot_NoLegs) do + for _, symbol in ipairs(symbolList) do + if targetIDS[index] == symbol then + found = true + break + end + end + end + end + -- + if not found then + local targetDEX = CGameSprite.m_derivedStats.m_nDEX + CGameSprite.m_bonusStats.m_nDEX + -- + local effectCodes = { + {["op"] = 15, ["p1"] = (targetDEX <= 1) and 0 or ((targetDEX > 2) and -2 or -1), ["dur"] = 24}, -- dex bonus + {["op"] = 176, ["p1"] = 80, ["p2"] = 5, ["dur"] = 24} -- movement rate bonus (mode: multiply %) + {["op"] = 139, ["p1"] = %feedback_strref_dex_mod%} -- feedback string + } + -- + for _, attributes in ipairs(effectCodes) do + CGameSprite:applyEffect({ + ["effectID"] = attributes["op"] or EEex_Error("opcode number not specified"), + ["effectAmount"] = attributes["p1"] or 0, + ["dwFlags"] = attributes["p2"] or 0, + ["duration"] = attributes["dur"] or 0, + ["savingThrow"] = 0x2, -- save vs. breath + ["saveMod"] = -1 * savebonus, + ["m_sourceRes"] = "%ARCHER_CALLED_SHOT%C", + ["m_sourceType"] = 1, + ["sourceID"] = CGameEffect.m_sourceId, + ["sourceTarget"] = CGameEffect.m_sourceTarget, + }) + end + else + CGameSprite:applyEffect({ + ["effectID"] = 324, -- immunity to resource and message + ["res"] = CGameEffect.m_sourceRes:get(), + ["sourceID"] = CGameEffect.m_sourceId, + ["sourceTarget"] = CGameEffect.m_sourceTarget, + }) + end + end + else + sourceSprite:applyEffect({ + ["effectID"] = 139, -- display string + ["effectAmount"] = %feedback_strref_bow_only%, + ["sourceID"] = sourceSprite.m_id, + ["sourceTarget"] = sourceSprite.m_id, + }) + end +end + +-- NWN-ish Called Shot ability. Make sure one and only one attack roll is performed -- + +EEex_Opcode_AddListsResolvedListener(function(sprite) + -- Sanity check + if not EEex_GameObject_IsSprite(sprite) then + return + end + -- + local equipment = sprite.m_equipment + local selectedWeapon = equipment.m_items:get(equipment.m_selectedWeapon) + local selectedWeaponHeader = selectedWeapon.pRes.pHeader + local selectedWeaponTypeStr = GT_Resource_IDSToSymbol["itemcat"][selectedWeaponHeader.itemType] + -- + if sprite:getLocalInt("cdtweaksCalledShot") == 1 then + if GT_Utility_EffectCheck(sprite, {["op"] = 0xF9, ["res"] = "%ARCHER_CALLED_SHOT%B"}) or GT_Utility_EffectCheck(sprite, {["op"] = 0xF9, ["res"] = "%ARCHER_CALLED_SHOT%C"}) then + if sprite.m_startedSwing == 1 and sprite:getLocalInt("gtCalledShotSwing") == 0 and (selectedWeaponTypeStr == "ARROW" or selectedWeaponTypeStr == "BOW") then + sprite:setLocalInt("gtCalledShotSwing", 1) + elseif (sprite.m_startedSwing == 0 and sprite:getLocalInt("gtCalledShotSwing") == 1) or not (selectedWeaponTypeStr == "ARROW" or selectedWeaponTypeStr == "BOW") then + sprite:setLocalInt("gtCalledShotSwing", 0) + -- + sprite.m_curAction.m_actionID = 0 -- nuke current action + -- + EEex_GameObject_ApplyEffect(sprite, + { + ["effectID"] = 321, -- remove effects by resource + ["res"] = "%ARCHER_CALLED_SHOT%", + ["sourceID"] = sprite.m_id, + ["sourceTarget"] = sprite.m_id, + }) + end + end + end +end) + +-- NWN-ish Called Shot ability. Morph the spell action into an attack action -- + +EEex_Action_AddSpriteStartedActionListener(function(sprite, action) + local ea = GT_Resource_SymbolToIDS["ea"] + -- + if sprite:getLocalInt("cdtweaksCalledShot") == 1 then + if action.m_actionID == 31 and (action.m_string1.m_pchData:get() == "%ARCHER_CALLED_SHOT%B" or action.m_string1.m_pchData:get() == "%ARCHER_CALLED_SHOT%C") then + if EEex_Sprite_GetCastTimer(sprite) == -1 then + -- + local effectCodes = { + {["op"] = 321, ["res"] = "%ARCHER_CALLED_SHOT%"}, -- remove effects by resource + {["op"] = 167, ["p1"] = -4}, -- missile thac0 bonus + {["op"] = 249, ["res"] = action.m_string1.m_pchData:get() == "%ARCHER_CALLED_SHOT%B" and "%ARCHER_CALLED_SHOT%B" or "%ARCHER_CALLED_SHOT%C"}, -- ranged hit effect + {["op"] = 142, ["p2"] = 82} -- icon: called shot + {["op"] = 408, ["res"] = "%ARCHER_CALLED_SHOT%P"}, -- projectile mutator + } + -- + for _, attributes in ipairs(effectCodes) do + sprite:applyEffect({ + ["effectID"] = attributes["op"] or EEex_Error("opcode number not specified"), + ["effectAmount"] = attributes["p1"] or 0, + ["dwFlags"] = attributes["p2"] or 0, + ["res"] = attributes["res"] or "", + ["durationType"] = 1, + ["m_sourceRes"] = "%ARCHER_CALLED_SHOT%", + ["m_sourceType"] = 1, + ["sourceID"] = sprite.m_id, + ["sourceTarget"] = sprite.m_id, + }) + end + -- + sprite:setLocalInt("gtCalledShotSwing", 0) + -- + if sprite.m_typeAI.m_EnemyAlly < ea["GOODCUTOFF"] then + action.m_actionID = 3 -- Attack() + else + action.m_actionID = 134 -- AttackReevaluate() + action.m_specificID = 100 -- ReevaluationPeriod + end + -- + sprite.m_castCounter = 0 + else + action.m_actionID = 0 -- nuke current action + -- + EEex_GameObject_ApplyEffect(sprite, + { + ["effectID"] = 321, -- remove effects by resource + ["res"] = "%ARCHER_CALLED_SHOT%", + ["sourceID"] = sprite.m_id, + ["sourceTarget"] = sprite.m_id, + }) + EEex_GameObject_ApplyEffect(sprite, + { + ["effectID"] = 139, -- display string + ["effectAmount"] = %feedback_strref_aura_free%, + ["sourceID"] = sprite.m_id, + ["sourceTarget"] = sprite.m_id, + }) + end + else + EEex_GameObject_ApplyEffect(sprite, + { + ["effectID"] = 321, -- remove effects by resource + ["res"] = "%ARCHER_CALLED_SHOT%", + ["sourceID"] = sprite.m_id, + ["sourceTarget"] = sprite.m_id, + }) + end + end +end) + +-- NWN-ish Called Shot ability. Cannot be used with AoE missiles (see f.i. Arrow of Detonation) -- + +%ARCHER_CALLED_SHOT%P = { + + ["typeMutator"] = function(context) + local actionSources = { + [EEex_Projectile_DecodeSource.CGameSprite_Swing] = true, + } + -- + local originatingSprite = context["originatingSprite"] -- CGameSprite + -- + if not (actionSources[context.decodeSource] and (GT_Utility_EffectCheck(originatingSprite, {["op"] = 0xF9, ["res"] = "%ARCHER_CALLED_SHOT%B"}) or GT_Utility_EffectCheck(originatingSprite, {["op"] = 0xF9, ["res"] = "%ARCHER_CALLED_SHOT%C"}))) then + return + end + end, + + ["projectileMutator"] = function(context) + local actionSources = { + [EEex_Projectile_DecodeSource.CGameSprite_Swing] = true, + } + -- + local originatingSprite = context["originatingSprite"] -- CGameSprite + -- + if not (actionSources[context.decodeSource] and (GT_Utility_EffectCheck(originatingSprite, {["op"] = 0xF9, ["res"] = "%ARCHER_CALLED_SHOT%B"}) or GT_Utility_EffectCheck(originatingSprite, {["op"] = 0xF9, ["res"] = "%ARCHER_CALLED_SHOT%C"}))) then + return + end + -- + local projectile = context["projectile"] -- CProjectile + -- + if EEex_Projectile_IsOfType(projectile, EEex_Projectile_Type["CProjectileArea"]) then + originatingSprite.m_curAction.m_actionID = 0 -- nuke current action + -- + originatingSprite:applyEffect({ + ["effectID"] = 321, -- remove effects by resource + ["res"] = "%ARCHER_CALLED_SHOT%", + ["sourceID"] = originatingSprite.m_id, + ["sourceTarget"] = originatingSprite.m_id, + }) + originatingSprite:applyEffect({ + ["effectID"] = 139, -- display string + ["effectAmount"] = %feedback_strref_AoE%, + ["sourceID"] = originatingSprite.m_id, + ["sourceTarget"] = originatingSprite.m_id, + }) + end + end, + + ["effectMutator"] = function(context) + local actionSources = { + [EEex_Projectile_AddEffectSource.CGameSprite_Swing] = true, + } + -- + local originatingSprite = context["originatingSprite"] -- CGameSprite + -- + if not (actionSources[context.addEffectSource] and (GT_Utility_EffectCheck(originatingSprite, {["op"] = 0xF9, ["res"] = "%ARCHER_CALLED_SHOT%B"}) or GT_Utility_EffectCheck(originatingSprite, {["op"] = 0xF9, ["res"] = "%ARCHER_CALLED_SHOT%C"}))) then + return + end + end, +} + +-- NWN-ish Called Shot ability. Gain ability -- + +EEex_Opcode_AddListsResolvedListener(function(sprite) + -- Sanity check + if not EEex_GameObject_IsSprite(sprite) then + return + end + -- internal function that grants the ability + local gain = function() + -- Mark the creature as 'feat granted' + sprite:setLocalInt("cdtweaksCalledShot", 1) + -- + local effectCodes = { + {["op"] = 172, ["res"] = "%ARCHER_CALLED_SHOT%B"}, -- remove spell + {["op"] = 171, ["res"] = "%ARCHER_CALLED_SHOT%B"}, -- give spell + {["op"] = 172, ["res"] = "%ARCHER_CALLED_SHOT%C"}, -- remove spell + {["op"] = 171, ["res"] = "%ARCHER_CALLED_SHOT%C"}, -- give spell + } + -- + for _, attributes in ipairs(effectCodes) do + sprite:applyEffect({ + ["effectID"] = attributes["op"] or -1, + ["res"] = attributes["res"] or "", + ["sourceID"] = sprite.m_id, + ["sourceTarget"] = sprite.m_id, + }) + end + end + -- Check creature's class / kit + local spriteKitStr = GT_Resource_IDSToSymbol["kit"][sprite.m_derivedStats.m_nKit] + -- + 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 + -- + local gainAbility = spriteKitStr == "FERALAN" + and (spriteClassStr == "RANGER" + or (spriteClassStr == "CLERIC_RANGER" and (EEex_IsBitUnset(spriteFlags, 0x8) or spriteLevel1 > spriteLevel2))) + and EEex_IsBitUnset(spriteFlags, 10) -- must not be fallen + -- + if sprite:getLocalInt("cdtweaksCalledShot") == 0 then + if gainAbility then + gain() + end + else + if gainAbility then + -- do nothing + else + -- Mark the creature as 'feat removed' + sprite:setLocalInt("cdtweaksCalledShot", 0) + -- + sprite:applyEffect({ + ["effectID"] = 172, -- remove spell + ["res"] = "%ARCHER_CALLED_SHOT%B", + ["sourceID"] = sprite.m_id, + ["sourceTarget"] = sprite.m_id, + }) + sprite:applyEffect({ + ["effectID"] = 172, -- remove spell + ["res"] = "%ARCHER_CALLED_SHOT%C", + ["sourceID"] = sprite.m_id, + ["sourceTarget"] = sprite.m_id, + }) + sprite:applyEffect({ + ["effectID"] = 321, -- remove effects by resource + ["res"] = "%ARCHER_CALLED_SHOT%", + ["sourceID"] = sprite.m_id, + ["sourceTarget"] = sprite.m_id, + }) + end + end +end) diff --git a/cdtweaks/luke/lua/kit/archer/passive_traits.lua b/cdtweaks/luke/lua/kit/archer/passive_traits.lua new file mode 100644 index 0000000..2638276 --- /dev/null +++ b/cdtweaks/luke/lua/kit/archer/passive_traits.lua @@ -0,0 +1,106 @@ +--[[ ++-----------------------------------------------------------------------------+ +| cdtweaks, Revised Archer Kit (+X missile thac0/damage bonus with bows only) | ++-----------------------------------------------------------------------------+ +--]] + +-- Apply bonus -- + +EEex_Opcode_AddListsResolvedListener(function(sprite) + -- Sanity check + if not EEex_GameObject_IsSprite(sprite) then + return + end + -- internal function that applies the actual bonus + local apply = function(bonus) + -- Update tracking var + sprite:setLocalInt("cdtweaksArcherKitBonus", bonus) + -- Mark the creature as 'bonus applied' + sprite:setLocalInt("cdtweaksRevisedArcher", 1) + -- + sprite:applyEffect({ + ["effectID"] = 321, -- Remove effects by resource + ["res"] = "%ARCHER_KIT_BONUS%", + ["sourceID"] = sprite.m_id, + ["sourceTarget"] = sprite.m_id, + }) + sprite:applyEffect({ + ["effectID"] = 167, -- Missile THAC0 bonus + ["durationType"] = 9, + ["effectAmount"] = bonus, + ["m_sourceRes"] = "%ARCHER_KIT_BONUS%", + ["sourceID"] = sprite.m_id, + ["sourceTarget"] = sprite.m_id, + }) + sprite:applyEffect({ + ["effectID"] = 286, -- Missile weapon damage bonus + ["durationType"] = 9, + ["effectAmount"] = bonus, + ["m_sourceRes"] = "%ARCHER_KIT_BONUS%", + ["sourceID"] = sprite.m_id, + ["sourceTarget"] = sprite.m_id, + }) + end + -- Check creature's equipment / class / kit / levels + local equipment = sprite.m_equipment + local selectedWeapon = equipment.m_items:get(equipment.m_selectedWeapon) + local selectedWeaponHeader = selectedWeapon.pRes.pHeader + -- + local spriteKitStr = GT_Resource_IDSToSymbol["kit"][sprite.m_derivedStats.m_nKit] + -- + local spriteClassStr = GT_Resource_IDSToSymbol["class"][sprite.m_typeAI.m_Class] + -- + local selectedWeaponTypeStr = GT_Resource_IDSToSymbol["itemcat"][selectedWeaponHeader.itemType] + -- + 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 + -- + local bonus = 0 + -- + if spriteClassStr == "RANGER" then + if spriteLevel1 <= 18 then + bonus = math.floor(spriteLevel1 / 3) + else + bonus = math.floor((spriteLevel1 - 18) / 5) + (18 / 3) + end + else + if spriteLevel2 <= 18 then + bonus = math.floor(spriteLevel2 / 3) + else + bonus = math.floor((spriteLevel2 - 18) / 5) + (18 / 3) + end + end + -- (Bow with arrows equipped || bow with unlimited ammo equipped) && Archer kit + local applyCondition = (selectedWeaponTypeStr == "ARROW" or selectedWeaponTypeStr == "BOW") + and spriteKitStr == "FERALAN" + and (spriteClassStr == "RANGER" + -- incomplete dual-class characters are not supposed to benefit from this passive feat + or (spriteClassStr == "CLERIC_RANGER" and (EEex_IsBitUnset(spriteFlags, 0x8) or spriteLevel1 > spriteLevel2))) + and EEex_IsBitUnset(spriteFlags, 10) -- not Fallen Ranger + and bonus > 0 + -- + if sprite:getLocalInt("cdtweaksRevisedArcher") == 0 then + if applyCondition then + apply(bonus) + end + else + if applyCondition then + -- Check if level has changed since the last application + if bonus ~= sprite:getLocalInt("cdtweaksArcherKitBonus") then + apply(bonus) + end + else + -- Mark the creature as 'bonus removed' + sprite:setLocalInt("cdtweaksRevisedArcher", 0) + -- + sprite:applyEffect({ + ["effectID"] = 321, -- Remove effects by resource + ["res"] = "%ARCHER_KIT_BONUS%", + ["sourceID"] = sprite.m_id, + ["sourceTarget"] = sprite.m_id, + }) + end + end +end) diff --git a/cdtweaks/luke/lua/m_gttbls.lua b/cdtweaks/luke/lua/m_gttbls.lua index 6861a6c..02831c7 100644 --- a/cdtweaks/luke/lua/m_gttbls.lua +++ b/cdtweaks/luke/lua/m_gttbls.lua @@ -29,7 +29,7 @@ EEex_GameState_AddInitializedListener(function() end) -- IDS EEex_Utility_NewScope(function() - local resources = { "EA", "GENERAL", "RACE", "CLASS", "GENDER", "ALIGN", "KIT", "ITEMCAT", "ITEMFLAG", "STATE", "STATS", "SPELL" } + local resources = { "EA", "GENERAL", "RACE", "CLASS", "GENDER", "ALIGN", "KIT", "ITEMCAT", "ITEMFLAG", "STATE", "STATS", "SPELL", "ANIMATE" } -- for _, v in ipairs(resources) do local data = EEex_Resource_LoadIDS(v) diff --git a/cdtweaks/readme-cdtweaks.html b/cdtweaks/readme-cdtweaks.html index 84c933f..b48e9f1 100644 --- a/cdtweaks/readme-cdtweaks.html +++ b/cdtweaks/readme-cdtweaks.html @@ -983,14 +983,33 @@
-8
thac0 penalty for using ranged weapons in close combat
+ This component also reworks the Called Shot ability. Here is the revised description:
+
CALLED SHOT: Grants the ability to make a potentially disabling attack against an opponent's arms or legs. Called shots are made at a -4 thac0 penalty. A successful called shot against the legs reduces the opponent's movement rate by 20% and gives them a -2 cumulative penalty to their dexterity. A successful called shot against the arms applies a cumulative -2 penalty to the creature's attack rolls.
+So to sum up, its core features are now all built around the bow (instead of any missile weapon).
++ Notes about Called Shot: +
Spontaneous Casting for Clerics [Luke]
EEex
This component aims at giving Clerics some Spontaneous Casting capabilities.
diff --git a/cdtweaks/setup-cdtweaks.tp2 b/cdtweaks/setup-cdtweaks.tp2 index 5e97fb6..af04072 100644 --- a/cdtweaks/setup-cdtweaks.tp2 +++ b/cdtweaks/setup-cdtweaks.tp2 @@ -2836,20 +2836,6 @@ REQUIRE_PREDICATE MOD_IS_INSTALLED ~EEex.tp2~ 0 @29 REQUIRE_PREDICATE FILE_EXISTS ~cdtweaks/languages/%LANGUAGE%/dual_wield.tra~ @7 LABEL ~cd_tweaks_dual_wield~ -/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ -/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ -///// \\\\\ -///// "Force" the Archer kit to use bows \\\\\ -///// \\\\\ -/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ -/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ - -BEGIN @268000 DESIGNATED 2680 -GROUP @9 -REQUIRE_PREDICATE MOD_IS_INSTALLED ~EEex.tp2~ 0 @29 -REQUIRE_PREDICATE FILE_EXISTS ~cdtweaks/languages/%LANGUAGE%/revised_archer.tra~ @7 -LABEL ~cd_tweaks_revised_archer~ - /////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ /////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ ///// \\\\\ @@ -4903,3 +4889,26 @@ GROUP @0 REQUIRE_PREDICATE GAME_IS ~bgee bg2ee eet~ @25 REQUIRE_PREDICATE MOD_IS_INSTALLED "EEex.tp2" 0 @29 LABEL ~cd_tweaks_dorns_sword~ + +/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ +/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ +///// \\\\\ +///// NWN-ish feats collection \\\\\ +///// \\\\\ +/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ +/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ + +/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ +/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ +///// \\\\\ +///// "Force" the Archer kit to use bows \\\\\ +///// \\\\\ +/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ +/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\/////\\\\\ + +BEGIN @600170 DESIGNATED 6170 +GROUP @30 +REQUIRE_PREDICATE GAME_IS ~bgee bg2ee eet iwdee~ @25 +REQUIRE_PREDICATE MOD_IS_INSTALLED ~EEex.tp2~ 0 @29 +REQUIRE_PREDICATE FILE_EXISTS ~cdtweaks/languages/%LANGUAGE%/revised_archer.tra~ @7 +LABEL ~cd_tweaks_nwn_revised_archer~ \ No newline at end of file