// TMW2 script // Author: Jesusalva // // Magic Script Core Functions // // Used for our pseudo-magic. // These are only helpers, you can add more restrictions and effects freely. // Here abizit() goes up to 10 instead of 5 function script abizit { if (!getskilllv(SKILL_MAGIC)) return 0; switch (getskilllv(SKILL_MAGIC)) { case 1: .@base = 10; break; case 2: .@base = 120; break; case 3: .@base = 1500; break; case 4: .@base = 20000; break; case 5: .@base = 250000; break; default: return 0; } return min(MAGIC_EXP/.@base, 10); return 0; } // AdjustSpellpower(power=100, {target=@skillTarget{, type=HARM_MAGI}}) function script AdjustSpellpower { .@power=getarg(0, 100); .@target=getarg(1, @skillTarget); .@type=getarg(2, HARM_MAGI); .@src=getcharid(3); // Prevents a possible glitch if (.@target < 1 || .@target == getcharid(3)) .@target=0; // Your magic power permanently increases your damage in 5% if (getskilllv(SKILL_MAGIC) > 1) .@power+=getskilllv(SKILL_MAGIC)*5-5; // Calculation FIX if (.@type == HARM_MAGI) { .@power+=(readparam2(UDT_INT)/5); .@dmg=rand2( readbattleparam(getcharid(3), UDT_MATKMIN), readbattleparam(getcharid(3), UDT_MATKMAX)); // Apply defense //if (.@target) // .@dmg=max(0, .@dmg-getunitdata(.@target, UDT_MDEF)); } else if (.@type == HARM_PHYS) { .@power+=(readparam2(UDT_STR)/5); .@dmg=rand2( readbattleparam(getcharid(3), UDT_ATKMIN), readbattleparam(getcharid(3), UDT_ATKMAX)); // Apply defense //if (.@target) // .@dmg=max(0, .@dmg-getunitdata(.@target, UDT_DEF)); } else { .@dmg = .@power; .@power = 100; } // Abizit Influence (50%~110% at best, perfect ctrl is 100%~110%) .@dmg = .@dmg * (50 + abizit() * rand2(5,6)) / 100; .@dmg = .@dmg * .@power / 100; return .@dmg; } // An alias for simplification // AdjustAttackpower(power=100, {target=@skillTarget{, type=HARM_PHYS}}) function script AdjustAttackpower { .@power=getarg(0, 100); .@target=getarg(1, @skillTarget); .@type=getarg(2, HARM_PHYS); return AdjustSpellpower(.@power, .@target, .@type); } // SkillID, EXP Points function script GetManaExp { .@sk=getarg(0); .@pt=getarg(1); if (LAST_SKILL != .@sk) { // Update skill memory LAST_SKILL[4]=LAST_SKILL[3]; LAST_SKILL[3]=LAST_SKILL[2]; LAST_SKILL[2]=LAST_SKILL[1]; LAST_SKILL[1]=LAST_SKILL[0]; LAST_SKILL[0]=.@sk; // Magic EXP is gained by switching skills often MAGIC_EXP=MAGIC_EXP+.@pt; } return; } // SkillID, MobID{, SkillLevelPerMob=2{, Level Override{, Summon=True}}} function script SummonMagic { .@sk=getarg(0); .@id=getarg(1); .@adj=getarg(2,2); .@lv=getarg(3,getskilllv(.@sk)); .@sm=getarg(4, true); if (.@adj < 1) { debugmes "\033[31mInvalid MobPerSkillLevel for SummonMagic (.@adj): "+.@adj+"\033[0m"; dispbottom l("Invalid parameter specified, blame saulc."); end; } if (!.@sm) getmapxy(.@m$, .@x, .@y, 0); // Cause effect // Summoned monsters live from 45 to 60 seconds, and each skill levels grants 10s extra life // The 35~50 is not a defect, remember skill starts at level 1... // PS. Abizit makes a variation from 80% to 130% of official values for (.@i = 0; .@i < (.@lv+(.@adj-1))/.@adj; .@i++) { .@lifetime=rand2(35,50)+.@lv*10; // Abizit makes lifetime vary (like AdjustSpellpower) .@lifetime = .@lifetime * (50 + abizit() * rand2(5,6)) / 100; // Unfortunately this version does not returns the summoned monster GID if (.@sm) summon("Summoned Monster", .@id, .@lifetime); else monster(.@m$, .@x, .@y, "Invoked Monster", .@id, 1); /* .@mids=summon("Summoned Monster", .@id, .@lifetime); .@bhp=getunitdata(.@mids, UDT_MAXHP); // Each skill level raises HP in 5% .@lvx=.@bhp + max(0, (.@lv-1)*.@bhp/20); // Abizit makes bonus HP vary (like AdjustSpellpower) .@lvx = .@lvx * (50 + abizit() * rand2(5,6)) / 100; setunitdata(.@mids, UDT_MAXHP, .@lvx); setunitdata(.@mids, UDT_HP, .@lvx); // Reconfigure monster modes .@opt=getunitdata(.@mids, UDT_MODE); // Disable looting if (.@opt & MD_LOOTER) .@opt=.@opt^MD_LOOTER; // All summons can suffer knockback if (.@opt & MD_NOKNOCKBACK) .@opt=.@opt^MD_NOKNOCKBACK; // Strip summons from BOSS mode and immunity if (.@opt & MD_BOSS) .@opt=.@opt^MD_BOSS; // Save new options setunitdata(.@mids, UDT_MODE, .@opt); */ } dispbottom l("All monsters summoned!"); return; } // SK_summon(ID, amount, mexp{, summon=True}) function script SK_summon { .@mob=getarg(0); .@amt=getarg(1); .@mex=getarg(2, 1); .@sum=getarg(3, true); .@lvl=getskilllv(SKILL_MAGIC); if ($@GM_OVERRIDE || debug) debugmes "Skill "+@skillId+" Lv "+@skillLv; if (ispcdead() || !.@lvl || !@skillLv) return; if (rand2(10) < abizit()) { // Summon Magic (with magic level bonus) SummonMagic(@skillId, .@mob, .@amt, .@lvl+@skillLv-1, @skillLv, .@sum); } else if (rand2(10) < abizit()) { // Re-roll dispbottom l("You cannot complete the casting correctly!"); SummonMagic(@skillId, .@mob, 1, 1, 1, .@sum); } else if (abizit() <= rand2(3)) { // Spell overwhelms you, causing it to be spawned as aggro vs you. (33%) dispbottom l("The spell takes a mind of its own backfires!"); getmapxy(.@m$, .@x, .@y, 0); .@opo=monster(.@m$, .@x, .@y, "Failed summon", .@mob, 1); unitattack(.@opo, getcharid(3)); } else { dispbottom l("The spell fails!"); } // Get a single mana experience point (FIXME) GetManaExp(@skillId, .@mex); return; } // areaharm(target, range, DMG, {type, element, filter, bl}) // Defaults to HARM_MISC, Ele_Neutral, filter filter_hostile and all BLs // Valid BL: BL_MOB | BL_PC | BL_HOM | BL_MER // Do not use: NPC, PET, ELEM // Range centers on caster (player), implement and use areaharm2 elsewhere function script areaharm { .@t=getarg(0); .@r=getarg(1); .@d=getarg(2); .@h=getarg(3, HARM_MISC); .@e=getarg(4, Ele_Neutral); .@f$=getarg(5, "filter_hostile"); .@b=getarg(6, BL_PC | BL_MOB | BL_MER | BL_HOM); getmapxy(.@m$, .@x, .@y, getunittype(.@t), .@t); .@c=getunits(.@b, .@mbs, false, .@m$, .@x-.@r, .@y-.@r, .@x+.@r, .@y+.@r); for (.@i = 0; .@i < .@c; .@i++) { // Filtering if (!callfunc(.@f$, .@mbs[.@i])) continue; harm(.@mbs[.@i], .@d, .@t, .@e); specialeffect(FX_ATTACK, AREA, .@mbs[.@i]); // TODO: Handle MobPt to don't overload timer system? } return; } // rectharm(target, x, y, DMG, {type, element, filter, bl}) // Same as areaharm() but causes a rectangle in (x,y) size, instead of a square function script rectharm { .@t=getarg(0); .@rx=getarg(1); .@ry=getarg(2); .@d=getarg(3); .@h=getarg(4, HARM_MISC); .@e=getarg(5, Ele_Neutral); .@f$=getarg(6, "filter_hostile"); .@b=getarg(7, BL_PC | BL_MOB | BL_MER | BL_HOM); getmapxy(.@m$, .@x, .@y, getunittype(.@t), .@t); .@c=getunits(.@b, .@mbs, false, .@m$, .@x-.@rx, .@y-.@ry, .@x+.@rx, .@y+.@ry); for (.@i = 0; .@i < .@c; .@i++) { // Filtering if (!callfunc(.@f$, .@mbs[.@i])) continue; harm(.@mbs[.@i], .@d, .@t, .@e); specialeffect(FX_ATTACK, AREA, .@mbs[.@i]); // TODO: Handle MobPt to don't overload timer system? } return; } // areasc(range, time, sc, bl, value, filter, target, chances) // Defaults to 3x3 square, sleep mob for 500ms. Ignores you. // Centered on player attached, 100% success chance // Need a player caster. Valid BL: BL_MOB | BL_PC | BL_HOM | BL_MER function script areasc { .@r=getarg(0, 3); .@d=getarg(1, 500); .@s=getarg(2, SC_SLEEP); .@b=getarg(3, BL_MOB); .@val=getarg(4, 1); .@f$=getarg(5, "filter_notme"); .@t=getarg(6, playerattached()); .@sr=getarg(7, 10000); getmapxy(.@m$, .@x, .@y, getunittype(.@t), .@t); .@c=getunits(.@b, .@mbs, false, .@m$, .@x-.@r, .@y-.@r, .@x+.@r, .@y+.@r); for (.@i = 0; .@i < .@c; .@i++) { // Filtering if (!callfunc(.@f$, .@mbs[.@i])) continue; sc_start .@s, .@d, .@val, .@sr, SCFLAG_NONE, .@mbs[.@i]; specialeffect(FX_BUFF, AREA, .@mbs[.@i]); } return; } // areasc2(map, x, y, {range, time, sc, bl, value, filter}) - can be used by NPC // Valid BL: BL_MOB | BL_PC | BL_HOM | BL_MER function script areasc2 { .@m$=getarg(0); .@x=getarg(1); .@y=getarg(2); .@r=getarg(3, 3); .@d=getarg(4, 500); .@s=getarg(5, SC_SLEEP); .@b=getarg(6, BL_MOB); .@val=getarg(7, 1); .@f$=getarg(8, "filter_always"); .@c=getunits(.@b, .@mbs, false, .@m$, .@x-.@r, .@y-.@r, .@x+.@r, .@y+.@r); for (.@i = 0; .@i < .@c; .@i++) { // Filtering if (!callfunc(.@f$, .@mbs[.@i])) continue; sc_start .@s, .@d, .@val, 10000, SCFLAG_NONE, .@mbs[.@i]; specialeffect(FX_BUFF, AREA, .@mbs[.@i]); } return; } // areasc3(range, time, sc, bl, val1, val2, filter) // Defaults to 3x3 square, sleep mob for 500ms. Ignores you. // Need a player caster. Valid BL: BL_MOB | BL_PC | BL_HOM | BL_MER function script areasc3 { .@r=getarg(0, 3); .@d=getarg(1, 500); .@s=getarg(2, SC_SLEEP); .@b=getarg(3, BL_MOB); .@v1=getarg(4, 1); .@v2=getarg(5, 1); .@f$=getarg(6, "filter_notme"); getmapxy(.@m$, .@x, .@y, 0); .@c=getunits(.@b, .@mbs, false, .@m$, .@x-.@r, .@y-.@r, .@x+.@r, .@y+.@r); for (.@i = 0; .@i < .@c; .@i++) { // Filtering if (!callfunc(.@f$, .@mbs[.@i])) continue; sc_start2 .@s, .@d, .@v1, .@v2, 10000, SCFLAG_NONE, .@mbs[.@i]; specialeffect(FX_BUFF, AREA, .@mbs[.@i]); } return; } // massprovoke(range, {map, x, y}) - player only function script massprovoke { getmapxy(.@m$, .@x, .@y, 0); .@r=getarg(0, 3); .@m$=getarg(1, .@m$); .@x=getarg(2, .@x); .@y=getarg(3, .@y); .@c=getunits(BL_MOB, .@mbs, false, .@m$, .@x-.@r, .@y-.@r, .@x+.@r, .@y+.@r); for (.@i = 0; .@i < .@c; .@i++) { //sc_start .@s, .@d, 1, 10000, SCFLAG_NONE, .@mbs[.@i]; aggravate .@mbs[.@i]; specialeffect(FX_MAGIC, AREA, .@mbs[.@i]); } return; } // getactivatedpoolskilllist(?) function script getactivatedpoolskilllist { return bitmask_count(FOCUSING); } // getdeactivatedpoolskilllist(?) function script getdeactivatedpoolskilllist { return (bitmask_count(FOCUSING)-FSKILL_TOTAL); } // getpoolskillFID(ID) function script getpoolskillFID { switch (getarg(0)) { case SKILL_MALLARDS_EYE: return FSKILL_MALLARDS_EYE; case SKILL_BRAWLING: return FSKILL_BRAWLING; case SKILL_SPEED: return FSKILL_SPEED; case SKILL_RESIST_POISON: return FSKILL_RESIST_POISON; case SKILL_ASTRAL_SOUL: return FSKILL_ASTRAL_SOUL; case SKILL_RAGING: return FSKILL_RAGING; } return Exception("Invalid focus skill ID: "+getarg(0), RB_DEFAULT|RB_ISFATAL); } // poolskill(skill) function script poolskill { if (bitmask_count(FOCUSING) < getskilllv(SKILL_POOL)) { FOCUSING=FOCUSING | getpoolskillFID(getarg(0)); recalcstatus(); return true; } return false; } // unpoolskill(skill) function script unpoolskill { .@f=getpoolskillFID(getarg(0)); if (FOCUSING & .@f) FOCUSING=FOCUSING ^ .@f; recalcstatus(); return; } // isfocused(skill) function script isfocused { .@f=getpoolskillFID(getarg(0)); return (FOCUSING & .@f); } // mcharge(item, school, charges) {inheirs = @skillId} function script mcharge { .@it=getarg(0); .@sc=getarg(1); .@cr=getarg(2, 0); if (@MCHARGE[@skillId] < 1) { delitem .@it, 1; @MCHARGE[@skillId] = .@cr+getskilllv(SKILL_MAGIC)+getskilllv(.@cr); // Low supply warning if (countitem(.@it) <= 3) dispbottom col(b(l("Warning, %d remaining: %s", countitem(.@it), getitemname(.@it))), 1); } @MCHARGE[@skillId]-=1; return; } // transcheck( {item 1, amount 1}, {item 2, amount 2}... ) // returns true upon success function script transcheck { if (getargcount() < 2 || getargcount() % 2 != 0) return Exception("Faulty transcheck invoked - error"); // Count items for (.@i=0;.@i < getargcount(); .@i++) { if (countitem(getarg(.@i)) < getarg(.@i+1)) return false; .@i++; } // Delete Items for (.@i=0;.@i < getargcount(); .@i++) { delitem getarg(.@i), getarg(.@i+1); .@i++; } return true; } - script Magic Load NPC_HIDDEN,{ OnInit: end; OnSkillInvoke: callfunc("HUB_SkillInvoke"); end; OnPCBonusEvent: callfunc("HUB_PCBonus"); end; }