From 4124cdaa4a234731cc91fc42d8e67ebd38bdbc1c Mon Sep 17 00:00:00 2001 From: Lemongrass3110 Date: Sun, 10 Mar 2024 22:10:28 +0100 Subject: [PATCH] Initial implementation of Night Watch (#8150) Taken from #7024 Co-authored-by: munkrej Co-authored-by: Atemo --- db/re/skill_db.yml | 477 +++++++++++++++++++++++++++++++++++ db/re/skill_tree.yml | 89 +++++++ db/re/status.yml | 89 +++++++ src/map/battle.cpp | 263 ++++++++++++++++++- src/map/script_constants.hpp | 10 + src/map/skill.cpp | 182 +++++++++++-- src/map/skill.hpp | 2 + src/map/status.cpp | 32 +++ src/map/status.hpp | 12 + 9 files changed, 1138 insertions(+), 18 deletions(-) diff --git a/db/re/skill_db.yml b/db/re/skill_db.yml index 14bbece650..e3bf3005b5 100644 --- a/db/re/skill_db.yml +++ b/db/re/skill_db.yml @@ -41379,6 +41379,473 @@ Body: SplashArea: 6 Requires: SpCost: 1 + - Id: 5401 + Name: NW_P_F_I + Description: P.F.I + MaxLevel: 10 + - Id: 5402 + Name: NW_GRENADE_MASTERY + Description: Grenade Mastery + MaxLevel: 10 + - Id: 5403 + Name: NW_INTENSIVE_AIM + Description: Intensive Aim + MaxLevel: 1 + Type: Weapon + TargetType: Self + DamageFlags: + NoDamage: true + Hit: Single + HitCount: 1 + Duration1: -1 + Requires: + SpCost: + - Level: 1 + Amount: 10 + Status: Intensive_Aim + - Id: 5404 + Name: NW_GRENADE_FRAGMENT + Description: Grenade Fragment + MaxLevel: 7 + Type: Weapon + TargetType: Self + DamageFlags: + NoDamage: true + Hit: Single + HitCount: 1 + Duration1: 300000 + Cooldown: 2000 + Requires: + SpCost: 50 + - Id: 5405 + Name: NW_THE_VIGILANTE_AT_NIGHT + Description: The Vigilante at Night + MaxLevel: 5 + Type: Weapon + TargetType: Self + DamageFlags: + Splash: true + Hit: Multi_Hit + HitCount: 4 + Element: Weapon + SplashArea: + - Level: 1 + Area: 2 + - Level: 2 + Area: 2 + - Level: 3 + Area: 2 + - Level: 4 + Area: 3 + - Level: 5 + Area: 3 + GiveAp: 1 + AfterCastActDelay: 1000 + Cooldown: 500 + CastCancel: true + FixedCastTime: 1500 + Requires: + SpCost: + - Level: 1 + Amount: 49 + - Level: 2 + Amount: 53 + - Level: 3 + Amount: 57 + - Level: 4 + Amount: 61 + - Level: 5 + Amount: 65 + Weapon: + Gatling: true + Shotgun: true + Ammo: + Bullet: true + AmmoAmount: 10 + - Id: 5406 + Name: NW_ONLY_ONE_BULLET + Description: Only One Bullet + MaxLevel: 5 + Type: Weapon + TargetType: Attack + DamageFlags: + Critical: true + Range: -9 + Hit: Single + HitCount: 1 + Element: Weapon + GiveAp: 1 + AfterCastActDelay: 500 + Cooldown: 300 + CastCancel: true + FixedCastTime: 1000 + Requires: + SpCost: + - Level: 1 + Amount: 39 + - Level: 2 + Amount: 43 + - Level: 3 + Amount: 47 + - Level: 4 + Amount: 51 + - Level: 5 + Amount: 55 + Weapon: + Revolver: true + Rifle: true + Ammo: + Bullet: true + AmmoAmount: 1 + - Id: 5407 + Name: NW_SPIRAL_SHOOTING + Description: Spiral Shooting + MaxLevel: 5 + Type: Weapon + TargetType: Attack + DamageFlags: + Critical: true + Splash: true + Range: -9 + Hit: Multi_Hit + HitCount: 1 + Element: Weapon + SplashArea: + - Level: 1 + Area: 2 + - Level: 2 + Area: 2 + - Level: 3 + Area: 2 + - Level: 4 + Area: 3 + - Level: 5 + Area: 3 + GiveAp: 1 + AfterCastActDelay: 1000 + Cooldown: 500 + CastCancel: true + FixedCastTime: 1000 + Requires: + SpCost: + - Level: 1 + Amount: 48 + - Level: 2 + Amount: 53 + - Level: 3 + Amount: 58 + - Level: 4 + Amount: 63 + - Level: 5 + Amount: 68 + Weapon: + Grenade: true + Rifle: true + Ammo: + Bullet: true + AmmoAmount: 6 + - Id: 5408 + Name: NW_MAGAZINE_FOR_ONE + Description: Magazine for One + MaxLevel: 5 + Type: Weapon + TargetType: Attack + DamageFlags: + Critical: true + Range: -9 + Hit: Multi_Hit + HitCount: 6 + Element: Weapon + GiveAp: 1 + AfterCastActDelay: 1000 + Cooldown: 500 + CastCancel: true + FixedCastTime: 1000 + Requires: + SpCost: + - Level: 1 + Amount: 42 + - Level: 2 + Amount: 46 + - Level: 3 + Amount: 50 + - Level: 4 + Amount: 54 + - Level: 5 + Amount: 58 + Weapon: + Revolver: true + Gatling: true + Ammo: + Bullet: true + AmmoAmount: 6 + - Id: 5409 + Name: NW_WILD_FIRE + Description: Wild Fire + MaxLevel: 5 + Type: Weapon + TargetType: Ground + Range: -9 + Hit: Single + HitCount: -3 + Element: Weapon + DamageFlags: + Splash: true + SplashArea: + - Level: 1 + Area: 2 + - Level: 2 + Area: 2 + - Level: 3 + Area: 2 + - Level: 4 + Area: 3 + - Level: 5 + Area: 3 + GiveAp: 1 + AfterCastActDelay: 1000 + Cooldown: 500 + CastCancel: true + FixedCastTime: 1000 + Requires: + SpCost: + - Level: 1 + Amount: 51 + - Level: 2 + Amount: 55 + - Level: 3 + Amount: 59 + - Level: 4 + Amount: 63 + - Level: 5 + Amount: 67 + Weapon: + Shotgun: true + Grenade: true + Ammo: + Bullet: true + AmmoAmount: 5 + - Id: 5410 + Name: NW_BASIC_GRENADE + Description: Basic Grenade + MaxLevel: 5 + Type: Weapon + TargetType: Ground + Range: -9 + Hit: Single + HitCount: -2 + Element: Weapon + DamageFlags: + Splash: true + SplashArea: + - Level: 1 + Area: 1 + - Level: 2 + Area: 1 + - Level: 3 + Area: 1 + - Level: 4 + Area: 2 + - Level: 5 + Area: 2 + GiveAp: 2 + Cooldown: 300 + CastCancel: true + FixedCastTime: 1000 + Requires: + SpCost: + - Level: 1 + Amount: 36 + - Level: 2 + Amount: 42 + - Level: 3 + Amount: 48 + - Level: 4 + Amount: 54 + - Level: 5 + Amount: 60 + ItemCost: + - Item: Nw_Grenade + Amount: 1 + - Id: 5411 + Name: NW_HASTY_FIRE_IN_THE_HOLE + Description: Hasty Fire in the Hole + MaxLevel: 5 + Type: Weapon + TargetType: Ground + Range: -9 + Hit: Multi_Hit + HitCount: -2 + Element: Weapon + DamageFlags: + Splash: true + SplashArea: 2 + GiveAp: 3 + Cooldown: 1000 + CastCancel: true + FixedCastTime: 1000 + AfterCastActDelay: 1500 + Requires: + SpCost: + - Level: 1 + Amount: 50 + - Level: 2 + Amount: 53 + - Level: 3 + Amount: 56 + - Level: 4 + Amount: 59 + - Level: 5 + Amount: 62 + ItemCost: + - Item: Nw_Grenade + Amount: 3 + - Id: 5412 + Name: NW_GRENADES_DROPPING + Description: Grenades Dropping + MaxLevel: 5 + Type: Weapon + TargetType: Ground + Range: -9 + Hit: Multi_Hit + HitCount: 3 + Element: Weapon + DamageFlags: + Splash: true + SplashArea: + - Level: 1 + Area: 5 + - Level: 2 + Area: 5 + - Level: 3 + Area: 5 + - Level: 4 + Area: 4 + - Level: 5 + Area: 4 + GiveAp: 5 + Cooldown: 4500 + CastCancel: true + FixedCastTime: 1000 + Requires: + SpCost: + - Level: 1 + Amount: 60 + - Level: 2 + Amount: 66 + - Level: 3 + Amount: 72 + - Level: 4 + Amount: 78 + - Level: 5 + Amount: 84 + ItemCost: + - Item: Nw_Grenade + Amount: 15 + Duration1: 4000 + Duration2: 100 + Unit: + Id: Grenades_Dropping + Layout: 0 + Range: + - Level: 1 + Size: 1 + - Level: 2 + Size: 1 + - Level: 3 + Size: 1 + - Level: 4 + Size: 2 + - Level: 5 + Size: 2 + Interval: 250 + Target: Enemy + Flag: + NoOverlap: true + PathCheck: true + - Id: 5413 + Name: NW_AUTO_FIRING_LAUNCHER + Description: Auto Firing Launcher + MaxLevel: 5 + Type: Weapon + TargetType: Self + DamageFlags: + NoDamage: true + Duration1: + - Level: 1 + Time: 120000 + - Level: 2 + Time: 150000 + - Level: 3 + Time: 180000 + - Level: 4 + Time: 210000 + - Level: 5 + Time: 240000 + Cooldown: 30000 + CastCancel: true + FixedCastTime: 3000 + Requires: + SpCost: + - Level: 1 + Amount: 90 + - Level: 2 + Amount: 100 + - Level: 3 + Amount: 110 + - Level: 4 + Amount: 120 + - Level: 5 + Amount: 130 + Status: Auto_Firing_Launcher + - Id: 5414 + Name: NW_HIDDEN_CARD + Description: Hidden Card + MaxLevel: 10 + Type: Weapon + TargetType: Self + DamageFlags: + NoDamage: true + Duration1: 300000 + Cooldown: 60000 + CastCancel: true + FixedCastTime: 2000 + Requires: + SpCost: 150 + ApCost: 150 + Status: Hidden_Card + - Id: 5415 + Name: NW_MISSION_BOMBARD + Description: Mission Bombard + MaxLevel: 10 + Type: Weapon + TargetType: Ground + Range: -9 + Hit: Multi_Hit + HitCount: -3 + Element: Weapon + DamageFlags: + Splash: true + SplashArea: 4 + Cooldown: 10000 + CastCancel: true + FixedCastTime: 1000 + Requires: + SpCost: 100 + ApCost: 35 + ItemCost: + - Item: Nw_Grenade + Amount: 15 + Duration1: 10000 + Unit: + Id: Mission_Bombard + Layout: 0 + Range: 2 + Interval: 250 + Target: Enemy + Flag: + NoOverlap: true + PathCheck: true - Id: 5449 Name: HN_SELFSTUDY_TATICS Description: Self Study Tactics @@ -42159,6 +42626,16 @@ Body: SpCost: 150 ApCost: 150 Status: Rulebreak + - Id: 5496 + Name: NW_THE_VIGILANTE_AT_NIGHT_GUN_GATLING + Description: The Vigilante At Night Gun Gatling + MaxLevel: 5 + CastCancel: true + - Id: 5497 + Name: NW_THE_VIGILANTE_AT_NIGHT_GUN_SHOTGUN + Description: The Vigilante At Night Gun Shotgun + MaxLevel: 5 + CastCancel: true - Id: 6001 Name: DK_DRAGONIC_BREATH Description: Dragonic Breath diff --git a/db/re/skill_tree.yml b/db/re/skill_tree.yml index 9788a49f67..d9213f46bd 100644 --- a/db/re/skill_tree.yml +++ b/db/re/skill_tree.yml @@ -7864,6 +7864,95 @@ Body: Royal_Guard: true Royal_Guard_T: true Imperial_Guard: true + - Job: Night_Watch + Inherit: + Novice: true + Gunslinger: true + Rebellion: true + Tree: + - Name: NW_P_F_I + MaxLevel: 10 + - Name: NW_GRENADE_MASTERY + MaxLevel: 10 + - Name: NW_INTENSIVE_AIM + MaxLevel: 1 + Requires: + - Name: NW_P_F_I + Level: 1 + - Name: NW_HIDDEN_CARD + MaxLevel: 10 + Requires: + - Name: NW_P_F_I + Level: 5 + - Name: NW_INTENSIVE_AIM + Level: 1 + - Name: NW_BASIC_GRENADE + MaxLevel: 5 + Requires: + - Name: NW_GRENADE_MASTERY + Level: 3 + - Name: NW_GRENADE_FRAGMENT + MaxLevel: 7 + Requires: + - Name: NW_GRENADE_MASTERY + Level: 1 + - Name: NW_THE_VIGILANTE_AT_NIGHT + MaxLevel: 5 + Requires: + - Name: NW_P_F_I + Level: 3 + - Name: NW_INTENSIVE_AIM + Level: 1 + - Name: NW_ONLY_ONE_BULLET + MaxLevel: 5 + Requires: + - Name: NW_P_F_I + Level: 3 + - Name: NW_INTENSIVE_AIM + Level: 1 + - Name: NW_SPIRAL_SHOOTING + MaxLevel: 5 + Requires: + - Name: NW_P_F_I + Level: 3 + - Name: NW_INTENSIVE_AIM + Level: 1 + - Name: NW_MAGAZINE_FOR_ONE + MaxLevel: 5 + Requires: + - Name: NW_P_F_I + Level: 3 + - Name: NW_INTENSIVE_AIM + Level: 1 + - Name: NW_WILD_FIRE + MaxLevel: 5 + Requires: + - Name: NW_P_F_I + Level: 3 + - Name: NW_INTENSIVE_AIM + Level: 1 + - Name: NW_HASTY_FIRE_IN_THE_HOLE + MaxLevel: 5 + Requires: + - Name: NW_BASIC_GRENADE + Level: 3 + - Name: NW_GRENADES_DROPPING + MaxLevel: 5 + Requires: + - Name: NW_HASTY_FIRE_IN_THE_HOLE + Level: 3 + - Name: NW_AUTO_FIRING_LAUNCHER + MaxLevel: 5 + Requires: + - Name: NW_GRENADES_DROPPING + Level: 3 + - Name: NW_MISSION_BOMBARD + MaxLevel: 10 + Requires: + - Name: NW_GRENADE_MASTERY + Level: 5 + - Name: NW_GRENADES_DROPPING + Level: 3 - Job: Hyper_Novice Inherit: Novice: true diff --git a/db/re/status.yml b/db/re/status.yml index 2170fd945f..e3eab6f6cf 100644 --- a/db/re/status.yml +++ b/db/re/status.yml @@ -8787,3 +8787,92 @@ Body: - Status: Rulebreak Icon: EFST_RULEBREAK DurationLookup: HN_RULEBREAK + - Status: Intensive_Aim + Icon: EFST_INTENSIVE_AIM + States: + NoMove: true + CalcFlags: + Batk: true + Hit: true + Cri: true + Flags: + BlEffect: true + DisplayPc: true + SendVal1: true + NoSave: true + NoBanishingBuster: true + NoDispell: true + NoClearance: true + - Status: Intensive_Aim_Count + Icon: EFST_INTENSIVE_AIM_COUNT + Flags: + DisplayPc: true + SendVal1: true + NoSave: true + NoBanishingBuster: true + NoDispell: true + NoClearance: true + - Status: Grenade_Fragment_1 + Icon: EFST_GRENADE_FRAGMENT_1 + DurationLookup: NW_GRENADE_FRAGMENT + EndOnStart: + Grenade_Fragment_2: true + Grenade_Fragment_3: true + Grenade_Fragment_4: true + Grenade_Fragment_5: true + Grenade_Fragment_6: true + - Status: Grenade_Fragment_2 + Icon: EFST_GRENADE_FRAGMENT_2 + DurationLookup: NW_GRENADE_FRAGMENT + EndOnStart: + Grenade_Fragment_1: true + Grenade_Fragment_3: true + Grenade_Fragment_4: true + Grenade_Fragment_5: true + Grenade_Fragment_6: true + - Status: Grenade_Fragment_3 + Icon: EFST_GRENADE_FRAGMENT_3 + DurationLookup: NW_GRENADE_FRAGMENT + EndOnStart: + Grenade_Fragment_1: true + Grenade_Fragment_2: true + Grenade_Fragment_4: true + Grenade_Fragment_5: true + Grenade_Fragment_6: true + - Status: Grenade_Fragment_4 + Icon: EFST_GRENADE_FRAGMENT_4 + DurationLookup: NW_GRENADE_FRAGMENT + EndOnStart: + Grenade_Fragment_1: true + Grenade_Fragment_2: true + Grenade_Fragment_3: true + Grenade_Fragment_5: true + Grenade_Fragment_6: true + - Status: Grenade_Fragment_5 + Icon: EFST_GRENADE_FRAGMENT_5 + DurationLookup: NW_GRENADE_FRAGMENT + EndOnStart: + Grenade_Fragment_1: true + Grenade_Fragment_2: true + Grenade_Fragment_3: true + Grenade_Fragment_4: true + Grenade_Fragment_6: true + - Status: Grenade_Fragment_6 + Icon: EFST_GRENADE_FRAGMENT_6 + DurationLookup: NW_GRENADE_FRAGMENT + EndOnStart: + Grenade_Fragment_1: true + Grenade_Fragment_2: true + Grenade_Fragment_3: true + Grenade_Fragment_4: true + Grenade_Fragment_5: true + - Status: Auto_Firing_Launcher + Icon: EFST_AUTO_FIRING_LAUNCHEREFST + DurationLookup: NW_AUTO_FIRING_LAUNCHER + Flags: + SendVal1: true + - Status: Hidden_Card + Icon: EFST_HIDDEN_CARD + DurationLookup: NW_HIDDEN_CARD + CalcFlags: + All: true diff --git a/src/map/battle.cpp b/src/map/battle.cpp index 3e14e60a27..ac181660ac 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -2585,12 +2585,20 @@ void battle_consume_ammo(map_session_data*sd, int skill, int lv) { int qty = 1; + if( sd == nullptr ){ + return; + } + if (!battle_config.arrow_decrement) return; if (skill) { qty = skill_get_ammo_qty(skill, lv); if (!qty) qty = 1; + + if( skill == NW_MAGAZINE_FOR_ONE && sd->weapontype1 == W_GATLING ){ + qty += 4; + } } if (sd->equip_index[EQI_AMMO] >= 0) //Qty check should have been done in skill_check_condition @@ -2621,6 +2629,7 @@ static int battle_range_type(struct block_list *src, struct block_list *target, case BO_ACIDIFIED_ZONE_FIRE_ATK: case BO_ACIDIFIED_ZONE_GROUND_ATK: case BO_ACIDIFIED_ZONE_WIND_ATK: + case NW_THE_VIGILANTE_AT_NIGHT: return BF_LONG; case NJ_KIRIKAGE: // Cast range mimics NJ_SHADOWJUMP but damage is considered melee case GC_CROSSIMPACT: // Cast range is 7 cells and player jumps to target but skill is considered melee @@ -2997,6 +3006,18 @@ static bool is_attack_critical(struct Damage* wd, struct block_list *src, struct case WH_GALESTORM: if (sc && !sc->getSCE(SC_CALAMITYGALE)) return false; + break; + case NW_ONLY_ONE_BULLET: + case NW_SPIRAL_SHOOTING: + if( sd == nullptr || sd->weapontype1 != W_RIFLE ){ + return false; + } + break; + case NW_MAGAZINE_FOR_ONE: + if( sd == nullptr || sd->weapontype1 != W_REVOLVER ){ + return false; + } + break; } if(tsd && tsd->bonus.critical_def) cri = cri * ( 100 - tsd->bonus.critical_def ) / 100; @@ -3282,8 +3303,25 @@ static bool attack_ignores_def(struct Damage* wd, struct block_list *src, struct if (sc && sc->getSCE(SC_FUSION)) return true; - if (skill_id == RK_WINDCUTTER && sd && sd->status.weapon == W_2HSWORD) - return true; + if( sd != nullptr ){ + switch( skill_id ){ + case RK_WINDCUTTER: + if( sd->status.weapon == W_2HSWORD ){ + return true; + } + break; + case NW_THE_VIGILANTE_AT_NIGHT: + if( sd->status.weapon == W_GATLING ){ + return true; + } + break; + case NW_ONLY_ONE_BULLET: + if( sd->status.weapon == W_REVOLVER ){ + return true; + } + break; + } + } if (skill_id != CR_GRANDCROSS && skill_id != NPC_GRANDDARKNESS) { //Ignore Defense? @@ -3427,6 +3465,27 @@ int battle_get_weapon_element(struct Damage* wd, struct block_list *src, struct if (sd && sd->flicker) //Force RL_H_MINE deals fire damage if activated by RL_FLICKER element = ELE_FIRE; break; + case NW_BASIC_GRENADE: + case NW_HASTY_FIRE_IN_THE_HOLE: + case NW_GRENADES_DROPPING: + case NW_MISSION_BOMBARD: + // Night Watch Grenade Fragment elementals affecting those skills. + if( sc != nullptr ){ + if( sc->getSCE( SC_GRENADE_FRAGMENT_1 ) != nullptr ){ + element = ELE_WATER; + }else if( sc->getSCE( SC_GRENADE_FRAGMENT_2 ) != nullptr ){ + element = ELE_WIND; + }else if( sc->getSCE( SC_GRENADE_FRAGMENT_3 ) != nullptr ){ + element = ELE_EARTH; + }else if( sc->getSCE( SC_GRENADE_FRAGMENT_4 ) != nullptr ){ + element = ELE_FIRE; + }else if( sc->getSCE( SC_GRENADE_FRAGMENT_5 ) != nullptr ){ + element = ELE_DARK; + }else if( sc->getSCE( SC_GRENADE_FRAGMENT_6 ) != nullptr ){ + element = ELE_HOLY; + } + } + break; } if (sc && sc->getSCE(SC_GOLDENE_FERSE) && ((!skill_id && (rnd() % 100 < sc->getSCE(SC_GOLDENE_FERSE)->val4)) || skill_id == MH_STAHL_HORN)) @@ -4186,6 +4245,18 @@ static void battle_calc_multi_attack(struct Damage* wd, struct block_list *src,s } break; #endif + case NW_SPIRAL_SHOOTING: + if (sd && sd->weapontype1 == W_GRENADE) + wd->div_ += 1; + break; + case NW_MAGAZINE_FOR_ONE: + if (sd && sd->weapontype1 == W_GATLING) + wd->div_ += 4; + break; + case NW_THE_VIGILANTE_AT_NIGHT: + if (sd && sd->weapontype1 == W_GATLING) + wd->div_ += 3; + break; } } @@ -5864,6 +5935,85 @@ static int battle_calc_attack_skill_ratio(struct Damage* wd, struct block_list * skillratio += 5 * sstatus->pow; RE_LVL_DMOD(100); break; + case NW_HASTY_FIRE_IN_THE_HOLE: + skillratio += -100 + 1500 + 1050 * skill_lv; + skillratio += pc_checkskill( sd, NW_GRENADE_MASTERY ) * 20; + skillratio += 5 * sstatus->con; + RE_LVL_DMOD(100); + break; + case NW_BASIC_GRENADE: + skillratio += -100 + 1000 + 950 * skill_lv; + skillratio += pc_checkskill( sd, NW_GRENADE_MASTERY ) * 50; + skillratio += 5 * sstatus->con; + RE_LVL_DMOD(100); + break; + case NW_GRENADES_DROPPING: + skillratio += -100 + 550 + 850 * skill_lv; + skillratio += pc_checkskill( sd, NW_GRENADE_MASTERY ) * 30; + skillratio += 5 * sstatus->con; + RE_LVL_DMOD(100); + break; + case NW_WILD_FIRE: + skillratio += -100 + 1000 + 2300 * skill_lv; + skillratio += 5 * sstatus->con; + if (sc && sc->getSCE(SC_INTENSIVE_AIM_COUNT)) + skillratio += sc->getSCE(SC_INTENSIVE_AIM_COUNT)->val1 * 500 * skill_lv; + if (sd && sd->weapontype1 == W_SHOTGUN) + skillratio += 150 * skill_lv; + RE_LVL_DMOD(100); + break; + case NW_MAGAZINE_FOR_ONE: + skillratio += -100 + 100 + 450 * ( skill_lv - 1 ); + skillratio += 5 * sstatus->con; + if (sc && sc->getSCE(SC_INTENSIVE_AIM_COUNT)) + skillratio += sc->getSCE(SC_INTENSIVE_AIM_COUNT)->val1 * 100 * skill_lv; + if (sd && sd->weapontype1 == W_REVOLVER) + skillratio += 50 + 100 * (skill_lv-1); + RE_LVL_DMOD(100); + break; + case NW_SPIRAL_SHOOTING: + skillratio += -100 + 1000 + 1500 * skill_lv; + skillratio += 5 * sstatus->con; + if (sc && sc->getSCE(SC_INTENSIVE_AIM_COUNT)) + skillratio += sc->getSCE(SC_INTENSIVE_AIM_COUNT)->val1 * 150 * skill_lv; + if (sd && sd->weapontype1 == W_RIFLE) + skillratio += 200 + 200 * skill_lv; + RE_LVL_DMOD(100); + break; + case NW_ONLY_ONE_BULLET: + skillratio += -100 + 800 + 1350 * skill_lv; + skillratio += 5 * sstatus->con; + if (sc && sc->getSCE(SC_INTENSIVE_AIM_COUNT)) + skillratio += sc->getSCE(SC_INTENSIVE_AIM_COUNT)->val1 * 350 * skill_lv; + if (sd && sd->weapontype1 == W_REVOLVER) { + skillratio += 150 * skill_lv; + } + RE_LVL_DMOD(100); + break; + case NW_THE_VIGILANTE_AT_NIGHT: + if (sd && sd->weapontype1 == W_GATLING) { + skillratio += -100 + 300 * skill_lv; + if (sc && sc->getSCE(SC_INTENSIVE_AIM_COUNT)) + skillratio += sc->getSCE(SC_INTENSIVE_AIM_COUNT)->val1 * 100 * skill_lv; + } else { + skillratio += -100 + 800 + 700 * skill_lv; + if (sc && sc->getSCE(SC_INTENSIVE_AIM_COUNT)) + skillratio += sc->getSCE(SC_INTENSIVE_AIM_COUNT)->val1 * 200 * skill_lv; + } + skillratio += 5 * sstatus->con; + RE_LVL_DMOD(100); + break; + case NW_MISSION_BOMBARD: + if( wd->miscflag&SKILL_ALTDMG_FLAG ){ + skillratio += -100 + 5000 + 1800 * skill_lv; + skillratio += pc_checkskill( sd, NW_GRENADE_MASTERY ) * 100; + }else{ + skillratio += -100 + 800 + 200 * skill_lv; + skillratio += pc_checkskill( sd, NW_GRENADE_MASTERY ) * 30; + } + skillratio += 5 * sstatus->con; + RE_LVL_DMOD(100); + break; } return skillratio; } @@ -6089,6 +6239,8 @@ static void battle_attack_sc_bonus(struct Damage* wd, struct block_list *src, st if (sc->getSCE(SC_MIRACLE)) anger_id = 2; // Always treat all monsters as star flagged monster when in miracle state + if (sc->getSCE(SC_HIDDEN_CARD) && (wd->flag&BF_LONG) == BF_LONG) + RE_ALLATK_ADDRATE(wd, sc->getSCE(SC_HIDDEN_CARD)->val3); } if ((wd->flag&(BF_LONG|BF_MAGIC)) == BF_LONG) { @@ -9773,6 +9925,113 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t sd->state.autocast = 0; } + if( sc->getSCE( SC_AUTO_FIRING_LAUNCHER ) ){ + uint16 skill_id; + uint16 skill_lv; + + switch( sc->getSCE( SC_AUTO_FIRING_LAUNCHER )->val1 ){ + case 1: + skill_id = NW_BASIC_GRENADE; + skill_lv = pc_checkskill( sd, skill_id ); + + if( skill_lv > 0 && rnd_chance( 6, 100 ) ){ + sd->state.autocast = 1; + skill_castend_pos2( src, target->x, target->y, skill_id, skill_lv, tick, flag ); + battle_autocast_aftercast( src, skill_id, skill_lv, tick ); + sd->state.autocast = 0; + } + break; + + case 2: + skill_id = NW_BASIC_GRENADE; + skill_lv = pc_checkskill( sd, skill_id ); + + if( skill_lv > 0 && rnd_chance( 7, 100 ) ){ + sd->state.autocast = 1; + skill_castend_pos2( src, target->x, target->y, skill_id, skill_lv, tick, flag ); + battle_autocast_aftercast( src, skill_id, skill_lv, tick ); + sd->state.autocast = 0; + } + break; + + case 3: + skill_id = NW_HASTY_FIRE_IN_THE_HOLE; + skill_lv = pc_checkskill( sd, skill_id ); + + if( skill_lv > 0 && rnd_chance( 3, 100 ) ){ + sd->state.autocast = 1; + skill_castend_pos2( src, target->x, target->y, skill_id, skill_lv, tick, flag ); + battle_autocast_aftercast( src, skill_id, skill_lv, tick ); + sd->state.autocast = 0; + } + + skill_id = NW_BASIC_GRENADE; + skill_lv = pc_checkskill( sd, skill_id ); + + if( skill_lv > 0 && rnd_chance( 8, 100 ) ){ + sd->state.autocast = 1; + skill_castend_pos2( src, target->x, target->y, skill_id, skill_lv, tick, flag ); + battle_autocast_aftercast( src, skill_id, skill_lv, tick ); + sd->state.autocast = 0; + } + break; + + case 4: + skill_id = NW_HASTY_FIRE_IN_THE_HOLE; + skill_lv = pc_checkskill( sd, skill_id ); + + if( skill_lv > 0 && rnd_chance( 5, 100 ) ){ + sd->state.autocast = 1; + skill_castend_pos2( src, target->x, target->y, skill_id, skill_lv, tick, flag ); + battle_autocast_aftercast( src, skill_id, skill_lv, tick ); + sd->state.autocast = 0; + } + + skill_id = NW_BASIC_GRENADE; + skill_lv = pc_checkskill( sd, skill_id ); + + if( skill_lv > 0 && rnd_chance( 9, 100 ) ){ + sd->state.autocast = 1; + skill_castend_pos2( src, target->x, target->y, skill_id, skill_lv, tick, flag ); + battle_autocast_aftercast( src, skill_id, skill_lv, tick ); + sd->state.autocast = 0; + } + break; + + case 5: + skill_id = NW_GRENADES_DROPPING; + skill_lv = pc_checkskill( sd, skill_id ); + + if( skill_lv > 0 && rnd_chance( 3, 100 ) ){ + sd->state.autocast = 1; + skill_castend_pos2( src, target->x, target->y, skill_id, skill_lv, tick, flag ); + battle_autocast_aftercast( src, skill_id, skill_lv, tick ); + sd->state.autocast = 0; + } + + skill_id = NW_HASTY_FIRE_IN_THE_HOLE; + skill_lv = pc_checkskill( sd, skill_id ); + + if( skill_lv > 0 && rnd_chance( 7, 100 ) ){ + sd->state.autocast = 1; + skill_castend_pos2( src, target->x, target->y, skill_id, skill_lv, tick, flag ); + battle_autocast_aftercast( src, skill_id, skill_lv, tick ); + sd->state.autocast = 0; + } + + skill_id = NW_BASIC_GRENADE; + skill_lv = pc_checkskill( sd, skill_id ); + + if( skill_lv > 0 && rnd_chance( 10, 100 ) ){ + sd->state.autocast = 1; + skill_castend_pos2( src, target->x, target->y, skill_id, skill_lv, tick, flag ); + battle_autocast_aftercast( src, skill_id, skill_lv, tick ); + sd->state.autocast = 0; + } + break; + } + } + // Autocasted skills from super elemental supportive buffs. if (sc->getSCE(SC_FLAMETECHNIC_OPTION) && rnd() % 100 < 7) battle_autocast_elembuff_skill(sd, target, MG_FIREBOLT, tick, flag); diff --git a/src/map/script_constants.hpp b/src/map/script_constants.hpp index d1f1451fce..c739b58c7a 100644 --- a/src/map/script_constants.hpp +++ b/src/map/script_constants.hpp @@ -1900,6 +1900,16 @@ export_constant(SC_GROUNDGRAVITY); export_constant(SC_BREAKINGLIMIT); export_constant(SC_RULEBREAK); + export_constant(SC_INTENSIVE_AIM); + export_constant(SC_INTENSIVE_AIM_COUNT); + export_constant(SC_GRENADE_FRAGMENT_1); + export_constant(SC_GRENADE_FRAGMENT_2); + export_constant(SC_GRENADE_FRAGMENT_3); + export_constant(SC_GRENADE_FRAGMENT_4); + export_constant(SC_GRENADE_FRAGMENT_5); + export_constant(SC_GRENADE_FRAGMENT_6); + export_constant(SC_AUTO_FIRING_LAUNCHER); + export_constant(SC_HIDDEN_CARD); #ifdef RENEWAL export_constant(SC_EXTREMITYFIST2); diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 303ee1d2bb..293616f267 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -4794,6 +4794,29 @@ static TIMER_FUNC(skill_timerskill){ skill_unitsetting(src, skl->skill_id, skl->skill_lv, tmpx, tmpy, skill_get_unit_interval(skl->skill_id)); } break; + case NW_HASTY_FIRE_IN_THE_HOLE: + skill_castend_pos2(src, skl->x, skl->y, skl->skill_id, skl->skill_lv, tick, skl->flag); + break; + case NW_GRENADES_DROPPING: { + int area = skill_get_splash(skl->skill_id, skl->skill_lv); + short tmpx = 0, tmpy = 0; + + tmpx = skl->x - area + rnd() % (area * 2 + 1); + tmpy = skl->y - area + rnd() % (area * 2 + 1); + skill_unitsetting(src, skl->skill_id, skl->skill_lv, tmpx, tmpy, skl->flag); + } + break; + case NW_MISSION_BOMBARD: { + int area = skill_get_unit_range(skl->skill_id, skl->skill_lv); + int range = skill_get_splash(skl->skill_id, skl->skill_lv); + short tmpx = 0, tmpy = 0; + + tmpx = skl->x - range + rnd() % (range * 2 + 1); + tmpy = skl->y - range + rnd() % (range * 2 + 1); + map_foreachinarea(skill_area_sub, src->m, tmpx - range, tmpy - range, tmpx + range, tmpy + range, BL_CHAR, + src, skl->skill_id, skl->skill_lv, tick, skl->flag | BCT_ENEMY | SD_SPLASH | 1, skill_castend_damage_id); + } + break; } } } while (0); @@ -5235,6 +5258,10 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint case ABR_DUAL_CANNON_FIRE: case ABR_INFINITY_BUSTER: case MT_TRIPLE_LASER: + case NW_MISSION_BOMBARD: + case NW_HASTY_FIRE_IN_THE_HOLE: + case NW_BASIC_GRENADE: + case NW_WILD_FIRE: skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); break; case DK_DRAGONIC_AURA: @@ -5916,6 +5943,24 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint } } break; + case NW_THE_VIGILANTE_AT_NIGHT: + if (flag & 1) + skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag); + break; + case NW_SPIRAL_SHOOTING: + if (flag & 1) { + skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag); + } else { + int splash = skill_get_splash(skill_id, skill_lv); + + if (sd && sd->weapontype1 == W_GRENADE) + splash += 2; + clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); + map_foreachinrange(skill_area_sub, bl, splash, BL_CHAR, src, skill_id, skill_lv, tick, flag | BCT_ENEMY | SD_SPLASH | 1, skill_castend_damage_id); + if (sc && sc->getSCE(SC_INTENSIVE_AIM_COUNT)) + status_change_end(src, SC_INTENSIVE_AIM_COUNT); + } + break; //Place units around target case NJ_BAKUENRYU: @@ -7077,6 +7122,13 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint } break; + case NW_MAGAZINE_FOR_ONE: + case NW_ONLY_ONE_BULLET: + skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); + if (sc && sc->getSCE(SC_INTENSIVE_AIM_COUNT)) + status_change_end(src, SC_INTENSIVE_AIM_COUNT); + break; + default: ShowWarning("skill_castend_damage_id: Unknown skill used:%d\n",skill_id); clif_skill_damage(src, bl, tick, status_get_amotion(src), tstatus->dmotion, @@ -7101,7 +7153,8 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint } // perform skill requirement consumption - skill_consume_requirement(sd,skill_id,skill_lv,2); + if (!(flag&SKILL_NOCONSUME_REQ)) + skill_consume_requirement(sd,skill_id,skill_lv,2); } return 0; @@ -7297,6 +7350,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui type = skill_get_sc(skill_id); tsc = status_get_sc(bl); + status_change* sc = status_get_sc(src); tsce = (tsc && type != SC_NONE)?tsc->getSCE(type):NULL; if (src!=bl && type > SC_NONE && @@ -7651,8 +7705,6 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui return 1; } - status_change* sc = status_get_sc(src); - if( sc && tsc ) { if( !sc->getSCE(SC_MARIONETTE) && !tsc->getSCE(SC_MARIONETTE2) ) @@ -8025,7 +8077,6 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui sc_start(src, bl, type, 100, skill_lv, skill_get_time2(skill_id, skill_lv)); } else { uint16 climax_lv = 0, splash_size = skill_get_splash(skill_id, skill_lv); - status_change *sc = status_get_sc(src); if (sc && sc->getSCE(SC_CLIMAX)) climax_lv = sc->getSCE(SC_CLIMAX)->val1; @@ -8566,7 +8617,6 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui case ABC_ABYSS_DAGGER: case BO_EXPLOSIVE_POWDER: { - status_change *sc = status_get_sc(src); int starget = BL_CHAR|BL_SKILL; if (skill_id == SR_HOWLINGOFLION) @@ -11176,8 +11226,6 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui case WL_SUMMONWB: case WL_SUMMONSTONE: { - status_change *sc = status_get_sc(src); - if (sc == nullptr) break; @@ -11687,7 +11735,6 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui case WM_LERADS_DEW: case WM_UNLIMITED_HUMMING_VOICE: if( flag&1 ) { // These affect to to all party members near the caster. - status_change *sc = status_get_sc(src); if( sc && sc->getSCE(type) ) { sc_start2(src,bl,type,100,skill_lv,pc_checkskill(sd, WM_LESSON),skill_get_time(skill_id,skill_lv)); } @@ -12094,9 +12141,9 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui s_elemental_data *ele = BL_CAST(BL_ELEM, src); if( ele ) { sc_type type2 = (sc_type)(type-1); - status_change *sc = status_get_sc(&ele->bl); + status_change *esc = status_get_sc(&ele->bl); - if( (sc && sc->getSCE(type2)) || (tsc && tsc->getSCE(type)) ) { + if( (esc && esc->getSCE(type2)) || (tsc && tsc->getSCE(type)) ) { status_change_end(src,type); status_change_end(bl,type2); } else { @@ -12121,11 +12168,11 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui case EL_WATER_SCREEN: { s_elemental_data *ele = BL_CAST(BL_ELEM, src); if( ele ) { - status_change *sc = status_get_sc(&ele->bl); + status_change *esc = status_get_sc(&ele->bl); sc_type type2 = (sc_type)(type-1); clif_skill_nodamage(src,src,skill_id,skill_lv,1); - if( (sc && sc->getSCE(type2)) || (tsc && tsc->getSCE(type)) ) { + if( (esc && esc->getSCE(type2)) || (tsc && tsc->getSCE(type)) ) { status_change_end(bl,type); status_change_end(src,type2); } else { @@ -12865,6 +12912,54 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR, src, skill_id, skill_lv, tick, flag | BCT_ENEMY | 1, skill_castend_damage_id); break; + case NW_THE_VIGILANTE_AT_NIGHT: + i = skill_get_splash(skill_id, skill_lv); + skill_area_temp[0] = 0; + skill_area_temp[1] = bl->id; + skill_area_temp[2] = 0; + + if (sd && sd->weapontype1 == W_GATLING) { + i += 3; + clif_skill_nodamage(src, bl, NW_THE_VIGILANTE_AT_NIGHT_GUN_GATLING, skill_lv, 1); + } else + clif_skill_nodamage(src, bl, NW_THE_VIGILANTE_AT_NIGHT_GUN_SHOTGUN, skill_lv, 1); + map_foreachinrange(skill_area_sub, bl, i, BL_CHAR, src, skill_id, skill_lv, tick, flag | BCT_ENEMY | SD_SPLASH | 1, skill_castend_damage_id); + if (sc && sc->getSCE(SC_INTENSIVE_AIM_COUNT)) + status_change_end(src, SC_INTENSIVE_AIM_COUNT); + break; + + case NW_INTENSIVE_AIM: + if (tsc && tsc->getSCE(type)) { + status_change_end(src, SC_INTENSIVE_AIM_COUNT); + status_change_end(bl, type); + } else { + status_change_end(src, SC_INTENSIVE_AIM_COUNT); + sc_start(src, bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)); + } + clif_skill_nodamage(src, src, skill_id, skill_lv, 1); + break; + + case NW_HIDDEN_CARD: + case NW_AUTO_FIRING_LAUNCHER: + sc_start(src, bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)); + clif_skill_nodamage(src, src, skill_id, skill_lv, 1); + break; + + case NW_GRENADE_FRAGMENT: + status_change_end(src, type); + if (skill_lv < 7) + sc_start(src, bl, (sc_type)(SC_GRENADE_FRAGMENT_1 -1 + skill_lv), 100, skill_lv, skill_get_time(skill_id, skill_lv)); + else if (skill_lv == 7) { + status_change_end(src, SC_GRENADE_FRAGMENT_1); + status_change_end(src, SC_GRENADE_FRAGMENT_2); + status_change_end(src, SC_GRENADE_FRAGMENT_3); + status_change_end(src, SC_GRENADE_FRAGMENT_4); + status_change_end(src, SC_GRENADE_FRAGMENT_5); + status_change_end(src, SC_GRENADE_FRAGMENT_6); + } + clif_skill_nodamage(src, src, skill_id, skill_lv, 1); + break; + default: { std::shared_ptr skill = skill_db.find(skill_id); ShowWarning("skill_castend_nodamage_id: missing code case for skill %s(%d)\n", skill ? skill->name : "UNKNOWN", skill_id); @@ -12875,8 +12970,6 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui } if (skill_id != SR_CURSEDCIRCLE && skill_id != NPC_SR_CURSEDCIRCLE) { - status_change *sc = status_get_sc(src); - if (sc && sc->getSCE(SC_CURSEDCIRCLE_ATKER)) // Should only remove after the skill had been casted. status_change_end(src,SC_CURSEDCIRCLE_ATKER); } @@ -12896,7 +12989,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui } skill_onskillusage(sd, bl, skill_id, tick); // perform skill requirement consumption - skill_consume_requirement(sd,skill_id,skill_lv,2); + if (!(flag&SKILL_NOCONSUME_REQ)) + skill_consume_requirement(sd,skill_id,skill_lv,2); } map_freeblock_unlock(); @@ -13603,6 +13697,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui case SU_CN_METEOR: case NPC_RAINOFMETEOR: case HN_METEOR_STORM_BUSTER: + case NW_GRENADES_DROPPING: break; //Effect is displayed on respective switch case. default: if(skill_get_inf(skill_id)&INF_SELF_SKILL) @@ -13643,6 +13738,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui break; case SR_RIDEINLIGHTNING: + case NW_BASIC_GRENADE: i = skill_get_splash(skill_id, skill_lv); map_foreachinallarea(skill_area_sub, src->m, x-i, y-i, x+i, y+i, BL_CHAR, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_damage_id); @@ -14545,6 +14641,53 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui } break; + case NW_WILD_FIRE: + i = skill_get_splash(skill_id, skill_lv); + if (sd && sd->status.weapon == W_GRENADE) + i += 2; + map_foreachinallarea(skill_area_sub, + src->m, x - i, y - i, x + i, y + i, BL_CHAR, + src, skill_id, skill_lv, tick, flag | BCT_ENEMY | 1, + skill_castend_damage_id); + if (sc && sc->getSCE(SC_INTENSIVE_AIM_COUNT)) + status_change_end(src, SC_INTENSIVE_AIM_COUNT); + break; + case NW_HASTY_FIRE_IN_THE_HOLE: + i = skill_get_splash(skill_id, skill_lv); + if (flag & 1){ + i++; + } + if (flag & 2){ + i++; + } + map_foreachinallarea(skill_area_sub, + src->m, x - i, y - i, x + i, y + i, BL_CHAR, + src, skill_id, skill_lv, tick, flag | BCT_ENEMY | 1, + skill_castend_damage_id); + if (!(flag & 1)) { + skill_addtimerskill(src, tick + 300, 0, x, y, skill_id, skill_lv, 0, flag | 1 | SKILL_NOCONSUME_REQ); + skill_addtimerskill(src, tick + 600, 0, x, y, skill_id, skill_lv, 0, flag | 3 | SKILL_NOCONSUME_REQ); + } + break; + case NW_GRENADES_DROPPING: { + uint16 splash = skill_get_splash(skill_id, skill_lv); + uint16 tmpx = rnd_value( x - splash, x + splash ); + uint16 tmpy = rnd_value( y - splash, y + splash ); + skill_unitsetting(src, skill_id, skill_lv, tmpx, tmpy, flag); + for (i = 0; i <= (skill_get_time(skill_id, skill_lv) / skill_get_unit_interval(skill_id)); i++) { + skill_addtimerskill(src, tick + (t_tick)i*skill_get_unit_interval(skill_id), 0, x, y, skill_id, skill_lv, 0, flag); + } + } break; + case NW_MISSION_BOMBARD: + i = skill_get_splash(skill_id,skill_lv); + map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,BL_CHAR|BL_SKILL,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|SKILL_ALTDMG_FLAG|1,skill_castend_damage_id); + skill_unitsetting(src, skill_id, skill_lv, x, y, flag); + + for (i = 1; i <= (skill_get_time(skill_id, skill_lv) / skill_get_unit_interval(skill_id)); i++) { + skill_addtimerskill(src, tick + (t_tick)i*skill_get_unit_interval(skill_id), 0, x, y, skill_id, skill_lv, 0, flag); + } + break; + default: ShowWarning("skill_castend_pos2: Unknown skill used:%d\n",skill_id); return 1; @@ -14563,7 +14706,8 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui } skill_onskillusage(sd, NULL, skill_id, tick); // perform skill requirement consumption - skill_consume_requirement(sd,skill_id,skill_lv,2); + if (!(flag&SKILL_NOCONSUME_REQ)) + skill_consume_requirement(sd,skill_id,skill_lv,2); } return 0; @@ -15211,6 +15355,10 @@ std::shared_ptr skill_unitsetting(struct block_list *src, ui case WH_FLAMETRAP: limit += 3000 * (sd ? pc_checkskill(sd, WH_ADVANCED_TRAP) : 5); break; + + case NW_GRENADES_DROPPING: + limit = skill_get_time2(skill_id,skill_lv); + break; } // Init skill unit group @@ -18711,6 +18859,8 @@ struct s_skill_condition skill_get_requirement(map_session_data* sd, uint16 skil req.mhp = skill->require.mhp[skill_lv-1]; req.weapon = skill->require.weapon; req.ammo_qty = skill->require.ammo_qty[skill_lv-1]; + if (skill_id == NW_MAGAZINE_FOR_ONE && sd->weapontype1 == W_GATLING) + req.ammo_qty += 4; if (req.ammo_qty) req.ammo = skill->require.ammo; diff --git a/src/map/skill.hpp b/src/map/skill.hpp index c1ac243df3..16c6132392 100644 --- a/src/map/skill.hpp +++ b/src/map/skill.hpp @@ -41,6 +41,8 @@ class status_change; /// To control alternative skill scalings #define SKILL_ALTDMG_FLAG 0x10 +/// Make skill ignore requirement consumption [Muh] +#define SKILL_NOCONSUME_REQ 0x20 /// Constants to identify a skill's nk value (damage properties) /// The NK value applies only to non INF_GROUND_SKILL skills diff --git a/src/map/status.cpp b/src/map/status.cpp index f1490db7c1..06621064ee 100644 --- a/src/map/status.cpp +++ b/src/map/status.cpp @@ -4169,6 +4169,10 @@ int status_calc_pc_sub(map_session_data* sd, uint8 opt) base_status->mdef++; } +// ----- CONCENTRATION CALCULATION ----- + if ((skill = pc_checkskill(sd, NW_GRENADE_MASTERY)) > 0) + base_status->con += skill; + // ------ ATTACK CALCULATION ------ // Base batk value is set in status_calc_misc @@ -4383,6 +4387,8 @@ int status_calc_pc_sub(map_session_data* sd, uint8 opt) base_status->patk += skill; if ((skill = pc_checkskill(sd, HN_SELFSTUDY_SOCERY)) > 0) base_status->smatk += skill; + if ((skill = pc_checkskill(sd, NW_P_F_I)) > 0 && (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE)) + base_status->patk += skill + 2; // 2-Handed Staff Mastery if( sd->status.weapon == W_2HSTAFF && ( skill = pc_checkskill( sd, AG_TWOHANDSTAFF ) ) > 0 ){ @@ -4759,6 +4765,8 @@ int status_calc_pc_sub(map_session_data* sd, uint8 opt) sd->bonus.short_attack_atk_rate += 5 * sc->getSCE( SC_RUSH_QUAKE2 )->val1; sd->bonus.long_attack_atk_rate += 5 * sc->getSCE( SC_RUSH_QUAKE2 )->val1; } + if (sc->getSCE(SC_HIDDEN_CARD)) + sd->bonus.long_attack_atk_rate += sc->getSCE(SC_HIDDEN_CARD)->val3; if (sc->getSCE(SC_DEADLY_DEFEASANCE)) sd->special_state.no_magic_damage = 0; if (sc->getSCE(SC_CLIMAX_DES_HU)) @@ -7070,6 +7078,8 @@ static unsigned short status_calc_batk(struct block_list *bl, status_change *sc, batk += 20; if(sc->getSCE(SC_SKF_ATK)) batk += sc->getSCE(SC_SKF_ATK)->val1; + if (sc->getSCE(SC_INTENSIVE_AIM)) + batk += 150; return (unsigned short)cap_value(batk,0,USHRT_MAX); } @@ -7386,6 +7396,8 @@ static signed short status_calc_critical(struct block_list *bl, status_change *s critical += sc->getSCE(SC_MTF_HITFLEE)->val1; if (sc->getSCE(SC_PACKING_ENVELOPE9)) critical += sc->getSCE(SC_PACKING_ENVELOPE9)->val1 * 10; + if (sc->getSCE(SC_INTENSIVE_AIM)) + critical += 300; return (short)cap_value(critical,10,SHRT_MAX); } @@ -7462,6 +7474,8 @@ static signed short status_calc_hit(struct block_list *bl, status_change *sc, in hit += sc->getSCE(SC_LIMIT_POWER_BOOSTER)->val1; if (sc->getSCE(SC_ACARAJE)) hit += 5; + if (sc->getSCE(SC_INTENSIVE_AIM)) + hit += 250; return (short)cap_value(hit,1,SHRT_MAX); } @@ -8518,6 +8532,8 @@ static signed short status_calc_patk(struct block_list *bl, status_change *sc, i if( sc->getSCE( SC_ATTACK_STANCE ) ){ patk += sc->getSCE( SC_ATTACK_STANCE )->val3; } + if (sc->getSCE(SC_HIDDEN_CARD)) + patk += sc->getSCE(SC_HIDDEN_CARD)->val2; return (short)cap_value(patk, 0, SHRT_MAX); } @@ -12741,6 +12757,13 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty case SC_WEAPONBREAKER: val2 = val1 * 2 * 100; // Chance to break weapon break; + case SC_INTENSIVE_AIM: + tick = 500; + break; + case SC_HIDDEN_CARD: + val2 = 3 * val1; + val3 = 10 * val1; + break; default: if (calc_flag.none() && scdb->skill_id == 0 && scdb->icon == EFST_BLANK && scdb->opt1 == OPT1_NONE && scdb->opt2 == OPT2_NONE && scdb->state.none() && scdb->flag.none() && scdb->endonstart.empty() && scdb->endreturn.empty() && scdb->fail.empty() && scdb->endonend.empty()) { @@ -14891,6 +14914,15 @@ TIMER_FUNC(status_change_timer){ if (sce->val4 >= 0) skill_castend_damage_id( bl, bl, NPC_KILLING_AURA, sce->val1, tick, 0 ); break; + case SC_INTENSIVE_AIM: + if (!sc || !sc->getSCE(SC_INTENSIVE_AIM_COUNT)) + sce->val4 = 0; + if (sce->val4 < 10) { + sce->val4++; + sc_start(bl, bl, SC_INTENSIVE_AIM_COUNT, 100, sce->val4, INFINITE_TICK); + } + sc_timer_next(500 + tick); + return 0; } // If status has an interval and there is at least 100ms remaining time, wait for next interval diff --git a/src/map/status.hpp b/src/map/status.hpp index fb1a3a95aa..16b7cc6381 100644 --- a/src/map/status.hpp +++ b/src/map/status.hpp @@ -1293,6 +1293,18 @@ enum sc_type : int16 { SC_BREAKINGLIMIT, SC_RULEBREAK, + // Night Watch + SC_INTENSIVE_AIM, + SC_INTENSIVE_AIM_COUNT, + SC_GRENADE_FRAGMENT_1, + SC_GRENADE_FRAGMENT_2, + SC_GRENADE_FRAGMENT_3, + SC_GRENADE_FRAGMENT_4, + SC_GRENADE_FRAGMENT_5, + SC_GRENADE_FRAGMENT_6, + SC_AUTO_FIRING_LAUNCHER, + SC_HIDDEN_CARD, + #ifdef RENEWAL SC_EXTREMITYFIST2, //! NOTE: This SC should be right before SC_MAX, so it doesn't disturb if RENEWAL is disabled #endif