summaryrefslogtreecommitdiff
path: root/npc/magic
diff options
context:
space:
mode:
authorJesusaves <cpntb1@ymail.com>2021-07-07 20:36:00 -0300
committerJesusaves <cpntb1@ymail.com>2021-07-07 20:36:00 -0300
commit6967b9e0b63d19141d4f59b7933a960ac81916d0 (patch)
tree8ecd9cd2461f3598cb7faece71eb77162aa4ef2a /npc/magic
parent9877fd4ad81120af8e63e96d5d429092014dc017 (diff)
parent046c659193e1ca8bd13478678d1277df8bf9395c (diff)
downloadserverdata-6967b9e0b63d19141d4f59b7933a960ac81916d0.tar.gz
serverdata-6967b9e0b63d19141d4f59b7933a960ac81916d0.tar.bz2
serverdata-6967b9e0b63d19141d4f59b7933a960ac81916d0.tar.xz
serverdata-6967b9e0b63d19141d4f59b7933a960ac81916d0.zip
Merge branch 'master' into legacy
Override all files with 'master' version. This version is meant to preserve git history as requested by bjorn.
Diffstat (limited to 'npc/magic')
-rw-r--r--npc/magic/config.txt434
-rw-r--r--npc/magic/final.txt259
-rw-r--r--npc/magic/level0-monsterinfo.txt38
-rw-r--r--npc/magic/level0-reapercry.txt228
-rw-r--r--npc/magic/level0-wand.txt25
-rw-r--r--npc/magic/level1-aggravate.txt16
-rw-r--r--npc/magic/level1-experience.txt55
-rw-r--r--npc/magic/level1-flare-dart.txt23
-rw-r--r--npc/magic/level1-grow-plants.txt29
-rw-r--r--npc/magic/level1-lesser-heal.txt63
-rw-r--r--npc/magic/level1-magic-blade.txt27
-rw-r--r--npc/magic/level1-summon-maggots.txt15
-rw-r--r--npc/magic/level1-transmutations.txt95
-rw-r--r--npc/magic/level2-arrow-hail.txt44
-rw-r--r--npc/magic/level2-happy-curse.txt32
-rw-r--r--npc/magic/level2-lay-on-hands.txt71
-rw-r--r--npc/magic/level2-lightning-strike.txt24
-rw-r--r--npc/magic/level2-magic-knuckles.txt19
-rw-r--r--npc/magic/level2-protect.txt51
-rw-r--r--npc/magic/level2-rain.txt55
-rw-r--r--npc/magic/level2-shear.txt54
-rw-r--r--npc/magic/level2-summon-monsters.txt53
-rw-r--r--npc/magic/level2-toxic-dart.txt23
23 files changed, 1733 insertions, 0 deletions
diff --git a/npc/magic/config.txt b/npc/magic/config.txt
new file mode 100644
index 00000000..f8135b43
--- /dev/null
+++ b/npc/magic/config.txt
@@ -0,0 +1,434 @@
+// 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;
+
+ // 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;
+}
+
diff --git a/npc/magic/final.txt b/npc/magic/final.txt
new file mode 100644
index 00000000..30dce5e9
--- /dev/null
+++ b/npc/magic/final.txt
@@ -0,0 +1,259 @@
+// TMW2 scripts.
+// Authors:
+// Jesusalva
+// TMW Org.
+// Description:
+// HUB functions (Magic)
+// HUB_SkillInvoke ( )
+function script HUB_SkillInvoke {
+ // Something is... wrong
+ if (!@skillId) {
+ Exception("ILLEGAL SKILL PASSED TO HUB. It has been compromised, Jim.", RB_DEBUGMES|RB_IRCBROADCAST);
+ debugmes "Legal Caster: %s", strcharinfo(0);
+ debugmes "Effective Caster: %d", @skillCaster;
+ return;
+ }
+
+ /* *********************************************************************** */
+ // 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 && @skillId != BS_GREED)
+ Exception("System ERROR, HSI."+@skillId+" INVALID CAST (got "+@skillLv+" expected "+getskilllv(@skillId)+", sub-LC."+(getcharid(3)-2000000)+")", RB_DEBUGMES|RB_ISFATAL);
+ // You are AFK for over 3 minutes, that's crazy, disregard
+ if (checkidle() > 180)
+ return;
+
+ // Record to database
+ skillInvoke[@skillId] = skillInvoke[@skillId] + 1;
+
+ // Script-based skills
+ /* *********************************************************************** */
+ switch (@skillId) {
+ case TMW2_FAKESKILL:
+ charcommand("@refresh"); // Possibly broken on too up-to-date Herc
+ break;
+ // Level 0
+ case SKILL_CONFRINGO:
+ SK_Confringo(); break;
+ case SKILL_REAPERCRY:
+ Reapercry(); break;
+ // Level 1
+ case SKILL_ABIZIT:
+ SK_Abizit(); break;
+ case SKILL_MONSTERINFO:
+ SK_Miteyo(); break;
+ case EVOL_AREA_PROVOKE:
+ SK_Itenplz(); break;
+ case SKILL_FLAR:
+ SK_Flar(); break;
+ case SKILL_MODRIPHOO:
+ case SKILL_MODRISUMP:
+ case SKILL_MODRIYIKAM:
+ case SKILL_MODRILAX:
+ SK_GrowPlants(); break; // FIXME: Unreleased/cannot be learned
+ case SKILL_LUM:
+ SK_Lum(); break;
+ case SKILL_CHIZA:
+ SK_Chiza(); break;
+ case SKILL_PARUM:
+ case SKILL_GOLE:
+ SK_Transmute(); break;
+ // Level 2
+ case SKILL_INMA:
+ SK_Inma(); break;
+ case SKILL_JOYPLIM:
+ SK_Joyplim(); break;
+ case SKILL_CHIPCHIP:
+ SK_Shear(); break;
+ case SKILL_KALAKARENK:
+ case SKILL_KALBOO:
+ case SKILL_KALGINA:
+ case SKILL_KALRENK:
+ SK_SummonLv2(); break;
+ case SKILL_HALHISS:
+ case SKILL_HELORP:
+ SK_SummonLv2(); break;
+ case SKILL_KAFLOSH:
+ SK_Kaflosh(); break;
+ case SKILL_BETSANC:
+ SK_Betsanc(); break;
+ case SKILL_ASORM:
+ SK_Asorm(); break;
+ case SKILL_INGRAV:
+ SK_Ingrav(); break;
+ case SKILL_UPMARMU:
+ SK_Uparmu(); break;
+ case SKILL_FRILLYAR:
+ SK_Frillyar(); break;
+ case SKILL_PHLEX:
+ SK_Phlex(); break;
+ case SKILL_KULARZUFRILL:
+ case SKILL_ZUKMINBIRF:
+ case SKILL_PATMUPLOO:
+ case SKILL_PATVILOREE:
+ case SKILL_PATLOREE:
+ case SKILL_MANPAHIL:
+ SK_Transmute(); break;
+ // Level 3
+ // Level 4
+ // Level 5
+ ////////////////////////////////
+ /* XXX: Fire Class
+ // (May burn targets for damage over time)
+ case TMW2_ARMAGEDDON:
+ .@PW=140+(10*@skillLv);
+ .@RG=5+(@skillLv/5);
+ // 18% chance, 3s, 3x3 radius
+ areaharm(@skillTarget, .@RG, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Fire);
+ areasc(.@RG, 6000, SC_BLOODING, BL_MOB|BL_PC|BL_HOM|BL_MER, 1, "filter_hostile", @skillTarget, 1800);
+ GetManaExp(TMW2_FIREBALL, 3);
+ break;
+ ////////////////////////////////
+ // XXX: Earth Class
+ case TMW2_GAIABREAK:
+ .@PWA=170+(30*@skillLv);
+ .@PWB=110+(10*@skillLv);
+ .@dmg=AdjustSpellpower(.@PWA);
+ .@dsub=AdjustSpellpower(.@PWB);
+ harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Earth);
+ rectharm(@skillTarget, 2, 5, .@dsub, HARM_MAGI, Ele_Earth);
+ areasc(2, 5000, SC_INCDEFRATE, BL_PC, 10, "filter_friendly");
+ GetManaExp(TMW2_METEORSTRIKE, 3);
+ break;
+ ////////////////////////////////
+ // XXX: Physical Class (Regular)
+ case TMW2_FALKONSTRIKE:
+ .@PW=100+(25*@skillLv);
+ .@ST=0+(10*@skillLv);
+ .@TM=100+(90*@skillLv);
+ harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral);
+ sc_start SC_STUN, .@TM, 1, .@ST, SCFLAG_NONE, @skillTarget;
+ GetManaExp(@skillId, 1);
+ break;
+ case TMW2_GROUNDSTRIKE:
+ .@PW=50+(40*@skillLv);
+ .@dmg=AdjustAttackpower(.@PW);
+ .@RG=2+(@skillLv/5);
+ .@TM=100+(@skillLv*200);
+ .@ST=500+(100*@skillLv);
+ .@EF=any(SC_STUN, SC_BLIND, SC_BLOODING, SC_BLIND, SC_BLOODING);
+ areaharm(@skillTarget, .@RG, .@dmg, HARM_PHYS, Ele_Neutral);
+ areasc(.@RG, .@TM, .@EF, BL_MOB | BL_PC | BL_HOM | BL_MER, 1, "filter_hostile", @skillTarget, .@ST);
+ GetManaExp(@skillId, 1);
+ break;
+ case TMW2_SUPREMEATTACK:
+ .@PW=100+(50*@skillLv);
+ harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral);
+ GetManaExp(@skillId, 1);
+ break;
+ ////////////////////////////////
+ // Summons which never fail
+ case TMW2_ZARKOR:
+ SummonMagic(@skillId, CaveMaggot, 2, @skillLv);
+ GetManaExp(@skillId, 1);
+ break;
+ // Summons which may fail
+ case TMW2_KALWULF:
+ SK_summon(Wolvern, 4, any(3,4));
+ break;
+ // More complex summons
+ case TMW2_FROZENHEART:
+ .@mobId=Moggun;
+ if (rand2(6,12) < abizit()+1)
+ {
+ .@mobId=Yeti;
+ }
+ SK_summon(.@mobId, 4, any(3,4));
+ break;
+ case TMW2_STONEHEART:
+ .@mobId=Terranite;
+ if (rand2(9,12) < abizit()+1 &&
+ BaseLevel > 80)
+ {
+ .@mobId=TerraniteProtector;
+ }
+ SK_summon(.@mobId, 4, any(4,5));
+ break;
+ */
+
+ // Experience only
+ default:
+ GetManaExp(@skillId, 1);
+ break;
+ }
+
+ // Debug
+ if ($@GM_OVERRIDE)
+ debugmes "Cast skill %d on level %d - Target %d",
+ @skillId, @skillLv, @skillTarget;
+
+ // Cleanup (double-safe)
+ @skillTarget = 0;
+ return;
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// HUB_PCBonus ()
+function script HUB_PCBonus {
+ /* Passive Skills */
+ if (FOCUSING & FSKILL_ASTRAL_SOUL) {
+ bonus bMatkRate, 3*getskilllv(SKILL_ASTRAL_SOUL);
+ }
+ if (FOCUSING & FSKILL_RAGING) {
+ bonus bCritical, 2*getskilllv(SKILL_RAGING);
+ }
+ if (FOCUSING & FSKILL_SPEED) {
+ bonus bSpeedAddRate, 1*getskilllv(SKILL_SPEED);
+ bonus bAspd, 2*getskilllv(SKILL_SPEED);
+ }
+ if (FOCUSING & FSKILL_RESIST_POISON) {
+ .@l=5*getskilllv(SKILL_RESIST_POISON);
+ bonus2(bResEff, Eff_Silence, .@l);
+ bonus2(bResEff, Eff_Poison, .@l);
+ bonus2(bResEff, Eff_DPoison, .@l);
+ bonus2(bResEff, Eff_Curse, .@l);
+ bonus2(bResEff, Eff_Blind, .@l);
+ }
+ if (FOCUSING & FSKILL_BRAWLING) {
+ if (getequipid(EQI_HAND_R) < 1) {
+ bonus bAtk, 10*getskilllv(SKILL_BRAWLING);
+ } else if (getiteminfo(
+ getequipid(EQI_HAND_R), ITEMINFO_SUBTYPE) == W_FIST) {
+ bonus bAtk, 10*getskilllv(SKILL_BRAWLING);
+ }
+ }
+ if (FOCUSING & FSKILL_MALLARDS_EYE) {
+ bonus bAtkRange, (2+getskilllv(SKILL_MALLARDS_EYE))/3;
+ bonus bHit, 5*getskilllv(SKILL_MALLARDS_EYE);
+ }
+ return;
+}
+
diff --git a/npc/magic/level0-monsterinfo.txt b/npc/magic/level0-monsterinfo.txt
new file mode 100644
index 00000000..0a891055
--- /dev/null
+++ b/npc/magic/level0-monsterinfo.txt
@@ -0,0 +1,38 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: SKILL_MONSTERINFO (Level 1)
+// School: General 1
+
+function script SK_Miteyo {
+ .@mobId=getunitdata(@skillTarget, UDT_CLASS);
+ if (.@mobId > 1000) {
+ // Decide how much detail you get based on how much magic you have
+ if (getskilllv(SKILL_MAGIC) > 2) {
+ charcommand("@mi "+.@mobId);
+ } else {
+ .@mhp = getmonsterinfo(.@mobId, MOB_MAXHP);
+ // Truncate HP
+ if (.@mhp > 1000)
+ .@mhp -= .@mhp % 1000;
+ else if (.@mhp > 100)
+ .@mhp -= .@mhp % 100;
+ else
+ .@mhp -= .@mhp % 10;
+ // Randomize atk
+ .@atk = rand2(getmonsterinfo(.@mobId, MOB_ATK1),
+ getmonsterinfo(.@mobId, MOB_ATK2));
+ // Send the obfuscated information
+ dispbottom l("%s - Level %d (~%s HP, ~%s ATK)",
+ getmonsterinfo(.@mobId, MOB_NAME),
+ getmonsterinfo(.@mobId, MOB_LV),
+ fnum(.@mhp),
+ fnum(.@atk));
+ }
+ } else {
+ // Not a valid monster
+ dispbottom l("This is not a monster; I cannot use monsterinfo on it.");
+ }
+ return;
+}
+
diff --git a/npc/magic/level0-reapercry.txt b/npc/magic/level0-reapercry.txt
new file mode 100644
index 00000000..1b6f07d5
--- /dev/null
+++ b/npc/magic/level0-reapercry.txt
@@ -0,0 +1,228 @@
+// #reapercry
+// Spell to warp to Keshlam Outskirts
+// Variable: @reapercry -> Holds summon state
+//
+// If this was Evol2 engine, I would be able to easily use bitmasks,
+// Having a custom password sequence unique per character.
+// But alas, on TMWA, I can't really abuse player variables.
+// So I allow you to cheat. Or not. Lemme think. Account variable...
+// This is actually an old script and I'm too lazy to redo.
+
+// Returns true if player inside candle area
+// call("alacriuspos", bitwise)
+
+function script alacriuspos {
+ @alacriusp = 0;
+
+ // Left side
+ if (1 == getarg(0) && #CRYPT_PASSWORD & getarg(0))
+ goto L_25_53_29_56;
+ if (2 == getarg(0) && #CRYPT_PASSWORD & getarg(0))
+ goto L_20_47_22_51;
+ if (4 == getarg(0) && #CRYPT_PASSWORD & getarg(0))
+ goto L_22_40_25_43;
+ if (8 == getarg(0) && #CRYPT_PASSWORD & getarg(0))
+ goto L_29_40_32_43;
+ if (16 == getarg(0) && #CRYPT_PASSWORD & getarg(0))
+ goto L_32_47_35_50;
+ if (32 == getarg(0) && #CRYPT_PASSWORD & getarg(0))
+ goto L_25_53_29_56;
+ // Left not set
+ if (1 == getarg(0))
+ goto L_29_40_32_43;
+ if (2 == getarg(0))
+ goto L_32_47_35_50;
+ if (4 == getarg(0))
+ goto L_25_53_29_56;
+ if (8 == getarg(0))
+ goto L_20_47_22_51;
+ if (16 == getarg(0))
+ goto L_22_40_25_43;
+ if (32 == getarg(0))
+ goto L_29_40_32_43;
+ // Right side
+ if (64 == getarg(0) && #CRYPT_PASSWORD & getarg(0))
+ goto L_49_53_53_56;
+ if (128 == getarg(0) && #CRYPT_PASSWORD & getarg(0))
+ goto L_43_47_46_51;
+ if (256 == getarg(0) && #CRYPT_PASSWORD & getarg(0))
+ goto L_46_40_49_43;
+ if (512 == getarg(0) && #CRYPT_PASSWORD & getarg(0))
+ goto L_53_40_56_43;
+ if (1024 == getarg(0) && #CRYPT_PASSWORD & getarg(0))
+ goto L_56_47_58_50;
+ if (2048 == getarg(0) && #CRYPT_PASSWORD & getarg(0))
+ goto L_49_53_53_56;
+ // Right side not set
+ if (64 == getarg(0))
+ goto L_53_40_56_43;
+ if (128 == getarg(0))
+ goto L_56_47_58_50;
+ if (256 == getarg(0))
+ goto L_49_53_53_56;
+ if (512 == getarg(0))
+ goto L_43_47_46_51;
+ if (1024 == getarg(0))
+ goto L_46_40_49_43;
+ if (2048 == getarg(0))
+ goto L_53_40_56_43;
+
+ // Nope, all wrong
+ @alacriusp = 0;
+ return;
+
+L_29_40_32_43:
+ @alacriusp = isin("027-8", 29, 40, 32, 43);
+ return;
+L_22_40_25_43:
+ @alacriusp = isin("027-8", 22, 40, 25, 43);
+ return;
+L_20_47_22_51:
+ @alacriusp = isin("027-8", 20, 47, 22, 51);
+ return;
+L_25_53_29_56:
+ @alacriusp = isin("027-8", 25, 53, 29, 56);
+ return;
+L_32_47_35_50:
+ @alacriusp = isin("027-8", 32, 47, 35, 50);
+ return;
+
+L_53_40_56_43:
+ @alacriusp = isin("027-8", 53, 40, 56, 43);
+ return;
+L_46_40_49_43:
+ @alacriusp = isin("027-8", 46, 40, 49, 43);
+ return;
+L_43_47_46_51:
+ @alacriusp = isin("027-8", 43, 47, 46, 51);
+ return;
+L_49_53_53_56:
+ @alacriusp = isin("027-8", 49, 53, 53, 56);
+ return;
+L_56_47_58_50:
+ @alacriusp = isin("027-8", 56, 47, 58, 50);
+ return;
+
+}
+
+function script Reapercry {
+ // Not in Terogan's Room
+ if (getmapname() != "027-8") end;
+
+ // Terogan is asleep
+ if (!$@CRYPT_FIGHT3) end;
+
+ // No password is set o.o
+ if (!#CRYPT_PASSWORD) end;
+
+ // Switch the quest state
+ if (@reapercry == 0)
+ goto L_Check0;
+ if (@reapercry == 1)
+ goto L_Check1;
+ if (@reapercry == 2)
+ goto L_Check2;
+ if (@reapercry == 3)
+ goto L_Check3;
+ if (@reapercry == 4)
+ goto L_Check4;
+ if (@reapercry == 5)
+ goto L_Check5;
+
+ // Something went wrong!!
+ end;
+
+L_Check0:
+ callfunc("alacriuspos", 1);
+ if (@alacriusp) goto L_Exec0;
+ callfunc("alacriuspos", 64);
+ if (@alacriusp) goto L_Exec0;
+
+ @reapercry = 0;
+ misceffect FX_MAGIC_DARK_EXPLOSION, strcharinfo(0);
+ end;
+
+L_Exec0:
+ misceffect FX_FIRE_EXPLOSION, strcharinfo(0); // Hmm FIXME
+ @reapercry = 1;
+ end;
+
+L_Check1:
+ callfunc("alacriuspos", 2);
+ if (@alacriusp) goto L_Exec1;
+ callfunc("alacriuspos", 128);
+ if (@alacriusp) goto L_Exec1;
+
+ @reapercry = 0;
+ misceffect FX_MAGIC_DARK_EXPLOSION, strcharinfo(0);
+ end;
+
+L_Exec1:
+ misceffect FX_FIRE_EXPLOSION, strcharinfo(0); // Hmm FIXME
+ @reapercry = 2;
+ end;
+
+L_Check2:
+ callfunc("alacriuspos", 4);
+ if (@alacriusp) goto L_Exec2;
+ callfunc("alacriuspos", 256);
+ if (@alacriusp) goto L_Exec2;
+
+ @reapercry = 0;
+ misceffect FX_MAGIC_DARK_EXPLOSION, strcharinfo(0);
+ end;
+
+L_Exec2:
+ misceffect FX_FIRE_EXPLOSION, strcharinfo(0); // Hmm FIXME
+ @reapercry = 3;
+ end;
+
+L_Check3:
+ callfunc("alacriuspos", 8);
+ if (@alacriusp) goto L_Exec3;
+ callfunc("alacriuspos", 512);
+ if (@alacriusp) goto L_Exec3;
+
+ @reapercry = 0;
+ misceffect FX_MAGIC_DARK_EXPLOSION, strcharinfo(0);
+ end;
+
+L_Exec3:
+ misceffect FX_FIRE_EXPLOSION, strcharinfo(0); // Hmm FIXME
+ @reapercry = 4;
+ end;
+
+L_Check4:
+ callfunc("alacriuspos", 16);
+ if (@alacriusp) goto L_Exec4;
+ callfunc("alacriuspos", 1024);
+ if (@alacriusp) goto L_Exec4;
+
+ @reapercry = 0;
+ misceffect FX_MAGIC_DARK_EXPLOSION, strcharinfo(0);
+ end;
+
+L_Exec4:
+ misceffect FX_FIRE_EXPLOSION, strcharinfo(0); // Hmm FIXME
+ @reapercry = 5;
+ end;
+
+L_Check5:
+ callfunc("alacriuspos", 32);
+ if (@alacriusp) goto L_Exec5;
+ callfunc("alacriuspos", 2048);
+ if (@alacriusp) goto L_Exec5;
+
+ @reapercry = 0;
+ misceffect FX_MAGIC_DARK_EXPLOSION, strcharinfo(0);
+ end;
+
+L_Exec5:
+ // This means cast sequence complete
+ // Maybe FX_MAGIC_BLUE_TELEPORT? (iilia's effect)
+ @reapercry = 0;
+ getexp 100000, 0;
+ misceffect FX_MAGIC_TELEPORT, strcharinfo(0);
+ warp "099-7", 75, 36;
+ end;
+}
diff --git a/npc/magic/level0-wand.txt b/npc/magic/level0-wand.txt
new file mode 100644
index 00000000..1c3d7b2b
--- /dev/null
+++ b/npc/magic/level0-wand.txt
@@ -0,0 +1,25 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: SKILL_CONFRINGO (Level 1)
+// School: General 0
+
+function script SK_Confringo {
+ // Additional checks
+ if (QL_MORGAN == 2)
+ set QL_MORGAN, 3;
+ // Effective magic code
+ .@PW=90+(7*@skillLv);
+ .@dmg=AdjustSpellpower(.@PW);
+ .@max=(1+getskilllv(SKILL_MAGIC))*100;
+ // Noobs adjustment
+ if (.@dmg < readparam(bInt))
+ .@dmg=rand2(.@dmg, readparam(bInt));
+ // Pro adjustment
+ if (.@dmg > .@max)
+ .@dmg = .@max + ((.@dmg - .@max) * 30 / 100);
+ harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Neutral);
+ return;
+}
+
+
diff --git a/npc/magic/level1-aggravate.txt b/npc/magic/level1-aggravate.txt
new file mode 100644
index 00000000..c97d8b47
--- /dev/null
+++ b/npc/magic/level1-aggravate.txt
@@ -0,0 +1,16 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: EVOL_AREA_PROVOKE (Level 1)
+// School: Nature 1
+
+function script SK_Itenplz {
+ /*
+ if (@skillTargetX && @skillTargetY)
+ massprovoke(1+@skillLv, getmap(), @skillTargetX, @skillTargetY);
+ else
+ */
+ massprovoke(1+@skillLv);
+ return;
+}
+
diff --git a/npc/magic/level1-experience.txt b/npc/magic/level1-experience.txt
new file mode 100644
index 00000000..7435fb71
--- /dev/null
+++ b/npc/magic/level1-experience.txt
@@ -0,0 +1,55 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: SKILL_ABIZIT (Level 1)
+// School: General 1
+
+// ShowAbizit({dialog=true})
+function script ShowAbizit {
+ .@dial=getarg(0, true);
+ if (.@dial)
+ mesn l("Current Magic Control");
+
+ 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;
+}
+
+function script SK_Abizit {
+ ShowAbizit(false);
+ return;
+}
+
diff --git a/npc/magic/level1-flare-dart.txt b/npc/magic/level1-flare-dart.txt
new file mode 100644
index 00000000..2446fa9c
--- /dev/null
+++ b/npc/magic/level1-flare-dart.txt
@@ -0,0 +1,23 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: SKILL_FLAR (Level 1)
+// School: War 1
+
+function script SK_Flar {
+ // Charge code, item check is in skill_db.conf (FIXME)
+ mcharge(SulphurPowder, SKILL_MAGIC_WAR, 1);
+ // Effective magic code
+ .@PW=70+(5*@skillLv);
+ // Weather modifiers
+ if ("#WeatherCore"::weather(MASK_RAIN))
+ .@PW-=10;
+ if ("#WeatherCore"::weather(MASK_SANDSTORM))
+ .@PW+=10;
+ // Calculate real damage
+ .@dmg=AdjustSpellpower(.@PW);
+ harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Fire);
+ GetManaExp(@skillId, 1);
+ return;
+}
+
diff --git a/npc/magic/level1-grow-plants.txt b/npc/magic/level1-grow-plants.txt
new file mode 100644
index 00000000..66e105b4
--- /dev/null
+++ b/npc/magic/level1-grow-plants.txt
@@ -0,0 +1,29 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: Multiple (Level 1)
+// School: Nature 1
+
+function script SK_GrowPlants {
+ // Setup
+ switch (@skillId) {
+ case SKILL_MODRIPHOO:
+ .@it = AlizarinHerb; .@mobId = AlizarinPlant; break;
+ case SKILL_MODRISUMP:
+ .@it = CobaltHerb; .@mobId = CobaltPlant; break;
+ case SKILL_MODRIYIKAM:
+ .@it = GambogeHerb; .@mobId = GambogePlant; break;
+ case SKILL_MODRILAX:
+ .@it = MauveHerb; .@mobId = MauvePlant; break;
+ default: return;
+ }
+ if (getarg(0, true)) {
+ // Consume reagents
+ delitem Root, 1;
+ delitem .@it, 1;
+ }
+ // Continue but with a special flag
+ SK_summon(.@mobId, 2, 1, false);
+ return;
+}
+
diff --git a/npc/magic/level1-lesser-heal.txt b/npc/magic/level1-lesser-heal.txt
new file mode 100644
index 00000000..b65f9625
--- /dev/null
+++ b/npc/magic/level1-lesser-heal.txt
@@ -0,0 +1,63 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: SKILL_LUM (Level 1)
+// School: Life 1
+
+function script SK_Lum {
+ // In some cases it is... aborted
+ if (getunittype(@skillTarget) == UNITTYPE_PC) {
+ .@me=getcharid(3);
+ .@ok=true;
+ attachrid(@skillTarget);
+ // Kill the GM Event
+ if (isequipped(MagicGMTopHat))
+ .@ok=false;
+ // Ailments cannot be bypassed nor healed until Lv 3 Life Magic
+ if (getstatus(SC_BLOODING) ||
+ getstatus(SC_HALT_REGENERATION) ||
+ getstatus(SC_CURSE))
+ .@ok=false;
+ if (getstatus(SC_POISON) && !getstatus(SC_SLOWPOISON))
+ .@ok=false;
+ if (getstatus(SC_DPOISON) && !getstatus(SC_SLOWPOISON))
+ .@ok=false;
+ // Already dead
+ if (Hp < 1)
+ .@ok=false;
+ // Finished
+ .@limit=MaxHp-Hp;
+ detachrid();
+ attachrid(.@me);
+ if (!.@ok) return;
+ } else {
+ if (getunitdata(@skillTarget, UDT_HP) < 1) return;
+ .@limit=getunitdata(@skillTarget, UDT_MAXHP)-
+ getunitdata(@skillTarget, UDT_HP);
+ }
+
+ // No need for healing? Otherwise, take reagent
+ if (.@limit <= 0) return;
+ delitem Lifestone, 1;
+
+ // Real healing happens here
+ .@PW=60+(10*@skillLv);
+ .@dmg=AdjustSpellpower(.@PW);
+ sc_start(SC_M_LIFEPOTION, 5000, 1+max(0, .@dmg/5), 10000,
+ SCFLAG_NOAVOID|SCFLAG_FIXEDTICK|SCFLAG_FIXEDRATE, @skillTarget);
+ specialeffect(FX_MAGIC_WHITE, AREA, @skillTarget);
+ if (@skillTarget != getcharid(3))
+ specialeffect(FX_MAGIC_WHITE, AREA, getcharid(3));
+
+ // Specifics
+ if (getskilllv(SKILL_MAGIC_DARK) >= 1)
+ SC_Bonus(2, SC_HALT_REGENERATION, 1);
+
+ // Gives EXP according to how much you healed
+ if (@skillTarget != getcharid(3))
+ setq2(MagicQuest_Healing, getq2(MagicQuest_Healing)+1);
+ getexp min(.@dmg, .@limit)*getskilllv(SKILL_MAGIC_LIFE), .@PW/10;
+ GetManaExp(@skillId, 1);
+ return;
+}
+
diff --git a/npc/magic/level1-magic-blade.txt b/npc/magic/level1-magic-blade.txt
new file mode 100644
index 00000000..ae98d4c4
--- /dev/null
+++ b/npc/magic/level1-magic-blade.txt
@@ -0,0 +1,27 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: SKILL_CHIZA (Level 1)
+// School: War 1
+
+function script SK_Chiza {
+ // Charge code, item check is here =( FIXME
+ if (countitem(Dagger)) {
+ mcharge(Dagger, SKILL_MAGIC_WAR, 8); .@PW=100;
+ } else if (countitem(SharpKnife)) {
+ mcharge(SharpKnife, SKILL_MAGIC_WAR, 6); .@PW=95;
+ } else if (countitem(Knife)) {
+ mcharge(Knife, SKILL_MAGIC_WAR, 4); .@PW=90;
+ } else {
+ dispbottom b("Chiza: ")+l("You need a Dagger, Sharp Knife or Knife to use!");
+ return;
+ }
+ .@PW+=(10*@skillLv);
+ // Effective magic code
+ .@dmg=(AdjustSpellpower(.@PW)+AdjustAttackpower(.@PW))/2;
+ harm(@skillTarget, .@dmg, HARM_PHYS, Ele_Neutral);
+ GetManaExp(@skillId, 1);
+ return;
+}
+
+
diff --git a/npc/magic/level1-summon-maggots.txt b/npc/magic/level1-summon-maggots.txt
new file mode 100644
index 00000000..7ca9f74d
--- /dev/null
+++ b/npc/magic/level1-summon-maggots.txt
@@ -0,0 +1,15 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: SKILL_KALMURK (Level 1)
+// School: Astral 1
+
+function script SK_Kalmurk {
+ delitem Root, 1;
+ delitem .@it, 1;
+ specialeffect(FX_MAGIC_MAGGOT_CAST, AREA, getcharid(3));
+ sleep2(cap_value(5400-AdjustSpellpower(100)-@skillLv*400, 500, 5000));
+ SK_summon(Maggot, 2, 1);
+ return;
+}
+
diff --git a/npc/magic/level1-transmutations.txt b/npc/magic/level1-transmutations.txt
new file mode 100644
index 00000000..88e916fc
--- /dev/null
+++ b/npc/magic/level1-transmutations.txt
@@ -0,0 +1,95 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: MULTIPLE (Level 1)
+// School: Transmutation *
+
+function script SK_Transmute {
+ // Setup
+ switch (@skillId) {
+ case SKILL_PARUM:
+ .@prize = MoubooFigurine; .@pc = 1;
+ .@mexp = 1; .@fx = FX_MAGIC_WOOD_CAST;
+ setarray .@component, RawLog;
+ setarray .@co_amount, 1;
+ setarray .@failure, Iten, WarpedLog, WarpedLog;
+ break;
+ case SKILL_KULARZUFRILL:
+ .@prize = Arrow; .@pc = AdjustSpellpower(40+@skillLv*2);
+ .@mexp = 2; .@fx = FX_MAGIC_ARROW_CAST;
+ setarray .@component, RawLog;
+ setarray .@co_amount, 1;
+ setarray .@failure, WarpedLog, WarpedLog;
+ break;
+ case SKILL_ZUKMINBIRF:
+ .@prize = IronPowder; .@pc = 1+AdjustSpellpower(@skillLv*5);
+ .@mexp = 2; .@fx = FX_MAGIC_IRON_CAST;
+ setarray .@component, IronOre;
+ setarray .@co_amount, 1;
+ setarray .@failure, Iten, IronOre, IronOre;
+ break;
+ case SKILL_PATMUPLOO:
+ .@prize = CottonShirt; .@pc = 1;
+ .@mexp = 2; .@fx = FX_MAGIC_SHIRT_CAST;
+ setarray .@component, CottonCloth;
+ setarray .@co_amount, 5;
+ setarray .@failure, CottonCloth, CottonCloth;
+ break;
+ case SKILL_PATVILOREE:
+ .@prize = ShortTankTop; .@pc = 1;
+ .@mexp = 2; .@fx = FX_MAGIC_SHIRT_CAST;
+ setarray .@component, CottonCloth;
+ setarray .@co_amount, 3;
+ setarray .@failure, CottonCloth, CottonCloth;
+ break;
+ case SKILL_PATLOREE:
+ .@prize = TankTop; .@pc = 1;
+ .@mexp = 2; .@fx = FX_MAGIC_SHIRT_CAST;
+ setarray .@component, CottonCloth;
+ setarray .@co_amount, 4;
+ setarray .@failure, CottonCloth, CottonCloth;
+ break;
+ case SKILL_GOLE:
+ .@prize = SulphurPowder; .@pc = 1+AdjustSpellpower(@skillLv*20);
+ .@mexp = 1; .@fx = FX_MAGIC_SULPHUR_CAST;
+ setarray .@component, PileOfAsh;
+ setarray .@co_amount, 1;
+ setarray .@failure, PileOfAsh, PileOfAsh;
+ break;
+ case SKILL_MANPAHIL:
+ .@prize = Lifestone; .@pc = 10;
+ .@mexp = 2; .@fx = FX_MAGIC_STONE_CAST;
+ setarray .@component, BugLeg, MaggotSlime, MauveHerb, AlizarinHerb, CobaltHerb, GambogeHerb;
+ setarray .@co_amount, 1, 1, 1, 1, 1, 1;
+ setarray .@failure, Lifestone, Lifestone;
+ break;
+ default: return;
+ }
+ // Check for items
+ for (.@i=0; .@i < getarraysize(.@component); .@i++) {
+ if (countitem(.@component[.@i]) < .@co_amount[.@i]) {
+ dispbottom l("You do not have enough %s (min %d)",
+ getitemname(.@component[.@i]), .@co_amount[.@i]);
+ return;
+ }
+ }
+ // Delete reagents
+ inventoryplace Iten, 1, .@prize, .@pc;
+ for (.@i=0; .@i < getarraysize(.@component); .@i++) {
+ delitem(.@component[.@i], .@co_amount[.@i]);
+ }
+ // Effect and EXP
+ specialeffect(.@fx, AREA, getcharid(3));
+ GetManaExp(@skillId, .@mexp);
+ // The chances of success are based on magic exp, abizit and skill level
+ .@rand100 = abizit()*10 + cap_value(MAGIC_EXP/100, 0, 50) + (@skillLv*5);
+ if (.@rand100 > rand2(100)) {
+ getitem .@prize, .@pc;
+ } else {
+ dispbottom l("Your magic takes a mind of its own!");
+ getitem any_of(.@failure), 1;
+ }
+ return;
+}
+
+
diff --git a/npc/magic/level2-arrow-hail.txt b/npc/magic/level2-arrow-hail.txt
new file mode 100644
index 00000000..217f7fb0
--- /dev/null
+++ b/npc/magic/level2-arrow-hail.txt
@@ -0,0 +1,44 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: SKILL_FRILLYAR (Level 1)
+// School: War 2
+
+function script SK_Frillyar {
+ // Additional check for ammo
+ .@it=getequipid(EQI_AMMO);
+ if (.@it < 1) {
+ dispbottom l("Please equip your ammo first!");
+ return;
+ }
+ if (countitem(.@it) < 15) {
+ dispbottom l("You need at least %d %s to use this spell!",
+ 15, getitemname(.@it));
+ return;
+ }
+ // Delete items
+ delitem(SulphurPowder, 1);
+ delitem(.@it, 15);
+ // Proccess attack power
+ .@RG=4+(@skillLv/2);
+ .@PW=90+(10*@skillLv);
+ .@PW+=getiteminfo(.@it, ITEMINFO_ATK);
+ // Weather modifiers
+ if ("#WeatherCore"::weather(MASK_RAIN))
+ .@PW-=10;
+ if ("#WeatherCore"::weather(MASK_SANDSTORM))
+ .@PW-=10;
+ if ("#WeatherCore"::weather(MASK_SNOW))
+ .@PW-=10;
+ // Effective magic code
+ .@dmg=AdjustSpellpower(.@PW);
+ .@dmg+=getiteminfo(.@it, ITEMINFO_ATK);
+ specialeffect(FX_MAGIC_AHAIL_CAST, AREA, @skillTarget);
+ areaharm(@skillTarget, .@RG, .@dmg, HARM_PHYS, Ele_Neutral);
+ harm(@skillTarget, .@dmg/20, HARM_MAGI, Ele_Holy);
+ specialeffect(FX_ARROW_HAIL, AREA, @skillTarget);
+ GetManaExp(@skillId, 2);
+ return;
+}
+
+
diff --git a/npc/magic/level2-happy-curse.txt b/npc/magic/level2-happy-curse.txt
new file mode 100644
index 00000000..1e1096a1
--- /dev/null
+++ b/npc/magic/level2-happy-curse.txt
@@ -0,0 +1,32 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: SKILL_JOYPLIM (Level 1)
+// School: General 2
+
+function script SK_Joyplim {
+ if (getarg(0, true))
+ delitem GingerBreadMan, 1;
+
+ .@b=(BL_PC | BL_PET | BL_MER | BL_HOM); // | BL_MOB
+ .@r=3+cap_value(getskilllv(SKILL_MAGIC)+(@skillLv/2), 1, 12); // Your radius
+ .@s=AdjustSpellpower(100); // Your magical stamina
+ .@c=cap_value(11-@skillLv, 1, 10)*2; // MATK stamina cost per char
+ .@e=(getskilllv(SKILL_MAGIC_DARK) ? E_EVIL : E_HAPPY); // Emote
+
+ getmapxy(.@m$, .@x, .@y, 0);
+
+ // TODO: Maybe put this loop in a timer, etc? But... Do we want to?
+ .@c=getunits(.@b, .@mbs, false, .@m$, .@x-.@r, .@y-.@r, .@x+.@r, .@y+.@r);
+ for (.@i = 0; .@i < .@c; .@i++) {
+ unitemote(.@mbs[.@i], E_HAPPY);
+ // Stamina cost
+ .@s-=.@c;
+ if (.@s < 0)
+ break;
+ }
+ specialeffect(FX_MAGIC_JOY_CAST, AREA, getcharid(3));
+ GetManaExp(@skillId, 2);
+ return;
+}
+
diff --git a/npc/magic/level2-lay-on-hands.txt b/npc/magic/level2-lay-on-hands.txt
new file mode 100644
index 00000000..1d515324
--- /dev/null
+++ b/npc/magic/level2-lay-on-hands.txt
@@ -0,0 +1,71 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: SKILL_INMA (Level 1)
+// School: Life 2
+
+function script SK_Inma {
+ // FIXME: #inma Mouboo
+ // In some cases it is... aborted
+ if (getunittype(@skillTarget) == UNITTYPE_PC) {
+ .@me=getcharid(3);
+ .@ok=true;
+ attachrid(@skillTarget);
+ // TODO: detect if is a bot...
+ // FIXME: 099-4 and 099-5 special rules
+ // Kill the GM Event
+ if (isequipped(MagicGMTopHat))
+ .@ok=false;
+ // Ailments which prevent inma from working
+ if (getstatus(SC_BLOODING) ||
+ getstatus(SC_HALT_REGENERATION) ||
+ getstatus(SC_CURSE))
+ .@ok=false;
+ if (getstatus(SC_POISON) && !getstatus(SC_SLOWPOISON))
+ .@ok=false;
+ if (getstatus(SC_DPOISON) && !getstatus(SC_SLOWPOISON))
+ .@ok=false;
+ // Already dead
+ if (Hp < 1)
+ .@ok=false;
+ // Finished
+ .@limit=MaxHp-Hp;
+ detachrid();
+ attachrid(.@me);
+ if (!.@ok) return;
+ } else {
+ if (getunitdata(@skillTarget, UDT_HP) < 1) return;
+ .@limit=getunitdata(@skillTarget, UDT_MAXHP)-
+ getunitdata(@skillTarget, UDT_HP);
+ }
+ if (@skillTarget == getcharid(3)) return; // No self casting
+ if (isequipped(MagicGMTopHat)) return; // Kill the GM event
+ if (.@limit <= 0) return; // No need for healing
+ // Apply effects
+ .@PW=60+(20*@skillLv);
+ .@dmg=AdjustSpellpower(.@PW);
+
+ // Capped to what you need or your own health - the smallest of them
+ .@dmg = min(.@dmg, .@limit, Hp);
+
+ // Pay with 20% of your healing total
+ heal -(.@dmg/5), 0;
+
+ // Heal the target instantly
+ harm(@skillTarget, -(.@dmg), HARM_MISC);
+ specialeffect(FX_MAGIC_WHITE, AREA, @skillTarget);
+ specialeffect(FX_MAGIC_WHITE, AREA, getcharid(3));
+
+ // Specifics
+ if (getskilllv(SKILL_MAGIC_DARK) >= 2)
+ SC_Bonus(10, SC_HALT_REGENERATION, 1);
+ else
+ SC_Bonus(5, SC_HALT_REGENERATION, 1);
+
+ // Gives EXP according to how much you healed
+ setq2(MagicQuest_Healing, getq2(MagicQuest_Healing)+1);
+ getexp .@dmg*getskilllv(SKILL_MAGIC_LIFE), .@PW/10;
+ GetManaExp(@skillId, 2);
+ return;
+}
+
diff --git a/npc/magic/level2-lightning-strike.txt b/npc/magic/level2-lightning-strike.txt
new file mode 100644
index 00000000..97692d3e
--- /dev/null
+++ b/npc/magic/level2-lightning-strike.txt
@@ -0,0 +1,24 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: SKILL_INGRAV (Level 1)
+// School: War 2
+
+function script SK_Ingrav {
+ // This one time, effects will come before
+ specialeffect(FX_MAGIC_BOLT_CAST, AREA, getcharid(3));
+ specialeffect(FX_LIGHTNING1+rand2(3), AREA, @skillTarget);
+ // Charge code, item check is in skill_db.conf (FIXME)
+ mcharge(IronPowder, SKILL_MAGIC_WAR, 1);
+ .@PW=110+(20*@skillLv);
+ // Weather modifiers
+ .@dmg=AdjustSpellpower(.@PW);
+ if ("#WeatherCore"::weather(MASK_RAIN))
+ harm(getcharid(3), .@dmg*2/3, HARM_MAGI, Ele_Nature);
+ // Effective magic code
+ harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Nature);
+ GetManaExp(@skillId, 2);
+ return;
+}
+
+
diff --git a/npc/magic/level2-magic-knuckles.txt b/npc/magic/level2-magic-knuckles.txt
new file mode 100644
index 00000000..d14c8981
--- /dev/null
+++ b/npc/magic/level2-magic-knuckles.txt
@@ -0,0 +1,19 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: SKILL_UPMARMU (Level 1)
+// School: War 2
+
+function script SK_Uparmu {
+ // Charge code, item check is in skill_db.conf (FIXME)
+ mcharge(Beer, SKILL_MAGIC_WAR, 5);
+ .@PW=100+(10*@skillLv);
+ // TODO: What's the diff from Chiza?
+ // Effective magic code
+ .@dmg=AdjustSpellpower(.@PW);
+ harm(@skillTarget, .@dmg, HARM_PHYS, Ele_Neutral);
+ GetManaExp(@skillId, 2);
+ return;
+}
+
+
diff --git a/npc/magic/level2-protect.txt b/npc/magic/level2-protect.txt
new file mode 100644
index 00000000..36e25b45
--- /dev/null
+++ b/npc/magic/level2-protect.txt
@@ -0,0 +1,51 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: Betsanc and Asorm (Level 1)
+// School: Nature/Astral 2
+
+function script SK_Betsanc {
+ // party-guild filter
+ if (!filter_sameguildorparty(@skillTarget)) {
+ dispbottom b("Betsanc: ")+l("Skill can only be cast on party or guild members!");
+ return;
+ }
+ // no GM Hat/Bots? Not needed due filter?
+ delitem HardSpike, 1;
+ // Same duration as Kaflosh
+ .@PW=80+(20*@skillLv);
+ .@dmg=AdjustSpellpower(.@PW);
+ .@time=5+.@dmg/11;
+ .@PX=10+cap_value(.@dmg/33, 0, 20);
+ // SC, Time, DEF+, ASPD-
+ sc_start2(SC_PHYSICAL_SHIELD, .@time*1000, .@PX*2, .@PX, 10000,
+ SCFLAG_NOAVOID|SCFLAG_FIXEDTICK|SCFLAG_FIXEDRATE, @skillTarget);
+ specialeffect FX_MAGIC_SHIELD_CAST, AREA, @skillTarget;
+ GetManaExp(@skillId, 2);
+ return;
+}
+
+function script SK_Asorm {
+ // party-guild filter
+ if (!filter_sameguildorparty(@skillTarget)) {
+ dispbottom b("Betsanc: ")+l("Skill can only be cast on party or guild members!");
+ return;
+ }
+ // no GM Hat/Bots? Not needed due filter?
+ delitem SmallMushroom, 1;
+ // Same duration as Kaflosh
+ .@PW=80+(20*@skillLv);
+ .@dmg=AdjustSpellpower(.@PW);
+ .@time=5+.@dmg/11;
+ .@PX=10+cap_value(.@dmg/33, 0, 20);
+ // SC_STONESKIN(??, def, mdef); Usually for mobs. Skill NPC_ANTIMAGIC
+ // SC_FREYJASCROLL(MDEF, PerfectFlee)
+ // SC_MDEFSET(MDEF)
+ // SC, Time, MDEF+
+ sc_start(SC_MDEFSET, .@time*1000, .@PX*2, 10000,
+ SCFLAG_NOAVOID|SCFLAG_FIXEDTICK|SCFLAG_FIXEDRATE, @skillTarget);
+ specialeffect FX_MAGIC_BARRIER_CAST, AREA, @skillTarget;
+ GetManaExp(@skillId, 2);
+ return;
+}
+
diff --git a/npc/magic/level2-rain.txt b/npc/magic/level2-rain.txt
new file mode 100644
index 00000000..30171c06
--- /dev/null
+++ b/npc/magic/level2-rain.txt
@@ -0,0 +1,55 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: SKILL_KAFLOSH (Level 1)
+// School: Nature 2
+
+function script SK_Kaflosh {
+ .@m$=getmap();
+ .@cl="#WeatherCore"::climate(.@m$);
+
+ // Bad Climate
+ if (.@cl == CLIMATE_NONE) {
+ dispbottom l("It was impossible to conjure rain clouds on this map.");
+ return;
+ }
+
+ // Techinically not needed
+ .@rain="#WeatherCore"::weather(MASK_RAIN);
+ if (.@rain) {
+ dispbottom l("It is already raining!");
+ return;
+ }
+
+ // From now on, you'll have the cost paid and the MEXP deposited
+ delitem BottleOfWater, 1;
+ GetManaExp(@skillId, 2);
+
+ // Adjusted MATK will determine rain duration (11 ATK = 1s)
+ .@PW=80+(20*@skillLv);
+ .@dmg=AdjustSpellpower(.@PW);
+ .@time=.@dmg/11;
+ //debugmes "KAFLOSH: Damage %d Time %d Climate %d", .@dmg, .@time, .@cl;
+ .@time+=getskilllv(SKILL_MAGIC_NATURE)-2; // +1 second per nature magic lv
+
+ // Adverse climate (eg. desert and icelands) will cut this to 1/3
+ if (.@cl != CLIMATE_MODERATE)
+ .@time=.@time/3;
+
+ // Rain must last at least 5 seconds or the clouds won't even gather
+ if (.@time < 5) {
+ dispbottom l("You do not have suffice magic power to make rain.");
+ return;
+ }
+
+ // Invoke the rain clouds; Everything else should work out of the box
+ // The builtin checks are not necessary in this case =D
+ // But would be for a sandstorm or snowstorm.
+ "#WeatherCore"::weather_override(MASK_RAIN, .@time, .@m$, true);
+
+ // Eventually, a quest or another will depend on Kaflosh, so
+ if (isin("006-1", 82, 59, 14))
+ callfunc "QuestTreeTrigger", 1;
+ return;
+}
+
diff --git a/npc/magic/level2-shear.txt b/npc/magic/level2-shear.txt
new file mode 100644
index 00000000..f848b36d
--- /dev/null
+++ b/npc/magic/level2-shear.txt
@@ -0,0 +1,54 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: SKILL_CHIPCHIP (Level 1)
+// School: Nature 2
+
+function script SK_Shear {
+ .@mobGD=getarg(0, @skillTarget);
+ if (.@mobGD <= 0)
+ return;
+
+ // We only want monsters
+ if (getunittype(.@mobGD) != UNITTYPE_MOB) {
+ dispbottom l("This skill can only be used on monsters!");
+ return;
+ }
+
+ // Global data
+ setarray .@valid, Fluffy, EasterFluffy, SpikyMushroom, Mouboo, MauvePlant, CobaltPlant, GambogePlant, AlizarinPlant, Silkworm, Pinkie;
+ setarray .@prize, WhiteFur, WhiteFur, HardSpike, CottonCloth, MauveHerb, CoblatHerb, GambogeHerb, AlizarinHerb, SilkCocoon, PinkAntenna;
+ setarray .@score, 300, 300, 250, 175, 700, 700, 700, 700, 300, 180;
+
+ // Specific data
+ .@mobID=getunitdata(.@mobGD, UDT_CLASS);
+ .@matk=AdjustSpellpower(40+(10*@skillLv));
+ .@idx=array_find(.@valid, .@mobID);
+
+ // Invalid target
+ if (.@idx < 0) return;
+
+ // Not yet sheared
+ if (array_rfind(@shear, .@mobGD) < 0) {
+ array_push(@shear, @mobGD);
+ if (.@matk > .@score[.@idx])
+ getitem .@prize[.@idx], 1;
+ }
+
+ // Sagratha bonus
+ if (.@mobId == Fluffy || .@mobId == Mouboo || .@mobId == Pinkie)
+ QuestSagathaHappy(any(true, true, false));
+
+ // Special effect
+ specialeffect(FX_MAGIC_SHEAR_CAST, AREA, getcharid(3));
+ specialeffect(FX_MAGIC_SHEAR_CAST, AREA, .@mobGD);
+
+ // Truncate.
+ // We're saving the GID so it must be "big enough"
+ // But not too big so rfind() is not expensive
+ if (getarraysize(@study) > 99) {
+ deletearray(@study, 30);
+ }
+ return;
+}
+
diff --git a/npc/magic/level2-summon-monsters.txt b/npc/magic/level2-summon-monsters.txt
new file mode 100644
index 00000000..9c28e639
--- /dev/null
+++ b/npc/magic/level2-summon-monsters.txt
@@ -0,0 +1,53 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: Multiple (Level 1)
+// School: Astral/Dark 2
+
+// SK_SummonLv2()
+function script SK_SummonLv2 {
+ // Setup
+ switch (@skillId) {
+ case SKILL_KALAKARENK:
+ .@it = WhiteFur; .@mobId = Fluffy; .@am = 3; .@cl = Root;
+ .@fx1 = FX_MAGIC_FLUFFY_CAST; .@fx2 = FX_MAGIC_FLUFFY_SPAWN; break;
+ case SKILL_KALBOO:
+ .@it = MoubooFigurine; .@mobId = Mouboo; .@am = 4; .@cl = Root;
+ .@fx1 = FX_MAGIC_MOUBOO_CAST; .@fx2 = FX_MAGIC_MOUBOO_SPAWN; break;
+ case SKILL_KALGINA:
+ .@it = PinkAntenna; .@mobId = Pinkie; .@am = 2; .@cl = Root;
+ .@fx1 = FX_MAGIC_PINKY_CAST; .@fx2 = FX_MAGIC_PINKY_SPAWN; break;
+ case SKILL_KALRENK:
+ .@it = HardSpike; .@mobId = SpikyMushroom; .@am = 2; .@cl = Root;
+ .@fx1 = FX_MAGIC_SPIKY_CAST; .@fx2 = FX_MAGIC_SPIKY_SPAWN; break;
+
+ // Should non-astral magic be here?
+ case SKILL_HALHISS:
+ .@it = SnakeEgg; .@mobId = Snake; .@am = 4; .@cl = DarkCrystal;
+ .@fx1 = FX_MAGIC_SNAKE_CAST; .@fx2 = FX_MAGIC_SNAKE_SPAWN; break;
+ case SKILL_HELORP:
+ .@it = SmallMushroom; .@am = 3; .@cl = DarkCrystal;
+ .@mobId = any(WickedMushroom, WickedMushroom, WickedMushroom,
+ WickedMushroom, WickedMushroom, WickedMushroom,
+ EvilMushroom, Moonshroom, Moonshroom, Moonshroom);
+ .@fx1 = FX_MAGIC_WICKED_CAST; .@fx2 = FX_MAGIC_WICKED_SPAWN; break;
+
+ default: return;
+ }
+ // Consume reagents
+ delitem .@cl, 1;
+ delitem .@it, 1;
+ // Estimate the cast time
+ if (.@cl == Root)
+ .@ct = 400+rand2(1001-getskilllv(MAGIC_SKILL_ASTRAL)*100);
+ else if (.@cl == DarkCrystal)
+ .@ct = 400+rand2(1001-getskilllv(MAGIC_SKILL_DARK)*100);
+ // Summon the monsters (or fail trying to)
+ specialeffect(.@fx1, AREA, getcharid(3));
+ sleep2(.@ct);
+ SK_summon(.@mobId, .@am, 2);
+ specialeffect(.@fx2, AREA, getcharid(3));
+ return;
+}
+
+
diff --git a/npc/magic/level2-toxic-dart.txt b/npc/magic/level2-toxic-dart.txt
new file mode 100644
index 00000000..df21b89c
--- /dev/null
+++ b/npc/magic/level2-toxic-dart.txt
@@ -0,0 +1,23 @@
+// The Mana World script
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Magic Script: SKILL_PHLEX (Level 1)
+// School: Dark 2
+
+function script SK_Phlex {
+ // Charge code, item check is in skill_db.conf (FIXME)
+ mcharge(Root, SKILL_MAGIC_DARK, 1);
+ .@PW=100+(10*@skillLv);
+ // Effective magic code
+ .@dmg=AdjustSpellpower(.@PW);
+ harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Dark);
+ // May inflict poison
+ .@sc=(getskilllv(SKILL_MAGIC_DARK) > 3 ? SC_DPOISON : SC_POISON);
+ .@time=(1+.@dmg/33)*1000;
+ .@cth=1+rand2(.@time)/15;
+ sc_start .@sc, .@time, 1, .@cth, SCFLAG_NONE, @skillTarget;
+ GetManaExp(@skillId, 2);
+ return;
+}
+
+