// 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; 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_ATTACK|PCBLOCK_SKILL|PCBLOCK_USEITEM|PCBLOCK_MOVE, 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); } // 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_ATTACK|PCBLOCK_SKILL|PCBLOCK_USEITEM|PCBLOCK_COMMANDS, 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); return; } // HUB_SkillInvoke ( ) function script HUB_SkillInvoke { debugmes "Cast skill %d", @skillId; // 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) Exception("System ERROR, HSI."+@skillId+" INVALID CAST (got "+@skillLv+" expected "+getskilllv(@skillId)+", sub-LC."+(getcharid(3)-2000000)+")", RB_DEBUGMES|RB_ISFATAL); // 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_MPREGEN: SK_mpregen(); break; case EVOL_AREA_PROVOKE: if (@skillTargetX && @skillTargetY) massprovoke(1+@skillLv, getmap(), @skillTargetX, @skillTargetY); else massprovoke(1+@skillLv); 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; // Magic v3 case TMW2_NILFHEIM: SK_nilfheim(); GetManaExp(@skillId, 3); break; case TMW2_JUDGMENT: debugmes "Target: %d (%d,%d)", @skillTarget, @skillTargetX, @skillTargetY; areasc2(getmap(), @skillTargetX, @skillTargetY, 2, 10000, SC_BLOODING, BL_MOB|BL_PC); .@dmg=AdjustSpellpower(300); //harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Holy); areaharm(@skillTarget, 8, .@dmg, HARM_MAGI, Ele_Holy); break; /* case TMW2_MANABOMB: // TODO: areaharm(1x1 or same square?) // 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_MAGI, Ele_Ghost); Sp=0; break; // Weapon Overload attack case TMW2_OVERLOAD: .@PW=67+(33*@skillLv); areaharm(@skillTarget, 0, AdjustAttackpower(.@PW), HARM_MISC); break; // TODO: Ultimate Skills (T5/0) // Support magic // TODO: Debuffs // TODO: Buffs case TMW2_FIRSTAID: .@PW=90+(10*@skillLv); // First aid only works on you, so .@heal=max(AdjustSpellpower(.@PW), AdjustAttackpower(.@PW)); heal .@heal, 0; break; case TMW2_HEALING: .@PW=130+(20*@skillLv); harm(@skillTarget, -AdjustSpellpower(.@PW), HARM_MISC); break; case TMW2_MAGNUSHEAL: // Area healing .@PW=200+(20*@skillLv); areaharm(@skillTarget, 4, -AdjustSpellpower(.@PW), HARM_MISC, "filter_friendly"); break; // Provoke: builtin, Mass Provoke: See above // Mana Wisdom: Passive, Accumulate Power: builtin // FIXME: Windwalker, Last Standing Man // Destructive Magic case TMW2_FIREARROW: .@PW=140+(10*@skillLv); harm(@skillTarget, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Fire); break; case TMW2_FIREBALL: .@PW=140+(10*@skillLv); areaharm(@skillTarget, 3, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Fire); break; case TMW2_ARMAGEDDON: .@PW=140+(10*@skillLv); areasc2(getmap(), @skillTargetX, @skillTargetY, 2, 10000, SC_BLOODING, BL_MOB|BL_PC); areaharm(@skillTarget, 8, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Fire); break; case TMW2_FROSTDIVER: .@PW=80+(10*@skillLv); // 8% chance, 10s sc_start SC_FREEZE, 10000, 1, 800, flag?, @skillTarget; harm(@skillTarget, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Water); break; case TMW2_FROSTNOVA: .@PW=80+(10*@skillLv); // TODO: All this in area // 8% chance, 10s sc_start SC_FREEZE, 10000, 1, 800, flag?, @skillTarget; areaharm(@skillTarget, 6, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Water); break; case TMW2_NILFHEIM: // FIXME SK_Nilfheim // Nilfheim cast on self? .@PW=80+(10*@skillLv); areasc(8, 10000, SC_FREEZE, BL_PC | BL_MOB | BL_MER | BL_HOM, "filter_hostile"); // Maybe filter_notme() would work better, indeed areaharm(getcharid(3), 8, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Water); break; case TMW2_MAGICSTRIKE: case TMW2_LIGHTNINGBOLT: case TMW2_TEMPEST: break; case TMW2_NAPALMBEAT: case TMW2_HOLYLIGHT: case TMW2_JUDGEMENT: break; case TMW2_METEORSTRIKE: case TMW2_METEORSHOWER: case TMW2_GAIABREAK: // Defensive spell + single target earth DPS break; // Firewalk: Handled externally // Physical Class - mostly builtin // TODO: Archery effect-absorb skill // TODO: Something powerful for swords? // 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); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); 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); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); 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); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Water); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Earth); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Wind); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Holy); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Shadow); harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Ghost); 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: SummonMagic(@skillId, CaveMaggot, 2, @skillLv); GetManaExp(@skillId, 1); break; // Summons which may fail case TMW2_KALMURK: SK_summon(Maggot, 2, any(1,2)); break; 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: // TODO: Allow Pixie summoning SK_summon(any(FireFairy, EarthFairy, WaterFairy, WindFairy, PoisonFairy), 4, any(3,4)); break; // More complex summons 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: if (alignment() < 0 && !isequippedcnt(AegisShield)) break; SummonMagic(@skillId, Duck, 2, @skillLv); GetManaExp(@skillId, 1); break; // Special exception case TMW2_TRANSMIGRATION: doevent("sk#mkpot::OnCall"); break; // Experience only case SM_BASH: case MC_MAMMONITE: case AC_SHOWER: case KN_AUTOCOUNTER: case ASC_METEORASSAULT: case SN_SHARPSHOOTING: case AL_HEAL: case HW_MAGICPOWER: case SM_PROVOKE: case AB_HIGHNESSHEAL: case SN_WINDWALK: case MG_FIREBALL: case AL_HOLYLIGHT: case MG_SOULSTRIKE: case MG_NAPALMBEAT: case SO_FIREWALK: case WZ_FROSTNOVA: case MG_LIGHTNINGBOLT: case TF_BACKSLIDING: case MG_FIREWALL: case ALL_FULL_THROTTLE: case GC_DARKILLUSION: case NV_TRICKDEAD: case AC_CHARGEARROW: GetManaExp(@skillId, rand2(1,3)); break; } // Debug if ($@GM_OVERRIDE || debug) debugmes "Cast skill %d on level %d", @skillId, @skillLv; 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)) HONOR+=1; else HONOR+=.@honor; // It's negative. } // PvP, and a GM was slain if (.@gm >= 80) { rentitem MurdererCrown, (86400*7); // 1 week kamibroadcast("%s killed Game Master \"%s\", and now may wear a %s for a week.", strcharinfo(0), strcharinfo(0, "", killedrid), getitemlink(MurdererCrown)); } // 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; }