summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHoraK-FDF <horak-fdf@web.de>2021-12-07 23:01:55 +0100
committerHoraK-FDF <horak-fdf@web.de>2021-12-07 23:01:55 +0100
commit71d0cdfe8bdb0567303825850ad36a5e8506ab2b (patch)
tree28f778dc81a65c531a9485e38456a60d6b4daf3f
parentc1be7bd1eab5b0c230001c369292abe757a7f32a (diff)
parentcd6424df5baf7d46b96fa1ee4be500ad8b131a82 (diff)
downloadserverdata-71d0cdfe8bdb0567303825850ad36a5e8506ab2b.tar.gz
serverdata-71d0cdfe8bdb0567303825850ad36a5e8506ab2b.tar.bz2
serverdata-71d0cdfe8bdb0567303825850ad36a5e8506ab2b.tar.xz
serverdata-71d0cdfe8bdb0567303825850ad36a5e8506ab2b.zip
Merge branch 'xmas2021' of https://gitlab.com/themanaworld/evolved/serverdata into xmas2021
-rw-r--r--conf/map/maps.conf5
-rw-r--r--db/constants.conf22
-rw-r--r--db/map_index.txt27
-rw-r--r--db/pre-re/item_db.conf46
-rw-r--r--db/pre-re/mob_db.conf158
-rw-r--r--maps/pre-re/080-1.mcachebin0 -> 1452 bytes
-rw-r--r--maps/pre-re/080-3.mcachebin0 -> 1535 bytes
-rw-r--r--maps/pre-re/081-1.mcachebin0 -> 619 bytes
-rw-r--r--maps/pre-re/081-2.mcachebin0 -> 1488 bytes
-rw-r--r--maps/pre-re/081-3.mcachebin0 -> 145 bytes
-rw-r--r--npc/080-1/_import.txt3
-rw-r--r--npc/080-1/final.txt123
-rw-r--r--npc/080-3/_import.txt3
-rw-r--r--npc/080-3/cutscene.txt236
-rw-r--r--npc/081-1/_import.txt3
-rw-r--r--npc/081-1/dungeon.txt29
-rw-r--r--npc/081-2/_import.txt4
-rw-r--r--npc/081-2/_warps.txt3
-rw-r--r--npc/081-2/logic.txt322
-rw-r--r--npc/081-3/_import.txt3
-rw-r--r--npc/081-3/logic.txt203
-rw-r--r--npc/_import.txt5
-rw-r--r--npc/annuals/xmas/2021.txt565
-rw-r--r--npc/functions/clear_vars.txt36
-rw-r--r--npc/functions/global_event_handler.txt2
-rw-r--r--npc/functions/mob_points.txt11
-rw-r--r--npc/functions/vault.txt2
-rw-r--r--npc/magic/config.txt1
-rw-r--r--npc/scripts.conf1
29 files changed, 1797 insertions, 16 deletions
diff --git a/conf/map/maps.conf b/conf/map/maps.conf
index 68d2bd20..1fc1e11b 100644
--- a/conf/map/maps.conf
+++ b/conf/map/maps.conf
@@ -122,6 +122,11 @@ map_list: (
"069-2",
"070-1",
"070-3",
+ "080-1",
+ "080-3",
+ "081-1",
+ "081-2",
+ "081-3",
"099-1",
"099-2",
"099-3",
diff --git a/db/constants.conf b/db/constants.conf
index 21bfd5f3..ac58a2af 100644
--- a/db/constants.conf
+++ b/db/constants.conf
@@ -3185,6 +3185,9 @@ more than one separator can be used in a row (so 12_3___456 is illegal).
NPC432:432
NPC433:433
NPC434:434
+ NPC435:435
+ NPC436:436
+ NPC437:437
NPC32767:32767
// NPCs decently named
@@ -3214,7 +3217,7 @@ more than one separator can be used in a row (so 12_3___456 is illegal).
NPC_UNUSED__MOBTOMB: 565
NPC_WARP_DEBUG: 722
NPC_UNUSED__FLAG: 722
- NPC_NO_SPRITE: 999
+ NPC_NO_SPRITE: 400
NPC_HIDDEN: 32767
comment__: "Walk masks"
@@ -3376,6 +3379,23 @@ more than one separator can be used in a row (so 12_3___456 is illegal).
ROSSY_READY: 1023
ROSSY_BOSSCAVE: 1024
+ comment__: "Christmas 2021 Constants"
+ X21_ACCEPTED: 1
+ X21_CANDLES: 64
+ X21_PLANTPUZZLE: 128
+ X21_LEVEL3: 256
+ X21_RIDDLEDONE: 512
+ X21_BOSSDEFEAT: 1024
+ X21_ALYSSAINIT: 2048
+ X21_ALYSSARESCUE: 4096
+
+ X21BC1_ON: 1
+ X21BC1_OFF: 2
+ X21_EXP_PUZZLE: 15000
+ X21_TICKET_PUZZLE: 15
+ X21_TICKET_BOSS: 5
+ X21_TICKET_SUPPORT: 2
+
comment__: "Focus Constants"
FSKILL_MALLARDS_EYE: 1
FSKILL_BRAWLING: 2
diff --git a/db/map_index.txt b/db/map_index.txt
index 354f427b..be37ef1b 100644
--- a/db/map_index.txt
+++ b/db/map_index.txt
@@ -119,14 +119,19 @@
069-2 119
070-1 120
070-3 121
-099-1 122
-099-2 123
-099-3 124
-099-4 125
-099-5 126
-099-6 127
-099-7 128
-099-8 129
-botcheck 130
-fermi 131
-sec_pri 132
+080-1 122
+080-3 123
+081-1 124
+081-2 125
+081-3 126
+099-1 127
+099-2 128
+099-3 129
+099-4 130
+099-5 131
+099-6 132
+099-7 133
+099-8 134
+botcheck 135
+fermi 136
+sec_pri 137
diff --git a/db/pre-re/item_db.conf b/db/pre-re/item_db.conf
index a037f0cd..99467306 100644
--- a/db/pre-re/item_db.conf
+++ b/db/pre-re/item_db.conf
@@ -1112,6 +1112,7 @@ item_db: (
Id5260: 1
}
},
+/* Rings & Accessories */
{
Id: 702
AegisName: "WeddingRing"
@@ -1517,6 +1518,7 @@ item_db: (
bonus bHit, 25;
">
},
+/* Boots */
{
Id: 528
AegisName: "Boots"
@@ -4794,6 +4796,26 @@ item_db: (
ViewSprite: 5265
},
{
+ Id: 5276
+ AegisName: "ThermalNapalm"
+ Name: "Thermal Napalm"
+ Type: "IT_ETC"
+ Buy: 9999
+ Sell: 0
+ Weight: 1
+ ViewSprite: 5276
+ Trade: {
+ partneroverride: true
+ notrade: false
+ nodrop: true
+ nogstorage: true
+ nomail: true
+ noauction: true
+ noselltonpc: true
+ }
+},
+/* Chest Armor */
+{
Id: 523
AegisName: "LeatherShirt"
Name: "LeatherShirt"
@@ -12074,6 +12096,7 @@ item_db: (
bonus bMatkRate, -5;
">
},
+/* Usable items */
{
Id: 501
AegisName: "CactusDrink"
@@ -13738,6 +13761,29 @@ item_db: (
">
},
{
+ Id: 5277
+ AegisName: "XmasSeeds"
+ Name: "Christmas Seeds"
+ Type: "IT_USABLE"
+ Buy: 9999
+ Sell: 0
+ Weight: 1
+ ViewSprite: 5277
+ Trade: {
+ partneroverride: true
+ notrade: true
+ nodrop: true
+ nogstorage: true
+ nomail: true
+ noauction: true
+ noselltonpc: true
+ }
+ Script: <"
+ callfunc "X21_SEEDS";
+ ">
+},
+/* Legs Armor */
+{
Id: 586
AegisName: "CottonShorts"
Name: "CottonShorts"
diff --git a/db/pre-re/mob_db.conf b/db/pre-re/mob_db.conf
index 50798b57..67e3611d 100644
--- a/db/pre-re/mob_db.conf
+++ b/db/pre-re/mob_db.conf
@@ -7002,4 +7002,162 @@ mob_db: (
CandyPumpkin: 1250
}
},
+{
+ Id: 1164
+ SpriteName: "ChristmasTree"
+ Name: "Christmas Tree"
+ Lv: 40
+ Hp: 10000
+ Sp: 0
+ Exp: 3647
+ JExp: 392
+ AttackRange: 2
+ Attack: [40, 40]
+ Def: 20
+ Mdef: 40
+ Stats: {
+ Str: 30
+ Agi: 30
+ Vit: 30
+ Int: 30
+ Dex: 30
+ Luk: 30
+ }
+ ViewRange: 9
+ ChaseRange: 10
+ Size: 1
+ Race: 0
+ Element: (0, 1)
+ Mode: {
+ CanMove: false
+ CanAttack: true
+ ChangeTargetMelee: true
+ ChangeTargetChase: true
+ }
+ MoveSpeed: 190
+ AttackDelay: 1500
+ AttackMotion: 800
+ DamageMotion: 800
+},
+{
+ Id: 1165
+ SpriteName: "ChristmasGift"
+ Name: "Christmas Gift"
+ Lv: 1
+ Hp: 1
+ Sp: 0
+ Exp: 1
+ JExp: 1
+ AttackRange: 1
+ Attack: [0, 0]
+ Def: 0
+ Mdef: 5
+ Stats: {
+ Str: 0
+ Agi: 0
+ Vit: 0
+ Int: 0
+ Dex: 0
+ Luk: 0
+ }
+ ViewRange: 9
+ ChaseRange: 10
+ Size: 1
+ Race: 3
+ Element: (2, 1)
+ MoveSpeed: 800
+ AttackDelay: 800
+ AttackMotion: 672
+ DamageMotion: 480
+ Drops: {
+ FourLeafClover: 5
+ }
+},
+{
+ Id: 1166
+ SpriteName: "Snowman"
+ Name: "Snowman"
+ Lv: 100
+ Hp: 5000
+ Sp: 0
+ Exp: 2897
+ JExp: 689
+ AttackRange: 1
+ Attack: [60, 80]
+ Def: 5
+ Mdef: 20
+ Stats: {
+ Str: 15
+ Agi: 25
+ Vit: 40
+ Int: 15
+ Dex: 45
+ Luk: 30
+ }
+ ViewRange: 8
+ ChaseRange: 10
+ Size: 1
+ Race: 3
+ Element: (0, 1)
+ Mode: {
+ CanMove: true
+ Aggressive: true
+ CanAttack: true
+ ChangeTargetMelee: true
+ ChangeTargetChase: true
+ }
+ MoveSpeed: 800
+ AttackDelay: 1872
+ AttackMotion: 672
+ DamageMotion: 480
+ Drops: {
+ IronPotion: 800
+ ConcentrationPotion: 800
+ Diamond: 50
+ }
+},
+{
+ Id: 1167
+ SpriteName: "SnowmanBoss"
+ Name: "Der Schneemann"
+ Lv: 100
+ Hp: 35000
+ Sp: 0
+ Exp: 2897
+ JExp: 689
+ AttackRange: 3
+ Attack: [60, 80]
+ Def: 20
+ Mdef: 20
+ Stats: {
+ Str: 15
+ Agi: 15
+ Vit: 10
+ Int: 15
+ Dex: 75
+ Luk: 50
+ }
+ ViewRange: 14
+ ChaseRange: 16
+ Size: 1
+ Race: 3
+ Element: (0, 1)
+ Mode: {
+ CanMove: true
+ Aggressive: true
+ CanAttack: true
+ ChangeTargetMelee: true
+ ChangeTargetChase: true
+ Boss: true
+ }
+ MoveSpeed: 700
+ AttackDelay: 1672
+ AttackMotion: 672
+ DamageMotion: 480
+ Drops: {
+ IronPotion: 800
+ ConcentrationPotion: 800
+ Diamond: 50
+ }
+},
)
diff --git a/maps/pre-re/080-1.mcache b/maps/pre-re/080-1.mcache
new file mode 100644
index 00000000..09891164
--- /dev/null
+++ b/maps/pre-re/080-1.mcache
Binary files differ
diff --git a/maps/pre-re/080-3.mcache b/maps/pre-re/080-3.mcache
new file mode 100644
index 00000000..037aa549
--- /dev/null
+++ b/maps/pre-re/080-3.mcache
Binary files differ
diff --git a/maps/pre-re/081-1.mcache b/maps/pre-re/081-1.mcache
new file mode 100644
index 00000000..60f55155
--- /dev/null
+++ b/maps/pre-re/081-1.mcache
Binary files differ
diff --git a/maps/pre-re/081-2.mcache b/maps/pre-re/081-2.mcache
new file mode 100644
index 00000000..0e69fb70
--- /dev/null
+++ b/maps/pre-re/081-2.mcache
Binary files differ
diff --git a/maps/pre-re/081-3.mcache b/maps/pre-re/081-3.mcache
new file mode 100644
index 00000000..8250d273
--- /dev/null
+++ b/maps/pre-re/081-3.mcache
Binary files differ
diff --git a/npc/080-1/_import.txt b/npc/080-1/_import.txt
new file mode 100644
index 00000000..e28064cf
--- /dev/null
+++ b/npc/080-1/_import.txt
@@ -0,0 +1,3 @@
+// Map 080-1: Mysterious Glade
+// This file is generated automatically. All manually added changes will be removed when running the Converter.
+"npc/080-1/final.txt",
diff --git a/npc/080-1/final.txt b/npc/080-1/final.txt
new file mode 100644
index 00000000..72326ed1
--- /dev/null
+++ b/npc/080-1/final.txt
@@ -0,0 +1,123 @@
+// TMW Script.
+// Author:
+// Jesusalva
+// Notes:
+// Christmas 2021 - Mysterious Glade (Final fights, cutscene & final shops)
+// CANON:
+// From now and hence forth, by the power vested to me due to mapmakers being
+// lazy and all that, I declare that it is canon that:
+// - Gak and Alissa used to live on this glade
+// - Back in 2010, Gak went to Asphodel. First event.
+// - Between 2011 and 2015, Gak found out someone built a house here
+// - aka. The Pink Boots mage. Gak also got mad at how they got
+// Zealite and were using it for petty reasons.
+// - Gak took the mask as an offense to Alissa, and the manor as well.
+// - As I forgot who is the Pink Boots mage... I can't say any further.
+// - Oh, and in 2016, Gak went to visit Asphodel again.
+
+080-1,98,41,0 script Alissa#ED2 NPC106,{
+ end;
+OnInstanceInit:
+ disablenpc .name$;
+ end;
+OnInit:
+ .distance=4;
+ /* I thought in making a shop here */
+ /* Valentine Dress, Love Potions, Pinkie Crystal... */
+ /* Would be confusing and bad, so removed. */
+ end;
+}
+
+080-1,97,41,0 script Gak#X21 NPC183,{
+ // FIXME: Cutscene
+ // FIXME: Enablenpc Alissa#ED2
+ // FIXME: Do not repeat cutscene if quest once completed
+ // FIXME: Do not repeat cutscene if Alissa is enabled?
+ openshop;
+ closedialog;
+ end;
+
+OnInit:
+ .distance=4;
+ setarray .prizes, BlinkingEvil, BlinkingEvilRed, BlinkingEvilBlue,
+ BlinkingEvilPink, BlinkingEvilYellow, GuyFawkesMask,
+ OperaMask, JesterMask, GoblinMask, WitchHat;
+ setarray .prices, 1500, 1650, 1600,
+ 1650, 1600, 250,
+ 900, 700, 250, 450;
+ tradertype(NST_CUSTOM);
+ freeloop(true);
+ for (.@i = 0; .@i < getarraysize(.prizes); .@i++) {
+ sellitem(.prizes[.@i], .prices[.@i]);
+ }
+ freeloop(false);
+ end;
+
+OnCountFunds:
+ setcurrency(countitem(AquaTicket));
+ end;
+
+/* @price is total cost. @points is if we accept two items as currency. */
+OnPayFunds:
+ //dispbottom "Hi: price="+@price+" and points="+@points;
+ if( countitem(AquaTicket) < @price )
+ end;
+ /* Verify if you're not purchasing a dupe */
+ /* This requires servercode@712c09c2c6d848243c3426aeb3dbdf730c1e0b08 to work */
+ for (.@i=0;.@i < getarraysize(@bought_nameid); .@i++) {
+ if (debug || $@XMAS21_OVERRIDE)
+ debugmes("%dx %s", @bought_quantity[.@i], getitemname(@bought_nameid[.@i]));
+
+ .@arr = array_find(.prizes, @bought_nameid[.@i]);
+ if (.@arr < 0) { dispbottom "REPORT ME: Array Error (Gak)"; end;}
+ .@bit = (2 ** .@arr);
+
+ if (#X21PRIZES_GAK & .@bit) {
+ dispbottom l("You already purchased a(n) %s during the event.", getitemlink(@bought_nameid[.@i]));
+ dispbottom l("Therefore, the operation was cancelled.");
+ end;
+ }
+ }
+ delitem AquaTicket, @price;
+ /* Record the items on the bitmask (far more important) */
+ /* This requires servercode@712c09c2c6d848243c3426aeb3dbdf730c1e0b08 to work */
+ for (.@i=0;.@i < getarraysize(@bought_nameid); .@i++) {
+ .@arr = array_find(.prizes, @bought_nameid[.@i]);
+ if (.@arr < 0) { dispbottom "REPORT ME: Fatal Array Error (Gak)"; end;}
+ .@bit = (2 ** .@arr);
+ #X21PRIZES_GAK = #X21PRIZES_GAK | .@bit;
+ }
+ purchaseok();
+ end;
+}
+
+// But then, Gak *did* "kidnap" a zealite specialist, right?
+080-1,83,136,0 script #X21BarrierF NPC_HIDDEN,3,0,{
+ if ('XMAS21FINAL)
+ end;
+
+ if (mobcount(getmap(), "#X21BarrierF::OnKil")) {
+ dispbottom strcharinfo(0) + " : " + l("This clearly is not a real gate; Most likely an illusion to prevent access. I should be able to dispel it by killing the guardians.");
+ end;
+ }
+ delcells("X21LF@"+X21ID());
+ specialeffect(FX_MAGIC_WICKED_SPAWN, AREA, getcharid(3));
+ getitem AquaTicket, X21_TICKET_BOSS;
+ 'XMAS21FINAL = true;
+ maptimer2(getmap(), 10, "#X21BarrierF::OnSesame");
+ end;
+OnInit:
+ .distance=1;
+ end;
+OnSesame:
+ dispbottom l("Without the guardians, the illusion is just that: An illusion. The path is clear.");
+ getitem AquaTicket, X21_TICKET_SUPPORT;
+ getexp X21_EXP_PUZZLE, 0;
+ end;
+OnKil:
+ mapannounce getmap(), "One of the guardians has been eliminated by "+strcharinfo(0), 0;
+ getitem AquaTicket, X21_TICKET_SUPPORT + 1;
+ X21INIT();
+ end;
+}
+
diff --git a/npc/080-3/_import.txt b/npc/080-3/_import.txt
new file mode 100644
index 00000000..6bd09a7b
--- /dev/null
+++ b/npc/080-3/_import.txt
@@ -0,0 +1,3 @@
+// Map 080-3: Forgotten Glade
+// This file is generated automatically. All manually added changes will be removed when running the Converter.
+"npc/080-3/cutscene.txt",
diff --git a/npc/080-3/cutscene.txt b/npc/080-3/cutscene.txt
new file mode 100644
index 00000000..5cb03656
--- /dev/null
+++ b/npc/080-3/cutscene.txt
@@ -0,0 +1,236 @@
+// TMW Script.
+// Author:
+// Jesusalva
+// Notes:
+// Christmas 2021 - Peaceful Glade
+// FIXME: Santa must tell about Gak acting "out of character" by getting the
+// Mask of Blinking Evil. Otherwise, it'll be a seriously loose end.
+
+080-3,83,155,0 script #0803exit NPC_HIDDEN,1,0,{
+ end;
+OnTouch:
+ if (!isChristmas21()) { warp "081-1", 68, 31; end; }
+ if (!X21ED1_CLEAR()) { dispbottom l("It is too early to leave yet!"); end; }
+ mesn strcharinfo(0);
+ mesq l("Are we done here? We won't be able to return later!");
+ next;
+ if (askyesno() == ASK_YES)
+ warp "081-1", 68, 31;
+ closeclientdialog;
+ end;
+}
+
+/* The Story Books */
+080-3,116,116,0 script Book#X21B01 NPC_NO_SPRITE,{
+ mes l("Gorrik, member of the Bells Society. Thanks for attending the opera.");
+ next;
+ mes l("Along this %s, you will find the instructions regarding the plot to murder Efeniunkanduti Khan, king of Tonori.", getitemlink(OperaMask));
+ next;
+ mes l("It is of utmost importance to find out what happened to Fawkes. Therefore, wear a %s and infiltrate the palace.", getitemlink(JesterMask));
+ next;
+ mes l("Beware Akhendo-Al, prince of Tonori. Even the captain of the palace guard won't get away investigating the fire.");
+ next;
+ mes l("-- @@1226|The Silver Bell@@");
+ close;
+OnInit:
+ .distance=2;
+ end;
+}
+
+080-3,129,56,0 script Book#X21B02 NPC_NO_SPRITE,{
+ mes l("Gorrik's Diary, final entry");
+ next;
+ mes l("I was convinced my friend %s did not die on the explosion which destroyed Tulimshar palace. Therefore, I set out to find the truth about it.", "@@769|Guy Fawkes@@");
+ next;
+ mes l("But I had been fooled. King Khan has been murdered, and I was accused of regicide. All thanks to this accursed %s.", getitemlink(JesterMask));
+ next;
+ mes l("I am sure it was a plot so I would take the fall for the king's murder. However, I still have friends among the guard, whom know I would not kill your majesty. At least, not this way.");
+ next;
+ mes l("I'll take a %s, cut all my hair, and use the landbridge to reach the Woodlands. It should be weakly guarded, so hopefully, I'll make it out of Tonori safely.", getitemlink(Scissors));
+ next;
+ mes l("For precaution, I'll also stop using this name. Do not seek for me. For I shall exile myself until the last day of my life.");
+ close;
+OnInit:
+ .distance=2;
+ end;
+}
+
+080-3,46,80,0 script Book#X21B04 NPC_NO_SPRITE,{
+ mes l("Gak's Diary, firsy entry");
+ next;
+ mes l("So my new life begun, and I realized my training as a palace guard did not really cover survival in the wilderness... Or navigation on dense forests.");
+ next;
+ mes l("But just when I was about to resign myself to my fate, to die of hunger in the middle of Argaes southeastern forests... I saw what seemed like an illusion.");
+ next;
+ mes l("A candy house. With my judgment clouded from hunger, I immediately begun eating the wall, without a care in the world. It was %s!", "@@1281|shocking@@");
+ next;
+ mes l("But suddenly, a witch came out of the cottage. I ran for my life! However, being only used to deserts instead of rainforests, I could not outrun her. I thought my life was over, but...");
+ next;
+ mes l("...instead, she just scolded me. Told me how difficult was to maintain a sweet house. I never felt so much shame in my life, it was worse than if I had actually killed the king!");
+ next;
+ mes l("From now on, I'll be a witch's apprentice, to repay for what I ate. I am nervous, but... also glad for this chance, to rebuild my life, far from the conspiracies and backstabbings of Tulimshar court.");
+ close;
+OnInit:
+ .distance=2;
+ end;
+}
+
+080-3,64,120,0 script Book#X21B05 NPC_NO_SPRITE,{
+ mes l("Report to Hurnscald Government, year unknown.");
+ next;
+ mesn strcharinfo(0);
+ mes l("It describes a brutal goblin attack which decimated a small settlement southwest of Hurnscald, called Turnap."); // Later became known as Asphodel Moors
+ next;
+ mesn strcharinfo(0);
+ mes l("Apparently, the citizens there tried using %s to thwart goblin attacks, but did not account for the clan system of the goblin society.", getitemlink(GoblinMask));
+ next;
+ mesn strcharinfo(0);
+ mes l("...Needless to say, the goblins thought it was an invading tribe and decimated the town, killing and destroying anything on the vicinity. There has been ##Bno survivors##b.");
+ next;
+ mes l("The farthest body found was of a man called \"Gak\". According to the report, he likely tried to run away from the town alone but did not made it.");
+ close;
+OnInit:
+ .distance=2;
+ end;
+}
+
+080-3,98,90,0 script Package#X21B06 NPC_NO_SPRITE,{
+ mesn strcharinfo(0);
+ mes l("This bag is full of pictures. I'll briefly describe them.");
+ next;
+ mes l("1. A woman wearing a %s, close to a tall man whose face is obscured by an %s.", getitemlink(WitchHat), getitemlink(OperaMask));
+ next;
+ mes l("2. A photo of Asphodel Moors, back when it was inhabited by living beings.");
+ next;
+ mes l("3. A photo of a candy cottage. There's a man tending to it, but his face is not visible.");
+ next;
+ mes l("4. A photo of two great dragons clashing against a raging one.");
+ next;
+ mes l("5. The woman from the first photo, but without her hat and... floating? hovering? well, she is lying above the abyss.");
+ close;
+OnInit:
+ .distance=2;
+ end;
+}
+
+
+// FIXME: Alissa's NPC ID; Make more clear the sequence to players.
+080-3,85,52,0 script Alissa#ED1 NPC106,{
+ function rushed;
+ function ending;
+ if (!isChristmas21()) end;
+ if (.stable == 6) ending();
+ if (.stable >= 7) end;
+ mesn;
+ mesq l("...");
+ next;
+ mesn strcharinfo(0);
+ mesq l("Alissa seems unresponsive, as if her mind wasn't fully back in the world yet.");
+ next;
+ mesn strcharinfo(0);
+ mesq l("Perhaps I could ##Bshow her##b something?");
+ next;
+ select
+ l("I really don't know what to show."),
+ l("A Mask of Blinking Evil"),
+ l("An Opera Mask"),
+ l("A Jester Mask"),
+ l("A Witch's Hat"),
+ l("A Goblin Mask"),
+ l("A Fawkes Mask"),
+ l("Some Scissors"),
+ l("A Shock Sweet");
+ mes "";
+ if (@menu < 1) { closeclientdialog; close; }
+ if (.memory & (2 ** @menu)) {
+ mesn strcharinfo(0);
+ mesq l("...I already showed her that.");
+ close;
+ }
+ if (!.stable) {
+ mesc l("I probably should start with something about where she was supposed to be and who she is, and tell her the whole story from that.");
+ }
+
+ /* First Cutscene */
+ switch (@menu) {
+ case 2: // Blinking Mask
+ rushed(5);
+ mesc l("Suddenly, Alissa's eyes shot open.");
+ next;
+ mesn;
+ mesq l("...Gorrik is... Alive...?");
+ break;
+ case 3: // Opera Mask
+ rushed();
+ break;
+ case 4: // Jester Mask
+ rushed(3);
+ mesn;
+ mesq l("...I remember... Running from a coup'detat...");
+ next;
+ mesn;
+ mesq l("...What happened... to him...?");
+ break;
+ case 5: // Witch Hat
+ rushed(1);
+ mesn;
+ mesq l("...Gorrik...");
+ break;
+ case 6: // Goblin Mask
+ rushed(4);
+ mesn;
+ mesq l("...Yes... A slaughter...");
+ break;
+ case 7: // Fawkes Mask
+ rushed();
+ break;
+ case 8: // Scissors
+ rushed(2);
+ mesn;
+ mesq l("...His... Past?");
+ break;
+ case 9: // Shock Candy
+ rushed(0);
+ mesn;
+ mesq l("...The Candy House...");
+ break;
+ }
+ .memory = .memory | (2 ** @menu);
+ close;
+
+function rushed {
+ if (.stable == getarg(0, -1)) { .stable+=1; return; }
+ mesn strcharinfo(0);
+ mesq l("...No, I should look for more clues before showing this item.");
+ close;
+}
+
+function ending {
+ mesn;
+ mesq l("What happened to Gorrik...?");
+ next;
+ mesc l("While you do not know the details, you still try to explain the situation to Alissa.");
+ mesc l("How Gak became an undead, how long he sought for her, until finally, when he had almost given hope... How Santa decided to intervene.");
+ next;
+ mesc l("After hearing your history, you could not tell if Alissa was glad or sad.");
+ next;
+ mesn;
+ mesq l("...I'll follow.");
+ next;
+ mesn;
+ mesq l("Bring me to him.");
+ // (Poor Gak, he is up to an earful once player completes this quest...)
+ // FIXME: Prize?
+ .stable += 1;
+ X21ED1_DOCLEAR();
+ disablenpc instance_npcname(.name$);
+ close;
+}
+
+OnInit:
+ .distance=4;
+ .stable=0;
+ .memory=0;
+ end;
+}
+
diff --git a/npc/081-1/_import.txt b/npc/081-1/_import.txt
new file mode 100644
index 00000000..37fd1add
--- /dev/null
+++ b/npc/081-1/_import.txt
@@ -0,0 +1,3 @@
+// Map 081-1: Aethyr
+// This file is generated automatically... All manually added changes will be removed when running the Converter.
+"npc/081-1/dungeon.txt",
diff --git a/npc/081-1/dungeon.txt b/npc/081-1/dungeon.txt
new file mode 100644
index 00000000..fd125dd0
--- /dev/null
+++ b/npc/081-1/dungeon.txt
@@ -0,0 +1,29 @@
+// TMW Script.
+// Author:
+// Jesusalva
+// Notes:
+// Christmas 2021 - Northen Lights Dungeon Entrance
+
+081-1,68,30,0 script #0811Nexit NPC_HIDDEN,0,0,{
+ end;
+OnTouch:
+ if (!isChristmas21()) end;
+
+ /* We need to determine if instance exists and is needed */
+ X21INIT();
+
+ .@mapa$="0812@"+X21ID2();
+ warp .@mapa$, 90, 298;
+ end;
+}
+
+081-1,64,34,0 script #X21_SIGN01 NPC_NO_SPRITE,{
+ if (!isChristmas21()) end;
+ npctalk3 l("Northern Lights Dungeon - Form a party to join or enter alone. Be careful!");
+ end;
+OnInit:
+ .distance=5;
+ end;
+}
+
+
diff --git a/npc/081-2/_import.txt b/npc/081-2/_import.txt
new file mode 100644
index 00000000..f777ef1b
--- /dev/null
+++ b/npc/081-2/_import.txt
@@ -0,0 +1,4 @@
+// Map 081-2: Northern Lights
+// This file is generated automatically. All manually added changes will be removed when running the Converter.
+"npc/081-2/_warps.txt",
+"npc/081-2/logic.txt",
diff --git a/npc/081-2/_warps.txt b/npc/081-2/_warps.txt
new file mode 100644
index 00000000..22644e17
--- /dev/null
+++ b/npc/081-2/_warps.txt
@@ -0,0 +1,3 @@
+// This file is generated automatically. All manually added changes will be removed when running the Converter.
+// Map 081-2: Northern Lights warps
+081-2,90,299,0 warp #081-2_90_299 0,0,081-1,68,31
diff --git a/npc/081-2/logic.txt b/npc/081-2/logic.txt
new file mode 100644
index 00000000..ba9b6abd
--- /dev/null
+++ b/npc/081-2/logic.txt
@@ -0,0 +1,322 @@
+// TMW Script.
+// Author:
+// Jesusalva
+// Notes:
+// Christmas 2021 - Northen Lights Dungeon
+
+081-2,90,20,0 script #0812Nexit NPC_HIDDEN,0,0,{
+ end;
+OnTouch:
+ if (!isChristmas21()) end;
+
+ /* We need to determine if instance exists and is needed */
+ X21INIT();
+
+ .@mapn$="0813@"+X21ID2();
+ warp .@mapn$, 44, 49;
+ // FIXME: Cleanup if inst returns -4
+ //doevent instance_npcname("Pentagram#Xmas21A", .@inst)+"::OnClean";
+ end;
+}
+
+/* *************************************************************************** */
+/* Candle Puzzle */
+081-2,130,255,0 script #X21Candle_0 NPC_NO_SPRITE,{
+ if (!countitem(ThermalNapalm)) { dispbottom(l("I don't have anything to lit this candle.")); end; }
+ if ('XMAS21CANDLE) end;
+ // Extract ID
+ .@n$=strnpcinfo(2, "_-1");
+ explode(.@ni$, .@n$, "_");
+ .@id=atoi(.@ni$[1]);
+ if (.@id < 0) { consolemes(CONSOLEMES_ERROR, "ERROR, X21 Invalid Candle"); end; }
+ .@c = 'X21_CANDLE[.@id];
+ .@c$ = (.@c == 0 ? l("No") : (.@c == 1 ? l("Regular") : l("Magic")));
+
+ mes l("This candle is using %s Fire.", .@c$);
+ mes l("Do you want to change it?");
+ mesc l("Cost: 1x %s", getitemlink(ThermalNapalm)), 1;
+ next;
+ menuint
+ l("No, leave it alone."), -1,
+ rif(.@c != 1, l("Lit a regular fire.")), 1,
+ rif(.@c != 2, l("Lit a magic fire.")), 2,
+ rif(.@c != 0, l("Remove the fire.")), 0;
+ mes "";
+ if (@menuret < 0) { closeclientdialog; close; }
+ delitem ThermalNapalm, 1;
+ 'X21_CANDLE[.@id] = @menuret;
+ // Modify the flame sprite
+ setnpcdisplay(instance_npcname(strnpcinfo(0)), (@menuret ? (@menuret == 1 ? NPC436 : NPC437) : NPC_NO_SPRITE));
+ //debugmes "Sprite %s (%s) to %d", strnpcinfo(0), instance_npcname(strnpcinfo(0)), (@menuret ? (@menuret == 1 ? NPC436 : NPC437) : NPC_NO_SPRITE);
+ // What about 374 instead of 437?
+ // Unlock the gate once the puzzle is complete
+ if (X21CANDLE_CHECK()) {
+ delcells("X21L1@"+X21ID());
+ specialeffect(FX_MAGIC_WICKED_SPAWN, AREA, getcharid(3));
+ getexp X21_EXP_PUZZLE, 0;
+ getitem AquaTicket, X21_TICKET_PUZZLE;
+ X21INIT();
+ }
+ closeclientdialog;
+ close;
+OnInit:
+ .distance=1;
+ end;
+}
+
+081-2,131,257,0 duplicate(#X21Candle_0) #X21Candle_1 NPC_NO_SPRITE
+081-2,132,249,0 duplicate(#X21Candle_0) #X21Candle_2 NPC_NO_SPRITE
+081-2,133,253,0 duplicate(#X21Candle_0) #X21Candle_3 NPC_NO_SPRITE
+081-2,134,258,0 duplicate(#X21Candle_0) #X21Candle_4 NPC_NO_SPRITE
+081-2,135,251,0 duplicate(#X21Candle_0) #X21Candle_5 NPC_NO_SPRITE
+
+081-2,79,279,0 script Hint#X21C0 NPC_NO_SPRITE,{
+ .@c = 0;
+ .@c += ('COLORCODE[0] ? 0 : 1);
+ .@c += ('COLORCODE[1] ? 0 : 1);
+ .@c += ('COLORCODE[2] ? 0 : 1);
+ .@c += ('COLORCODE[3] ? 0 : 1);
+ .@c += ('COLORCODE[4] ? 0 : 1);
+ .@c += ('COLORCODE[5] ? 0 : 1);
+ dispbottom l("%d candles should always be off.", .@c);
+ end;
+OnInit:
+ .distance=1;
+ end;
+}
+
+081-2,65,274,0 script Hint#X21C1 NPC_NO_SPRITE,{
+ .@c = 0;
+ .@c += ('COLORCODE[0] == 1 ? 1 : 0);
+ .@c += ('COLORCODE[1] == 1 ? 1 : 0);
+ .@c += ('COLORCODE[2] == 1 ? 1 : 0);
+ .@c += ('COLORCODE[3] == 1 ? 1 : 0);
+ .@c += ('COLORCODE[4] == 1 ? 1 : 0);
+ .@c += ('COLORCODE[5] == 1 ? 1 : 0);
+ dispbottom l("%d candles should use regular fire.", .@c);
+ end;
+OnInit:
+ .distance=1;
+ end;
+}
+
+081-2,51,271,0 script Hint#X21C2 NPC_NO_SPRITE,{
+ if ('COLORCODE[0] == 2)
+ dispbottom l("The leftmost candle is magical.");
+ else if ('COLORCODE[5] == 2)
+ dispbottom l("The rightmost candle is magical.");
+ else
+ dispbottom l("Neither extremes are magical.");
+OnInit:
+ .distance=1;
+ end;
+}
+
+081-2,41,267,0 script Hint#X21C3 NPC_NO_SPRITE,{
+ if ('COLORCODE[0] == 'COLORCODE[5])
+ dispbottom l("The leftmost and the rightmost candle are identical.");
+ else
+ dispbottom l("The extremes are different.");
+OnInit:
+ .distance=1;
+ end;
+}
+
+081-2,51,247,0 script Hint#X21C4 NPC_NO_SPRITE,{
+ .@n$ = ('COLORCODE[2] ? ('COLORCODE[2] == 1 ? "Regular" : "Magical") : "No");
+ dispbottom l("The northmost candle should use %s Fire.", .@n$);
+OnInit:
+ .distance=1;
+ end;
+}
+
+081-2,63,236,0 script Hint#X21C5 NPC_NO_SPRITE,{
+ .@n$="";
+ if (!'COLORCODE[3])
+ .@n$=('OBSCURECOLOR ? "Regular" : "Magical");
+ else if ('COLORCODE[3] == 1)
+ .@n$=('OBSCURECOLOR ? "No" : "Magical");
+ else
+ .@n$=('OBSCURECOLOR ? "Regular" : "No");
+
+ dispbottom l("The central candle should NOT use %s Fire.", .@n$);
+OnInit:
+ .distance=1;
+ end;
+}
+
+081-2,88,257,0 script Hint#X21C6 NPC_NO_SPRITE,{
+ .@c=('COLORCODE[1]+'COLORCODE[4]);
+ if (.@c <= 1)
+ .@n$="Bleakness";
+ else if (.@c >= 3)
+ .@n$="Brightness, perhaps Magical Fire";
+ else
+ .@n$="either Regular Fire, or they may diverge";
+
+ dispbottom l("The two southern candles lean towards %s.", .@n$);
+OnInit:
+ .distance=1;
+ end;
+}
+
+081-2,105,213,0 script #X21Barrier1 NPC_HIDDEN,2,0,{
+ if (!'XMAS21CANDLE)
+ npctalkonce l("The strong shall yield the powder to enlighten the way, according to the established sequence.");
+ end;
+OnInit:
+ .distance=1;
+ end;
+}
+
+/* *************************************************************************** */
+/* Seeds Puzzle - See 2021.txt */
+
+081-2,127,129,0 script #X21Barrier2 NPC_HIDDEN,4,0,{
+ if (!'XMAS21TREE)
+ npctalkonce l("In this year Christmas, kids shall play ball. Once the Christmas Tree is set, the festivities may proceed.");
+ end;
+OnInit:
+ .distance=1;
+ end;
+}
+
+081-2,118,169,0 script #X21SeedFlag NPC_NO_SPRITE,{
+ if (!'XMAS21TREE)
+ npctalkonce l("The leaves in a room, the objects from the other; When they merge together, the festivities shall proceed.");
+ end;
+OnInit:
+ .distance=2;
+ end;
+}
+
+/* *************************************************************************** */
+/* Ambush Puzzle */
+
+081-2,49,73,0 script #X21Barrier3 NPC_HIDDEN,2,0,{
+ if (!'XMAS21AMBUSH)
+ npctalkonce l("One room, Seventeen buttons, Three truths, and a lever to bring joy to the children.");
+ end;
+OnInit:
+ .distance=1;
+ end;
+}
+
+081-2,112,90,0 script Nutcracker O'Panel#X21 NPC_NO_SPRITE,{
+ if ('XMAS21AMBUSH)
+ end;
+ mes l("This Nutcracker has a lever on it.");
+ mesc l("Do you flip it?"), 1;
+ next;
+ if (askyesno() == ASK_NO) {
+ closeclientdialog;
+ close;
+ }
+
+ .@goal = (2**'AMBUSHTRI[0]) + (2**'AMBUSHTRI[1]) + (2**'AMBUSHTRI[2]);
+ if ('AMBUSH_BMASK == .@goal) {
+ npctalk l("*beep*");
+ delcells("X21L3@"+X21ID());
+ 'XMAS21AMBUSH = true;
+ specialeffect(FX_MAGIC_WICKED_SPAWN, AREA, getcharid(3));
+ getexp X21_EXP_PUZZLE, 0;
+ getitem AquaTicket, X21_TICKET_PUZZLE;
+ X21INIT();
+ } else {
+ npctalk l("*click*");
+ }
+ 'AMBUSH_BMASK = 0;
+ .@amb = any(ManaGuardian, ManaGuardian, ManaSlayer, (X21TYPE() == IOT_PARTY ? ManaTyrant : ManaGuardian), Tengu, Sasquatch);
+ areamonster getmap(), 108, 82, 128, 102, strmobinfo(1, .@amb), .@amb, 1;
+ closeclientdialog;
+ close;
+OnInit:
+ .distance=1;
+ end;
+}
+
+081-2,117,87,0 script Star of Bethlem#X21_0 NPC_NO_SPRITE,{
+ if ('XMAS21AMBUSH)
+ end;
+ // Extract ID
+ .@n$=strnpcinfo(2, "_-1");
+ explode(.@ni$, .@n$, "_");
+ .@id=atoi(.@ni$[1]);
+ if (.@id < 0) { consolemes(CONSOLEMES_ERROR, "ERROR, X21 Invalid Ambush Hint"); end; }
+
+ .@n='AMBUSHTRI[.@id]+2; // Account for the invisible frame
+ .@c=(.@n % 5 == 0) ? 5 : (.@n % 5);
+ .@r=(.@n / 5)+((.@n % 5 == 0) ? 0 : 1);
+ npctalkonce l("%s column, %s row", X21SRLZ(.@c), X21SRLZ(.@r));
+ end;
+OnInit:
+ .distance=1;
+ end;
+}
+081-2,124,94,0 duplicate(Star of Bethlem#X21_0) Canes#X21_1 NPC_NO_SPRITE
+081-2,120,100,0 duplicate(Star of Bethlem#X21_0) Crown#X21_2 NPC_NO_SPRITE
+
+081-2,60,118,0 script Switch#X21AB_1 NPC_NO_SPRITE,{
+ if ('XMAS21AMBUSH)
+ end;
+ // Extract ID
+ .@n$=strnpcinfo(2, "_0");
+ explode(.@ni$, .@n$, "_");
+ .@id=atoi(.@ni$[1]);
+ if (.@id < 1) { consolemes(CONSOLEMES_ERROR, "ERROR, X21 Invalid Ambush Switch"); end; }
+
+ // Already flipped
+ .@bit=(2**.@id);
+ if ('AMBUSH_BMASK & .@bit)
+ end;
+
+ mesc l("There is a switch here.");
+ mesc l("Do you flip it?"), 1;
+ next;
+ if (askyesno() == ASK_YES) {
+ 'AMBUSH_BMASK = 'AMBUSH_BMASK | .@bit;
+ npctalk l("*click*");
+ }
+
+ closeclientdialog;
+ close;
+OnInit:
+ .distance=1;
+ end;
+}
+081-2,65,118,0 duplicate(Switch#X21AB_1) Switch#X21AB_2 NPC_NO_SPRITE
+081-2,70,118,0 duplicate(Switch#X21AB_1) Switch#X21AB_3 NPC_NO_SPRITE
+081-2,50,123,0 duplicate(Switch#X21AB_1) Switch#X21AB_4 NPC_NO_SPRITE
+081-2,55,123,0 duplicate(Switch#X21AB_1) Switch#X21AB_5 NPC_NO_SPRITE
+081-2,60,123,0 duplicate(Switch#X21AB_1) Switch#X21AB_6 NPC_NO_SPRITE
+081-2,65,123,0 duplicate(Switch#X21AB_1) Switch#X21AB_7 NPC_NO_SPRITE
+081-2,70,123,0 duplicate(Switch#X21AB_1) Switch#X21AB_8 NPC_NO_SPRITE
+081-2,50,128,0 duplicate(Switch#X21AB_1) Switch#X21AB_9 NPC_NO_SPRITE
+081-2,55,128,0 duplicate(Switch#X21AB_1) Switch#X21AB_10 NPC_NO_SPRITE
+081-2,60,128,0 duplicate(Switch#X21AB_1) Switch#X21AB_11 NPC_NO_SPRITE
+081-2,65,128,0 duplicate(Switch#X21AB_1) Switch#X21AB_12 NPC_NO_SPRITE
+081-2,70,128,0 duplicate(Switch#X21AB_1) Switch#X21AB_13 NPC_NO_SPRITE
+081-2,50,133,0 duplicate(Switch#X21AB_1) Switch#X21AB_14 NPC_NO_SPRITE
+081-2,55,133,0 duplicate(Switch#X21AB_1) Switch#X21AB_15 NPC_NO_SPRITE
+081-2,60,133,0 duplicate(Switch#X21AB_1) Switch#X21AB_16 NPC_NO_SPRITE
+081-2,65,133,0 duplicate(Switch#X21AB_1) Switch#X21AB_17 NPC_NO_SPRITE
+
+/* *************************************************************************** */
+/* The Enigma */
+
+/*
+// FIXME (137,68 and 130,56 and 120,69) (no sprite) (no code)
+081-2,137,68,0 script Enigma#X21A1 NPC_NO_SPRITE,{
+ mes l("Three Enigmas, one always say the truth, one always says a lie, and the third may either say the truth or a lie.");
+ mes "";
+ if ('TRUTHSTAT[0])
+ mesc l("The truth"), 1;
+ else
+ mesc l("A lie"), 1;
+ close;
+OnInit:
+ .distance=2;
+ end;
+}
+*/
+
diff --git a/npc/081-3/_import.txt b/npc/081-3/_import.txt
new file mode 100644
index 00000000..5e2a38a7
--- /dev/null
+++ b/npc/081-3/_import.txt
@@ -0,0 +1,3 @@
+// Map 081-3: Existential Frontier
+// This file is generated automatically. All manually added changes will be removed when running the Converter.
+"npc/081-3/logic.txt",
diff --git a/npc/081-3/logic.txt b/npc/081-3/logic.txt
new file mode 100644
index 00000000..d77bd34c
--- /dev/null
+++ b/npc/081-3/logic.txt
@@ -0,0 +1,203 @@
+// TMW Script.
+// Author:
+// Jesusalva
+// Notes:
+// Christmas 2021 - Boss Chamber #1
+// FIXME: What if players *fail* this? e.g. die
+
+081-3,44,36,0 script Pentagram#Xmas21A NPC424,{
+ .@id = X21ID();
+ if ($@XMAS21_BC1[.@id] == X21BC1_ON) end;
+ .@n$ = instance_npcname("Pentagram#Xmas21A");
+ .@cl = X21BC1_CLEAR();
+
+ mesn;
+ mesq l("Should we break the Pentagram?");
+ next;
+ select
+ l("I'm not ready yet."),
+ l("Bring it on!"),
+ rif(.@cl, l("Make it harder!")),
+ rif(.@cl, l("Make it a nightmare!")),
+ rif(.@cl, l("Kill me semi-instantly!"));
+ mes "";
+ closeclientdialog;
+ if (@menu == 1 || 'LOCKD) {
+ close;
+ }
+
+ 'LOCKD = true;
+ 'BC1LV = @menu - 1;
+ @BC1ME = getcharid(3);
+ .@mg = monster(getmap(), 44, 36, "Der Schneemann", SnowmanBoss, 1, "Pentagram#Xmas21A::OnFin");
+
+ // Prepare the difficulty settings
+ // Each difficulty level doubles the nÂș of players difficulty setting
+ // Can go up to 12 * 4 = 48, but we use 16 as the theoretical max
+ .@df = max(1, getmapusers(getmap()) * ('BC1LV * 2));
+ .@lv = BaseLevel;
+
+ setunitdata(.@mg, UDT_MAXHP, 5000 + (.@lv * 150) + (1200 * .@df));
+ setunitdata(.@mg, UDT_HP, 5000 + (.@lv * 150) + (1200 * .@df));
+
+ setunitdata(.@mg, UDT_ADELAY, 2200 - max(1200, 75 * .@df));
+ setunitdata(.@mg, UDT_ATKRANGE, 3);
+
+ /*
+ setunitdata(.@mg, UDT_ATKMIN, .@lv*4+.@df*3);
+ setunitdata(.@mg, UDT_ATKMAX, .@lv*4+.@df*3);
+ */
+ setunitdata(.@mg, UDT_ATKMIN, (.@lv+.@df)*5);
+ setunitdata(.@mg, UDT_ATKMAX, (.@lv+.@df)*6);
+ setunitdata(.@mg, UDT_DEF, min(50+.@df, 98));
+ setunitdata(.@mg, UDT_MDEF, min(50+.@df, 98));
+ setunitdata(.@mg, UDT_HIT, 9999);
+
+ // Destroy the instance only after half hour has passed
+ instance_set_timeout(1800, 1800);
+ initnpctimer;
+ setnpctimer(39000);
+ disablenpc(.@n$);
+ $@XMAS21_BC1[.@id] = X21BC1_ON;
+ .BC1ID = .@mg;
+ close;
+
+OnFin:
+ if (!playerattached()) {
+ consolemes(CONSOLEMES_ERROR, "Player not attached after Christmas 2021 victory! Script aborted!");
+ end;
+ }
+ killmonsterall(getmap());
+ getexp 10000, 0;
+ getitem AquaTicket, 1 + 'BC1LV;
+ maptimer(getmap(), 10, "Pentagram#Xmas21A::OnFlush");
+ end;
+
+// Distribute experience and handle quest progress
+OnFlush:
+ getexp 5000, 0;
+ if (@BC1ME > 0) {
+ if (!X21BC1_CLEAR()) {
+ dispbottom l("Whatever that guardian was, the path is now clear, and the gate, open.");
+ X21BC1_DOCLEAR();
+ }
+ }
+ @BC1ME = 0;
+ .@id = X21ID();
+ $@XMAS21_BC1[.@id] = X21BC1_OFF;
+ getitem AquaTicket, X21_TICKET_SUPPORT + ('BC1LV / 2);
+ end;
+
+// Pseudo-Magic
+OnTimer40000:
+ if (getunittype(.BC1ID) < 0) {
+ stopnpctimer;
+ end;
+ }
+
+ .CYCLE += 1;
+ .@m$=getmapinfo(MAPINFO_NAME); // We could use .map$ too
+ areamonster(.@m$, 31, 23, 58, 50, "Snowballz", Snowman, 1+getmapusers(.@m$));
+
+ if (.CYCLE % 2 != 0) {
+ initnpctimer;
+ end;
+ }
+
+ // The boss special skill depends on its health
+ .@hp = 100 * getunitdata(.BC1ID, UDT_HP) / getunitdata(.BC1ID, UDT_MAXHP);
+
+ // HP > 70%, just some warm-up
+ if (.@hp > 70) {
+ unittalk(.BC1ID, "Blizzard!");
+ rectharm(.BC1ID, 6, 6, rand2(250), HARM_MAGI, Ele_Neutral, "filter_always", BL_PC);
+
+ // HP < 70%, cripple half the attackers
+ } else if (.@hp > 40) {
+ .@sc = any(SC_SILENCE, SC_BLIND);
+ if (.@sc == SC_SILENCE)
+ unittalk(.BC1ID, "Silence!");
+ else
+ unittalk(.BC1ID, "Blind!");
+ areasc2(.@m$, 44, 36, 14, rand2(15000), BL_PC, .@sc);
+ rectharm(.BC1ID, 5, 5, rand2(50), HARM_MAGI, Ele_Neutral, "filter_always", BL_PC);
+
+ // HP < 40%, cause great damage
+ } else if (.@hp > 15) {
+ unittalk(.BC1ID, "Greater Blizzard!");
+ rectharm(.BC1ID, 8, 8, 100+rand2(250), HARM_MAGI, Ele_Neutral, "filter_always", BL_PC);
+
+ // HP < 15%, use more dangerous conditions
+ } else {
+ .@sc = any(SC_POISON, SC_CURSE);
+ if (.@sc == SC_SILENCE)
+ unittalk(.BC1ID, "Poison!");
+ else
+ unittalk(.BC1ID, "Curse!");
+ areasc2(.@m$, 44, 36, 14, rand2(15000), BL_PC, .@sc);
+ rectharm(.BC1ID, 5, 5, rand2(100), HARM_MAGI, Ele_Neutral, "filter_always", BL_PC);
+ }
+
+ // Repeat forever
+ initnpctimer;
+ end;
+
+OnClean:
+ enablenpc(.name$);
+ killmonsterall(getmapinfo(MAPINFO_NAME));
+OnInit:
+ .distance=2;
+ .BC1ID = 0;
+ .CYCLE = 0;
+ end;
+}
+
+
+081-3,44,22,0 script #0813Nexit NPC_HIDDEN,0,0,{
+ end;
+OnTouch:
+ if (!isChristmas21()) end;
+ if (!X21BC1_CLEAR()) {
+ dispbottom l("A strong magic barrier prevents me from using this exit.");
+ end;
+ }
+ .@id = X21ID();
+ if ($@XMAS21_BC1[.@id] == X21BC1_ON) end;
+ if ($@XMAS21_BC1[.@id] == X21BC1_OFF) {
+ mesn l("Northern Lights");
+ mes l("Past this gate, beyond the existential frontier, I can see the Northern Lights.");
+ mes l("Should we walk toward it?");
+ next;
+ select
+ l("Yes"),
+ l("Not yet"),
+ l("[Run away]");
+ mes "";
+ if (@menu == 1) {
+ /* We need to determine if instance exists and is needed */
+ X21INIT();
+
+ .@mapn$="0803@"+X21ID2();
+ warp .@mapn$, 83, 154;
+ }
+ // NOTE: Right now, anyone can do so?
+ else if (@menu == 3) {
+ enablenpc(instance_npcname("Pentagram#Xmas21A"));
+ warp "081-2", 90, 21;
+ 'LOCKD = false;
+ }
+ closeclientdialog;
+ close;
+ }
+ end;
+}
+
+081-3,44,50,0 script #0813Sexit NPC_HIDDEN,0,0,{
+ end;
+OnTouch:
+ .@id = X21ID();
+ if ($@XMAS21_BC1[.@id] == X21BC1_ON) end;
+ warp "081-2", 90, 21;
+ end;
+}
+
diff --git a/npc/_import.txt b/npc/_import.txt
index b8dcd152..7ac2d194 100644
--- a/npc/_import.txt
+++ b/npc/_import.txt
@@ -121,6 +121,11 @@
@include "npc/069-2/_import.txt"
@include "npc/070-1/_import.txt"
@include "npc/070-3/_import.txt"
+@include "npc/080-1/_import.txt"
+@include "npc/080-3/_import.txt"
+@include "npc/081-1/_import.txt"
+@include "npc/081-2/_import.txt"
+@include "npc/081-3/_import.txt"
@include "npc/099-1/_import.txt"
@include "npc/099-2/_import.txt"
@include "npc/099-3/_import.txt"
diff --git a/npc/annuals/xmas/2021.txt b/npc/annuals/xmas/2021.txt
new file mode 100644
index 00000000..27451e99
--- /dev/null
+++ b/npc/annuals/xmas/2021.txt
@@ -0,0 +1,565 @@
+// TMW Script.
+// Author:
+// Jesusalva
+// Notes:
+// Christmas 2021 - Configuration Files
+
+function script X21ID {
+ return (getcharid(1) > 0 ? getcharid(1) : getcharid(3));
+}
+
+function script X21ID2 {
+ return (getcharid(1) > 0 ? getcharid(1) : getcharid(0));
+}
+
+function script X21TYPE {
+ return (getcharid(1) > 0 ? IOT_PARTY : IOT_CHAR);
+}
+
+function script X21BC1_CLEAR {
+ return (XMAS2021 & X21_BOSSDEFEAT);
+}
+
+function script X21BC1_DOCLEAR {
+ XMAS2021 = (XMAS2021 | X21_BOSSDEFEAT);
+ return;
+}
+function script X21ED1_CLEAR {
+ return (XMAS2021 & X21_ALYSSARESCUE);
+}
+
+function script X21ED1_DOCLEAR {
+ XMAS2021 = (XMAS2021 | X21_ALYSSARESCUE);
+ return;
+}
+
+function script X21SRLZ {
+ .@n=getarg(0);
+ switch (.@n) {
+ case 1: return l("first");
+ case 2: return l("second");
+ case 3: return l("third");
+ case 4: return l("fourth");
+ case 5: return l("fifth");
+ //case 6: return l("sixth");
+ //case 7: return l("seventh");
+ }
+ return str(.@n);
+}
+
+function script X21QUESTON {
+ // Create and setup the challenge. Allow to reset quest.
+ XMAS2021 = X21_ACCEPTED;
+ /* Prepare something */
+ return;
+}
+
+function script X21INIT {
+ .@inst = instance_create("Northern Lights", X21ID(), X21TYPE());
+ // Instance must be created
+ if (.@inst >= 0) {
+ // We... Could use base name, actually, but whatever
+ .@mapa$ = "0812@"+X21ID2(); // Northern Lights
+ .@mapb$ = "0813@"+X21ID2(); // Existential Frontier
+ .@mapc$ = "0803@"+X21ID2(); // Forgotten/Mystic/Peaceful Glade
+ .@mapd$ = "0801@"+X21ID2(); // Peaceful Glade (Present)
+
+ instance_attachmap("081-2", .@inst, false, .@mapa$);
+ instance_attachmap("081-3", .@inst, false, .@mapb$);
+ instance_attachmap("080-3", .@inst, false, .@mapc$);
+ instance_attachmap("080-1", .@inst, false, .@mapd$);
+
+ instance_set_timeout(1800, 1800, .@inst);
+ instance_init(.@inst);
+
+ /* Create locks */
+ setcells .@mapa$, 103, 211, 107, 212, 1, "X21L1@"+X21ID();
+ setcells .@mapa$, 124, 127, 131, 128, 1, "X21L2@"+X21ID();
+ setcells .@mapa$, 47, 71, 51, 72, 1, "X21L3@"+X21ID();
+ setcells .@mapd$, 81, 131, 86, 135, 1, "X21LF@"+X21ID();
+
+ /* Spawn Special Monsters */
+ "#XMAS21Core"::spawn(Grinchboo, 5, .@mapa$);
+ "#XMAS21Core"::spawn(Grinchboo, 3, .@mapc$);
+ "#XMAS21Core"::spawn(Moonshroom, 10, .@mapa$);
+ "#XMAS21Core"::spawn(Moonshroom, 14, .@mapc$);
+ "#XMAS21Core"::spawn2(BlueSpark, 47, 191, 65, 212, 2, .@mapa$);
+ "#XMAS21Core"::spawn2(RedSpark, 47, 191, 65, 212, 2, .@mapa$);
+ if (X21TYPE() == IOT_PARTY)
+ "#XMAS21Core"::spawn2(Koyntety, 70, 250, 99, 265, 1, .@mapa$, true);
+ else
+ "#XMAS21Core"::spawn2(ManaGuardian, 70, 250, 99, 265, 1, .@mapa$, true);
+
+ /* Northern Lights Dungeon */
+ "#XMAS21Core"::spawn(WhiteSlime, 37, .@mapa$);
+ "#XMAS21Core"::spawn(Archant, 7, .@mapa$);
+ //"#XMAS21Core"::spawn(BlueSpark, 1, .@mapa$); //?
+ //"#XMAS21Core"::spawn(RedSpark, 1, .@mapa$); //?
+ "#XMAS21Core"::spawn(AzulSlime, 21, .@mapa$);
+ "#XMAS21Core"::spawn(SantaSlime, 18, .@mapa$);
+ "#XMAS21Core"::spawn(Moggun, 27, .@mapa$);
+ "#XMAS21Core"::spawn(Yeti, 4, .@mapa$);
+ "#XMAS21Core"::spawn(Wisp, 5, .@mapa$);
+ "#XMAS21Core"::spawn(Poltergeist, 12, .@mapa$);
+ "#XMAS21Core"::spawn(Spectre, 5, .@mapa$);
+ "#XMAS21Core"::spawn(ManaGhost, 14, .@mapa$);
+ "#XMAS21Core"::spawn(IceElement, 2, .@mapa$);
+ "#XMAS21Core"::spawn(IceSkull, 2, .@mapa$);
+ "#XMAS21Core"::spawn(Nutcracker, 6, .@mapa$);
+ "#XMAS21Core"::spawn(Fluffy, 16, .@mapa$);
+
+ /* Peaceful/Mystic/Forgotten Glade (Past) */
+ "#XMAS21Core"::spawn(CrotcherScorpion, 10, .@mapc$);
+ "#XMAS21Core"::spawn(Snail, 18, .@mapc$);
+ "#XMAS21Core"::spawn(Bee, 9, .@mapc$);
+ "#XMAS21Core"::spawn(Butterfly, 24, .@mapc$);
+ "#XMAS21Core"::spawn(PinkFlower, 42, .@mapc$);
+ "#XMAS21Core"::spawn(Pinkie, 32, .@mapc$);
+ "#XMAS21Core"::spawn(Hyvern, 6, .@mapc$);
+ "#XMAS21Core"::spawn(WitchGuard, 8, .@mapc$);
+
+ /* Peaceful/Mystic/Forgotten Glade (Present) */
+ "#XMAS21Core"::spawn(Fluffy, 9, .@mapd$);
+ "#XMAS21Core"::spawn(Snail, 11, .@mapd$);
+ "#XMAS21Core"::spawn(Bee, 5, .@mapd$);
+ "#XMAS21Core"::spawn(Butterfly, 15, .@mapd$);
+ "#XMAS21Core"::spawn(PinkFlower, 24, .@mapd$);
+ "#XMAS21Core"::spawn(Pinkie, 18, .@mapd$);
+ "#XMAS21Core"::spawn(Hyvern, 3, .@mapd$);
+
+ /* The Three Guardians */
+ .@bon = 0;
+ if (X21TYPE() == IOT_PARTY)
+ .@bon = 20000;
+
+ .@mg = monster(.@mapd$, 43, 47, "Der Schneemann (Alpha)", SnowmanBoss, 1, "#X21BarrierF::OnKil");
+ .@hp = .@bon + rand2(35000, 47000);
+ setunitdata(.@mg, UDT_MAXHP, .@hp);
+ setunitdata(.@mg, UDT_HP, .@hp);
+ setunitdata(.@mg, UDT_ATKRANGE, 2);
+ setunitdata(.@mg, UDT_ATKMIN, 30);
+ setunitdata(.@mg, UDT_ATKMAX, 40);
+
+ .@mg = monster(.@mapd$, 144, 154, "Der Schneemann (Beta)", SnowmanBoss, 1, "#X21BarrierF::OnKil");
+ .@hp = .@bon + rand2(35000, 47000);
+ setunitdata(.@mg, UDT_MAXHP, .@hp);
+ setunitdata(.@mg, UDT_HP, .@hp);
+ setunitdata(.@mg, UDT_ATKRANGE, 2);
+ setunitdata(.@mg, UDT_ATKMIN, 30);
+ setunitdata(.@mg, UDT_ATKMAX, 40);
+
+ .@mg = monster(.@mapd$, 42, 147, "Der Schneemann (Gamma)", SnowmanBoss, 1, "#X21BarrierF::OnKil");
+ .@hp = .@bon + rand2(35000, 47000);
+ setunitdata(.@mg, UDT_MAXHP, .@hp);
+ setunitdata(.@mg, UDT_HP, .@hp);
+ setunitdata(.@mg, UDT_ATKRANGE, 2);
+ setunitdata(.@mg, UDT_ATKMIN, 30);
+ setunitdata(.@mg, UDT_ATKMAX, 40);
+
+ /* The Three Guardians Friends */
+ if (X21TYPE() == IOT_PARTY) { .@g = 12; } else { .@g = 7; }
+
+ freeloop(true);
+ for (.@i=0; .@i < .@g; .@i++) {
+ .@mg = areamonster(.@mapd$, 30, 38, 50, 51, "Snowman (Alpha)", Snowman, 1);
+ .@hp = rand2(3500, 5700);
+ .@ak = rand2(70, 120);
+ setunitdata(.@mg, UDT_MAXHP, .@hp);
+ setunitdata(.@mg, UDT_HP, .@hp);
+ setunitdata(.@mg, UDT_ATKRANGE, 1);
+ setunitdata(.@mg, UDT_ATKMIN, .@ak);
+ setunitdata(.@mg, UDT_ATKMAX, .@ak+20);
+ }
+
+ for (.@i=0; .@i < .@g; .@i++) {
+ .@mg = areamonster(.@mapd$, 30, 38, 50, 51, "Snowman (Beta)", Snowman, 1);
+ .@hp = rand2(3500, 5700);
+ .@ak = rand2(70, 120);
+ setunitdata(.@mg, UDT_MAXHP, .@hp);
+ setunitdata(.@mg, UDT_HP, .@hp);
+ setunitdata(.@mg, UDT_ATKRANGE, 1);
+ setunitdata(.@mg, UDT_ATKMIN, .@ak);
+ setunitdata(.@mg, UDT_ATKMAX, .@ak+20);
+ }
+
+ for (.@i=0; .@i < .@g; .@i++) {
+ .@mg = areamonster(.@mapd$, 30, 38, 50, 51, "Snowman (Gamma)", Snowman, 1);
+ .@hp = rand2(3500, 5700);
+ .@ak = rand2(70, 120);
+ setunitdata(.@mg, UDT_MAXHP, .@hp);
+ setunitdata(.@mg, UDT_HP, .@hp);
+ setunitdata(.@mg, UDT_ATKRANGE, 1);
+ setunitdata(.@mg, UDT_ATKMIN, .@ak);
+ setunitdata(.@mg, UDT_ATKMAX, .@ak+20);
+ }
+ freeloop(false);
+
+ }
+ if (instance_id() >= 0)
+ instance_set_timeout(1800, 1800);
+ return;
+}
+
+function script X21CANDLE_CHECK {
+ if ('XMAS21CANDLE) return false;
+ // NOTE: We could check here if player already finished this
+ // And skip the challenge after litting one candle?
+ if ('X21_CANDLE[0] != 'COLORCODE[0]) return false;
+ if ('X21_CANDLE[1] != 'COLORCODE[1]) return false;
+ if ('X21_CANDLE[2] != 'COLORCODE[2]) return false;
+ if ('X21_CANDLE[3] != 'COLORCODE[3]) return false;
+ if ('X21_CANDLE[4] != 'COLORCODE[4]) return false;
+ if ('X21_CANDLE[5] != 'COLORCODE[5]) return false;
+ // This instance challenge is over
+ 'XMAS21CANDLE = true;
+ // Record on player as well, just to be safe.
+ if (XMAS2021 & X21_CANDLES) return true;
+ XMAS2021 = (XMAS2021 | X21_CANDLES);
+ return true;
+}
+
+function script X21SEED_CHECK {
+ /* Protip: You don't have to fill all spots
+ *
+ * If you plant a "L" it also works
+ * This is because "L" is the first letter of "Lazy"
+ * And I was particularly lazy to make a proper script
+ */
+ if ('XMAS21TREE) return false;
+ // NOTE: We could check here if player already finished this
+ // And skip the challenge after planting one tree?
+ if (!'XMAS21TREE_X[116]) return false;
+ if (!'XMAS21TREE_X[117]) return false;
+ if (!'XMAS21TREE_X[118]) return false;
+ if (!'XMAS21TREE_X[119]) return false;
+ if (!'XMAS21TREE_X[120]) return false;
+ if (!'XMAS21TREE_Y[168]) return false;
+ if (!'XMAS21TREE_Y[169]) return false;
+ if (!'XMAS21TREE_Y[170]) return false;
+ // This instance challenge is over
+ 'XMAS21TREE = true;
+ // Record on player as well, just to be safe.
+ if (XMAS2021 & X21_PLANTPUZZLE) return true;
+ XMAS2021 = (XMAS2021 | X21_PLANTPUZZLE);
+ return true;
+}
+
+function script X21_SEEDS {
+ getmapxy(.@m$, .@x, .@y, 0);
+ // If planted in the wrong region, vanish without effect
+ if (.@m$ != "0812@"+X21ID2())
+ return;
+ if (.@x < 107 || .@x > 126)
+ return;
+ if (.@y < 159 || .@y > 177)
+ return;
+ // Should never happen, so nuke everything
+ //if (instance_id() < 0) end;
+ // Already planted this spot
+ if ('XMAS21TREE_X[.@x] && 'XMAS21TREE_Y[.@y]) return;
+ // Record the success planting it
+ 'XMAS21TREE_X[.@x] = true;
+ 'XMAS21TREE_Y[.@y] = true;
+ // Sprout a Christmas Tree IF position was not already filled
+ monster(.@m$, .@x, .@y, strmobinfo(1, ChristmasTree), ChristmasTree, 1, "#XMAS21Core::OnX1164");
+ // Unlock the gate once the puzzle is complete
+ if (X21SEED_CHECK()) {
+ delcells("X21L2@"+X21ID());
+ specialeffect(FX_MAGIC_WICKED_SPAWN, AREA, getcharid(3));
+ getexp X21_EXP_PUZZLE, 0;
+ getitem AquaTicket, X21_TICKET_PUZZLE;
+ X21INIT();
+ }
+ return;
+}
+
+/* You cannot fiddle with party in this region */
+080-1 mapflag partylock
+080-3 mapflag partylock
+//081-1 mapflag partylock
+081-2 mapflag partylock
+081-3 mapflag partylock
+
+081-2 mapflag bexp 125
+080-3 mapflag bexp 125
+080-1 mapflag bexp 125
+
+/* Secure regions */
+081-3 mapflag zone MMO
+081-3 mapflag nosave 081-1,68,31
+
+/* Script Core */
+080-1,0,0,0 script #XMAS21Core NPC_HIDDEN,{
+ end;
+
+// "#XMAS21Core"::spawn(MonsterID, {Amount=1, {Map=this}})
+public function spawn {
+ if (playerattached())
+ .@m$=getarg(2, getmap());
+ else
+ .@m$=getarg(2);
+ .@n$="#XMAS21Core::On"+getarg(0);
+ if (mobcount(.@m$, .@n$) < 200)
+ areamonster .@m$, 20, 20, getmapinfo(MAPINFO_SIZE_X, .@m$)-20, getmapinfo(MAPINFO_SIZE_Y, .@m$)-20, strmobinfo(1, getarg(0)), getarg(0), getarg(1, 1), .@n$;
+
+ /* Aqua Ticket Drops */
+ if (playerattached()) {
+ if (instance_id() < 0) return; // No drops outside instanced maps
+ fix_mobkill(getarg(0));
+ }
+ return;
+}
+
+// "#XMAS21Core"::spawn2(MonsterID, X1, Y1, X2, Y2, {Amount=1, {Map=this}})
+// Function Changes from On<ID> to OnX<ID>
+public function spawn2 {
+ .@m$=getarg(6, getmap());
+ .@n$="#XMAS21Core::OnX"+getarg(0);
+ .@x1=max(getarg(1), 20);
+ .@y1=max(getarg(2), 20);
+ .@x2=min(getarg(3), getmapinfo(MAPINFO_SIZE_X, .@m$)-20);
+ .@y2=min(getarg(4), getmapinfo(MAPINFO_SIZE_Y, .@m$)-20);
+ .@noob=getarg(7, false);
+ /*
+ // Radius-based, was scrapped in favor of rectangles
+ .@x1=max(getarg(1)-getarg(3), 20);
+ .@y1=max(getarg(2)-getarg(3), 20);
+ .@x2=min(getarg(1)+getarg(3), getmapinfo(MAPINFO_SIZE_X, .@m$)-20);
+ .@y2=min(getarg(2)+getarg(3), getmapinfo(MAPINFO_SIZE_Y, .@m$)-20);
+ */
+ if (mobcount(.@m$, .@n$) < 200) {
+ .@mg=areamonster(.@m$, .@x1, .@y1, .@x2, .@y2, strmobinfo(1, getarg(0)), getarg(0), getarg(5, 1), .@n$);
+ if (.@noob && .@mg) {
+ .@hp = getunitdata(.@mg, UDT_MAXHP) * 3 / 5; // Reduce to 60% HP
+ .@ak = getunitdata(.@mg, UDT_ATKMIN) * 4 / 5; // Reduce to 80% ATK
+ .@ax = getunitdata(.@mg, UDT_ATKMAX) * 4 / 5; // Reduce to 80% ATK
+
+ setunitdata(.@mg, UDT_MAXHP, .@hp);
+ setunitdata(.@mg, UDT_HP, .@hp);
+ setunitdata(.@mg, UDT_ATKMIN, .@ak);
+ setunitdata(.@mg, UDT_ATKMAX, .@ax);
+ }
+ }
+ return;
+}
+
+/* Special */
+// Sparkles Chamber, drops seeds
+OnX1080:
+OnX1081:
+ spawn2(any(BlueSpark, RedSpark), 47, 191, 65, 212);
+ /* Give player enough seeds */
+ getmapxy(.@m$, .@x, .@y, 0);
+ .@x1=.@x-1;
+ .@y1=.@y-1;
+ .@x2=.@x+1;
+ .@y2=.@y+1;
+ .@x=cap_value(rand2(.@x1, .@x2), 47, 65);
+ .@y=cap_value(rand2(.@y1, .@y2), 191, 212);
+ makeitem(XmasSeeds, rand2(1, 3), .@m$, .@x, .@y);
+ end;
+// Mini-Boss Chamberlain, drops candle
+OnX1146:
+OnX1147:
+ spawn2(any(Flashmob, Koyntety), 70, 250, 99, 265, 1, getmap(), true);
+ /* Give player enough fuel potions */
+ getmapxy(.@m$, .@x, .@y, 0);
+ .@x1=.@x-1;
+ .@y1=.@y-1;
+ .@x2=.@x+1;
+ .@y2=.@y+1;
+ .@x=cap_value(rand2(.@x1, .@x2), 70, 99);
+ .@y=cap_value(rand2(.@y1, .@y2), 250, 265);
+ makeitem(ThermalNapalm, rand2(3, 5), .@m$, .@x, .@y);
+ end;
+// Mini-Boss Chamberlain, drops candle (easy mode)
+OnX1140:
+OnX1143:
+OnX1138:
+ spawn2(any(Tengu, ManaSlayer, ManaGuardian, ManaGuardian), 70, 250, 99, 265, 1, getmap(), true);
+ /* Give player enough fuel potions */
+ getmapxy(.@m$, .@x, .@y, 0);
+ .@x1=.@x-1;
+ .@y1=.@y-1;
+ .@x2=.@x+1;
+ .@y2=.@y+1;
+ .@x=cap_value(rand2(.@x1, .@x2), 70, 99);
+ .@y=cap_value(rand2(.@y1, .@y2), 250, 265);
+ makeitem(ThermalNapalm, rand2(3, 5), .@m$, .@x, .@y);
+ end;
+// Christmas Tree, spawns a (few?) gift boxes
+OnX1164:
+ getmapxy(.@m$, .@x, .@y, 0);
+ .@x1=.@x-2;
+ .@y1=.@y-2;
+ .@x2=.@x+2;
+ .@y2=.@y+2;
+ .@x=cap_value(rand2(.@x1, .@x2), 47, 65);
+ .@y=cap_value(rand2(.@y1, .@y2), 191, 212);
+ spawn2(ChristmasGift, .@x1, .@y1, .@x2, .@y2, rand2(1, 3));
+ end;
+// Christmas Gift Box, gives you Tickets
+OnX1165:
+ getmapxy(.@m$, .@x, .@y, 0);
+ .@x1=.@x-2;
+ .@y1=.@y-2;
+ .@x2=.@x+2;
+ .@y2=.@y+2;
+ .@x=cap_value(rand2(.@x1, .@x2), 47, 65);
+ .@y=cap_value(rand2(.@y1, .@y2), 191, 212);
+ makeitem(AquaTicket, any(1, 1, 2), .@m$, .@x, .@y);
+ end;
+
+/* Experimental */
+On1162:
+ spawn(Grinchboo, 2);
+ end;
+On1130:
+ spawn(Moonshroom);
+ end;
+
+/* Ice Caves */
+On1093:
+ spawn(WhiteSlime);
+ end;
+On1060:
+ spawn(Archant);
+ end;
+On1080:
+ spawn(BlueSpark);
+ end;
+On1081:
+ spawn(RedSpark);
+ end;
+On1100:
+ spawn(AzulSlime);
+ end;
+On1015:
+ spawn(SantaSlime);
+ end;
+On1061:
+ spawn(Moggun);
+ end;
+On1072:
+ spawn(Yeti);
+ end;
+On1040:
+ spawn(Wisp);
+ end;
+On1047:
+ spawn(Poltergeist);
+ end;
+On1042:
+ spawn(Spectre);
+ end;
+On1125:
+ spawn(ManaGhost);
+ end;
+On1071:
+ spawn(IceElement);
+ end;
+On1085:
+ spawn(IceSkull);
+ end;
+On1114:
+ spawn(Nutcracker);
+ end;
+On1020:
+ spawn(Fluffy);
+ end;
+
+/* Woodlands */
+On1084:
+ spawn(CrotcherScorpion);
+ end;
+On1041:
+ spawn(Snail);
+ end;
+On1049:
+ spawn(Bee);
+ end;
+On1055:
+ spawn(Butterfly);
+ end;
+On1014:
+ spawn(PinkFlower);
+ end;
+On1018:
+ spawn(Pinkie);
+ end;
+On1088:
+ spawn(Hyvern);
+ end;
+On1103:
+ spawn(WitchGuard);
+ end;
+
+/* Ambush */
+On1140:
+ spawn(Tengu);
+ end;
+On1138:
+ spawn(ManaGuardian);
+ end;
+On1143:
+ spawn(ManaSlayer);
+ end;
+On1150:
+ spawn(ManaTyrant);
+ end;
+On1147:
+ spawn(Koyntety);
+ end;
+On1146:
+ spawn(Flashmob);
+ end;
+
+/* Other Christmas settings */
+OnInstanceInit:
+ /* Prepare the color code */
+ 'COLORCODE[0] = rand2(0, 2);
+ 'COLORCODE[1] = rand2(0, 2);
+ 'COLORCODE[2] = rand2(0, 2);
+ 'COLORCODE[3] = rand2(0, 2);
+ 'COLORCODE[4] = rand2(0, 2);
+ 'COLORCODE[5] = rand2(0, 2);
+ 'OBSCURECOLOR = any(0, 1);
+
+ /* Prepare the Ambush Chamber */
+ 'AMBUSHTRI[0] = rand2(1, 17);
+ do
+ {
+ 'AMBUSHTRI[1] = rand2(1, 17);
+ } while ('AMBUSHTRI[1] == 'AMBUSHTRI[0]);
+ do
+ {
+ 'AMBUSHTRI[2] = rand2(1, 17);
+ } while ('AMBUSHTRI[2] == 'AMBUSHTRI[0] || 'AMBUSHTRI[2] == 'AMBUSHTRI[1]);
+ 'AMBUSH_BMASK = 0;
+
+ /* Prepare the Enigma */
+ 'TRUES=0;
+ 'TRUTHSTAT[0]=any(false, true);
+ if ('TRUTHSTAT[0])
+ 'TRUES+=1;
+ 'TRUTHSTAT[1]=any(false, true);
+ if ('TRUTHSTAT[1])
+ 'TRUES+=1;
+ 'TRUTHSTAT[2]=any(false, ('TRUES >= 2 ? false : true));
+ 'TRUES=0;
+ end;
+
+OnInit:
+ /* Aethyra - Frontier Town */
+ "#XMAS21Core"::spawn(Fluffy, 16, "081-1");
+ "#XMAS21Core"::spawn(Poltergeist, 7, "081-1");
+ "#XMAS21Core"::spawn(Wisp, 7, "081-1");
+ "#XMAS21Core"::spawn(Spectre, 7, "081-1");
+ "#XMAS21Core"::spawn(WhiteSlime, 12, "081-1");
+ "#XMAS21Core"::spawn(SantaSlime, 4, "081-1");
+ "#XMAS21Core"::spawn(Moggun, 10, "081-1");
+ "#XMAS21Core"::spawn(Moonshroom, 7, "081-1");
+ end;
+}
+
+
diff --git a/npc/functions/clear_vars.txt b/npc/functions/clear_vars.txt
index e5e6e5d4..aac27666 100644
--- a/npc/functions/clear_vars.txt
+++ b/npc/functions/clear_vars.txt
@@ -1,7 +1,11 @@
+// Update variables on server init, login and logout
-function script ClearVariables {
- if (@login_event != 1) goto L_Deprecated;
+function script isChristmas21 {
+ // FIXME: 12/12 = 346; 26/12 = 360; 09/01 = 9 (OBVIOUSLY)
+ return (gettime(8) >= 346 || gettime(8) <= 360 || $@XMAS21_OVERRIDE);
+}
+function script ClearVariables {
// Some temporary bugfix
GM = getgroupid();
@@ -138,10 +142,38 @@ function script ClearVariables {
//QUEST_MAGIC2 = 0;
OrumQuest = 0;
}
+
+ // Christmas 2021 Event
+ if (isChristmas21()) {
+ #OLD_EXP=BaseExp;
+ #OLD_LV=BaseLevel;
+ }
return;
}
+function script FlushVariables {
+ // Some variables should not be saved
+ GM = 0;
+ ##01_TMWEXP = 0;
+
+ // Christmas 2021 Event
+ if (isChristmas21()) {
+ #NEW_LV+=(BaseLevel-#OLD_LV);
+ if (BaseLevel != #OLD_LV) {
+ #NEW_EXP=BaseExp;
+ } else {
+ #NEW_EXP+=BaseExp-#OLD_EXP;
+ }
+ #OLD_EXP=0;
+ #OLD_LV=0;
+ if (getvaultid()) ##01_TMWEXP=#NEW_EXP; // (Techinically wrong)
+ }
+ return;
+}
+
+
+
function script ServerUpdate {
if ($@STARTUP) {
debugmes "Cowardly refusing to update server outside startup";
diff --git a/npc/functions/global_event_handler.txt b/npc/functions/global_event_handler.txt
index b63fb28f..f94e7fc8 100644
--- a/npc/functions/global_event_handler.txt
+++ b/npc/functions/global_event_handler.txt
@@ -15,7 +15,7 @@ OnPCLoginEvent:
end;
OnPCLogoutEvent:
- GM = 0;
+ FlushVariables();
vaultOnLogout();
end;
diff --git a/npc/functions/mob_points.txt b/npc/functions/mob_points.txt
index b4361f0a..fb13def9 100644
--- a/npc/functions/mob_points.txt
+++ b/npc/functions/mob_points.txt
@@ -15,6 +15,17 @@ function script MobPoints {
MONSTERS_KILLED+=1;
@mobId=killedrid;
+ if (isChristmas21() && compare(getmap(), "@"+callfunc("X21ID2"))) {
+ if (getmonsterinfo(getarg(0), MOB_LV) >= 150)
+ getitem AquaTicket, 2;
+ else if (getmonsterinfo(getarg(0), MOB_LV) >= 100 && any(true, false))
+ getitem AquaTicket, 2;
+ else if (getmonsterinfo(getarg(0), MOB_LV) >= 40 && any(true, false, false))
+ getitem AquaTicket, 1;
+ else if (rand2(5) == 3)
+ getitem AquaTicket, 1;
+ }
+
if (MPQUEST) {
.@moblv=strmobinfo(3,killedrid);
diff --git a/npc/functions/vault.txt b/npc/functions/vault.txt
index 4f45c871..2022b726 100644
--- a/npc/functions/vault.txt
+++ b/npc/functions/vault.txt
@@ -88,6 +88,8 @@ function script vaultOnLogout {
"VAR1V", ##01_TMWQUEST,
"VAR2N", "TMWGLOBAL",
"VAR2V", ##01_TMWGLOBAL,
+ "VAR2N", "TMWEXP",
+ "VAR2V", ##01_TMWEXP,
"VEXP", ##VAULT_EXP,
"GOTO", ##VAULT_GOTO,
"MLTO", ##VAULT_MLTO);
diff --git a/npc/magic/config.txt b/npc/magic/config.txt
index f8135b43..285fa379 100644
--- a/npc/magic/config.txt
+++ b/npc/magic/config.txt
@@ -237,7 +237,6 @@ function script rectharm {
continue;
harm(.@mbs[.@i], .@d, .@t, .@e);
specialeffect(FX_ATTACK, AREA, .@mbs[.@i]);
- // TODO: Handle MobPt to don't overload timer system?
}
return;
}
diff --git a/npc/scripts.conf b/npc/scripts.conf
index 2b6aa0f8..c5f4bc98 100644
--- a/npc/scripts.conf
+++ b/npc/scripts.conf
@@ -126,6 +126,7 @@
"npc/annuals/xmas/list.txt",
"npc/annuals/xmas/reagents.txt",
"npc/annuals/xmas/mobmanager.txt",
+"npc/annuals/xmas/2021.txt",
"npc/annuals/halloween/config.txt",
"npc/annuals/halloween/debug.txt",
"npc/annuals/halloween/munro.txt",