// 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.
// Important Variables:
// MAGIC_EXP
// Current mana magic experience
// LAST_SKILL
// Last Mana Skill used
// MAGIC_LVL
// Maximum tier of usable magic, capped by Mana Stone
// 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;
.@dmg=calcdmg(.@src, .@target, .@type);
// Calculation FIX
if (.@type == HARM_MAGI) {
.@dmg*=2;
.@power+=(readparam2(bInt)/2);
}
// Abizit Influence (80%~130% at best, worst shot at perfect ctrl is 105%)
.@dmg = .@dmg * (80 + abizit() * rand2(5,10)) / 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);
.@bonus=rand2(0,getskilllv(TMW2_SAGE)*3/2);
if (LAST_SKILL == .@sk) {
.@pt=limit(0, (.@pt+.@bonus)/3, 1);
.@bonus=0;
} else {
// 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_RP+=1;
// Magic RP is gained by switching skills often
}
// Update Magic EXP
MAGIC_EXP=MAGIC_EXP+.@pt+.@bonus;
return;
}
// DEPRECATED: Please do not use in newer scripts.
// It is for Transmigration skill only (@sk-trans needs it)
// SkillID, Mana{, MP per level}
function script MagicCheck {
// PRE EXECUTION
// Load Variables
.@sk=getarg(0);
.@mp=getarg(1);
.@amp=getarg(2,0);
// Check Skill
if (getskilllv(.@sk) < 1)
return 0;
// Load mana cost
.@mp=.@mp+getskilllv(.@sk)*.@amp-.@amp;
// Check mana
if (readparam(Sp) < .@mp) {
dispbottom l("Insufficient mana: @@/@@.", readparam(Sp), .@mp);
return 0;
}
// Apply mana cost
heal 0, 0-.@mp;
return 1;
}
// 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) {
consolewarn "\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=rand(35,50)+.@lv*10;
// Abizit makes lifetime vary (like AdjustSpellpower)
.@lifetime = .@lifetime * (80 + abizit() * rand2(5,10)) / 100;
if (isequippedcnt(AegisShield))
.@lifetime = .@lifetime * 5 / 2;
.@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 * (80 + abizit() * rand2(5,10)) / 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, .@h, .@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, .@h, .@e);
specialeffect(FX_ATTACK, AREA, .@mbs[.@i]);
// TODO: Handle MobPt to don't overload timer system?
}
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");
.@val=MAGIC_EXP+rand(-MAGIC_LVL*5, MAGIC_LVL*5);
.@base=((MAGIC_LVL*2)**3);
if (.@val > .@base*5)
mescordialog l("You are perfectly in control of your magic."), 3, .@dial;
else if (.@val > .@base*4)
mescordialog l("You are mostly in control of your magic."), 2, .@dial;
else if (.@val > .@base*3)
mescordialog l("You are somewhat in control of your magic."), 4, .@dial;
else if (.@val > .@base*2)
mescordialog l("Your magic is more powerful than you, but you can control."), 7, .@dial;
else if (.@val > .@base)
mescordialog l("You still are overwhelmed by your magic."), 6, .@dial;
else
mescordialog l("You are completly overwhelmed by your magic."), 1, .@dial;
return;
}
// alignment_cansummon()
function script alignment_cansummon {
if (alignment() < 0 && !isequippedcnt(AegisShield)) {
if (!@hatesummon) {
dispbottom l("Nature itself express hate against you!");
getmapxy(.@m$, .@x, .@y, 0);
// FIXME: .@mob is or may be undefined
.@opo=monster(.@m$, .@x, .@y, "Failed summon", .@mob, 1);
unitattack(.@opo, getcharid(3));
@hatesummon=true;
}
return false;
}
return true;
}
// SK_summon(ID, amount, mexp)
function script SK_summon {
.@mob=getarg(0);
.@amt=getarg(1);
.@mex=getarg(2);
if ($@GM_OVERRIDE || debug) debugmes "Skill "+@skillId+" Lv "+@skillLv;
// Blocked from summoning magic
if (!alignment_cansummon())
return;
if (rand2(5) < abizit()) {
// Summon Magic (with magic level bonus)
SummonMagic(@skillId, .@mob, .@amt, MAGIC_LVL+@skillLv-1, @skillLv);
} else if (rand2(5) < abizit()) {
// Re-roll
dispbottom l("You cannot complete the casting correctly!");
SummonMagic(@skillId, .@mob, 1, 1, 1);
} else if (abizit() <= 1 && any(true, false, false)) {
// 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 (this is NOT used by Mana Stone)
GetManaExp(@skillId, .@mex);
return;
}
/////////////////////////////////////////
// RegisterMagic(MSP, Skill, MaxLv, Item, Amount, Class, Cost, {PreReq, PostReq})
function script RegisterMagic {
.@msp=getarg(0);
.@ski=getarg(1);
.@max=getarg(2);
.@ite=getarg(3);
.@amo=getarg(4);
.@cla=getarg(5);
.@cos=getarg(6);
.@pre=getarg(7, false);
.@pos=getarg(8, false);
$@MSK_MSPCOST[.@ski]=.@msp;
$@MSK_MAXLV[.@ski]=.@max;
$@MSK_ITEM[.@ski]=.@ite;
$@MSK_AMOUNT[.@ski]=.@amo;
$@MSK_COST[.@ski]=.@cos;
$@MSK_CLASS[.@ski]=.@cla;
$@MSK_PREREQ[.@ski]=.@pre;
$@MSK_POSTREQ[.@ski]=.@pos;
//array_push($@MSK_CLASS[.@cla], .@ski); // 3D Arrays are not supported
array_push($@MSK_MAGIC, .@ski);
return;
}
- script Magic Load NPC_HIDDEN,{
OnInit:
// Cleanup in case of reload
deletearray($@MSK_MSPCOST);
deletearray($@MSK_MAXLV);
deletearray($@MSK_ITEM);
deletearray($@MSK_AMOUNT);
deletearray($@MSK_COST);
deletearray($@MSK_CLASS);
deletearray($@MSK_PREREQ);
deletearray($@MSK_POSTREQ);
/* RegisterMagic(MSP, Skill, MaxLv, Item, Amount,
Class, Cost, {PreReq, PostReq}) */
// Research Points (RP) range: 100~1000 [10k ~ 1M points]
//////////////////////// Scholarship
// Mana Wisdom
RegisterMagic(1, TMW2_SAGE, 5, SpellBookPage, 1,
CLASS_SCHOLARSHIP, 25);
// Accumulate Power
RegisterMagic(1, HW_MAGICPOWER, 5, SpellBookPage, 1,
CLASS_SCHOLARSHIP, 25);
// Windwalker
RegisterMagic(2, SN_WINDWALK, 3, SpellBookPage, 1,
CLASS_SCHOLARSHIP, 25);
// Last Standing Man
RegisterMagic(2, CR_TRUST, 3, SpellBookPage, 1,
CLASS_SCHOLARSHIP, 30);
// Resurrection
RegisterMagic(3, TMW2_RESURRECT, 10, SpellBookPage, 1,
CLASS_SCHOLARSHIP, 125);
/* Skillchain */
// First Aid
RegisterMagic(1, TMW2_FIRSTAID, 10, SpellBookPage, 1,
CLASS_SCHOLARSHIP, 50, false, TMW2_HEALING);
// Healing
RegisterMagic(2, TMW2_HEALING, 10, SpellBookPage, 1,
CLASS_SCHOLARSHIP, 75, TMW2_FIRSTAID, TMW2_MAGNUSHEAL);
// Magnus Healing
RegisterMagic(3, TMW2_MAGNUSHEAL, 10, SpellBookPage, 1,
CLASS_SCHOLARSHIP, 125, TMW2_HEALING, false);
/* Skillchain */
// Provoke
RegisterMagic(1, SM_PROVOKE, 1, SpellBookPage, 1,
CLASS_SCHOLARSHIP, 25, false, EVOL_AREA_PROVOKE);
// Mass Provoke
RegisterMagic(2, EVOL_AREA_PROVOKE, 10, SpellBookPage, 1,
CLASS_SCHOLARSHIP, 30, SM_PROVOKE, false);
//////////////////////// Physical Sciences
// Ground Strike
RegisterMagic(2, TMW2_GROUNDSTRIKE, 3, FluoPowder, 3,
CLASS_PHYSICAL, 30);
/* Skillchain */
// Falkon Punch
RegisterMagic(1, TMW2_FALKONSTRIKE, 10, FluoPowder, 3,
CLASS_PHYSICAL, 25, false, TMW2_SUPREMEATTACK);
// Supreme Attack
RegisterMagic(1, TMW2_SUPREMEATTACK, 10, FluoPowder, 3,
CLASS_PHYSICAL, 25, TMW2_FALKONSTRIKE, KN_AUTOCOUNTER);
// Counter Attack
RegisterMagic(2, KN_AUTOCOUNTER, 5, FluoPowder, 3,
CLASS_PHYSICAL, 75, TMW2_SUPREMEATTACK, false);
/* Skillchain */
// Arrow Shower
RegisterMagic(3, TMW2_ARROWSHOWER, 10, FluoPowder, 3,
CLASS_PHYSICAL, 60, false, SN_SHARPSHOOTING);
// Sharpshooter
RegisterMagic(3, SN_SHARPSHOOTING, 1, FluoPowder, 3,
CLASS_PHYSICAL, 50, TMW2_ARROWSHOWER, false);
//////////////////////// Destructive Magic
// Fire Walk
RegisterMagic(2, SO_FIREWALK, 2, Quill, 1,
CLASS_DESTRUCTION, 75);
/* Skillchain */
// Fire Arrow
RegisterMagic(1, TMW2_FIREARROW, 10, Quill, 1,
CLASS_DESTRUCTION, 25, false, TMW2_FIREBALL);
// Fire Ball
RegisterMagic(2, TMW2_FIREBALL, 10, Quill, 2,
CLASS_DESTRUCTION, 50, TMW2_FIREARROW, TMW2_ARMAGEDDON);
// Armageddon
RegisterMagic(3, TMW2_ARMAGEDDON, 5, Quill, 3,
CLASS_DESTRUCTION, 75, TMW2_FIREBALL, false);
/* Skillchain */
// Napalm Beat
RegisterMagic(1, TMW2_NAPALMBEAT, 10, Quill, 1,
CLASS_DESTRUCTION, 25, false, TMW2_HOLYLIGHT);
// Holy Light
RegisterMagic(2, TMW2_HOLYLIGHT, 10, Quill, 2,
CLASS_DESTRUCTION, 50, TMW2_NAPALMBEAT, TMW2_JUDGMENT);
// Judgement
RegisterMagic(3, TMW2_JUDGMENT, 5, Quill, 3,
CLASS_DESTRUCTION, 75, TMW2_HOLYLIGHT, false);
/* Skillchain */
// Frost Diver
RegisterMagic(1, TMW2_FROSTDIVER, 10, Quill, 1,
CLASS_DESTRUCTION, 25, false, TMW2_FROSTNOVA);
// Frost Nova
RegisterMagic(2, TMW2_FROSTNOVA, 10, Quill, 2,
CLASS_DESTRUCTION, 50, TMW2_FROSTDIVER, TMW2_NILFHEIM);
// Nilfheim
RegisterMagic(3, TMW2_NILFHEIM, 5, Quill, 3,
CLASS_DESTRUCTION, 75, TMW2_FROSTNOVA, false);
/* Skillchain */
// Magic Strike
RegisterMagic(1, TMW2_MAGICSTRIKE, 10, Quill, 1,
CLASS_DESTRUCTION, 20, false, TMW2_LIGHTNINGBOLT);
// Lightning Bolt
RegisterMagic(2, TMW2_LIGHTNINGBOLT, 10, Quill, 2,
CLASS_DESTRUCTION, 45, TMW2_MAGICSTRIKE, TMW2_TEMPEST);
// Tempest
RegisterMagic(3, TMW2_TEMPEST, 5, Quill, 3,
CLASS_DESTRUCTION, 65, TMW2_LIGHTNINGBOLT, false);
/* Skillchain */
// Meteor Strike
RegisterMagic(1, TMW2_METEORSTRIKE, 10, Quill, 1,
CLASS_DESTRUCTION, 25, false, TMW2_METEORSHOWER);
// Meteor Shower
RegisterMagic(2, TMW2_METEORSHOWER, 10, Quill, 2,
CLASS_DESTRUCTION, 50, TMW2_METEORSTRIKE, TMW2_GAIABREAK);
// Gaia Break
RegisterMagic(3, TMW2_GAIABREAK, 5, Quill, 3,
CLASS_DESTRUCTION, 75, TMW2_METEORSHOWER, false);
//////////////////////// Trickmaster
/* RegisterMagic(MSP, Skill, MaxLv, Item, Amount,
Class, Cost, {PreReq, PostReq}) */
RegisterMagic(1, TMW2_MANABOMB, 10, SulfurPowder, 1,
CLASS_TRICKS, 20);
RegisterMagic(1, TF_BACKSLIDING, 1, Lockpicks, 1,
CLASS_TRICKS, 24);
RegisterMagic(1, MG_FIREWALL, 10, Lockpicks, 1,
CLASS_TRICKS, 25);
RegisterMagic(1, AC_VULTURE, 2, Lockpicks, 1,
CLASS_TRICKS, 250);
RegisterMagic(1, SA_FREECAST, 1, Lockpicks, 1,
CLASS_TRICKS, 25);
RegisterMagic(1, ALL_FULL_THROTTLE, 1, Lockpicks, 1,
CLASS_TRICKS, 25);
RegisterMagic(2, GC_DARKILLUSION, 3, Lockpicks, 1,
CLASS_TRICKS, 25);
RegisterMagic(2, NV_TRICKDEAD, 1, Lockpicks, 1,
CLASS_TRICKS, 25);
//////////////////////// Other: Summonning
//////////////////////// Other: Misc
// Charged Shot
RegisterMagic(0, TMW2_CHARGEDARROW, 10, Manapple, 1,
CLASS_OTHER, 25);
// Study
RegisterMagic(0, TMW2_STUDY, 1, Manapple, 1,
CLASS_OTHER, 400);
end;
}