// TMW2 Scripts
// Author:
// Jesusalva
// Description:
// Celestia Yeti King's quest. This controls Soren's House and the three keysigns.
// Key #1
// Perform the rite at the Fountain
// Key #2
// Perform the rite at the south lake
// Key #3
// Perform the rite at the Button Area
//
// If the same player performs the rite on different places, HE'LL BE PENALIZED.
// Performing the rite spawn monsters, so watch out.
// Notes:
// I had two options:
// getvariableofnpc(<variable>, "<npc name>")
// This way, each summon point would have .caster and .lifetime
// getvariableofpc(<variable>, <account id>{, <default value>})
// This way, I could use @cast_places to control stuff.
//
// Obviously NPC variables was more sane.
//
// The use of compareandsetq HurnscaldQuest_Celestia
// Ensures you're NOT capable of skipping to final stage in order to finish
// the quest. Nice attempt, but that won't work. The scripts will advance,
// but you'll stay at the same quest state, and when it's checked, you will
// be with bound hands.
soren,105,57,0 script Soren's House NPC_NO_SPRITE,0,0,{
end;
OnTouch:
.@st1=getvariableofnpc(.lifetime, "Soren's Fountain")-gettimetick(2);
.@st2=getvariableofnpc(.lifetime, "Soren's Lake")-gettimetick(2);
.@st3=getvariableofnpc(.lifetime, "Soren's Gizmo")-gettimetick(2);
if (.@st1 > 0 && .@st2 > 0 && .@st3 > 0) {
@soren_penalty=0;
compareandsetq HurnscaldQuest_Celestia, 3, 4;
doevent("#SorenSanctum::OnStart");
warp "soren-2", 32, 36;
end;
} else {
mesn l("Soren's House Tutorial");
mesc l("There's a strong magic barrier. We need to disarm it in order to enter there.");
mesc l("There are three singularities on this island. If I disarm more than one, I'll have a penalty.");
mesc l("I should have full mana before attempting to disarm one.");
mesc l("Also, if I move away from the singularity during disarm process, it'll be lost.");
mesc l("The singularities keep arming themselves up again, so I have roughly five minutes between first disarm and entering here.");
mesc l("We should split our team, and have someone to protect our backs. Otherwise, we might not do it.");
// Protip: stock Elixir Of Life if you need to do this quest with less than 3 team members
close;
}
end;
// Some cleanup might be needed to don't raise difficulty infinitely
// So every day, at 03:23 AM, if no one is trying the quest, it'll get rid
// of the non-permanent monsters on Soren Village & Soren House.
OnClock0323:
if (getareausers("soren") == 0 &&
getareausers("soren-2") == 0 &&
getareausers("001-6") == 0 &&
getareausers("001-7") == 0) {
killmonster("soren", "all");
killmonster("soren-2", "all");
}
end;
}
soren,105,92,0 script Soren's Fountain NPC_NO_SPRITE,{
// Initial Checks
if (.lifetime > gettimetick(2)) {
npctalk l("This singularity will remain disarmed for @@ more!", FuzzyTime(.lifetime, 2, 2));
end;
}
if (.st) {
npctalk l("A disarm process is already running.");
end;
}
// Main menu
mesc l("Attempt to disarm the singularity?");
if (askyesno() == ASK_YES) {
.casterId=getcharid(3);
.st=1;
npctalk l("@@ started disarm process. Please stand by.", strcharinfo(0));
initnpctimer;
if (getvariableofnpc(.casterId, "Soren's Fountain") == .casterId)
@sorenp=10;
else
@sorenp=0;
}
close;
// Waves (total: 6 waves + 1 optional)
OnTimer5000:
OnTimer10300:
OnTimer14000:
OnTimer18000:
OnTimer25000:
OnTimer28000:
OnTimer31000:
if (!attachrid(.casterId)) {
npctalk "Disarm process aborted: Disarmer is gone.";
stopnpctimer; .st=0;
end;
}
if (!reachable(.x, .y, .distance)) {
npctalk l("Disarm process aborted: Disarmer is out of reach.");
stopnpctimer; .st=0;
end;
}
if (Sp < MaxSp/100*15) {
npctalk l("Disarm process aborted: Insufficient mana to proceed.");
stopnpctimer; .st=0;
end;
}
if (ispcdead()) {
npctalk l("Disarm process aborted: Disarmer is dead.");
stopnpctimer; .st=0;
end;
}
// Penalty Handler.
.@val=-2;
if (getvariableofnpc(.casterId, "Soren's Lake") == .casterId)
.@val=.@val-10;
if (getvariableofnpc(.casterId, "Soren's Gizmo") == .casterId)
.@val=.@val-10;
if (@sorenp)
.@val-=@sorenp;
percentheal (.@val/2), -13+.@val;
// Monster Gen
.@amount=rand2(.st/3+1, .st/2+1)+getareausers("soren", 12);
.@mid=rand2(1,3)+.st;
switch (.@mid) {
case 1:
case 2:
.@monsterId = CaveMaggot ; break;
case 3:
case 4:
.@monsterId = RedSlime ; break;
case 5:
case 6:
.@monsterId = LavaSlime ; break;
case 7:
case 8:
.@monsterId = any(Snake, GrassSnake, OldSnake, MountainSnake) ; break;
default: // case 9:
.@monsterId = any(Yeti, Yeti, MountainSnake) ; break;
}
// Item Gen
.@mid=rand(1,7)+.st;
switch (.@mid) {
case 1:
case 2:
.@itemId = Acorn ; break;
case 3:
case 4:
.@itemId = any(BugLeg, ChocolateMouboo) ; break;
case 5:
case 6:
.@itemId = OrangeCupcake ; break;
case 7:
case 8:
.@itemId = CherryCake ; break;
case 9:
case 10:
.@itemId = Chagashroom ; break;
case 11:
.@itemId = GingerBreadMan ; break;
case 12:
.@itemId = rand2(Diamond, Amethyst) ; break;
default: // case 13
.@itemId = rand2(CopperOre, TitaniumOre) ; break;
}
// Defines
.@lx=.x-.distance;
.@ly=.y-.distance;
.@ux=.x+.distance;
.@uy=.y+.distance;
// Core function
areamonster .map$, .@lx, .@ly, .@ux, .@uy, strmobinfo(1, .@monsterId), .@monsterId, .@amount; makeitem(.@itemId, 1, .map$, rand2(.@lx, .@ux), rand2(.@ly, .@uy)); ++.st;
specialeffect(54);
// If we're done with waves
if (.st >= 7) {
getexp rand2(1, 100), rand2(1, 10);
.lifetime=gettimetick(2)+330+rand2(0, 60);
npctalk l("Disarmed with success for: @@", FuzzyTime(.lifetime, 2, 2));
stopnpctimer; .st=0;
specialeffect(27);
}
end;
OnInit:
.sex = G_OTHER;
.distance = 3;
.casterId=0; // getcharid(0) → 3 is account number!!
.lifetime=0; // When will this gate expire (six minutes) (gettimetick(2) + (60*5))
.st=0; // Status after started
end;
}
soren,104,143,0 script Soren's Lake NPC_NO_SPRITE,{
// Initial Checks
if (.lifetime > gettimetick(2)) {
npctalk l("This singularity will remain disarmed for @@ more!", FuzzyTime(.lifetime, 2, 2));
end;
}
if (.st) {
npctalk l("A disarm process is already running.");
end;
}
// Main menu
mesc l("Attempt to disarm the singularity?");
if (askyesno() == ASK_YES) {
.casterId=getcharid(3);
.st=1;
npctalk l("@@ started disarm process. Please stand by.", strcharinfo(0));
initnpctimer;
if (getvariableofnpc(.casterId, "Soren's Lake") == .casterId)
@sorenp=10;
else
@sorenp=0;
}
close;
// Waves (total: 6 waves + 1 optional)
OnTimer4500:
OnTimer11300:
OnTimer17000:
OnTimer22000:
OnTimer26200:
OnTimer29100:
OnTimer30900:
if (!attachrid(.casterId)) {
npctalk "Disarm process aborted: Disarmer is gone.";
stopnpctimer; .st=0;
end;
}
if (!reachable(.x, .y, .distance)) {
npctalk l("Disarm process aborted: Disarmer is out of reach.");
stopnpctimer; .st=0;
end;
}
if (Sp < MaxSp/100*15) {
npctalk l("Disarm process aborted: Insufficient mana to proceed.");
stopnpctimer; .st=0;
end;
}
if (ispcdead()) {
npctalk l("Disarm process aborted: Disarmer is dead.");
stopnpctimer; .st=0;
end;
}
// Penalty Handler.
.@val=-2;
if (getvariableofnpc(.casterId, "Soren's Fountain") == .casterId)
.@val=.@val-10;
if (getvariableofnpc(.casterId, "Soren's Gizmo") == .casterId)
.@val=.@val-10;
if (@sorenp)
.@val-=@sorenp;
percentheal (.@val/2), -13+.@val;
// Monster Gen. Lake spawns less
.@amount=rand2(.st/3+1, .st/2+1)+getareausers("soren", 12)-1;
.@mid=rand2(1,3)+.st;
switch (.@mid) {
case 1:
case 2:
.@monsterId = CaveMaggot ; break;
case 3:
case 4:
.@monsterId = RedSlime ; break;
case 5:
case 6:
.@monsterId = LavaSlime ; break;
case 7:
case 8:
.@monsterId = any(Snake, GrassSnake, OldSnake, MountainSnake) ; break;
default: // case 9:
.@monsterId = any(Yeti, Yeti, MountainSnake) ; break;
}
// Item Gen have a different mechanic here
.@mid=rand(.st, .st*2+1);
switch (.@mid) {
case 1:
case 2:
.@itemId = Acorn ; break;
case 3:
case 4:
.@itemId = any(BugLeg, ChocolateMouboo) ; break;
case 5:
case 6:
.@itemId = OrangeCupcake ; break;
case 7:
case 8:
.@itemId = CherryCake ; break;
case 9:
case 10:
.@itemId = Chagashroom ; break;
case 11:
.@itemId = HastePotion ; break;
case 12:
.@itemId = rand2(Diamond, Amethyst) ; break;
default: // case 13
.@itemId = rand2(CopperOre, TitaniumOre) ; break;
}
// Defines for Lake are fixed on the same spot.
.@lx=104; .@ux=104;
.@ly=141; .@uy=141;
// Core function
areamonster .map$, .@lx, .@ly, .@ux, .@uy, strmobinfo(1, .@monsterId), .@monsterId, .@amount; makeitem(.@itemId, 1, .map$, rand2(.@lx, .@ux), rand2(.@ly, .@uy)); ++.st;
// If we're done with waves
if (.st >= 7) {
.lifetime=gettimetick(2)+330+rand2(0, 60);
npctalk l("Disarmed with success for: @@", FuzzyTime(.lifetime, 2, 2));
stopnpctimer; .st=0;
}
end;
OnInit:
.sex = G_OTHER;
.distance = 2;
.casterId=""; // getcharid(3) → Account number!!
.lifetime=0; // When will this gate expire (five~six minutes) (gettimetick(2) + (60*5))
.st=0; // Status after started
end;
}
soren,107,37,0 script Soren's Gizmo NPC_NO_SPRITE,{
// Initial Checks
if (.lifetime > gettimetick(2)) {
npctalk l("This singularity will remain disarmed for @@ more!", FuzzyTime(.lifetime, 2, 2));
end;
}
if (.st) {
npctalk l("A disarm process is already running.");
end;
}
// Main menu
mesc l("Attempt to disarm the singularity?");
if (askyesno() == ASK_YES) {
.casterId=getcharid(3);
.st=1;
npctalk l("@@ started disarm process. Please stand by.", strcharinfo(0));
initnpctimer;
if (getvariableofnpc(.casterId, "Soren's Gizmo") == .casterId)
@sorenp=10;
else
@sorenp=0;
}
close;
// Waves (total: 6 waves + 1 optional)
OnTimer6200:
OnTimer9300:
OnTimer13900:
OnTimer17200:
OnTimer24500:
OnTimer27400:
OnTimer31200:
if (!attachrid(.casterId)) {
npctalk "Disarm process aborted: Disarmer is gone.";
stopnpctimer; .st=0;
end;
}
if (!reachable(.x, .y, .distance)) {
npctalk l("Disarm process aborted: Disarmer is out of reach.");
stopnpctimer; .st=0;
end;
}
if (Sp < MaxSp/100*15) {
npctalk l("Disarm process aborted: Insufficient mana to proceed.");
stopnpctimer; .st=0;
end;
}
if (ispcdead()) {
npctalk l("Disarm process aborted: Disarmer is dead.");
stopnpctimer; .st=0;
end;
}
// Penalty Handler.
.@val=-2;
if (getvariableofnpc(.casterId, "Soren's Lake") == .casterId)
.@val=.@val-10;
if (getvariableofnpc(.casterId, "Soren's Fountain") == .casterId)
.@val=.@val-10;
if (@sorenp)
.@val-=@sorenp;
percentheal (.@val/2), -13+.@val;
// Monster Gen. Gizmo spawns more monsters because you can move more
.@amount=rand2(.st/3+1, .st/2+1)+getareausers("soren", 12)+1;
.@mid=rand2(1,3)+.st;
switch (.@mid) {
case 1:
case 2:
.@monsterId = CaveMaggot ; break;
case 3:
case 4:
.@monsterId = RedSlime ; break;
case 5:
case 6:
.@monsterId = LavaSlime ; break;
case 7:
case 8:
.@monsterId = any(Snake, GrassSnake, OldSnake, MountainSnake) ; break;
default: // case 9:
.@monsterId = any(Yeti, Yeti, MountainSnake) ; break;
}
// Item Gen
.@mid=rand(1,7)+.st;
switch (.@mid) {
case 1:
case 2:
.@itemId = Acorn ; break;
case 3:
case 4:
.@itemId = any(BugLeg, ChocolateMouboo) ; break;
case 5:
case 6:
.@itemId = OrangeCupcake ; break;
case 7:
case 8:
.@itemId = CherryCake ; break;
case 9:
case 10:
.@itemId = Chagashroom ; break;
case 11:
.@itemId = HastePotion ; break;
case 12:
.@itemId = rand2(Diamond, Amethyst) ; break;
default: // case 13
.@itemId = rand2(CopperOre, TitaniumOre) ; break;
}
// Defines
.@lx=.x-.distance;
.@ly=.y-.distance;
.@ux=.x+.distance;
.@uy=.y+.distance;
// Core function
areamonster .map$, .@lx, .@ly, .@ux, .@uy, strmobinfo(1, .@monsterId), .@monsterId, .@amount; makeitem(.@itemId, 1, .map$, rand2(.@lx, .@ux), rand2(.@ly, .@uy)); ++.st;
// If we're done with waves
if (.st >= 7) {
.lifetime=gettimetick(2)+330+rand2(0, 60);
npctalk l("Disarmed with success for: @@", FuzzyTime(.lifetime, 2, 2));
stopnpctimer; .st=0;
}
end;
OnInit:
.sex = G_OTHER;
.distance = 4;
.casterId=""; // getcharid(3) → Account number!!
.lifetime=0; // When will this gate expire (five~six minutes) (gettimetick(2) + (60*5))
.st=0; // Status after started
end;
}
029-0,143,120,0 duplicate(Guild Storage) Guild Storekeeper#FoS NPC_TERRY