// 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)/2);
.@dmg=rand2(
getunitdata(getcharid(3), UDT_MATKMIN),
getunitdata(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)/2);
.@dmg=rand2(
getunitdata(getcharid(3), UDT_ATKMIN),
getunitdata(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}}
function script SummonMagic {
.@sk=getarg(0);
.@id=getarg(1);
.@adj=getarg(2,2);
.@lv=getarg(3,getskilllv(.@sk));
if (.@adj < 1) {
debugmes "\033[31mInvalid MobPerSkillLevel for SummonMagic (.@adj): "+.@adj+"\033[0m";
dispbottom l("Invalid parameter specified, blame saulc.");
end;
}
// 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
summon("Summoned Monster", .@id, .@lifetime);
/*
.@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;
}
// 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;
}
// SK_summon(ID, amount, mexp)
function script SK_summon {
.@mob=getarg(0);
.@amt=getarg(1);
.@mex=getarg(2, 1);
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);
} else if (rand2(10) < abizit()) {
// Re-roll
dispbottom l("You cannot complete the casting correctly!");
SummonMagic(@skillId, .@mob, 1, 1, 1);
} 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;
}
// 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;
}
// mescordialog(text, color, {dialog=1})
function script mescordialog {
if (getarg(2, true))
mesc getarg(0), getarg(1);
else
dispbottom col(getarg(0), getarg(1));
return;
}
// ShowAbizit({dialog=1})
function script ShowAbizit {
.@dial=getarg(0, true);
if (.@dial)
mesn l("Current Magic Control");
// FIXME
switch (abizit()) {
case 10:
mescordialog l("Magic flows naturally from you, readily and with ease. You feel in perfect control of your magic."), 3, .@dial;
break;
case 9:
mescordialog l("You feel in almost perfect control of your magic."), 2, .@dial;
break;
case 8:
mescordialog l("You feel that you have very good control of your magic."), 2, .@dial;
break;
case 7:
mescordialog l("You feel quite in control of your magic."), 4, .@dial;
break;
case 6:
mescordialog l("You feel mostly in control of your magic."), 4, .@dial;
break;
case 5:
mescordialog l("You feel somewhat in control of your magic."), 7, .@dial;
break;
case 4:
mescordialog l("You feel you still have a few difficulties in controlling your magic."), 7, .@dial;
break;
case 3:
mescordialog l("Trying to control your magic is still rather troublesome."), 6, .@dial;
break;
case 2:
mescordialog l("You feel that you have only the bare minimum of control over your magic."), 6, .@dial;
break;
case 1:
mescordialog l("You feel quite overwhelmed by your magic, but are beginning to see patterns."), 1, .@dial;
break;
case 0:
mescordialog l("You feel completely overwhelmed by your magic."), 1, .@dial;
break;
}
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);
}
- script Magic Load NPC_HIDDEN,{
OnInit:
end;
OnSkillInvoke:
callfunc("HUB_SkillInvoke");
end;
OnPCBonusEvent:
callfunc("HUB_PCBonus");
end;
}