// TMW2 scripts. // Authors: // Jesusalva // TMW Org. // Description: // HUB functions (Login, Logout, Death) // HUB_Login () function script HUB_Login { getmapxy(.@mapa$, .@a,.@b, 0); // Login on Blue Sage Workshop/Library if (.@mapa$ == "020-7-1") { addtimer(1000, "#BlueSageHUB::OnCycle"); } // Random Treasure cleaning if (CHAREG_CLEANUP < gettimetick(2)-CHEST_WAITTIME) deletearray RNGTREASURE_DATE; // PVP Cooldown cleaning if (CHAREG_CLEANUP < gettimetick(2)-PVP_WAITTIME) deletearray PVP_COOLDOWN; // Mirror Lake functionality if (getvaultid()) { .@gto=get_byte(##00_INFO, 3); .@mlp=get_nibble(##00_INFO, 5); // Work only on new chars, or chars which cleared Tulimshar. if (.@gto == $WID && (!getq(General_Narrator) || (getq(General_Narrator) && BaseLevel > 20))) { // Warp to the proper Mirror Lake switch (.@mlp) { case 1: warp "018-7-1", 90, 47; LOCATION$ = "LoF"; break; default: warp "014-4", 28, 31; LOCATION$ = "LoF"; break; } // Send debug information debugmes("Vault User %d moved to lake %d.", getvaultid(), .@mlp); // Handle new user (non-native) accounts if (!getq(General_Narrator)) { getitembound MirrorLakeArmor, 1, 4; equip(MirrorLakeArmor); TUTORIAL=true; //BaseLevel = get_byte(##00_INFO, 0) + 1; // TODO: Display quick tutorial dispbottom l("Mirror Lake : Obtain help with %s.", b("@info")); } // Unset the target lake/world set_byte(##00_INFO, 0, 3); set_nibble(##00_INFO, 0, 5); } } return; } // HUB_Logout ( {dead} ) function script HUB_Logout { .@dead=getarg(0, false); getmapxy(.@mapa$, .@a,.@b, 0); .@zone$=getmapinfo(MAPINFO_ZONE, .@mapa$); // Hardcore Server if ($HARDCORE && .@dead) { // Update Absolutions if (ABSOLVE_DAY != gettimeparam(GETTIME_DAYOFMONTH)) { ABSOLVE_DAY=gettimeparam(GETTIME_DAYOFMONTH); ABSOLVE_CNT=0; } // Main Loop if (@grace) { // Grace is upon you (ie. script death) @grace=false; } else if (.@zone$ == "MMO" && ABSOLVE_CNT <= 3) { // Absolve (limited attempts) ABSOLVE_CNT+=1; dispbottom l("This is a special map so your death is not counted."); dispbottom b(l("You have %d non-counting deaths remaining today.", 3-ABSOLVE_CNT)); if (ABSOLVE_CNT == 3) dispbottom col(l("WARNING: if you die again today in a special map it will be PERMANENT."), 1); } else { // Meet your final demise! atcommand("@dropall"); makeitem CoinBag, Zeny/500, .@mapa$, .@a, .@b; Zeny=0; //resetlvl(2); // FIXME: Split the exp // TODO: Warp back to Candor or it'll be unplayable // TODO: It could be @jail, but it is buggy atcommand("@jailfor 1d "+strcharinfo(0)); } // Vanished on Cindy Cave } else if (.@mapa$ == "021-4" && strcharinfo(0) == $@CINDY_HERO$) { donpcevent("Cindy#Outside::OnReckless"); recovery(getcharid(3)); warp any("010-1", "010-2"), 0, 0; sc_start2 SC_POISON, 1, 90, 10000; die(); if (!$HARDCORE) heal -1, -1; } else if (.@mapa$ == "021-4") { .@pl = getmapusers("021-4")-1; if (.@pl < 1) donpcevent("Cindy#Outside::OnCleanUp"); recovery(getcharid(3)); warp "Save", 0, 0; } // Logout while donating blood if (getq(HurnscaldQuest_BloodDonor) == 2) { slide 35, 28; setpcblock(PCBLOCK_SOFT, false); setq HurnscaldQuest_BloodDonor, 0, gettimetick(2)+3600; // one hour penalty } // Logout/Death on Nard's ship hold if (compare(.@mapa$, "002-2") || compare(.@mapa$,"nard")) { setq2 ShipQuests_Peter, 0; setq3 ShipQuests_Peter, -1; } // Logout on botcheck area if (compare(.@mapa$,"botcheck") && !.@dead) { if (!is_staff()) { atcommand "@jail "+strcharinfo(0); rodex_sendmail(getcharid(0), "TMW2 Team", "Error detected!", "Please contact GM Team, error code: BOTCHECK-AUTOJAILED"); } } // Died on Terranite Cave where exp penalty is lower if (.@mapa$ == "015-6" && .@dead) { @deathpenalty_override=2; @deathpenalty_realvalue=readparam(BaseExp); @deathpenalty_realvaljob=readparam(JobExp); } // Died or logged out at Tulimshar Arena (FIXME) if (compare(.@mapa$, "ARENA")) { if ('UDTf) { 'UDTf=0; killmonster(getmap(), instance_npcname("Arnea#003-13")+"::OnGladius"); deltimer(instance_npcname("Arnea#003-13")+"::OnVerify"); } } // Died or logged out at Candor Battle if (.@mapa$ == "006-1") { if (@crazypoints > CRAZYPOINTS) { CRAZYPOINTS=@crazypoints; dispbottom l("Crazyfefe Cave: New Highscore: @@ points", CRAZYPOINTS); @crazypoints=0; } } // Died or logged during Bandit Lord fight if (.@mapa$ == "015-2") { if (isin("015-2", 228, 227, 282, 280)) killmonster("015-2", "#BanditLordDen::OnLordDeath"); } // Died or logged out on Blue Sage House if (.@mapa$ == "020-7-1") { callfunc("BSClearNest", @nestid); } // Died or logged out on Player Story 2 - Magic School Port if (compare(.@mapa$, "0030")) { if (@ASSASSIN > 0) delcells "MQ2Wall"+getcharid(0); killmonsterall(getmap()); } // Died or logged out on Player Story 5 - Forgotten Throne Room if (compare(.@mapa$, "hmc")) { if (@instid > 0) .@n$=instance_npcname("#Core02331", @instid); else .@n$=instance_npcname("#Core02331"); deltimer(.@n$+"::OnW01"); deltimer(.@n$+"::OnW02"); deltimer(.@n$+"::OnE07"); deltimer(.@n$+"::OnE08"); deltimer(.@n$+"::OnE09"); deltimer(.@n$+"::OnE10"); deltimer(.@n$+"::OnE11"); deltimer(.@n$+"::OnE12"); killmonsterall(getmap()); } // Died or logged out on Player Story 6 - Forgotten Shrine if (compare(.@mapa$, "brb3")) { //.@n$=instance_npcname("#Core02331"); .@q=getq(LoFQuest_Barbara); // Reset quest to: Forgotten Chamber Puzzle incomplete if (.@q < 4 && .@q >= 2) { setq1 LoFQuest_Barbara, 1; setq3 LoFQuest_Barbara, 1; // FIXME: Enable Mana Stone#01863 dispbottom col(l("WARNING: You died at Forgotten Shrine and the Shrine defense triggered."), 1); dispbottom col(l("WARNING: Your progress on the quest was lost!"), 1); } } // Died or logged out during Sagratha Fight if (compare(.@mapa$, "sgt2")) { setq1 HurnscaldQuest_Sagratha, 3; setq3 HurnscaldQuest_Sagratha, 0; } // First death produces a warning message if (PC_DIE_COUNTER <= 1 && .@dead) { dispbottom l("Dying outside a town square will cause EXP loss."); } // If you were travelling and died/logged out, cleaning is needed if (@timer_navio_running) { @timer_navio_running=0; // Logged out? Correct your position to inside the ship // Of course, this is messy... But still better than Save Point if (!.@dead) { if (compare(.@mapa$, "016-")) warp "016-1", 28, 27; else if (compare(.@mapa$, "002-")) warp "002-1", 55, 40; else warp "Save", 0, 0; } } // Crazyfefe hot fix if (.@dead) { // It was PK if (killerrid > 2000000 && killerrid < 2100000) { // PVP flag was off /* if (!getmapflag(.@mapa$, mf_pvp) && !getmapflag(.@mapa$, mf_pvp_noparty) && !getmapflag(.@mapa$, mf_pvpnoguild)) { recovery(getcharid(3)); warp .@mapa$, .@a, .@b; percentheal 100, 100; dispbottom l("REVENGE TIME!"); .@trueid=getcharid(3); //detachrid(); attachrid(killerrid); setpcblock(PCBLOCK_SOFT, true); sc_start SC_WALKSPEED,120000,50; sc_end SC_CASH_PLUSEXP; sc_end SC_OVERLAPEXPUP; sc_start SC_OVERLAPEXPUP, 300000, -20; dispbottom l("For cowardingly killing in a \"secure\" area, you will be severely punished."); //Karma+=1; sc_start SC_STUN, 15000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK; addtimer(15000, "#mobptsys::OnUnlock"); percentheal -88, -100; detachrid(); attachrid(.@trueid); } */ HONOR+=1; } } // This allows code to override death penalty, just once: // @deathpenalty_override // Valid values: 1- No penalty. 2- Halved penalty. // You must also set: @deathpenalty_realvalue and @deathpenalty_realvaljob if (@deathpenalty_override && .@dead) { if (is_staff()) debugmes("Old values: %d %d Current Values: %d %d", @deathpenalty_realvalue, @deathpenalty_realvaljob, readparam(BaseExp), readparam(JobExp)); addtimer(300, "#QuirinoHUB::OnNoPenaltyCommand"); } // Register logout time if (!.@dead) { CHAREG_CLEANUP=gettimetick(2); // Send updates to Vault API if (getvaultid()) { .@api$=json_encode("UID", ##VAULT, "GID", getcharid(3), "VAR1N", "MLQUEST", "VAR1V", ##01_MLQUEST, "VAR2N", "MLWORLD", "VAR2V", ##01_MLWORLD, "VEXP", ##VAULT_EXP, "GOTO", 0, "MLTO", 0); ##VAULT_EXP=0; api_send(API_FLUSHVAULT, .@api$); // Destroy temporary characters if (countitem(MirrorLakeArmor)) { delitem MirrorLakeArmor, countitem(MirrorLakeArmor); clearitem(); resetlvl(2); resetstatus(); resetskill(); warp "000-0", 22, 24; debugmes("Vault User %d reset!", getvaultid()); } } } callfunc "02524_Avenge_BlackBox", .@dead; return; } // HUB_SkillInvoke ( ) function script HUB_SkillInvoke { // Something is... wrong if (!@skillId) { Exception("ILLEGAL SKILL PASSED TO HUB. It has been compromised, Jim.", RB_DEBUGMES|RB_IRCBROADCAST); debugmes "Legal Caster: %s", strcharinfo(0); debugmes "Effective Caster: %d", @skillCaster; return; } // Homunculus Cycle /* *********************************************************************** */ //debugmes "Skill caster %d", @skillCaster; if (@skillCaster != getcharid(3)) { switch (@skillId) { case TMW2_SKILLX: sc_start SC_RICHMANKIM, 180000, @skillLv+rand2(9*@skillLv), 10000, SCFLAG_NONE, @skillTarget; gethomunexp @skillLv; break; case TMW2_HOMUN_HEAL: .@heal=gethominfo(6)*(5+rand2(@skillLv)); heal .@heal, 0; harm(@skillCaster, -.@heal, HARM_MISC); break; case TMW2_LITTLE_WONDERS: sc_end SC_POISON; sc_end SC_CURSE; sc_end SC_SILENCE; sc_end SC_CONFUSION; sc_end SC_BLIND; sc_start SC_CURSE, rand2(3500, 5000), 1, 10000, SCFLAG_NONE, @skillCaster; break; } return; } /* *********************************************************************** */ // TODO: Detect what was script-cast and what was player-case. Then, readd RB_IRCBROADCAST // If you can't do this: You can't do this if (getskilllv(@skillId) < @skillLv && @skillId != BS_GREED) Exception("System ERROR, HSI."+@skillId+" INVALID CAST (got "+@skillLv+" expected "+getskilllv(@skillId)+", sub-LC."+(getcharid(3)-2000000)+")", RB_DEBUGMES|RB_ISFATAL); // You are AFK for over 3 minutes, that's crazy, disregard if (checkidle() > 180) return; // Record to database skillInvoke[@skillId] = skillInvoke[@skillId] + 1; // Script-based skills /* *********************************************************************** */ switch (@skillId) { case TMW2_FAKESKILL: atcommand("@refresh"); break; case TMW2_FAKESKILL2: CMD_toevent(); break; case TMW2_CRAFT: UserCtrlPanel(); break; case TMW2_OVHFIRE: SK_OVHFire(); break; case TMW2_MPREGEN: SK_mpregen(); break; case TMW2_STUDY: SK_study(@skillTarget); break; case EVOL_AREA_PROVOKE: if (@skillTargetX && @skillTargetY) massprovoke(1+@skillLv, getmap(), @skillTargetX, @skillTargetY); else massprovoke(1+@skillLv); // SC_PROVOKE ? GetManaExp(@skillId, rand2(1,3)); break; case TMW2_GD_INCALL: GD_allboost(); GetManaExp(GD_DEVELOPMENT, 10); break; case TMW2_GD_REGEN: GD_regenerating(); GetManaExp(GD_DEVELOPMENT, 10); break; case TMW2_GD_DEFUP: GD_defboost(); GetManaExp(GD_DEVELOPMENT, 10); break; case TMW2_GD_BATTLEPLAN: GD_atkboost(); GetManaExp(GD_DEVELOPMENT, 10); break; case TMW2_GD_ATKUP: GD_atkboost2(); GetManaExp(GD_DEVELOPMENT, 10); break; case TMW2_GD_CRITUP: GD_critboost(); GetManaExp(GD_DEVELOPMENT, 10); break; case TMW2_GD_AUTOREVIVE: GD_autorevive(); GetManaExp(GD_DEVELOPMENT, 10); break; case TMW2_GDP_MAXPOWER: SK_maximizepower(); GetManaExp(GD_DEVELOPMENT, 10); break; case TMW2_GDP_SPREGEN: SK_spregen(); GetManaExp(GD_DEVELOPMENT, 10); break; // Weapon Overload attack case TMW2_OVERLOAD: .@PW=200+(@skillLv > 3 ? @skillLv : 0)+(@skillLv > 7 ? @skillLv*2 : 0); areaharm(@skillTarget, 0, AdjustAttackpower(.@PW), HARM_MISC); break; //////////////////////////////// // Magic v3 // The basic offensive skill from Trickmaster case TMW2_MANABOMB: // This skill takes 100% mana for a 1:1 ratio damage // And is a trick. Each level improves ratio in 1 // Has no cooldown, so it is powerful with pots // And is a good starter offensive skill areaharm(@skillTarget, 0, Sp*@skillLv, HARM_MISC, Ele_Ghost); Sp=0; GetManaExp(@skillId, 1); break; //////////////////////////////// // XXX: Healing Class case TMW2_FIRSTAID: .@PW=90+(10*@skillLv); // First aid only works on you, so .@heal=max(AdjustSpellpower(.@PW), AdjustAttackpower(.@PW)); heal .@heal, 0; GetManaExp(TMW2_HEALING, 1); break; case TMW2_HEALING: .@PW=130+(20*@skillLv); harm(@skillTarget, -AdjustSpellpower(.@PW), HARM_MISC); GetManaExp(TMW2_HEALING, 2); break; case TMW2_MAGNUSHEAL: // Area healing .@PW=200+(20*@skillLv); .@RG=4+(@skillLv/5); areaharm(@skillTarget, .@RG, -AdjustSpellpower(.@PW), HARM_MISC, "filter_friendly"); GetManaExp(TMW2_HEALING, 3); break; //////////////////////////////// // XXX: Fire Class // (May burn targets for damage over time) case TMW2_FIREARROW: .@PW=140+(10*@skillLv); // 4% chance, 2.5s harm(@skillTarget, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Fire); sc_start SC_BLOODING, 4500, 1, 400, SCFLAG_NONE, @skillTarget; GetManaExp(TMW2_FIREBALL, 1); break; case TMW2_FIREBALL: .@PW=140+(10*@skillLv); .@RG=2+(@skillLv/5); // 22% chance, 2.5s areaharm(@skillTarget, .@RG, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Fire); sc_start SC_BLOODING, 2500, 1, 4200, SCFLAG_NONE, @skillTarget; GetManaExp(TMW2_FIREBALL, 2); break; case TMW2_ARMAGEDDON: .@PW=140+(10*@skillLv); .@RG=5+(@skillLv/5); // 18% chance, 3s, 3x3 radius areaharm(@skillTarget, .@RG, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Fire); areasc(.@RG, 6000, SC_BLOODING, BL_MOB|BL_PC|BL_HOM|BL_MER, 1, "filter_hostile", @skillTarget, 1800); GetManaExp(TMW2_FIREBALL, 3); break; //////////////////////////////// // XXX: Holy Class // (Single DPS + AOE) case TMW2_NAPALMBEAT: .@PW=35+(5*@skillLv); .@dmg=AdjustSpellpower(.@PW); .@RG=2+(@skillLv/3); harm(@skillTarget, .@dmg/10, HARM_MAGI, Ele_Holy); areaharm(@skillTarget, .@RG, .@dmg, HARM_MAGI, Ele_Holy); GetManaExp(TMW2_HOLYLIGHT, 1); break; case TMW2_HOLYLIGHT: .@PW=125+(25*@skillLv); .@dmg=AdjustSpellpower(.@PW); harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Holy); areaharm(@skillTarget, 1, .@dmg/5, HARM_MAGI, Ele_Holy); GetManaExp(TMW2_HOLYLIGHT, 2); break; case TMW2_JUDGMENT: .@PW=250+(50*@skillLv); .@SPW=60+(15*@skillLv); .@dmg=AdjustSpellpower(.@PW); .@dsub=AdjustSpellpower(.@SPW); .@RG=3+(@skillLv/5); harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Holy); areaharm(@skillTarget, .@RG, .@dsub, HARM_MAGI, Ele_Holy); GetManaExp(TMW2_HOLYLIGHT, 3); break; //////////////////////////////// // XXX: Wind Class // (Smaller cooldown than others) case TMW2_MAGICSTRIKE: .@PW=125+(25*@skillLv); .@dmg=AdjustSpellpower(.@PW); harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Wind); GetManaExp(TMW2_LIGHTNINGBOLT, 1); break; case TMW2_LIGHTNINGBOLT: .@PW=150+(50*@skillLv); .@dmg=AdjustSpellpower(.@PW); harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Wind); GetManaExp(TMW2_LIGHTNINGBOLT, 2); break; case TMW2_TEMPEST: .@PW=125+(25*@skillLv); .@dmg=AdjustSpellpower(.@PW); .@RG=2+(@skillLv/5); areaharm(@skillTarget, .@RG, .@dmg, HARM_MAGI, Ele_Wind); GetManaExp(TMW2_LIGHTNINGBOLT, 3); break; //////////////////////////////// // XXX: Ice Class // (May freeze the targets) case TMW2_FROSTDIVER: .@PW=80+(10*@skillLv); // 22% chance, 2.5s harm(@skillTarget, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Water); sc_start SC_FREEZE, 2500, 1, 2200, SCFLAG_NONE, @skillTarget; GetManaExp(TMW2_NILFHEIM, 1); break; case TMW2_FROSTNOVA: .@PW=80+(10*@skillLv); .@RG=2+(@skillLv/5); // 18% chance, 3s, 3x3 radius areaharm(@skillTarget, .@RG, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Water); areasc(.@RG, 3000, SC_FREEZE, BL_MOB|BL_PC|BL_HOM|BL_MER, 1, "filter_hostile", @skillTarget, 1800); GetManaExp(TMW2_NILFHEIM, 2); break; case TMW2_NILFHEIM: // Nilfheim cast on self? .@PW=80+(10*@skillLv); .@RG=4+(@skillLv/5); areaharm(getcharid(3), .@RG*3/2, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Water); areasc(.@RG, 15000, SC_FREEZE, BL_PC | BL_MOB | BL_MER | BL_HOM, 1, "filter_hostile"); // Maybe filter_notme() would work better, indeed GetManaExp(TMW2_NILFHEIM, 3); break; //////////////////////////////// // XXX: Earth Class // DEF Effects at Gaia Break, more expensive case TMW2_METEORSTRIKE: .@PW=130+(20*@skillLv); .@dmg=AdjustSpellpower(.@PW); .@TM=1200+(@skillLv*300); harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Earth); sc_start SC_STUN, .@TM, 1, 800, SCFLAG_NONE, @skillTarget; GetManaExp(TMW2_METEORSTRIKE, 1); break; case TMW2_METEORSHOWER: .@PW=130+(15*@skillLv); .@dmg=AdjustSpellpower(.@PW); .@RG=3+(@skillLv/5); .@TM=800+(@skillLv*200); areaharm(@skillTarget, .@RG, .@dmg, HARM_MAGI, Ele_Earth); areasc(.@RG, .@TM, SC_STUN, BL_MOB | BL_PC | BL_HOM | BL_MER, 1, "filter_hostile", @skillTarget, 800); GetManaExp(TMW2_METEORSTRIKE, 2); break; case TMW2_GAIABREAK: .@PWA=170+(30*@skillLv); .@PWB=110+(10*@skillLv); .@dmg=AdjustSpellpower(.@PWA); .@dsub=AdjustSpellpower(.@PWB); harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Earth); rectharm(@skillTarget, 2, 5, .@dsub, HARM_MAGI, Ele_Earth); areasc(2, 5000, SC_INCDEFRATE, BL_PC, 10, "filter_friendly"); GetManaExp(TMW2_METEORSTRIKE, 3); break; /* // TODO: Ultimate Skills (T5/0) // Sanctum: AoE resurrection, HP like Resurrect, 30min+ cooldown and // inflict ailment on caster. // [Ultimate] revives the whole map, like @raisemap case TMW2_SANCTUM: SK_sanctum(); break; // Support magic // STATUS: Awaiting for Alchemy rework // TODO: Debuffs // TODO: Buffs // XXX: Resurrection skills // [MAPZONE] Need special care because scripts // [WIP] Maybe a custom recovery and a flag @canRevive ? // --------------------------------------------- // Revive: Makes char alive with 5% HP/Lvl // Resurrect: Makes char alive with ±80% HP and give a protection SC (lvl) // Redemption: AoE resurrection (HP works like revive, though) case TMW2_REVIVE: SK_revive(@skillTarget); break; case TMW2_RESSURECT: SK_ressurect(@skillLv); break; // Physical Class - mostly builtin // TODO: Archery effect-absorb skill case TMW2_ARROWOFDEVOUR: //.@x=getstatus(SC_POISON)+getstatus(SC_DPOISON); // if SC_POISON or SC_DPOISON break; case TMW2_ARROWOFURGES: // if SC_SILENCE or SC_FEAR break; case TMW2_ULTIMATEARROW: // if SC_BLIND or SC_BLOODING break; case TMW2_EXECUTION: // if SC_CURSE or SC_FEAR break; case TMW2_SHATTERARROW: // if SC_STONE or SC_STUN break; case TMW2_ARROWOFADVANTAGE: // IF SC_BURNING or SC_CONFUSION break; case TMW2_WAKENINGARROW: // if SC_SLEEP or SC_DEEP_SLEEP break; case TMW2_BURNINGARROW: // if SC_FREEZE or SC_COLD break; // TODO Assassin skill // Higher chance of success if monster HP+ATK < your own HP // (Maybe put level on that?) Never works on boss // Maybe arrow only? case TMW2_ASSASSINATE: // SC_COMA TODO break; case TMW2_SNARE: // 100.00% inflict STUN/SLEEP/whatever // Does not work on boss monsters // (Maybe the one which makes next hit critical?) // Was it SC_STONE? break; // Arrow pierce? (Maybe arrow shot disregarding defense, HARM_MISC ?) case TMW2_RICHNESS: // Passive, bonus2 bAddGetZenyNum, 5, 100; - Better not break; // TODO: Effect resisting passives? // Windwalker... No wait. // Immunity to knockback? Meeeeh. Easier to have a trait-giving quest. // After all, we have about 20 traits and you can only pick 5. // XXX: Sword skills are a 5-skills combo // No cast time, but cooldown present // DEF lowers as cast, damage based on combo. case TMW2_HORIZONTALSLASH: .@PW=100; .@PW+=(5*@skillLv); @SCombo=@skillTarget; harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); GetManaExp(@skillId, rand2(1,3)); break; case TMW2_DIAGONALSLASH: .@PW=100; if (LAST_SKILL[0] == TMW2_HORIZONTALSLASH) .@PW+=20; if (.@PW >= 120) .@PW+=30; if (@SCombo != @skillTarget) .@PW=100; .@PW+=(5*@skillLv); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); GetManaExp(@skillId, rand2(1,3)); break; case TMW2_VERTICALSLASH: .@PW=100; if (LAST_SKILL[0] == TMW2_DIAGONALSLASH) .@PW+=30; if (LAST_SKILL[1] == TMW2_HORIZONTALSLASH) .@PW+=20; if (.@PW >= 150) .@PW+=100; if (@SCombo != @skillTarget) .@PW=100; .@PW+=(5*@skillLv); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); GetManaExp(@skillId, rand2(1,3)); break; case TMW2_STAB: .@PW=100; if (LAST_SKILL[0] == TMW2_VERTICALSLASH) .@PW+=40; if (LAST_SKILL[1] == TMW2_DIAGONALSLASH) .@PW+=30; if (LAST_SKILL[2] == TMW2_HORIZONTALSLASH) .@PW+=20; if (.@PW >= 190) .@PW+=200; if (@SCombo != @skillTarget) .@PW=100; .@PW+=(5*@skillLv); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); GetManaExp(@skillId, rand2(1,3)); break; case TMW2_GRANDBLAST: .@PW=100; if (LAST_SKILL[0] == TMW2_STAB) .@PW+=50; if (LAST_SKILL[1] == TMW2_VERTICALSLASH) .@PW+=40; if (LAST_SKILL[2] == TMW2_DIAGONALSLASH) .@PW+=30; if (LAST_SKILL[3] == TMW2_HORIZONTALSLASH) .@PW+=20; if (.@PW >= 200) .@PW+=300; if (@SCombo != @skillTarget) .@PW=100; .@PW+=(5*@skillLv); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Holy); GetManaExp(@skillId, rand2(2,3)); break; */ //////////////////////////////// // XXX: Physical Class (Regular) case TMW2_FALKONSTRIKE: .@PW=100+(25*@skillLv); .@ST=0+(10*@skillLv); .@TM=100+(90*@skillLv); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); sc_start SC_STUN, .@TM, 1, .@ST, SCFLAG_NONE, @skillTarget; GetManaExp(@skillId, rand2(1,3)); break; case TMW2_GROUNDSTRIKE: .@PW=50+(40*@skillLv); .@dmg=AdjustAttackpower(.@PW); .@RG=2+(@skillLv/5); .@TM=100+(@skillLv*200); .@ST=500+(100*@skillLv); .@EF=any(SC_STUN, SC_BLIND, SC_BLOODING, SC_BLIND, SC_BLOODING); areaharm(@skillTarget, .@RG, .@dmg, HARM_PHYS, Ele_Neutral); areasc(.@RG, .@TM, .@EF, BL_MOB | BL_PC | BL_HOM | BL_MER, 1, "filter_hostile", @skillTarget, .@ST); GetManaExp(@skillId, rand2(1,3)); break; case TMW2_SUPREMEATTACK: .@PW=100+(50*@skillLv); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); GetManaExp(@skillId, rand2(1,3)); break; //////////////////////////////// // XXX: Physical Class (Archery) case TMW2_CHARGEDARROW: .@PW=100+(50*@skillLv); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); GetManaExp(@skillId, rand2(1,3)); break; case TMW2_ARROWSHOWER: .@PW=150+(10*@skillLv); .@dmg=AdjustAttackpower(.@PW); .@RG=1+(@skillLv/3); areaharm(@skillTarget, .@RG, .@dmg, HARM_PHYS, Ele_Neutral); GetManaExp(@skillId, rand2(1,3)); break; //////////////////////////////// // XXX: Brawling Class case TMW2_BRAWLING: // 75x3 = 225 .@PW=70+(5*@skillLv); // Using a shield, so power is halved if (getequipid(EQI_HAND_L) > 0) .@PW=.@PW/2; harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); sleep2(10); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); sleep2(10); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); break; case TMW2_BEARSTRIKE: // 60x5 = 300 .@PW=55+(5*@skillLv); // Using a shield, so power is halved if (getequipid(EQI_HAND_L) > 0) .@PW=.@PW/2; harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); sleep2(10); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); sleep2(10); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); sleep2(10); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); sleep2(10); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); break; case TMW2_ALLINONE: // 45x8 = 360 .@PW=40+(5*@skillLv); // Using a shield, so power is halved if (getequipid(EQI_HAND_L) > 0) .@PW=.@PW/2; //harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Fire); sleep2(10); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Water); sleep2(10); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Earth); sleep2(10); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Wind); sleep2(10); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Holy); sleep2(10); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Shadow); sleep2(10); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Ghost); sleep2(10); // The main elemental-less blast hits all in same square, // and also hits behind (and on your square) rectharm(@skillTarget, 0, 1, AdjustAttackpower(.@PW/2), HARM_PHYS, Ele_Neutral); break; //////////////////////////////// // CLASS_OTHER case TMW2_PARUM: SK_parum(); break; case TMW2_DEMURE: SK_Demure(); break; case TMW2_DRAGOKIN: SK_Dragokin(); break; // Summons which never fail case TMW2_ZARKOR: alignment_cansummon(); SummonMagic(@skillId, CaveMaggot, 2, @skillLv); GetManaExp(@skillId, 1); break; // Summons which may fail case TMW2_KALWULF: SK_summon(Wolvern, 4, any(3,4)); break; case TMW2_KALBOO: SK_summon(Mouboo, 4, any(2,3)); break; case TMW2_KALSPIKE: SK_summon(PoisonSpikyMushroom, 4, any(2,3)); break; case TMW2_CUTEHEART: SK_summon(Fluffy, 4, any(2,3)); break; // Slightly more complex summons case TMW2_LIMERIZER: SK_summon(any(GreenSlime,AzulSlime,RedSlime,AngryYellowSlime), 2, any(3,4)); break; case TMW2_FAIRYKINGDOM: SK_summon(any(FireFairy, EarthFairy, WaterFairy, WindFairy, PoisonFairy), 4, any(3,4)); break; case TMW2_FAIRYEMPIRE: SK_summon(any(VanityPixie, HolyPixie, ShadowPixie, NulityPixie), 5, any(4,5)); break; // More complex summons case TMW2_KALMURK: .@mobId=Maggot; if (abizit() > 4 && GHMEMO[GHQ_GetQuestIDByMonsterID(Maggot)] >= 10000 && MAGIC_LVL >= 3) { .@mobId=any(Maggot, Maggot, Maggot, Maggot, GiantMaggot); } SK_summon(.@mobId, (.@mobId == Maggot ? 2 : 4), any(1,2)); break; case TMW2_HALHISS: .@mobId=Snake; if (abizit() > 3 && GHMEMO[GHQ_GetQuestIDByMonsterID(MountainSnake)] >= 10000 && rand2(1,3) == 2) { .@mobId=MountainSnake; } SK_summon(.@mobId, 4, any(3,4)); break; case TMW2_FROZENHEART: .@mobId=Moggun; if (rand2(6,12) < (abizit()*2)+1) { .@mobId=Yeti; } SK_summon(.@mobId, 4, any(3,4)); break; case TMW2_STONEHEART: .@mobId=Terranite; if (rand2(9,12) < (abizit()*2)+1 && BaseLevel > 80) { .@mobId=TerraniteProtector; } SK_summon(.@mobId, 4, any(4,5)); break; case TMW2_DUCKY: .@mobId=Duck; .@q=getq(LilitQuest_PiratesOfSARAH); if (!alignment_cansummon()) break; if (abizit() > 4 && .@q > 2 && MAGIC_LVL >= 3) { // GHQ Complete: 33% chances // Otherwise: 8% chances if (GHMEMO[GHQ_GetQuestIDByMonsterID(Duck)] >= 10000) .@mobId=any(Duck, Duck, EliteDuck); else .@mobId=any(Duck, Duck, Duck, Duck, Duck, Duck, Duck, Duck, Duck, Duck, Duck, EliteDuck); } SummonMagic(@skillId, .@mobId, 2); GetManaExp(@skillId, 1); break; // Special exception case TMW2_TRANSMIGRATION: doevent("sk#mkpot::OnCall"); break; // Experience only case KN_AUTOCOUNTER: case SN_SHARPSHOOTING: case HW_MAGICPOWER: case SM_PROVOKE: case SN_WINDWALK: case SO_FIREWALK: case TF_BACKSLIDING: case MG_FIREWALL: case ALL_FULL_THROTTLE: case GC_DARKILLUSION: case NV_TRICKDEAD: GetManaExp(@skillId, rand2(1,3)); break; } // Debug if ($@GM_OVERRIDE) debugmes "Cast skill %d on level %d - Target %d", @skillId, @skillLv, @skillTarget; // Cleanup (double-safe) @skillTarget = 0; return; } // When you kill a player, some special care is needed // Only a few maps will give you experience for PK: Tulimshar's Guards Arena, // Frostia Imperial PVP Arena, Call Of Dusty, Arena Quirino Voraz. // HUB_PvP ( ) - killedrid must be set function script HUB_PvP { // Update global PK count $PLAYERS_KILLED+=1; // Prepare local variables .@atk=get_BR(getcharid(3)); .@def=get_BR(killedrid); .@honor=calc_HR(getcharid(3), killedrid); .@bxp=max(readparam(BaseLevel, killedrid), .@def); .@jxp=readparam(JobLevel, killedrid); .@m$=getmap(); .@gm=getgroupid(killedrid); // This is an official PVP Map if (ispvpmap(.@m$)) { // Honorable Death if (.@honor >= 0) { HONOR+=.@honor; } else { // Dishonorable... But... Legit? if (is_bandit(killedrid) || getmap() == "001-8") .@honor=1; HONOR+=.@honor; // It's negative. } // PvP, and a GM was slain if (.@gm >= 80) { rentitem MurdererCrown, (86400*7); // 1 week kamibroadcast(strcharinfo(0)+" killed Game Master \""+strcharinfo(0, "", killedrid)+"\", and now may wear a "+getitemlink(MurdererCrown)+" for a week."); } // It was a duel! } else { // Honorable Duel: HONOR +30% if (.@honor > 0) HONOR+=max(1, .@honor*3/10); else if (.@honor < 0) HONOR+=1; // ^ Dishonorable duel, but was a duel! } // Report about honor dispbottom l("%d vs %d: Honor (%d)", .@atk, .@def, .@honor); // TODO: Start using readparam2() to read if the opponent was worthy // That is, read total attack, defense, HP, evasion and hit chance // And compare with your own readparam2(), then use a % and a table // based on your (assassin's) level. if (compare(.@m$, "001-8")) { // Quirino Voraz PVP Arena // You get 5 times killed player level, and 1 time job level getexp .@bxp*5, .@jxp; } else if (compare(.@m$, "ARENA") || compare(.@m$, "003-13")) { // Tulimshar Duel Arena // You get 3 times killed player level, and 2 times job level getexp .@bxp*3, .@jxp*2; } else if (compare(.@m$, "001-10")) { // Call Of Dusty // You get 3 times killed player level, and 3 times job level getexp .@bxp*3, .@jxp*3; } else if (compare(.@m$, "001-10-1")) { // Call Of Dusty Boss Room // You _may_ get a Bottled Dusty at random, but dead player status affect if (.@def > .@atk/10) { if (rand2(0,250) < readparam2(bLuk)+readparam2(bLuk, killedrid)) getitem BottledDust, any(1,1,2); } } else { // Anywhere else // You get 0.5 times killed player level, and 0 times job level getexp (.@bxp/2), 0; } return; } // HUB_PCBonus () function script HUB_PCBonus { /* Rebirth Traits */ if (PCBONUS & PCB_ATKBONUS) { bonus bAtk, 25; } if (PCBONUS & PCB_MATKBONUS) { bonus bMatk, 25; } if (PCBONUS & PCB_DEFBONUS) { bonus bDef, 20; } if (PCBONUS & PCB_MDEFBONUS) { bonus bMdef, 20; } if (PCBONUS & PCB_EVDBONUS) { bonus bFlee, 20; } if (PCBONUS & PCB_HITBONUS) { bonus bHit, 25; } if (PCBONUS & PCB_CRITBONUS) { bonus bCritical, 5; } if (PCBONUS & PCB_DOUBLEATK) { bonus bDoubleAddRate, 5; } if (PCBONUS & PCB_ALLSTATS) { bonus bAllStats, 1; } if (PCBONUS & PCB_HPBONUS) { bonus bMaxHP, 500; } if (PCBONUS & PCB_MPBONUS) { bonus bMaxSP, 200; } if (PCBONUS & PCB_ASPDBONUS) { bonus bAspd, 10; } if (PCBONUS & PCB_WSPDBONUS) { bonus bSpeedAddRate, 5; } if (PCBONUS & PCB_WEIGHTBONUS) { bonus bAddMaxWeight, 1000; } if (PCBONUS & PCB_EXPBONUS) { bonus2 bExpAddRace, RC_All, 10; } if (PCBONUS & PCB_NOKNOCKBACK) { bonus bNoKnockback, 1; } if (PCBONUS & PCB_SPLASHMASTER) { bonus bSplashRange, 1; } if (PCBONUS & PCB_RANGEMASTER) { bonus bAtkRange, 1; } if (Class != Savior && !(PCBONUS & PCB_LEGENDARY)) { bonus2 bCriticalAddRace, RC_Legendary, -25; bonus2 bSPDrainValueRace, RC_Legendary, -5; bonus2 bAddRace, RC_Legendary, -40; bonus2 bMagicAddRace, RC_Legendary, -20; } if (PCBONUS & PCB_LEGENDARY) { bonus bDefRatioAtkRace, RC_Legendary; } /* Passive Skills */ bonus2 bSubRace, RC_Legendary, getskilllv(AL_DP)-10; return; }