// TMW2 script
// Author: Jesusalva <admin@tmw2.org>
//
// 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;
// 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);
if ($@GM_OVERRIDE || debug) debugmes "Skill "+@skillId+" Lv "+@skillLv;
if (rand2(10) < abizit()) {
// Summon Magic (with magic level bonus)
SummonMagic(@skillId, .@mob, .@amt, MAGIC_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 {
@skilllist_count=bitmask_count(FOCUSING);
// FIXME @skilllist_name$ @skilllist_id? Or just rewrite Luca
return @skilllist_count;
}
// getdeactivatedpoolskilllist(?)
function script getdeactivatedpoolskilllist {
@skilllist_count=bitmask_count(FOCUSING)-FSKILL_TOTAL;
return @skilllist_count;
}
// 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));
return true;
}
return false;
}
// unpoolskill(skill)
function script unpoolskill {
.@f=getpoolskillFID(getarg(0));
if (FOCUSING & .@f)
FOCUSING=FOCUSING ^ .@f;
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",
3, getitemname(.@it))), 1);
}
@MCHARGE[@skillId]-=1;
return;
}
- script Magic Load NPC_HIDDEN,{
OnInit:
end;
OnSkillInvoke:
callfunc("HUB_SkillInvoke");
end;
OnPCBonusEvent:
callfunc("HUB_PCBonus");
end;
}