diff --git a/data/dfndata/create/glassblowing.dfn b/data/dfndata/create/glassblowing.dfn
index 18c05abd7..b91971368 100644
--- a/data/dfndata/create/glassblowing.dfn
+++ b/data/dfndata/create/glassblowing.dfn
@@ -2,7 +2,7 @@
{
NAME=Empty Bottle
ID=0x0f0e
-RESOURCE=SAND 1
+RESOURCE=SAND 1 0x96d
SKILL=0 1000 1150
ADDITEM=emptybottle
MINRANK=1
@@ -14,7 +14,7 @@ SOUND=0x0242
{
NAME=flask (small)
ID=0x182e
-RESOURCE=SAND 2
+RESOURCE=SAND 2 0x96d
SKILL=0 1000 1150
ADDITEM=0x182e
MINRANK=1
@@ -26,7 +26,7 @@ SOUND=0x0242
{
NAME=flask (medium)
ID=0x182a
-RESOURCE=SAND 3
+RESOURCE=SAND 3 0x96d
SKILL=0 1000 1150
ADDITEM=0x182a
MINRANK=1
@@ -38,7 +38,7 @@ SOUND=0x0242
{
NAME=flask (curved)
ID=0x1832
-RESOURCE=SAND 3
+RESOURCE=SAND 3 0x96d
SKILL=0 1000 1150
ADDITEM=0x1832
MINRANK=1
@@ -50,7 +50,7 @@ SOUND=0x0242
{
NAME=flask (large #1)
ID=0x1838
-RESOURCE=SAND 4
+RESOURCE=SAND 4 0x96d
SKILL=0 1000 1150
ADDITEM=0x1838
MINRANK=1
@@ -62,7 +62,7 @@ SOUND=0x0242
{
NAME=flask (large #2)
ID=0x183b
-RESOURCE=SAND 5
+RESOURCE=SAND 5 0x96d
SKILL=0 1000 1150
ADDITEM=0x183b
MINRANK=1
@@ -74,7 +74,7 @@ SOUND=0x0242
{
NAME=flask (bubbling blue)
ID=0x1844
-RESOURCE=SAND 5
+RESOURCE=SAND 5 0x96d
SKILL=0 1000 1150
ADDITEM=bubblingblueflask
MINRANK=1
@@ -86,7 +86,7 @@ SOUND=0x0242
{
NAME=flask (bubbling purple)
ID=0x1841
-RESOURCE=SAND 5
+RESOURCE=SAND 5 0x96d
SKILL=0 1000 1150
ADDITEM=bubblingpurpleflask
MINRANK=1
@@ -98,7 +98,7 @@ SOUND=0x0242
{
NAME=flask (bubbling red)
ID=0x183e
-RESOURCE=SAND 7
+RESOURCE=SAND 7 0x96d
SKILL=0 1000 1150
ADDITEM=bubblingredflask
MINRANK=1
@@ -110,7 +110,7 @@ SOUND=0x0242
{
NAME=empty vials
ID=0x185b
-RESOURCE=SAND 8
+RESOURCE=SAND 8 0x96d
SKILL=0 1000 1150
ADDITEM=emptyvials
MINRANK=1
@@ -122,7 +122,7 @@ SOUND=0x0242
{
NAME=full vials
ID=0x185d
-RESOURCE=SAND 8
+RESOURCE=SAND 8 0x96d
SKILL=0 1000 1150
ADDITEM=fullvials
MINRANK=1
@@ -134,10 +134,106 @@ SOUND=0x0242
{
NAME=spinning hourglass
ID=0x1810
-RESOURCE=SAND 10
+RESOURCE=SAND 10 0x96d
SKILL=0 1000 1150
ADDITEM=0x1810
MINRANK=1
MAXRANK=10
SOUND=0x0242
+}
+
+[ITEM 3012]
+{
+NAME=hollow prism
+ID=0x2f5d
+RESOURCE=SAND 8 0x96d
+SKILL=0 1000 1500
+ADDITEM=hollowprism
+MINRANK=1
+MAXRANK=10
+SOUND=0x0242
+}
+
+[ITEM 3013]
+{
+NAME=gargoyle floor mirror
+ID=0x403A
+RESOURCE=SAND 20 0x96d
+SKILL=0 750 1250
+ADDITEM=gargoylefloormirror
+MINRANK=1
+MAXRANK=10
+SOUND=0x0242
+}
+
+[ITEM 3014]
+{
+NAME=gargoyle wall mirror
+ID=0x4044
+RESOURCE=SAND 10 0x96d
+SKILL=0 700 1200
+ADDITEM=gargoylewallmirror
+MINRANK=1
+MAXRANK=10
+SOUND=0x0242
+}
+
+[ITEM 3015]
+{
+NAME=empty venom vial
+ID=0x0E24
+RESOURCE=SAND 1 0x96d
+SKILL=0 520 1000
+ADDITEM=emptyvenomvial
+MINRANK=1
+MAXRANK=10
+SOUND=0x0242
+}
+
+[ITEM 3016]
+{
+NAME=empty oil flask
+ID=0x1C18
+RESOURCE=SAND 5 0x96d
+SKILL=0 600 1100
+ADDITEM=emptyoilflask
+MINRANK=1
+MAXRANK=10
+SOUND=0x0242
+}
+
+[ITEM 3017]
+{
+NAME=workable glass
+ID=0x4B80
+RESOURCE=SAND 10 0x96d
+SKILL=0 550 1050
+ADDITEM=workableglass
+MINRANK=1
+MAXRANK=10
+SOUND=0x0242
+}
+
+[ITEM 3018]
+{
+NAME=glass sword
+ID=0x090c
+RESOURCE=SAND 14 0x96d
+SKILL=0 550 1050
+ADDITEM=glasssword
+MINRANK=1
+MAXRANK=10
+SOUND=0x0242
+}
+
+[ITEM 3019]
+{
+NAME=glass staff
+ID=0x0905
+RESOURCE=SAND 10 0x96d
+SKILL=0 530 1030
+ADDITEM=glassstaff
+MINRANK=1
+MAXRANK=10
+SOUND=0x0242
}
\ No newline at end of file
diff --git a/data/dfndata/create/masonry.dfn b/data/dfndata/create/masonry.dfn
new file mode 100644
index 000000000..99973ac63
--- /dev/null
+++ b/data/dfndata/create/masonry.dfn
@@ -0,0 +1,747 @@
+[ITEM 3500]
+{
+NAME=Vase
+ID=0x0b46
+RESOURCE=STONE 1
+SKILL=11 520 1020
+ADDITEM=0x0b46
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3501]
+{
+NAME=Large Vase
+ID=0x0b45
+RESOURCE=STONE 3
+SKILL=11 520 1020
+ADDITEM=0x0b45
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3502]
+{
+NAME=small Urn
+ID=0x241c
+RESOURCE=STONE 3
+SKILL=11 820 1030
+ADDITEM=0x241c
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3503]
+{
+NAME=Tower sculpture
+ID=0x241a
+RESOURCE=STONE 3
+SKILL=11 820 1030
+ADDITEM=0x241a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3504]
+{
+NAME=gargoyle painting
+ID=0x403d
+RESOURCE=STONE 3
+SKILL=11 820 1330
+ADDITEM=0x403d
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3505]
+{
+NAME=gargoyle sculpture
+ID=0x403f
+RESOURCE=STONE 3
+SKILL=11 820 1320
+ADDITEM=0x403f
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3506]
+{
+NAME=gargoyle vase
+ID=0x4042
+RESOURCE=STONE 3
+SKILL=11 800 1260
+ADDITEM=0x4042
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3507]
+{
+NAME=Tall 18th anniversary Vase
+ID=0x9bc7
+RESOURCE=STONE 6
+SKILL=11 600 1100
+ADDITEM=0x9bc7
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3508]
+{
+NAME=short 18th anniversary Vase
+ID=0x9bca
+RESOURCE=STONE 6
+SKILL=11 600 1100
+ADDITEM=0x9bca
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// furniture /////
+//////////////////////////////////////
+
+[ITEM 3509]
+{
+NAME=stone chair
+ID=0x1218
+RESOURCE=STONE 4
+SKILL=11 550 105
+ADDITEM=0x1218
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3510]
+{
+NAME=stone table (east)
+ID=0x14f0
+RESOURCE=STONE 6
+SKILL=11 650 1150
+ADDITEM=mediumstonetableeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3511]
+{
+NAME=stone table (south)
+ID=0x14f0
+RESOURCE=STONE 6
+SKILL=11 650 1150
+ADDITEM=mediumstonetablesouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3512]
+{
+NAME=large stone table (east)
+ID=0x14f0
+RESOURCE=STONE 9
+SKILL=11 750 1250
+ADDITEM=largestonetableeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3513]
+{
+NAME=large stone table (south)
+ID=0x14f0
+RESOURCE=STONE 9
+SKILL=11 750 1250
+ADDITEM=largestonetablesouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3514]
+{
+NAME=ritual table
+ID=0x14f0
+RESOURCE=STONE 8
+SKILL=11 940 1030
+ADDITEM=ritualtabledeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// statues /////
+//////////////////////////////////////
+
+[ITEM 3515]
+{
+NAME=small statue (south)
+ID=0x139a
+RESOURCE=STONE 3
+SKILL=11 600 1100
+ADDITEM=0x139a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3516]
+{
+NAME=small statue (north)
+ID=0x139b
+RESOURCE=STONE 3
+SKILL=11 600 1100
+ADDITEM=0x139b
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3517]
+{
+NAME=small statue (east)
+ID=0x139c
+RESOURCE=STONE 3
+SKILL=11 600 1100
+ADDITEM=0x139c
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3518]
+{
+NAME=pegasus statuette
+ID=0x139d
+RESOURCE=STONE 4
+SKILL=11 700 1200
+ADDITEM=0x139d
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3519]
+{
+NAME=gargoyle statue
+ID=0x494e
+RESOURCE=STONE 20
+SKILL=11 540 1040
+ADDITEM=0x494e
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3520]
+{
+NAME=gryphon statue
+ID=0x494e
+RESOURCE=STONE 15
+SKILL=11 540 1040
+ADDITEM=0x494e
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// Misc addons /////
+//////////////////////////////////////
+
+[ITEM 3521]
+{
+NAME=stone anvil (east)
+ID=0x14f0
+RESOURCE=STONE 20
+SKILL=11 780 1280
+ADDITEM=stoneanvileastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3522]
+{
+NAME=stone anvil (south)
+ID=0x14f0
+RESOURCE=STONE 20
+SKILL=11 780 1280
+ADDITEM=stoneanvilsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3523]
+{
+NAME=large gargish bed (east)
+ID=0x14f0
+RESOURCE=STONE 3
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=largegargoylebedeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3524]
+{
+NAME=large gargish bed (south)
+ID=0x14f0
+RESOURCE=STONE 3
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=largegargoylebedsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3525]
+{
+NAME=gargish cot (east)
+ID=0x14f0
+RESOURCE=STONE 3
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=gargishcoteasteastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3526]
+{
+NAME=gargish cot (south)
+ID=0x14f0
+RESOURCE=STONE 3
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=gargishcoteastsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone armor /////
+//////////////////////////////////////
+
+[ITEM 3527]
+{
+NAME=gargish stone arms
+ID=0x0283
+RESOURCE=STONE 8
+SKILL=11 560 1060
+ADDITEM=femalegargishstonearms
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3528]
+{
+NAME=gargish stone chest
+ID=0x0285
+RESOURCE=STONE 12
+SKILL=11 560 1060
+ADDITEM=femalegargishstonechest
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3529]
+{
+NAME=gargish stone leggings
+ID=0x0289
+RESOURCE=STONE 10
+SKILL=11 560 1060
+ADDITEM=femalegargishstoneleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3530]
+{
+NAME=gargish stone kilt
+ID=0x0287
+RESOURCE=STONE 6
+SKILL=11 480 980
+ADDITEM=femalegargishstonekilt
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3531]
+{
+NAME=gargish stone arms
+ID=0x0284
+RESOURCE=STONE 8
+SKILL=11 560 1060
+ADDITEM=gargishstonearms
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3532]
+{
+NAME=gargish stone chest
+ID=0x0286
+RESOURCE=STONE 12
+SKILL=11 560 1060
+ADDITEM=gargishstonechest
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3533]
+{
+NAME=gargish stone leggings
+ID=0x028a
+RESOURCE=STONE 10
+SKILL=11 560 1060
+ADDITEM=gargishstoneleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3534]
+{
+NAME=gargish stone kilt
+ID=0x0288
+RESOURCE=STONE 6
+SKILL=11 480 980
+ADDITEM=gargishstonekilt
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3535]
+{
+NAME=large stone shield
+ID=0x4205
+RESOURCE=STONE 16
+SKILL=11 550 1060
+ADDITEM=largestoneshield
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3536]
+{
+NAME=gargish stone amulet
+ID=0x4D0a
+RESOURCE=STONE 3
+SKILL=11 600 1100
+ADDITEM=0x4D0a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone weapons /////
+//////////////////////////////////////
+
+[ITEM 3537]
+{
+NAME=stone war sword
+ID=0x4D0a
+RESOURCE=STONE 18
+SKILL=11 550 1050
+ADDITEM=stonewarsword
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone walls /////
+//////////////////////////////////////
+
+[ITEM 3538]
+{
+NAME=rough windowless
+ID=0x01D0
+RESOURCE=STONE 10
+SKILL=11 600 1100
+ADDITEM=roughwindowless1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3539]
+{
+NAME=rough window
+ID=0x01D3
+RESOURCE=STONE 10
+SKILL=11 600 1100
+ADDITEM=roughwindow1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3540]
+{
+NAME=rough arch
+ID=0x01D5
+RESOURCE=STONE 10
+SKILL=11 600 1100
+ADDITEM=rougharch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3541]
+{
+NAME=rough pillar
+ID=0x01Da
+RESOURCE=STONE 10
+SKILL=11 600 1100
+ADDITEM=roughpillar
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3542]
+{
+NAME=rough rounded arch
+ID=0x01DB
+RESOURCE=STONE 10
+SKILL=11 600 1100
+ADDITEM=roughroundedarch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3543]
+{
+NAME=rough small arch
+ID=0x01E0
+RESOURCE=STONE 10
+SKILL=11 600 1100
+ADDITEM=roughsmallarch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3544]
+{
+NAME=rough angled pillar
+ID=0x01E6
+RESOURCE=STONE 10
+SKILL=11 600 1100
+ADDITEM=roughangledpillar1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3545]
+{
+NAME=short rough
+ID=0x01E8
+RESOURCE=STONE 10
+SKILL=11 600 1100
+ADDITEM=shortrough1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3546]
+{
+NAME=Stone Door S In
+ID=0x0326
+RESOURCE=STONE 10
+SKILL=11 600 1100
+ADDITEM=StoneDoor_S_In
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3547]
+{
+NAME=Stone Door E Out
+ID=0x032C
+RESOURCE=STONE 10
+SKILL=11 600 1100
+ADDITEM=StoneDoor_E_Out
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3548]
+{
+NAME=Stone Door S Out
+ID=0x032a
+RESOURCE=STONE 10
+SKILL=11 600 1100
+ADDITEM=StoneDoor_S_Out
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3549]
+{
+NAME=Stone Door E In
+ID=0x0330
+RESOURCE=STONE 10
+SKILL=11 600 1100
+ADDITEM=StoneDoor_E_In
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone stairs /////
+//////////////////////////////////////
+
+[ITEM 3550]
+{
+NAME=short rough
+ID=0x0788
+RESOURCE=STONE 5
+SKILL=11 600 1100
+ADDITEM=roughblock
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3551]
+{
+NAME=rough steps
+ID=0x0789
+RESOURCE=STONE 5
+SKILL=11 600 1100
+ADDITEM=roughsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3552]
+{
+NAME=rough corner steps
+ID=0x078d
+RESOURCE=STONE 5
+SKILL=11 600 1100
+ADDITEM=roughcornersteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3553]
+{
+NAME=rough rounded corner step
+ID=0x0791
+RESOURCE=STONE 5
+SKILL=11 600 1100
+ADDITEM=roughroundedcornerseps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3554]
+{
+NAME=rough inset steps
+ID=0x0795
+RESOURCE=STONE 5
+SKILL=11 600 1100
+ADDITEM=roughinsetsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3555]
+{
+NAME=rough rounded inset steps
+ID=0x0799
+RESOURCE=STONE 5
+SKILL=11 600 1100
+ADDITEM=roughroundedisetsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone floors /////
+//////////////////////////////////////
+
+[ITEM 3556]
+{
+NAME=light paver
+ID=0x0519
+RESOURCE=STONE 5
+SKILL=11 600 1100
+ADDITEM=lightpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3557]
+{
+NAME=medium paver
+ID=0x0519
+RESOURCE=STONE 5
+SKILL=11 600 1100
+ADDITEM=mediumpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3558]
+{
+NAME=dark paver
+ID=0x0519
+RESOURCE=STONE 5
+SKILL=11 600 1100
+ADDITEM=darkpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
\ No newline at end of file
diff --git a/data/dfndata/create/masonry_agapite.dfn b/data/dfndata/create/masonry_agapite.dfn
new file mode 100644
index 000000000..fd07b68b6
--- /dev/null
+++ b/data/dfndata/create/masonry_agapite.dfn
@@ -0,0 +1,704 @@
+[ITEM 4100]
+{
+NAME=Vase
+ID=0x0b46
+RESOURCE=STONE 1 0x979
+SKILL=11 520 1020
+ADDITEM=0x0b46
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4101]
+{
+NAME=Large Vase
+ID=0x0b45
+RESOURCE=STONE 3 0x979
+SKILL=11 520 1020
+ADDITEM=0x0b45
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4102]
+{
+NAME=small Urn
+ID=0x241c
+RESOURCE=STONE 3 0x979
+SKILL=11 820 1030
+ADDITEM=0x241c
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4103]
+{
+NAME=Tower sculpture
+ID=0x241a
+RESOURCE=STONE 3 0x979
+SKILL=11 820 1030
+ADDITEM=0x241a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4104]
+{
+NAME=gargoyle painting
+ID=0x403d
+RESOURCE=STONE 3 0x979
+SKILL=11 820 1330
+ADDITEM=0x403d
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4105]
+{
+NAME=gargoyle sculpture
+ID=0x403f
+RESOURCE=STONE 3 0x979
+SKILL=11 820 1320
+ADDITEM=0x403f
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4106]
+{
+NAME=gargoyle vase
+ID=0x4042
+RESOURCE=STONE 3 0x979
+SKILL=11 800 1260
+ADDITEM=0x4042
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4107]
+{
+NAME=Tall 18th anniversary Vase
+ID=0x9bc7
+RESOURCE=STONE 6 0x979
+SKILL=11 600 1100
+ADDITEM=0x9bc7
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4108]
+{
+NAME=short 18th anniversary Vase
+ID=0x9bca
+RESOURCE=STONE 6 0x979
+SKILL=11 600 1100
+ADDITEM=0x9bca
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// furniture /////
+//////////////////////////////////////
+
+[ITEM 4109]
+{
+NAME=stone chair
+ID=0x1218
+RESOURCE=STONE 4 0x979
+SKILL=11 550 1050
+ADDITEM=0x1218
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4110]
+{
+NAME=stone table (east)
+ID=0x14f0
+RESOURCE=STONE 6 0x979
+SKILL=11 650 1150
+ADDITEM=mediumstonetableeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4111]
+{
+NAME=stone table (south)
+ID=0x14f0
+RESOURCE=STONE 6 0x979
+SKILL=11 650 1150
+ADDITEM=mediumstonetablesouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4112]
+{
+NAME=large stone table (east)
+ID=0x14f0
+RESOURCE=STONE 9 0x979
+SKILL=11 750 1250
+ADDITEM=largestonetableeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4113]
+{
+NAME=large stone table (south)
+ID=0x14f0
+RESOURCE=STONE 9 0x979
+SKILL=11 750 1250
+ADDITEM=largestonetablesouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4114]
+{
+NAME=ritual table
+ID=0x14f0
+RESOURCE=STONE 8 0x979
+SKILL=11 940 1030
+ADDITEM=ritualtabledeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// statues /////
+//////////////////////////////////////
+
+[ITEM 4115]
+{
+NAME=small statue (south)
+ID=0x139a
+RESOURCE=STONE 3 0x979
+SKILL=11 600 1100
+ADDITEM=0x139a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4116]
+{
+NAME=small statue (north)
+ID=0x139b
+RESOURCE=STONE 3 0x979
+SKILL=11 600 1100
+ADDITEM=0x139b
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4117]
+{
+NAME=small statue (east)
+ID=0x139c
+RESOURCE=STONE 3 0x979
+SKILL=11 600 1100
+ADDITEM=0x139c
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4118]
+{
+NAME=pegasus statuette
+ID=0x139d
+RESOURCE=STONE 4 0x979
+SKILL=11 700 1200
+ADDITEM=0x139d
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4119]
+{
+NAME=gargoyle statue
+ID=0x494e
+RESOURCE=STONE 20 0x979
+SKILL=11 540 1040
+ADDITEM=0x494e
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4120]
+{
+NAME=gryphon statue
+ID=0x494e
+RESOURCE=STONE 15 0x979
+SKILL=11 540 1040
+ADDITEM=0x494e
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// Misc addons /////
+//////////////////////////////////////
+
+[ITEM 4121]
+{
+NAME=stone anvil (east)
+ID=0x14f0
+RESOURCE=STONE 20 0x979
+SKILL=11 780 1280
+ADDITEM=stoneanvileastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4122]
+{
+NAME=stone anvil (south)
+ID=0x14f0
+RESOURCE=STONE 20 0x979
+SKILL=11 780 1280
+ADDITEM=stoneanvilsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4123]
+{
+NAME=large gargish bed (east)
+ID=0x14f0
+RESOURCE=STONE 3 0x979
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=largegargoylebedeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4124]
+{
+NAME=large gargish bed (south)
+ID=0x14f0
+RESOURCE=STONE 3 0x979
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=largegargoylebedsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4125]
+{
+NAME=gargish cot (east)
+ID=0x14f0
+RESOURCE=STONE 3 0x979
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=gargishcoteasteastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4126]
+{
+NAME=gargish cot (south)
+ID=0x14f0
+RESOURCE=STONE 3 0x979
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=gargishcoteastsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone armor /////
+//////////////////////////////////////
+
+[ITEM 4127]
+{
+NAME=gargish stone arms
+ID=0x0283
+RESOURCE=STONE 8 0x979
+SKILL=11 560 1060
+ADDITEM=femalegargishstonearms
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4128]
+{
+NAME=gargish stone chest
+ID=0x0285
+RESOURCE=STONE 12 0x979
+SKILL=11 560 1060
+ADDITEM=femalegargishstonechest
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4129]
+{
+NAME=gargish stone leggings
+ID=0x0289
+RESOURCE=STONE 10 0x979
+SKILL=11 560 1060
+ADDITEM=femalegargishstoneleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4130]
+{
+NAME=gargish stone kilt
+ID=0x0287
+RESOURCE=STONE 6 0x979
+SKILL=11 480 980
+ADDITEM=femalegargishstonekilt
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4131]
+{
+NAME=gargish stone arms
+ID=0x0284
+RESOURCE=STONE 8 0x979
+SKILL=11 560 1060
+ADDITEM=gargishstonearms
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4132]
+{
+NAME=gargish stone chest
+ID=0x0286
+RESOURCE=STONE 12 0x979
+SKILL=11 560 1060
+ADDITEM=gargishstonechest
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4133]
+{
+NAME=gargish stone leggings
+ID=0x028a
+RESOURCE=STONE 10 0x979
+SKILL=11 560 1060
+ADDITEM=gargishstoneleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4134]
+{
+NAME=gargish stone kilt
+ID=0x0288
+RESOURCE=STONE 6 0x979
+SKILL=11 480 980
+ADDITEM=gargishstonekilt
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4135]
+{
+NAME=large stone shield
+ID=0x4205
+RESOURCE=STONE 16 0x979
+SKILL=11 550 1060
+ADDITEM=largestoneshield
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4136]
+{
+NAME=gargish stone amulet
+ID=0x4D0a
+RESOURCE=STONE 3 0x979
+SKILL=11 600 1100
+ADDITEM=0x4D0a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone weapons /////
+//////////////////////////////////////
+
+[ITEM 4137]
+{
+NAME=stone war sword
+ID=0x4D0a
+RESOURCE=STONE 18 0x979
+SKILL=11 550 1050
+ADDITEM=stonewarsword
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone walls /////
+//////////////////////////////////////
+
+[ITEM 4138]
+{
+NAME=rough windowless
+ID=0x01D0
+RESOURCE=STONE 10 0x979
+SKILL=11 600 1100
+ADDITEM=roughwindowless1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4139]
+{
+NAME=rough window
+ID=0x01D3
+RESOURCE=STONE 10 0x979
+SKILL=11 600 1100
+ADDITEM=roughwindow1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4140]
+{
+NAME=rough arch
+ID=0x01D5
+RESOURCE=STONE 10 0x979
+SKILL=11 600 1100
+ADDITEM=rougharch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4141]
+{
+NAME=rough pillar
+ID=0x01Da
+RESOURCE=STONE 10 0x979
+SKILL=11 600 1100
+ADDITEM=roughpillar
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4142]
+{
+NAME=rough rounded arch
+ID=0x01DB
+RESOURCE=STONE 10 0x979
+SKILL=11 600 1100
+ADDITEM=roughroundedarch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4143]
+{
+NAME=rough small arch
+ID=0x01E0
+RESOURCE=STONE 10 0x979
+SKILL=11 600 1100
+ADDITEM=roughsmallarch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4144]
+{
+NAME=rough angled pillar
+ID=0x01E6
+RESOURCE=STONE 10 0x979
+SKILL=11 600 1100
+ADDITEM=roughangledpillar1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4145]
+{
+NAME=short rough
+ID=0x01E8
+RESOURCE=STONE 10 0x979
+SKILL=11 600 1100
+ADDITEM=shortrough1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//[ITEM 4146]
+//[ITEM 4147]
+//[ITEM 4148]
+//[ITEM 4149]
+
+//////////////////////////////////////
+//// stone stairs /////
+//////////////////////////////////////
+
+[ITEM 4150]
+{
+NAME=short rough
+ID=0x0788
+RESOURCE=STONE 5 0x979
+SKILL=11 600 1100
+ADDITEM=roughblock
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4151]
+{
+NAME=rough steps
+ID=0x0789
+RESOURCE=STONE 5 0x979
+SKILL=11 600 1100
+ADDITEM=roughsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4152]
+{
+NAME=rough corner steps
+ID=0x078d
+RESOURCE=STONE 5 0x979
+SKILL=11 600 1100
+ADDITEM=roughcornersteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4153]
+{
+NAME=rough rounded corner step
+ID=0x0791
+RESOURCE=STONE 5 0x979
+SKILL=11 600 1100
+ADDITEM=roughroundedcornerseps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4154]
+{
+NAME=rough inset steps
+ID=0x0795
+RESOURCE=STONE 5 0x979
+SKILL=11 600 1100
+ADDITEM=roughinsetsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4155]
+{
+NAME=rough rounded inset steps
+ID=0x0799
+RESOURCE=STONE 5 0x979
+SKILL=11 600 1100
+ADDITEM=roughroundedisetsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone floors /////
+//////////////////////////////////////
+
+[ITEM 4156]
+{
+NAME=light paver
+ID=0x0519
+RESOURCE=STONE 5 0x979
+SKILL=11 600 1100
+ADDITEM=lightpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4157]
+{
+NAME=medium paver
+ID=0x0519
+RESOURCE=STONE 5 0x979
+SKILL=11 600 1100
+ADDITEM=mediumpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4158]
+{
+NAME=dark paver
+ID=0x0519
+RESOURCE=STONE 5 0x979
+SKILL=11 600 1100
+ADDITEM=darkpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
\ No newline at end of file
diff --git a/data/dfndata/create/masonry_bronze.dfn b/data/dfndata/create/masonry_bronze.dfn
new file mode 100644
index 000000000..5fb705bd3
--- /dev/null
+++ b/data/dfndata/create/masonry_bronze.dfn
@@ -0,0 +1,704 @@
+[ITEM 3900]
+{
+NAME=Vase
+ID=0x0b46
+RESOURCE=STONE 1 0x6d6
+SKILL=11 520 1020
+ADDITEM=0x0b46
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3901]
+{
+NAME=Large Vase
+ID=0x0b45
+RESOURCE=STONE 3 0x6d6
+SKILL=11 520 1020
+ADDITEM=0x0b45
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3902]
+{
+NAME=small Urn
+ID=0x241c
+RESOURCE=STONE 3 0x6d6
+SKILL=11 820 1030
+ADDITEM=0x241c
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3903]
+{
+NAME=Tower sculpture
+ID=0x241a
+RESOURCE=STONE 3 0x6d6
+SKILL=11 820 1030
+ADDITEM=0x241a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3904]
+{
+NAME=gargoyle painting
+ID=0x403d
+RESOURCE=STONE 3 0x6d6
+SKILL=11 820 1330
+ADDITEM=0x403d
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3905]
+{
+NAME=gargoyle sculpture
+ID=0x403f
+RESOURCE=STONE 3 0x6d6
+SKILL=11 820 1320
+ADDITEM=0x403f
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3906]
+{
+NAME=gargoyle vase
+ID=0x4042
+RESOURCE=STONE 3 0x6d6
+SKILL=11 800 1260
+ADDITEM=0x4042
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3907]
+{
+NAME=Tall 18th anniversary Vase
+ID=0x9bc7
+RESOURCE=STONE 6 0x6d6
+SKILL=11 600 1100
+ADDITEM=0x9bc7
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3908]
+{
+NAME=short 18th anniversary Vase
+ID=0x9bca
+RESOURCE=STONE 6 0x6d6
+SKILL=11 600 1100
+ADDITEM=0x9bca
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// furniture /////
+//////////////////////////////////////
+
+[ITEM 3909]
+{
+NAME=stone chair
+ID=0x1218
+RESOURCE=STONE 4 0x6d6
+SKILL=11 550 1050
+ADDITEM=0x1218
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3910]
+{
+NAME=stone table (east)
+ID=0x14f0
+RESOURCE=STONE 6 0x6d6
+SKILL=11 650 1150
+ADDITEM=mediumstonetableeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3911]
+{
+NAME=stone table (south)
+ID=0x14f0
+RESOURCE=STONE 6 0x6d6
+SKILL=11 650 1150
+ADDITEM=mediumstonetablesouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3912]
+{
+NAME=large stone table (east)
+ID=0x14f0
+RESOURCE=STONE 9 0x6d6
+SKILL=11 750 1250
+ADDITEM=largestonetableeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3913]
+{
+NAME=large stone table (south)
+ID=0x14f0
+RESOURCE=STONE 9 0x6d6
+SKILL=11 750 1250
+ADDITEM=largestonetablesouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3914]
+{
+NAME=ritual table
+ID=0x14f0
+RESOURCE=STONE 8 0x6d6
+SKILL=11 940 1030
+ADDITEM=ritualtabledeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// statues /////
+//////////////////////////////////////
+
+[ITEM 3915]
+{
+NAME=small statue (south)
+ID=0x139a
+RESOURCE=STONE 3 0x6d6
+SKILL=11 600 1100
+ADDITEM=0x139a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3916]
+{
+NAME=small statue (north)
+ID=0x139b
+RESOURCE=STONE 3 0x6d6
+SKILL=11 600 1100
+ADDITEM=0x139b
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3917]
+{
+NAME=small statue (east)
+ID=0x139c
+RESOURCE=STONE 3 0x6d6
+SKILL=11 600 1100
+ADDITEM=0x139c
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3918]
+{
+NAME=pegasus statuette
+ID=0x139d
+RESOURCE=STONE 4 0x6d6
+SKILL=11 700 1200
+ADDITEM=0x139d
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3919]
+{
+NAME=gargoyle statue
+ID=0x494e
+RESOURCE=STONE 20 0x6d6
+SKILL=11 540 1040
+ADDITEM=0x494e
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3920]
+{
+NAME=gryphon statue
+ID=0x494e
+RESOURCE=STONE 15 0x6d6
+SKILL=11 540 1040
+ADDITEM=0x494e
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// Misc addons /////
+//////////////////////////////////////
+
+[ITEM 3921]
+{
+NAME=stone anvil (east)
+ID=0x14f0
+RESOURCE=STONE 20 0x6d6
+SKILL=11 780 1280
+ADDITEM=stoneanvileastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3922]
+{
+NAME=stone anvil (south)
+ID=0x14f0
+RESOURCE=STONE 20 0x6d6
+SKILL=11 780 1280
+ADDITEM=stoneanvilsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3923]
+{
+NAME=large gargish bed (east)
+ID=0x14f0
+RESOURCE=STONE 3 0x6d6
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=largegargoylebedeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3924]
+{
+NAME=large gargish bed (south)
+ID=0x14f0
+RESOURCE=STONE 3 0x6d6
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=largegargoylebedsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3925]
+{
+NAME=gargish cot (east)
+ID=0x14f0
+RESOURCE=STONE 3 0x6d6
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=gargishcoteasteastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3926]
+{
+NAME=gargish cot (south)
+ID=0x14f0
+RESOURCE=STONE 3 0x6d6
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=gargishcoteastsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone armor /////
+//////////////////////////////////////
+
+[ITEM 3927]
+{
+NAME=gargish stone arms
+ID=0x0283
+RESOURCE=STONE 8 0x6d6
+SKILL=11 560 1060
+ADDITEM=femalegargishstonearms
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3928]
+{
+NAME=gargish stone chest
+ID=0x0285
+RESOURCE=STONE 12 0x6d6
+SKILL=11 560 1060
+ADDITEM=femalegargishstonechest
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3929]
+{
+NAME=gargish stone leggings
+ID=0x0289
+RESOURCE=STONE 10 0x6d6
+SKILL=11 560 1060
+ADDITEM=femalegargishstoneleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3930]
+{
+NAME=gargish stone kilt
+ID=0x0287
+RESOURCE=STONE 6 0x6d6
+SKILL=11 480 980
+ADDITEM=femalegargishstonekilt
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3931]
+{
+NAME=gargish stone arms
+ID=0x0284
+RESOURCE=STONE 8 0x6d6
+SKILL=11 560 1060
+ADDITEM=gargishstonearms
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3932]
+{
+NAME=gargish stone chest
+ID=0x0286
+RESOURCE=STONE 12 0x6d6
+SKILL=11 560 1060
+ADDITEM=gargishstonechest
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3933]
+{
+NAME=gargish stone leggings
+ID=0x028a
+RESOURCE=STONE 10 0x6d6
+SKILL=11 560 1060
+ADDITEM=gargishstoneleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3934]
+{
+NAME=gargish stone kilt
+ID=0x0288
+RESOURCE=STONE 6 0x6d6
+SKILL=11 480 980
+ADDITEM=gargishstonekilt
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3935]
+{
+NAME=large stone shield
+ID=0x4205
+RESOURCE=STONE 16 0x6d6
+SKILL=11 550 1060
+ADDITEM=largestoneshield
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3936]
+{
+NAME=gargish stone amulet
+ID=0x4D0a
+RESOURCE=STONE 3 0x6d6
+SKILL=11 600 1100
+ADDITEM=0x4D0a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone weapons /////
+//////////////////////////////////////
+
+[ITEM 3937]
+{
+NAME=stone war sword
+ID=0x4D0a
+RESOURCE=STONE 18 0x6d6
+SKILL=11 550 1050
+ADDITEM=stonewarsword
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone walls /////
+//////////////////////////////////////
+
+[ITEM 3938]
+{
+NAME=rough windowless
+ID=0x01D0
+RESOURCE=STONE 10 0x6d6
+SKILL=11 600 1100
+ADDITEM=roughwindowless1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3939]
+{
+NAME=rough window
+ID=0x01D3
+RESOURCE=STONE 10 0x6d6
+SKILL=11 600 1100
+ADDITEM=roughwindow1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3940]
+{
+NAME=rough arch
+ID=0x01D5
+RESOURCE=STONE 10 0x6d6
+SKILL=11 600 1100
+ADDITEM=rougharch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3941]
+{
+NAME=rough pillar
+ID=0x01Da
+RESOURCE=STONE 10 0x6d6
+SKILL=11 600 1100
+ADDITEM=roughpillar
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3942]
+{
+NAME=rough rounded arch
+ID=0x01DB
+RESOURCE=STONE 10 0x6d6
+SKILL=11 600 1100
+ADDITEM=roughroundedarch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3943]
+{
+NAME=rough small arch
+ID=0x01E0
+RESOURCE=STONE 10 0x6d6
+SKILL=11 600 1100
+ADDITEM=roughsmallarch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3944]
+{
+NAME=rough angled pillar
+ID=0x01E6
+RESOURCE=STONE 10 0x6d6
+SKILL=11 600 1100
+ADDITEM=roughangledpillar1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3945]
+{
+NAME=short rough
+ID=0x01E8
+RESOURCE=STONE 10 0x6d6
+SKILL=11 600 1100
+ADDITEM=shortrough1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//[ITEM 3946]
+//[ITEM 3947]
+//[ITEM 3948]
+//[ITEM 3949]
+
+//////////////////////////////////////
+//// stone stairs /////
+//////////////////////////////////////
+
+[ITEM 3950]
+{
+NAME=short rough
+ID=0x0788
+RESOURCE=STONE 5 0x6d6
+SKILL=11 600 1100
+ADDITEM=roughblock
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3951]
+{
+NAME=rough steps
+ID=0x0789
+RESOURCE=STONE 5 0x6d6
+SKILL=11 600 1100
+ADDITEM=roughsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3952]
+{
+NAME=rough corner steps
+ID=0x078d
+RESOURCE=STONE 5 0x6d6
+SKILL=11 600 1100
+ADDITEM=roughcornersteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3953]
+{
+NAME=rough rounded corner step
+ID=0x0791
+RESOURCE=STONE 5 0x6d6
+SKILL=11 600 1100
+ADDITEM=roughroundedcornerseps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3954]
+{
+NAME=rough inset steps
+ID=0x0795
+RESOURCE=STONE 5 0x6d6
+SKILL=11 600 1100
+ADDITEM=roughinsetsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3955]
+{
+NAME=rough rounded inset steps
+ID=0x0799
+RESOURCE=STONE 5 0x6d6
+SKILL=11 600 1100
+ADDITEM=roughroundedisetsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone floors /////
+//////////////////////////////////////
+
+[ITEM 3956]
+{
+NAME=light paver
+ID=0x0519
+RESOURCE=STONE 5 0x6d6
+SKILL=11 600 1100
+ADDITEM=lightpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3957]
+{
+NAME=medium paver
+ID=0x0519
+RESOURCE=STONE 5 0x6d6
+SKILL=11 600 1100
+ADDITEM=mediumpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3958]
+{
+NAME=dark paver
+ID=0x0519
+RESOURCE=STONE 5 0x6d6
+SKILL=11 600 1100
+ADDITEM=darkpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
\ No newline at end of file
diff --git a/data/dfndata/create/masonry_copper.dfn b/data/dfndata/create/masonry_copper.dfn
new file mode 100644
index 000000000..017e58ce9
--- /dev/null
+++ b/data/dfndata/create/masonry_copper.dfn
@@ -0,0 +1,704 @@
+[ITEM 3800]
+{
+NAME=Vase
+ID=0x0b46
+RESOURCE=STONE 1 0x7dd
+SKILL=11 520 1020
+ADDITEM=0x0b46
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3801]
+{
+NAME=Large Vase
+ID=0x0b45
+RESOURCE=STONE 3 0x7dd
+SKILL=11 520 1020
+ADDITEM=0x0b45
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3802]
+{
+NAME=small Urn
+ID=0x241c
+RESOURCE=STONE 3 0x7dd
+SKILL=11 820 1030
+ADDITEM=0x241c
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3803]
+{
+NAME=Tower sculpture
+ID=0x241a
+RESOURCE=STONE 3 0x7dd
+SKILL=11 820 1030
+ADDITEM=0x241a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3804]
+{
+NAME=gargoyle painting
+ID=0x403d
+RESOURCE=STONE 3 0x7dd
+SKILL=11 820 1330
+ADDITEM=0x403d
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3805]
+{
+NAME=gargoyle sculpture
+ID=0x403f
+RESOURCE=STONE 3 0x7dd
+SKILL=11 820 1320
+ADDITEM=0x403f
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3806]
+{
+NAME=gargoyle vase
+ID=0x4042
+RESOURCE=STONE 3 0x7dd
+SKILL=11 800 1260
+ADDITEM=0x4042
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3807]
+{
+NAME=Tall 18th anniversary Vase
+ID=0x9bc7
+RESOURCE=STONE 6 0x7dd
+SKILL=11 600 1100
+ADDITEM=0x9bc7
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3808]
+{
+NAME=short 18th anniversary Vase
+ID=0x9bca
+RESOURCE=STONE 6 0x7dd
+SKILL=11 600 1100
+ADDITEM=0x9bca
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// furniture /////
+//////////////////////////////////////
+
+[ITEM 3809]
+{
+NAME=stone chair
+ID=0x1218
+RESOURCE=STONE 4 0x7dd
+SKILL=11 550 1050
+ADDITEM=0x1218
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3810]
+{
+NAME=stone table (east)
+ID=0x14f0
+RESOURCE=STONE 6 0x7dd
+SKILL=11 650 1150
+ADDITEM=mediumstonetableeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3811]
+{
+NAME=stone table (south)
+ID=0x14f0
+RESOURCE=STONE 6 0x7dd
+SKILL=11 650 1150
+ADDITEM=mediumstonetablesouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3812]
+{
+NAME=large stone table (east)
+ID=0x14f0
+RESOURCE=STONE 9 0x7dd
+SKILL=11 750 1250
+ADDITEM=largestonetableeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3813]
+{
+NAME=large stone table (south)
+ID=0x14f0
+RESOURCE=STONE 9 0x7dd
+SKILL=11 750 1250
+ADDITEM=largestonetablesouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3814]
+{
+NAME=ritual table
+ID=0x14f0
+RESOURCE=STONE 8 0x7dd
+SKILL=11 940 1030
+ADDITEM=ritualtabledeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// statues /////
+//////////////////////////////////////
+
+[ITEM 3815]
+{
+NAME=small statue (south)
+ID=0x139a
+RESOURCE=STONE 3 0x7dd
+SKILL=11 600 1100
+ADDITEM=0x139a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3816]
+{
+NAME=small statue (north)
+ID=0x139b
+RESOURCE=STONE 3 0x7dd
+SKILL=11 600 1100
+ADDITEM=0x139b
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3817]
+{
+NAME=small statue (east)
+ID=0x139c
+RESOURCE=STONE 3 0x7dd
+SKILL=11 600 1100
+ADDITEM=0x139c
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3818]
+{
+NAME=pegasus statuette
+ID=0x139d
+RESOURCE=STONE 4 0x7dd
+SKILL=11 700 1200
+ADDITEM=0x139d
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3819]
+{
+NAME=gargoyle statue
+ID=0x494e
+RESOURCE=STONE 20 0x7dd
+SKILL=11 540 1040
+ADDITEM=0x494e
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3820]
+{
+NAME=gryphon statue
+ID=0x494e
+RESOURCE=STONE 15 0x7dd
+SKILL=11 540 1040
+ADDITEM=0x494e
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// Misc addons /////
+//////////////////////////////////////
+
+[ITEM 3821]
+{
+NAME=stone anvil (east)
+ID=0x14f0
+RESOURCE=STONE 20 0x7dd
+SKILL=11 780 1280
+ADDITEM=stoneanvileastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3822]
+{
+NAME=stone anvil (south)
+ID=0x14f0
+RESOURCE=STONE 20 0x7dd
+SKILL=11 780 1280
+ADDITEM=stoneanvilsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3823]
+{
+NAME=large gargish bed (east)
+ID=0x14f0
+RESOURCE=STONE 3 0x7dd
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=largegargoylebedeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3824]
+{
+NAME=large gargish bed (south)
+ID=0x14f0
+RESOURCE=STONE 3 0x7dd
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=largegargoylebedsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3825]
+{
+NAME=gargish cot (east)
+ID=0x14f0
+RESOURCE=STONE 3 0x7dd
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=gargishcoteasteastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3826]
+{
+NAME=gargish cot (south)
+ID=0x14f0
+RESOURCE=STONE 3 0x7dd
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=gargishcoteastsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone armor /////
+//////////////////////////////////////
+
+[ITEM 3827]
+{
+NAME=gargish stone arms
+ID=0x0283
+RESOURCE=STONE 8 0x7dd
+SKILL=11 560 1060
+ADDITEM=femalegargishstonearms
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3828]
+{
+NAME=gargish stone chest
+ID=0x0285
+RESOURCE=STONE 12 0x7dd
+SKILL=11 560 1060
+ADDITEM=femalegargishstonechest
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3829]
+{
+NAME=gargish stone leggings
+ID=0x0289
+RESOURCE=STONE 10 0x7dd
+SKILL=11 560 1060
+ADDITEM=femalegargishstoneleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3830]
+{
+NAME=gargish stone kilt
+ID=0x0287
+RESOURCE=STONE 6 0x7dd
+SKILL=11 480 980
+ADDITEM=femalegargishstonekilt
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3831]
+{
+NAME=gargish stone arms
+ID=0x0284
+RESOURCE=STONE 8 0x7dd
+SKILL=11 560 1060
+ADDITEM=gargishstonearms
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3832]
+{
+NAME=gargish stone chest
+ID=0x0286
+RESOURCE=STONE 12 0x7dd
+SKILL=11 560 1060
+ADDITEM=gargishstonechest
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3833]
+{
+NAME=gargish stone leggings
+ID=0x028a
+RESOURCE=STONE 10 0x7dd
+SKILL=11 560 1060
+ADDITEM=gargishstoneleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3834]
+{
+NAME=gargish stone kilt
+ID=0x0288
+RESOURCE=STONE 6 0x7dd
+SKILL=11 480 980
+ADDITEM=gargishstonekilt
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3835]
+{
+NAME=large stone shield
+ID=0x4205
+RESOURCE=STONE 16 0x7dd
+SKILL=11 550 1060
+ADDITEM=largestoneshield
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3836]
+{
+NAME=gargish stone amulet
+ID=0x4D0a
+RESOURCE=STONE 3 0x7dd
+SKILL=11 600 1100
+ADDITEM=0x4D0a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone weapons /////
+//////////////////////////////////////
+
+[ITEM 3837]
+{
+NAME=stone war sword
+ID=0x4D0a
+RESOURCE=STONE 18 0x7dd
+SKILL=11 550 1050
+ADDITEM=stonewarsword
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone walls /////
+//////////////////////////////////////
+
+[ITEM 3838]
+{
+NAME=rough windowless
+ID=0x01D0
+RESOURCE=STONE 10 0x7dd
+SKILL=11 600 1100
+ADDITEM=roughwindowless1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3839]
+{
+NAME=rough window
+ID=0x01D3
+RESOURCE=STONE 10 0x7dd
+SKILL=11 600 1100
+ADDITEM=roughwindow1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3840]
+{
+NAME=rough arch
+ID=0x01D5
+RESOURCE=STONE 10 0x7dd
+SKILL=11 600 1100
+ADDITEM=rougharch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3841]
+{
+NAME=rough pillar
+ID=0x01Da
+RESOURCE=STONE 10 0x7dd
+SKILL=11 600 1100
+ADDITEM=roughpillar
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3842]
+{
+NAME=rough rounded arch
+ID=0x01DB
+RESOURCE=STONE 10 0x7dd
+SKILL=11 600 1100
+ADDITEM=roughroundedarch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3843]
+{
+NAME=rough small arch
+ID=0x01E0
+RESOURCE=STONE 10 0x7dd
+SKILL=11 600 1100
+ADDITEM=roughsmallarch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3844]
+{
+NAME=rough angled pillar
+ID=0x01E6
+RESOURCE=STONE 10 0x7dd
+SKILL=11 600 1100
+ADDITEM=roughangledpillar1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3845]
+{
+NAME=short rough
+ID=0x01E8
+RESOURCE=STONE 10 0x7dd
+SKILL=11 600 1100
+ADDITEM=shortrough1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//[ITEM 3846]
+//[ITEM 3847]
+//[ITEM 3848]
+//[ITEM 3849]
+
+//////////////////////////////////////
+//// stone stairs /////
+//////////////////////////////////////
+
+[ITEM 3850]
+{
+NAME=short rough
+ID=0x0788
+RESOURCE=STONE 5 0x7dd
+SKILL=11 600 1100
+ADDITEM=roughblock
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3851]
+{
+NAME=rough steps
+ID=0x0789
+RESOURCE=STONE 5 0x7dd
+SKILL=11 600 1100
+ADDITEM=roughsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3852]
+{
+NAME=rough corner steps
+ID=0x078d
+RESOURCE=STONE 5 0x7dd
+SKILL=11 600 1100
+ADDITEM=roughcornersteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3853]
+{
+NAME=rough rounded corner step
+ID=0x0791
+RESOURCE=STONE 5 0x7dd
+SKILL=11 600 1100
+ADDITEM=roughroundedcornerseps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3854]
+{
+NAME=rough inset steps
+ID=0x0795
+RESOURCE=STONE 5 0x7dd
+SKILL=11 600 1100
+ADDITEM=roughinsetsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3855]
+{
+NAME=rough rounded inset steps
+ID=0x0799
+RESOURCE=STONE 5 0x7dd
+SKILL=11 600 1100
+ADDITEM=roughroundedisetsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone floors /////
+//////////////////////////////////////
+
+[ITEM 3856]
+{
+NAME=light paver
+ID=0x0519
+RESOURCE=STONE 5 0x7dd
+SKILL=11 600 1100
+ADDITEM=lightpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3857]
+{
+NAME=medium paver
+ID=0x0519
+RESOURCE=STONE 5 0x7dd
+SKILL=11 600 1100
+ADDITEM=mediumpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3858]
+{
+NAME=dark paver
+ID=0x0519
+RESOURCE=STONE 5 0x7dd
+SKILL=11 600 1100
+ADDITEM=darkpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
\ No newline at end of file
diff --git a/data/dfndata/create/masonry_dullcopper.dfn b/data/dfndata/create/masonry_dullcopper.dfn
new file mode 100644
index 000000000..223b6d47b
--- /dev/null
+++ b/data/dfndata/create/masonry_dullcopper.dfn
@@ -0,0 +1,704 @@
+[ITEM 3600]
+{
+NAME=Vase
+ID=0x0b46
+RESOURCE=STONE 1 0x973
+SKILL=11 520 1020
+ADDITEM=0x0b46
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3601]
+{
+NAME=Large Vase
+ID=0x0b45
+RESOURCE=STONE 3 0x973
+SKILL=11 520 1020
+ADDITEM=0x0b45
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3602]
+{
+NAME=small Urn
+ID=0x241c
+RESOURCE=STONE 3 0x973
+SKILL=11 820 1030
+ADDITEM=0x241c
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3603]
+{
+NAME=Tower sculpture
+ID=0x241a
+RESOURCE=STONE 3 0x973
+SKILL=11 820 1030
+ADDITEM=0x241a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3604]
+{
+NAME=gargoyle painting
+ID=0x403d
+RESOURCE=STONE 3 0x973
+SKILL=11 820 1330
+ADDITEM=0x403d
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3605]
+{
+NAME=gargoyle sculpture
+ID=0x403f
+RESOURCE=STONE 3 0x973
+SKILL=11 820 1320
+ADDITEM=0x403f
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3606]
+{
+NAME=gargoyle vase
+ID=0x4042
+RESOURCE=STONE 3 0x973
+SKILL=11 800 1260
+ADDITEM=0x4042
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3607]
+{
+NAME=Tall 18th anniversary Vase
+ID=0x9bc7
+RESOURCE=STONE 6 0x973
+SKILL=11 600 1100
+ADDITEM=0x9bc7
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3608]
+{
+NAME=short 18th anniversary Vase
+ID=0x9bca
+RESOURCE=STONE 6 0x973
+SKILL=11 600 1100
+ADDITEM=0x9bca
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// furniture /////
+//////////////////////////////////////
+
+[ITEM 3609]
+{
+NAME=stone chair
+ID=0x1218
+RESOURCE=STONE 4 0x973
+SKILL=11 550 1050
+ADDITEM=0x1218
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3610]
+{
+NAME=stone table (east)
+ID=0x14f0
+RESOURCE=STONE 6 0x973
+SKILL=11 650 1150
+ADDITEM=mediumstonetableeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3611]
+{
+NAME=stone table (south)
+ID=0x14f0
+RESOURCE=STONE 6 0x973
+SKILL=11 650 1150
+ADDITEM=mediumstonetablesouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3612]
+{
+NAME=large stone table (east)
+ID=0x14f0
+RESOURCE=STONE 9 0x973
+SKILL=11 750 1250
+ADDITEM=largestonetableeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3613]
+{
+NAME=large stone table (south)
+ID=0x14f0
+RESOURCE=STONE 9 0x973
+SKILL=11 750 1250
+ADDITEM=largestonetablesouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3614]
+{
+NAME=ritual table
+ID=0x14f0
+RESOURCE=STONE 8 0x973
+SKILL=11 940 1030
+ADDITEM=ritualtabledeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// statues /////
+//////////////////////////////////////
+
+[ITEM 3615]
+{
+NAME=small statue (south)
+ID=0x139a
+RESOURCE=STONE 3 0x973
+SKILL=11 600 1100
+ADDITEM=0x139a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3616]
+{
+NAME=small statue (north)
+ID=0x139b
+RESOURCE=STONE 3 0x973
+SKILL=11 600 1100
+ADDITEM=0x139b
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3617]
+{
+NAME=small statue (east)
+ID=0x139c
+RESOURCE=STONE 3 0x973
+SKILL=11 600 1100
+ADDITEM=0x139c
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3618]
+{
+NAME=pegasus statuette
+ID=0x139d
+RESOURCE=STONE 4 0x973
+SKILL=11 700 1200
+ADDITEM=0x139d
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3619]
+{
+NAME=gargoyle statue
+ID=0x494e
+RESOURCE=STONE 20 0x973
+SKILL=11 540 1040
+ADDITEM=0x494e
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3620]
+{
+NAME=gryphon statue
+ID=0x494e
+RESOURCE=STONE 15 0x973
+SKILL=11 540 1040
+ADDITEM=0x494e
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// Misc addons /////
+//////////////////////////////////////
+
+[ITEM 3621]
+{
+NAME=stone anvil (east)
+ID=0x14f0
+RESOURCE=STONE 20 0x973
+SKILL=11 780 1280
+ADDITEM=stoneanvileastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3622]
+{
+NAME=stone anvil (south)
+ID=0x14f0
+RESOURCE=STONE 20 0x973
+SKILL=11 780 1280
+ADDITEM=stoneanvilsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3623]
+{
+NAME=large gargish bed (east)
+ID=0x14f0
+RESOURCE=STONE 3 0x973
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=largegargoylebedeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3624]
+{
+NAME=large gargish bed (south)
+ID=0x14f0
+RESOURCE=STONE 3 0x973
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=largegargoylebedsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3625]
+{
+NAME=gargish cot (east)
+ID=0x14f0
+RESOURCE=STONE 3 0x973
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=gargishcoteasteastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3626]
+{
+NAME=gargish cot (south)
+ID=0x14f0
+RESOURCE=STONE 3 0x973
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=gargishcoteastsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone armor /////
+//////////////////////////////////////
+
+[ITEM 3627]
+{
+NAME=gargish stone arms
+ID=0x0283
+RESOURCE=STONE 8 0x973
+SKILL=11 560 1060
+ADDITEM=femalegargishstonearms
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3628]
+{
+NAME=gargish stone chest
+ID=0x0285
+RESOURCE=STONE 12 0x973
+SKILL=11 560 1060
+ADDITEM=femalegargishstonechest
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3629]
+{
+NAME=gargish stone leggings
+ID=0x0289
+RESOURCE=STONE 10 0x973
+SKILL=11 560 1060
+ADDITEM=femalegargishstoneleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3630]
+{
+NAME=gargish stone kilt
+ID=0x0287
+RESOURCE=STONE 6 0x973
+SKILL=11 480 980
+ADDITEM=femalegargishstonekilt
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3631]
+{
+NAME=gargish stone arms
+ID=0x0284
+RESOURCE=STONE 8 0x973
+SKILL=11 560 1060
+ADDITEM=gargishstonearms
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3632]
+{
+NAME=gargish stone chest
+ID=0x0286
+RESOURCE=STONE 12 0x973
+SKILL=11 560 1060
+ADDITEM=gargishstonechest
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3633]
+{
+NAME=gargish stone leggings
+ID=0x028a
+RESOURCE=STONE 10 0x973
+SKILL=11 560 1060
+ADDITEM=gargishstoneleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3634]
+{
+NAME=gargish stone kilt
+ID=0x0288
+RESOURCE=STONE 6 0x973
+SKILL=11 480 980
+ADDITEM=gargishstonekilt
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3635]
+{
+NAME=large stone shield
+ID=0x4205
+RESOURCE=STONE 16 0x973
+SKILL=11 550 1060
+ADDITEM=largestoneshield
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3636]
+{
+NAME=gargish stone amulet
+ID=0x4D0a
+RESOURCE=STONE 3 0x973
+SKILL=11 600 1100
+ADDITEM=0x4D0a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone weapons /////
+//////////////////////////////////////
+
+[ITEM 3637]
+{
+NAME=stone war sword
+ID=0x4D0a
+RESOURCE=STONE 18 0x973
+SKILL=11 550 1050
+ADDITEM=stonewarsword
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone walls /////
+//////////////////////////////////////
+
+[ITEM 3638]
+{
+NAME=rough windowless
+ID=0x01D0
+RESOURCE=STONE 10 0x973
+SKILL=11 600 1100
+ADDITEM=roughwindowless1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3639]
+{
+NAME=rough window
+ID=0x01D3
+RESOURCE=STONE 10 0x973
+SKILL=11 600 1100
+ADDITEM=roughwindow1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3640]
+{
+NAME=rough arch
+ID=0x01D5
+RESOURCE=STONE 10 0x973
+SKILL=11 600 1100
+ADDITEM=rougharch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3641]
+{
+NAME=rough pillar
+ID=0x01Da
+RESOURCE=STONE 10 0x973
+SKILL=11 600 1100
+ADDITEM=roughpillar
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3642]
+{
+NAME=rough rounded arch
+ID=0x01DB
+RESOURCE=STONE 10 0x973
+SKILL=11 600 1100
+ADDITEM=roughroundedarch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3643]
+{
+NAME=rough small arch
+ID=0x01E0
+RESOURCE=STONE 10 0x973
+SKILL=11 600 1100
+ADDITEM=roughsmallarch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3644]
+{
+NAME=rough angled pillar
+ID=0x01E6
+RESOURCE=STONE 10 0x973
+SKILL=11 600 1100
+ADDITEM=roughangledpillar1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3645]
+{
+NAME=short rough
+ID=0x01E8
+RESOURCE=STONE 10 0x973
+SKILL=11 600 1100
+ADDITEM=shortrough1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//[ITEM 3646]
+//[ITEM 3647]
+//[ITEM 3648]
+//[ITEM 3649]
+
+//////////////////////////////////////
+//// stone stairs /////
+//////////////////////////////////////
+
+[ITEM 3650]
+{
+NAME=short rough
+ID=0x0788
+RESOURCE=STONE 5 0x973
+SKILL=11 600 1100
+ADDITEM=roughblock
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3651]
+{
+NAME=rough steps
+ID=0x0789
+RESOURCE=STONE 5 0x973
+SKILL=11 600 1100
+ADDITEM=roughsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3652]
+{
+NAME=rough corner steps
+ID=0x078d
+RESOURCE=STONE 5 0x973
+SKILL=11 600 1100
+ADDITEM=roughcornersteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3653]
+{
+NAME=rough rounded corner step
+ID=0x0791
+RESOURCE=STONE 5 0x973
+SKILL=11 600 1100
+ADDITEM=roughroundedcornerseps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3654]
+{
+NAME=rough inset steps
+ID=0x0795
+RESOURCE=STONE 5 0x973
+SKILL=11 600 1100
+ADDITEM=roughinsetsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3655]
+{
+NAME=rough rounded inset steps
+ID=0x0799
+RESOURCE=STONE 5 0x973
+SKILL=11 600 1100
+ADDITEM=roughroundedisetsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone floors /////
+//////////////////////////////////////
+
+[ITEM 3656]
+{
+NAME=light paver
+ID=0x0519
+RESOURCE=STONE 5 0x973
+SKILL=11 600 1100
+ADDITEM=lightpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3657]
+{
+NAME=medium paver
+ID=0x0519
+RESOURCE=STONE 5 0x973
+SKILL=11 600 1100
+ADDITEM=mediumpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3658]
+{
+NAME=dark paver
+ID=0x0519
+RESOURCE=STONE 5 0x973
+SKILL=11 600 1100
+ADDITEM=darkpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
\ No newline at end of file
diff --git a/data/dfndata/create/masonry_gold.dfn b/data/dfndata/create/masonry_gold.dfn
new file mode 100644
index 000000000..22daec47a
--- /dev/null
+++ b/data/dfndata/create/masonry_gold.dfn
@@ -0,0 +1,704 @@
+[ITEM 4000]
+{
+NAME=Vase
+ID=0x0b46
+RESOURCE=STONE 1 0x8a5
+SKILL=11 520 1020
+ADDITEM=0x0b46
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4001]
+{
+NAME=Large Vase
+ID=0x0b45
+RESOURCE=STONE 3 0x8a5
+SKILL=11 520 1020
+ADDITEM=0x0b45
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4002]
+{
+NAME=small Urn
+ID=0x241c
+RESOURCE=STONE 3 0x8a5
+SKILL=11 820 1030
+ADDITEM=0x241c
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4003]
+{
+NAME=Tower sculpture
+ID=0x241a
+RESOURCE=STONE 3 0x8a5
+SKILL=11 820 1030
+ADDITEM=0x241a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4004]
+{
+NAME=gargoyle painting
+ID=0x403d
+RESOURCE=STONE 3 0x8a5
+SKILL=11 820 1330
+ADDITEM=0x403d
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4005]
+{
+NAME=gargoyle sculpture
+ID=0x403f
+RESOURCE=STONE 3 0x8a5
+SKILL=11 820 1320
+ADDITEM=0x403f
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4006]
+{
+NAME=gargoyle vase
+ID=0x4042
+RESOURCE=STONE 3 0x8a5
+SKILL=11 800 1260
+ADDITEM=0x4042
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4007]
+{
+NAME=Tall 18th anniversary Vase
+ID=0x9bc7
+RESOURCE=STONE 6 0x8a5
+SKILL=11 600 1100
+ADDITEM=0x9bc7
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4008]
+{
+NAME=short 18th anniversary Vase
+ID=0x9bca
+RESOURCE=STONE 6 0x8a5
+SKILL=11 600 1100
+ADDITEM=0x9bca
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// furniture /////
+//////////////////////////////////////
+
+[ITEM 4009]
+{
+NAME=stone chair
+ID=0x1218
+RESOURCE=STONE 4 0x8a5
+SKILL=11 550 1050
+ADDITEM=0x1218
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4010]
+{
+NAME=stone table (east)
+ID=0x14f0
+RESOURCE=STONE 6 0x8a5
+SKILL=11 650 1150
+ADDITEM=mediumstonetableeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4011]
+{
+NAME=stone table (south)
+ID=0x14f0
+RESOURCE=STONE 6 0x8a5
+SKILL=11 650 1150
+ADDITEM=mediumstonetablesouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4012]
+{
+NAME=large stone table (east)
+ID=0x14f0
+RESOURCE=STONE 9 0x8a5
+SKILL=11 750 1250
+ADDITEM=largestonetableeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4013]
+{
+NAME=large stone table (south)
+ID=0x14f0
+RESOURCE=STONE 9 0x8a5
+SKILL=11 750 1250
+ADDITEM=largestonetablesouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4014]
+{
+NAME=ritual table
+ID=0x14f0
+RESOURCE=STONE 8 0x8a5
+SKILL=11 940 1030
+ADDITEM=ritualtabledeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// statues /////
+//////////////////////////////////////
+
+[ITEM 4015]
+{
+NAME=small statue (south)
+ID=0x139a
+RESOURCE=STONE 3 0x8a5
+SKILL=11 600 1100
+ADDITEM=0x139a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4016]
+{
+NAME=small statue (north)
+ID=0x139b
+RESOURCE=STONE 3 0x8a5
+SKILL=11 600 1100
+ADDITEM=0x139b
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4017]
+{
+NAME=small statue (east)
+ID=0x139c
+RESOURCE=STONE 3 0x8a5
+SKILL=11 600 1100
+ADDITEM=0x139c
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4018]
+{
+NAME=pegasus statuette
+ID=0x139d
+RESOURCE=STONE 4 0x8a5
+SKILL=11 700 1200
+ADDITEM=0x139d
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4019]
+{
+NAME=gargoyle statue
+ID=0x494e
+RESOURCE=STONE 20 0x8a5
+SKILL=11 540 1040
+ADDITEM=0x494e
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4020]
+{
+NAME=gryphon statue
+ID=0x494e
+RESOURCE=STONE 15 0x8a5
+SKILL=11 540 1040
+ADDITEM=0x494e
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// Misc addons /////
+//////////////////////////////////////
+
+[ITEM 4021]
+{
+NAME=stone anvil (east)
+ID=0x14f0
+RESOURCE=STONE 20 0x8a5
+SKILL=11 780 1280
+ADDITEM=stoneanvileastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4022]
+{
+NAME=stone anvil (south)
+ID=0x14f0
+RESOURCE=STONE 20 0x8a5
+SKILL=11 780 1280
+ADDITEM=stoneanvilsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4023]
+{
+NAME=large gargish bed (east)
+ID=0x14f0
+RESOURCE=STONE 3 0x8a5
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=largegargoylebedeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4024]
+{
+NAME=large gargish bed (south)
+ID=0x14f0
+RESOURCE=STONE 3 0x8a5
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=largegargoylebedsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4025]
+{
+NAME=gargish cot (east)
+ID=0x14f0
+RESOURCE=STONE 3 0x8a5
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=gargishcoteasteastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4026]
+{
+NAME=gargish cot (south)
+ID=0x14f0
+RESOURCE=STONE 3 0x8a5
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=gargishcoteastsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone armor /////
+//////////////////////////////////////
+
+[ITEM 4027]
+{
+NAME=gargish stone arms
+ID=0x0283
+RESOURCE=STONE 8 0x8a5
+SKILL=11 560 1060
+ADDITEM=femalegargishstonearms
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4028]
+{
+NAME=gargish stone chest
+ID=0x0285
+RESOURCE=STONE 12 0x8a5
+SKILL=11 560 1060
+ADDITEM=femalegargishstonechest
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4029]
+{
+NAME=gargish stone leggings
+ID=0x0289
+RESOURCE=STONE 10 0x8a5
+SKILL=11 560 1060
+ADDITEM=femalegargishstoneleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4030]
+{
+NAME=gargish stone kilt
+ID=0x0287
+RESOURCE=STONE 6 0x8a5
+SKILL=11 480 980
+ADDITEM=femalegargishstonekilt
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4031]
+{
+NAME=gargish stone arms
+ID=0x0284
+RESOURCE=STONE 8 0x8a5
+SKILL=11 560 1060
+ADDITEM=gargishstonearms
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4032]
+{
+NAME=gargish stone chest
+ID=0x0286
+RESOURCE=STONE 12 0x8a5
+SKILL=11 560 1060
+ADDITEM=gargishstonechest
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4033]
+{
+NAME=gargish stone leggings
+ID=0x028a
+RESOURCE=STONE 10 0x8a5
+SKILL=11 560 1060
+ADDITEM=gargishstoneleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4034]
+{
+NAME=gargish stone kilt
+ID=0x0288
+RESOURCE=STONE 6 0x8a5
+SKILL=11 480 980
+ADDITEM=gargishstonekilt
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4035]
+{
+NAME=large stone shield
+ID=0x4205
+RESOURCE=STONE 16 0x8a5
+SKILL=11 550 1060
+ADDITEM=largestoneshield
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4036]
+{
+NAME=gargish stone amulet
+ID=0x4D0a
+RESOURCE=STONE 3 0x8a5
+SKILL=11 600 1100
+ADDITEM=0x4D0a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone weapons /////
+//////////////////////////////////////
+
+[ITEM 4037]
+{
+NAME=stone war sword
+ID=0x4D0a
+RESOURCE=STONE 18 0x8a5
+SKILL=11 550 1050
+ADDITEM=stonewarsword
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone walls /////
+//////////////////////////////////////
+
+[ITEM 4038]
+{
+NAME=rough windowless
+ID=0x01D0
+RESOURCE=STONE 10 0x8a5
+SKILL=11 600 1100
+ADDITEM=roughwindowless1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4039]
+{
+NAME=rough window
+ID=0x01D3
+RESOURCE=STONE 10 0x8a5
+SKILL=11 600 1100
+ADDITEM=roughwindow1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4040]
+{
+NAME=rough arch
+ID=0x01D5
+RESOURCE=STONE 10 0x8a5
+SKILL=11 600 1100
+ADDITEM=rougharch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4041]
+{
+NAME=rough pillar
+ID=0x01Da
+RESOURCE=STONE 10 0x8a5
+SKILL=11 600 1100
+ADDITEM=roughpillar
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4042]
+{
+NAME=rough rounded arch
+ID=0x01DB
+RESOURCE=STONE 10 0x8a5
+SKILL=11 600 1100
+ADDITEM=roughroundedarch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4043]
+{
+NAME=rough small arch
+ID=0x01E0
+RESOURCE=STONE 10 0x8a5
+SKILL=11 600 1100
+ADDITEM=roughsmallarch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4044]
+{
+NAME=rough angled pillar
+ID=0x01E6
+RESOURCE=STONE 10 0x8a5
+SKILL=11 600 1100
+ADDITEM=roughangledpillar1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4045]
+{
+NAME=short rough
+ID=0x01E8
+RESOURCE=STONE 10 0x8a5
+SKILL=11 600 1100
+ADDITEM=shortrough1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//[ITEM 4046]
+//[ITEM 4047]
+//[ITEM 4048]
+//[ITEM 4049]
+
+//////////////////////////////////////
+//// stone stairs /////
+//////////////////////////////////////
+
+[ITEM 4050]
+{
+NAME=short rough
+ID=0x0788
+RESOURCE=STONE 5 0x8a5
+SKILL=11 600 1100
+ADDITEM=roughblock
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4051]
+{
+NAME=rough steps
+ID=0x0789
+RESOURCE=STONE 5 0x8a5
+SKILL=11 600 1100
+ADDITEM=roughsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4052]
+{
+NAME=rough corner steps
+ID=0x078d
+RESOURCE=STONE 5 0x8a5
+SKILL=11 600 1100
+ADDITEM=roughcornersteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4053]
+{
+NAME=rough rounded corner step
+ID=0x0791
+RESOURCE=STONE 5 0x8a5
+SKILL=11 600 1100
+ADDITEM=roughroundedcornerseps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4054]
+{
+NAME=rough inset steps
+ID=0x0795
+RESOURCE=STONE 5 0x8a5
+SKILL=11 600 1100
+ADDITEM=roughinsetsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4055]
+{
+NAME=rough rounded inset steps
+ID=0x0799
+RESOURCE=STONE 5 0x8a5
+SKILL=11 600 1100
+ADDITEM=roughroundedisetsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone floors /////
+//////////////////////////////////////
+
+[ITEM 4056]
+{
+NAME=light paver
+ID=0x0519
+RESOURCE=STONE 5 0x8a5
+SKILL=11 600 1100
+ADDITEM=lightpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4057]
+{
+NAME=medium paver
+ID=0x0519
+RESOURCE=STONE 5 0x8a5
+SKILL=11 600 1100
+ADDITEM=mediumpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4058]
+{
+NAME=dark paver
+ID=0x0519
+RESOURCE=STONE 5 0x8a5
+SKILL=11 600 1100
+ADDITEM=darkpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
\ No newline at end of file
diff --git a/data/dfndata/create/masonry_shadowiron.dfn b/data/dfndata/create/masonry_shadowiron.dfn
new file mode 100644
index 000000000..40ce6105c
--- /dev/null
+++ b/data/dfndata/create/masonry_shadowiron.dfn
@@ -0,0 +1,704 @@
+[ITEM 3700]
+{
+NAME=Vase
+ID=0x0b46
+RESOURCE=STONE 1 0x966
+SKILL=11 520 1020
+ADDITEM=0x0b46
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3701]
+{
+NAME=Large Vase
+ID=0x0b45
+RESOURCE=STONE 3 0x966
+SKILL=11 520 1020
+ADDITEM=0x0b45
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3702]
+{
+NAME=small Urn
+ID=0x241c
+RESOURCE=STONE 3 0x966
+SKILL=11 820 1030
+ADDITEM=0x241c
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3703]
+{
+NAME=Tower sculpture
+ID=0x241a
+RESOURCE=STONE 3 0x966
+SKILL=11 820 1030
+ADDITEM=0x241a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3704]
+{
+NAME=gargoyle painting
+ID=0x403d
+RESOURCE=STONE 3 0x966
+SKILL=11 820 1330
+ADDITEM=0x403d
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3705]
+{
+NAME=gargoyle sculpture
+ID=0x403f
+RESOURCE=STONE 3 0x966
+SKILL=11 820 1320
+ADDITEM=0x403f
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3706]
+{
+NAME=gargoyle vase
+ID=0x4042
+RESOURCE=STONE 3 0x966
+SKILL=11 800 1260
+ADDITEM=0x4042
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3707]
+{
+NAME=Tall 18th anniversary Vase
+ID=0x9bc7
+RESOURCE=STONE 6 0x966
+SKILL=11 600 1100
+ADDITEM=0x9bc7
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3708]
+{
+NAME=short 18th anniversary Vase
+ID=0x9bca
+RESOURCE=STONE 6 0x966
+SKILL=11 600 1100
+ADDITEM=0x9bca
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// furniture /////
+//////////////////////////////////////
+
+[ITEM 3709]
+{
+NAME=stone chair
+ID=0x1218
+RESOURCE=STONE 4 0x966
+SKILL=11 550 1050
+ADDITEM=0x1218
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3710]
+{
+NAME=stone table (east)
+ID=0x14f0
+RESOURCE=STONE 6 0x966
+SKILL=11 650 1150
+ADDITEM=mediumstonetableeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3711]
+{
+NAME=stone table (south)
+ID=0x14f0
+RESOURCE=STONE 6 0x966
+SKILL=11 650 1150
+ADDITEM=mediumstonetablesouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3712]
+{
+NAME=large stone table (east)
+ID=0x14f0
+RESOURCE=STONE 9 0x966
+SKILL=11 750 1250
+ADDITEM=largestonetableeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3713]
+{
+NAME=large stone table (south)
+ID=0x14f0
+RESOURCE=STONE 9 0x966
+SKILL=11 750 1250
+ADDITEM=largestonetablesouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3714]
+{
+NAME=ritual table
+ID=0x14f0
+RESOURCE=STONE 8 0x966
+SKILL=11 940 1030
+ADDITEM=ritualtabledeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// statues /////
+//////////////////////////////////////
+
+[ITEM 3715]
+{
+NAME=small statue (south)
+ID=0x139a
+RESOURCE=STONE 3 0x966
+SKILL=11 600 1100
+ADDITEM=0x139a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3716]
+{
+NAME=small statue (north)
+ID=0x139b
+RESOURCE=STONE 3 0x966
+SKILL=11 600 1100
+ADDITEM=0x139b
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3717]
+{
+NAME=small statue (east)
+ID=0x139c
+RESOURCE=STONE 3 0x966
+SKILL=11 600 1100
+ADDITEM=0x139c
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3718]
+{
+NAME=pegasus statuette
+ID=0x139d
+RESOURCE=STONE 4 0x966
+SKILL=11 700 1200
+ADDITEM=0x139d
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3719]
+{
+NAME=gargoyle statue
+ID=0x494e
+RESOURCE=STONE 20 0x966
+SKILL=11 540 1040
+ADDITEM=0x494e
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3720]
+{
+NAME=gryphon statue
+ID=0x494e
+RESOURCE=STONE 15 0x966
+SKILL=11 540 1040
+ADDITEM=0x494e
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// Misc addons /////
+//////////////////////////////////////
+
+[ITEM 3721]
+{
+NAME=stone anvil (east)
+ID=0x14f0
+RESOURCE=STONE 20 0x966
+SKILL=11 780 1280
+ADDITEM=stoneanvileastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3722]
+{
+NAME=stone anvil (south)
+ID=0x14f0
+RESOURCE=STONE 20 0x966
+SKILL=11 780 1280
+ADDITEM=stoneanvilsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3723]
+{
+NAME=large gargish bed (east)
+ID=0x14f0
+RESOURCE=STONE 3 0x966
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=largegargoylebedeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3724]
+{
+NAME=large gargish bed (south)
+ID=0x14f0
+RESOURCE=STONE 3 0x966
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=largegargoylebedsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3725]
+{
+NAME=gargish cot (east)
+ID=0x14f0
+RESOURCE=STONE 3 0x966
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=gargishcoteasteastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3726]
+{
+NAME=gargish cot (south)
+ID=0x14f0
+RESOURCE=STONE 3 0x966
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=gargishcoteastsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone armor /////
+//////////////////////////////////////
+
+[ITEM 3727]
+{
+NAME=gargish stone arms
+ID=0x0283
+RESOURCE=STONE 8 0x966
+SKILL=11 560 1060
+ADDITEM=femalegargishstonearms
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3728]
+{
+NAME=gargish stone chest
+ID=0x0285
+RESOURCE=STONE 12 0x966
+SKILL=11 560 1060
+ADDITEM=femalegargishstonechest
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3729]
+{
+NAME=gargish stone leggings
+ID=0x0289
+RESOURCE=STONE 10 0x966
+SKILL=11 560 1060
+ADDITEM=femalegargishstoneleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3730]
+{
+NAME=gargish stone kilt
+ID=0x0287
+RESOURCE=STONE 6 0x966
+SKILL=11 480 980
+ADDITEM=femalegargishstonekilt
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3731]
+{
+NAME=gargish stone arms
+ID=0x0284
+RESOURCE=STONE 8 0x966
+SKILL=11 560 1060
+ADDITEM=gargishstonearms
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3732]
+{
+NAME=gargish stone chest
+ID=0x0286
+RESOURCE=STONE 12 0x966
+SKILL=11 560 1060
+ADDITEM=gargishstonechest
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3733]
+{
+NAME=gargish stone leggings
+ID=0x028a
+RESOURCE=STONE 10 0x966
+SKILL=11 560 1060
+ADDITEM=gargishstoneleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3734]
+{
+NAME=gargish stone kilt
+ID=0x0288
+RESOURCE=STONE 6 0x966
+SKILL=11 480 980
+ADDITEM=gargishstonekilt
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3735]
+{
+NAME=large stone shield
+ID=0x4205
+RESOURCE=STONE 16 0x966
+SKILL=11 550 1060
+ADDITEM=largestoneshield
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3736]
+{
+NAME=gargish stone amulet
+ID=0x4D0a
+RESOURCE=STONE 3 0x966
+SKILL=11 600 1100
+ADDITEM=0x4D0a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone weapons /////
+//////////////////////////////////////
+
+[ITEM 3737]
+{
+NAME=stone war sword
+ID=0x4D0a
+RESOURCE=STONE 18 0x966
+SKILL=11 550 1050
+ADDITEM=stonewarsword
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone walls /////
+//////////////////////////////////////
+
+[ITEM 3738]
+{
+NAME=rough windowless
+ID=0x01D0
+RESOURCE=STONE 10 0x966
+SKILL=11 600 1100
+ADDITEM=roughwindowless1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3739]
+{
+NAME=rough window
+ID=0x01D3
+RESOURCE=STONE 10 0x966
+SKILL=11 600 1100
+ADDITEM=roughwindow1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3740]
+{
+NAME=rough arch
+ID=0x01D5
+RESOURCE=STONE 10 0x966
+SKILL=11 600 1100
+ADDITEM=rougharch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3741]
+{
+NAME=rough pillar
+ID=0x01Da
+RESOURCE=STONE 10 0x966
+SKILL=11 600 1100
+ADDITEM=roughpillar
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3742]
+{
+NAME=rough rounded arch
+ID=0x01DB
+RESOURCE=STONE 10 0x966
+SKILL=11 600 1100
+ADDITEM=roughroundedarch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3743]
+{
+NAME=rough small arch
+ID=0x01E0
+RESOURCE=STONE 10 0x966
+SKILL=11 600 1100
+ADDITEM=roughsmallarch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3744]
+{
+NAME=rough angled pillar
+ID=0x01E6
+RESOURCE=STONE 10 0x966
+SKILL=11 600 1100
+ADDITEM=roughangledpillar1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3745]
+{
+NAME=short rough
+ID=0x01E8
+RESOURCE=STONE 10 0x966
+SKILL=11 600 1100
+ADDITEM=shortrough1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//[ITEM 3746]
+//[ITEM 3747]
+//[ITEM 3748]
+//[ITEM 3749]
+
+//////////////////////////////////////
+//// stone stairs /////
+//////////////////////////////////////
+
+[ITEM 3750]
+{
+NAME=short rough
+ID=0x0788
+RESOURCE=STONE 5 0x966
+SKILL=11 600 1100
+ADDITEM=roughblock
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3751]
+{
+NAME=rough steps
+ID=0x0789
+RESOURCE=STONE 5 0x966
+SKILL=11 600 1100
+ADDITEM=roughsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3752]
+{
+NAME=rough corner steps
+ID=0x078d
+RESOURCE=STONE 5 0x966
+SKILL=11 600 1100
+ADDITEM=roughcornersteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3753]
+{
+NAME=rough rounded corner step
+ID=0x0791
+RESOURCE=STONE 5 0x966
+SKILL=11 600 1100
+ADDITEM=roughroundedcornerseps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3754]
+{
+NAME=rough inset steps
+ID=0x0795
+RESOURCE=STONE 5 0x966
+SKILL=11 600 1100
+ADDITEM=roughinsetsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3755]
+{
+NAME=rough rounded inset steps
+ID=0x0799
+RESOURCE=STONE 5 0x966
+SKILL=11 600 1100
+ADDITEM=roughroundedisetsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone floors /////
+//////////////////////////////////////
+
+[ITEM 3756]
+{
+NAME=light paver
+ID=0x0519
+RESOURCE=STONE 5 0x966
+SKILL=11 600 1100
+ADDITEM=lightpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3757]
+{
+NAME=medium paver
+ID=0x0519
+RESOURCE=STONE 5 0x966
+SKILL=11 600 1100
+ADDITEM=mediumpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 3758]
+{
+NAME=dark paver
+ID=0x0519
+RESOURCE=STONE 5 0x966
+SKILL=11 600 1100
+ADDITEM=darkpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
\ No newline at end of file
diff --git a/data/dfndata/create/masonry_valorite.dfn b/data/dfndata/create/masonry_valorite.dfn
new file mode 100644
index 000000000..911b991b8
--- /dev/null
+++ b/data/dfndata/create/masonry_valorite.dfn
@@ -0,0 +1,704 @@
+[ITEM 4300]
+{
+NAME=Vase
+ID=0x0b46
+RESOURCE=STONE 1 0x8ab
+SKILL=11 520 1020
+ADDITEM=0x0b46
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4301]
+{
+NAME=Large Vase
+ID=0x0b45
+RESOURCE=STONE 3 0x8ab
+SKILL=11 520 1020
+ADDITEM=0x0b45
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4302]
+{
+NAME=small Urn
+ID=0x241c
+RESOURCE=STONE 3 0x8ab
+SKILL=11 820 1030
+ADDITEM=0x241c
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4303]
+{
+NAME=Tower sculpture
+ID=0x241a
+RESOURCE=STONE 3 0x8ab
+SKILL=11 820 1030
+ADDITEM=0x241a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4304]
+{
+NAME=gargoyle painting
+ID=0x403d
+RESOURCE=STONE 3 0x8ab
+SKILL=11 820 1330
+ADDITEM=0x403d
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4305]
+{
+NAME=gargoyle sculpture
+ID=0x403f
+RESOURCE=STONE 3 0x8ab
+SKILL=11 820 1320
+ADDITEM=0x403f
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4306]
+{
+NAME=gargoyle vase
+ID=0x4042
+RESOURCE=STONE 3 0x8ab
+SKILL=11 800 1260
+ADDITEM=0x4042
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4307]
+{
+NAME=Tall 18th anniversary Vase
+ID=0x9bc7
+RESOURCE=STONE 6 0x8ab
+SKILL=11 600 1100
+ADDITEM=0x9bc7
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4308]
+{
+NAME=short 18th anniversary Vase
+ID=0x9bca
+RESOURCE=STONE 6 0x8ab
+SKILL=11 600 1100
+ADDITEM=0x9bca
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// furniture /////
+//////////////////////////////////////
+
+[ITEM 4309]
+{
+NAME=stone chair
+ID=0x1218
+RESOURCE=STONE 4 0x8ab
+SKILL=11 550 1050
+ADDITEM=0x1218
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4310]
+{
+NAME=stone table (east)
+ID=0x14f0
+RESOURCE=STONE 6 0x8ab
+SKILL=11 650 1150
+ADDITEM=mediumstonetableeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4311]
+{
+NAME=stone table (south)
+ID=0x14f0
+RESOURCE=STONE 6 0x8ab
+SKILL=11 650 1150
+ADDITEM=mediumstonetablesouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4312]
+{
+NAME=large stone table (east)
+ID=0x14f0
+RESOURCE=STONE 9 0x8ab
+SKILL=11 750 1250
+ADDITEM=largestonetableeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4313]
+{
+NAME=large stone table (south)
+ID=0x14f0
+RESOURCE=STONE 9 0x8ab
+SKILL=11 750 1250
+ADDITEM=largestonetablesouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4314]
+{
+NAME=ritual table
+ID=0x14f0
+RESOURCE=STONE 8 0x8ab
+SKILL=11 940 1030
+ADDITEM=ritualtabledeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// statues /////
+//////////////////////////////////////
+
+[ITEM 4315]
+{
+NAME=small statue (south)
+ID=0x139a
+RESOURCE=STONE 3 0x8ab
+SKILL=11 600 1100
+ADDITEM=0x139a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4316]
+{
+NAME=small statue (north)
+ID=0x139b
+RESOURCE=STONE 3 0x8ab
+SKILL=11 600 1100
+ADDITEM=0x139b
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4317]
+{
+NAME=small statue (east)
+ID=0x139c
+RESOURCE=STONE 3 0x8ab
+SKILL=11 600 1100
+ADDITEM=0x139c
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4318]
+{
+NAME=pegasus statuette
+ID=0x139d
+RESOURCE=STONE 4 0x8ab
+SKILL=11 700 1200
+ADDITEM=0x139d
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4319]
+{
+NAME=gargoyle statue
+ID=0x494e
+RESOURCE=STONE 20 0x8ab
+SKILL=11 540 1040
+ADDITEM=0x494e
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4320]
+{
+NAME=gryphon statue
+ID=0x494e
+RESOURCE=STONE 15 0x8ab
+SKILL=11 540 1040
+ADDITEM=0x494e
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// Misc addons /////
+//////////////////////////////////////
+
+[ITEM 4321]
+{
+NAME=stone anvil (east)
+ID=0x14f0
+RESOURCE=STONE 20 0x8ab
+SKILL=11 780 1280
+ADDITEM=stoneanvileastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4322]
+{
+NAME=stone anvil (south)
+ID=0x14f0
+RESOURCE=STONE 20 0x8ab
+SKILL=11 780 1280
+ADDITEM=stoneanvilsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4323]
+{
+NAME=large gargish bed (east)
+ID=0x14f0
+RESOURCE=STONE 3 0x8ab
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=largegargoylebedeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4324]
+{
+NAME=large gargish bed (south)
+ID=0x14f0
+RESOURCE=STONE 3 0x8ab
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=largegargoylebedsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4325]
+{
+NAME=gargish cot (east)
+ID=0x14f0
+RESOURCE=STONE 3 0x8ab
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=gargishcoteasteastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4326]
+{
+NAME=gargish cot (south)
+ID=0x14f0
+RESOURCE=STONE 3 0x8ab
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=gargishcoteastsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone armor /////
+//////////////////////////////////////
+
+[ITEM 4327]
+{
+NAME=gargish stone arms
+ID=0x0283
+RESOURCE=STONE 8 0x8ab
+SKILL=11 560 1060
+ADDITEM=femalegargishstonearms
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4328]
+{
+NAME=gargish stone chest
+ID=0x0285
+RESOURCE=STONE 12 0x8ab
+SKILL=11 560 1060
+ADDITEM=femalegargishstonechest
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4329]
+{
+NAME=gargish stone leggings
+ID=0x0289
+RESOURCE=STONE 10 0x8ab
+SKILL=11 560 1060
+ADDITEM=femalegargishstoneleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4330]
+{
+NAME=gargish stone kilt
+ID=0x0287
+RESOURCE=STONE 6 0x8ab
+SKILL=11 480 980
+ADDITEM=femalegargishstonekilt
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4331]
+{
+NAME=gargish stone arms
+ID=0x0284
+RESOURCE=STONE 8 0x8ab
+SKILL=11 560 1060
+ADDITEM=gargishstonearms
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4332]
+{
+NAME=gargish stone chest
+ID=0x0286
+RESOURCE=STONE 12 0x8ab
+SKILL=11 560 1060
+ADDITEM=gargishstonechest
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4333]
+{
+NAME=gargish stone leggings
+ID=0x028a
+RESOURCE=STONE 10 0x8ab
+SKILL=11 560 1060
+ADDITEM=gargishstoneleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4334]
+{
+NAME=gargish stone kilt
+ID=0x0288
+RESOURCE=STONE 6 0x8ab
+SKILL=11 480 980
+ADDITEM=gargishstonekilt
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4335]
+{
+NAME=large stone shield
+ID=0x4205
+RESOURCE=STONE 16 0x8ab
+SKILL=11 550 1060
+ADDITEM=largestoneshield
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4336]
+{
+NAME=gargish stone amulet
+ID=0x4D0a
+RESOURCE=STONE 3 0x8ab
+SKILL=11 600 1100
+ADDITEM=0x4D0a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone weapons /////
+//////////////////////////////////////
+
+[ITEM 4337]
+{
+NAME=stone war sword
+ID=0x4D0a
+RESOURCE=STONE 18 0x8ab
+SKILL=11 550 1050
+ADDITEM=stonewarsword
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone walls /////
+//////////////////////////////////////
+
+[ITEM 4338]
+{
+NAME=rough windowless
+ID=0x01D0
+RESOURCE=STONE 10 0x8ab
+SKILL=11 600 1100
+ADDITEM=roughwindowless1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4339]
+{
+NAME=rough window
+ID=0x01D3
+RESOURCE=STONE 10 0x8ab
+SKILL=11 600 1100
+ADDITEM=roughwindow1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4340]
+{
+NAME=rough arch
+ID=0x01D5
+RESOURCE=STONE 10 0x8ab
+SKILL=11 600 1100
+ADDITEM=rougharch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4341]
+{
+NAME=rough pillar
+ID=0x01Da
+RESOURCE=STONE 10 0x8ab
+SKILL=11 600 1100
+ADDITEM=roughpillar
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4342]
+{
+NAME=rough rounded arch
+ID=0x01DB
+RESOURCE=STONE 10 0x8ab
+SKILL=11 600 1100
+ADDITEM=roughroundedarch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4343]
+{
+NAME=rough small arch
+ID=0x01E0
+RESOURCE=STONE 10 0x8ab
+SKILL=11 600 1100
+ADDITEM=roughsmallarch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4344]
+{
+NAME=rough angled pillar
+ID=0x01E6
+RESOURCE=STONE 10 0x8ab
+SKILL=11 600 1100
+ADDITEM=roughangledpillar1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4345]
+{
+NAME=short rough
+ID=0x01E8
+RESOURCE=STONE 10 0x8ab
+SKILL=11 600 1100
+ADDITEM=shortrough1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//[ITEM 4346]
+//[ITEM 4347]
+//[ITEM 4348]
+//[ITEM 4349]
+
+//////////////////////////////////////
+//// stone stairs /////
+//////////////////////////////////////
+
+[ITEM 4350]
+{
+NAME=short rough
+ID=0x0788
+RESOURCE=STONE 5 0x8ab
+SKILL=11 600 1100
+ADDITEM=roughblock
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4351]
+{
+NAME=rough steps
+ID=0x0789
+RESOURCE=STONE 5 0x8ab
+SKILL=11 600 1100
+ADDITEM=roughsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4352]
+{
+NAME=rough corner steps
+ID=0x078d
+RESOURCE=STONE 5 0x8ab
+SKILL=11 600 1100
+ADDITEM=roughcornersteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4353]
+{
+NAME=rough rounded corner step
+ID=0x0791
+RESOURCE=STONE 5 0x8ab
+SKILL=11 600 1100
+ADDITEM=roughroundedcornerseps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4354]
+{
+NAME=rough inset steps
+ID=0x0795
+RESOURCE=STONE 5 0x8ab
+SKILL=11 600 1100
+ADDITEM=roughinsetsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4355]
+{
+NAME=rough rounded inset steps
+ID=0x0799
+RESOURCE=STONE 5 0x8ab
+SKILL=11 600 1100
+ADDITEM=roughroundedisetsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone floors /////
+//////////////////////////////////////
+
+[ITEM 4356]
+{
+NAME=light paver
+ID=0x0519
+RESOURCE=STONE 5 0x8ab
+SKILL=11 600 1100
+ADDITEM=lightpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4357]
+{
+NAME=medium paver
+ID=0x0519
+RESOURCE=STONE 5 0x8ab
+SKILL=11 600 1100
+ADDITEM=mediumpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4358]
+{
+NAME=dark paver
+ID=0x0519
+RESOURCE=STONE 5 0x8ab
+SKILL=11 600 1100
+ADDITEM=darkpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
\ No newline at end of file
diff --git a/data/dfndata/create/masonry_verite.dfn b/data/dfndata/create/masonry_verite.dfn
new file mode 100644
index 000000000..e3b5b51f9
--- /dev/null
+++ b/data/dfndata/create/masonry_verite.dfn
@@ -0,0 +1,704 @@
+[ITEM 4200]
+{
+NAME=Vase
+ID=0x0b46
+RESOURCE=STONE 1 0x89f
+SKILL=11 520 1020
+ADDITEM=0x0b46
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4201]
+{
+NAME=Large Vase
+ID=0x0b45
+RESOURCE=STONE 3 0x89f
+SKILL=11 520 1020
+ADDITEM=0x0b45
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4202]
+{
+NAME=small Urn
+ID=0x241c
+RESOURCE=STONE 3 0x89f
+SKILL=11 820 1030
+ADDITEM=0x241c
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4203]
+{
+NAME=Tower sculpture
+ID=0x241a
+RESOURCE=STONE 3 0x89f
+SKILL=11 820 1030
+ADDITEM=0x241a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4204]
+{
+NAME=gargoyle painting
+ID=0x403d
+RESOURCE=STONE 3 0x89f
+SKILL=11 820 1330
+ADDITEM=0x403d
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4205]
+{
+NAME=gargoyle sculpture
+ID=0x403f
+RESOURCE=STONE 3 0x89f
+SKILL=11 820 1320
+ADDITEM=0x403f
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4206]
+{
+NAME=gargoyle vase
+ID=0x4042
+RESOURCE=STONE 3 0x89f
+SKILL=11 800 1260
+ADDITEM=0x4042
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4207]
+{
+NAME=Tall 18th anniversary Vase
+ID=0x9bc7
+RESOURCE=STONE 6 0x89f
+SKILL=11 600 1100
+ADDITEM=0x9bc7
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4208]
+{
+NAME=short 18th anniversary Vase
+ID=0x9bca
+RESOURCE=STONE 6 0x89f
+SKILL=11 600 1100
+ADDITEM=0x9bca
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// furniture /////
+//////////////////////////////////////
+
+[ITEM 4209]
+{
+NAME=stone chair
+ID=0x1218
+RESOURCE=STONE 4 0x89f
+SKILL=11 550 1050
+ADDITEM=0x1218
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4210]
+{
+NAME=stone table (east)
+ID=0x14f0
+RESOURCE=STONE 6 0x89f
+SKILL=11 650 1150
+ADDITEM=mediumstonetableeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4211]
+{
+NAME=stone table (south)
+ID=0x14f0
+RESOURCE=STONE 6 0x89f
+SKILL=11 650 1150
+ADDITEM=mediumstonetablesouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4212]
+{
+NAME=large stone table (east)
+ID=0x14f0
+RESOURCE=STONE 9 0x89f
+SKILL=11 750 1250
+ADDITEM=largestonetableeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4213]
+{
+NAME=large stone table (south)
+ID=0x14f0
+RESOURCE=STONE 9 0x89f
+SKILL=11 750 1250
+ADDITEM=largestonetablesouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4214]
+{
+NAME=ritual table
+ID=0x14f0
+RESOURCE=STONE 8 0x89f
+SKILL=11 940 1030
+ADDITEM=ritualtabledeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// statues /////
+//////////////////////////////////////
+
+[ITEM 4215]
+{
+NAME=small statue (south)
+ID=0x139a
+RESOURCE=STONE 3 0x89f
+SKILL=11 600 1100
+ADDITEM=0x139a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4216]
+{
+NAME=small statue (north)
+ID=0x139b
+RESOURCE=STONE 3 0x89f
+SKILL=11 600 1100
+ADDITEM=0x139b
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4217]
+{
+NAME=small statue (east)
+ID=0x139c
+RESOURCE=STONE 3 0x89f
+SKILL=11 600 1100
+ADDITEM=0x139c
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4218]
+{
+NAME=pegasus statuette
+ID=0x139d
+RESOURCE=STONE 4 0x89f
+SKILL=11 700 1200
+ADDITEM=0x139d
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4219]
+{
+NAME=gargoyle statue
+ID=0x494e
+RESOURCE=STONE 20 0x89f
+SKILL=11 540 1040
+ADDITEM=0x494e
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4220]
+{
+NAME=gryphon statue
+ID=0x494e
+RESOURCE=STONE 15 0x89f
+SKILL=11 540 1040
+ADDITEM=0x494e
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// Misc addons /////
+//////////////////////////////////////
+
+[ITEM 4221]
+{
+NAME=stone anvil (east)
+ID=0x14f0
+RESOURCE=STONE 20 0x89f
+SKILL=11 780 1280
+ADDITEM=stoneanvileastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4222]
+{
+NAME=stone anvil (south)
+ID=0x14f0
+RESOURCE=STONE 20 0x89f
+SKILL=11 780 1280
+ADDITEM=stoneanvilsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4223]
+{
+NAME=large gargish bed (east)
+ID=0x14f0
+RESOURCE=STONE 3 0x89f
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=largegargoylebedeastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4224]
+{
+NAME=large gargish bed (south)
+ID=0x14f0
+RESOURCE=STONE 3 0x89f
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=largegargoylebedsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4225]
+{
+NAME=gargish cot (east)
+ID=0x14f0
+RESOURCE=STONE 3 0x89f
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=gargishcoteasteastdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4226]
+{
+NAME=gargish cot (south)
+ID=0x14f0
+RESOURCE=STONE 3 0x89f
+RESOURCE=CLOTH 100
+SKILL=11 760 1260
+SKILL=34 700 750
+ADDITEM=gargishcoteastsouthdeed
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone armor /////
+//////////////////////////////////////
+
+[ITEM 4227]
+{
+NAME=gargish stone arms
+ID=0x0283
+RESOURCE=STONE 8 0x89f
+SKILL=11 560 1060
+ADDITEM=femalegargishstonearms
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4228]
+{
+NAME=gargish stone chest
+ID=0x0285
+RESOURCE=STONE 12 0x89f
+SKILL=11 560 1060
+ADDITEM=femalegargishstonechest
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4229]
+{
+NAME=gargish stone leggings
+ID=0x0289
+RESOURCE=STONE 10 0x89f
+SKILL=11 560 1060
+ADDITEM=femalegargishstoneleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4230]
+{
+NAME=gargish stone kilt
+ID=0x0287
+RESOURCE=STONE 6 0x89f
+SKILL=11 480 980
+ADDITEM=femalegargishstonekilt
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4231]
+{
+NAME=gargish stone arms
+ID=0x0284
+RESOURCE=STONE 8 0x89f
+SKILL=11 560 1060
+ADDITEM=gargishstonearms
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4232]
+{
+NAME=gargish stone chest
+ID=0x0286
+RESOURCE=STONE 12 0x89f
+SKILL=11 560 1060
+ADDITEM=gargishstonechest
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4233]
+{
+NAME=gargish stone leggings
+ID=0x028a
+RESOURCE=STONE 10 0x89f
+SKILL=11 560 1060
+ADDITEM=gargishstoneleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4234]
+{
+NAME=gargish stone kilt
+ID=0x0288
+RESOURCE=STONE 6 0x89f
+SKILL=11 480 980
+ADDITEM=gargishstonekilt
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4235]
+{
+NAME=large stone shield
+ID=0x4205
+RESOURCE=STONE 16 0x89f
+SKILL=11 550 1060
+ADDITEM=largestoneshield
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4236]
+{
+NAME=gargish stone amulet
+ID=0x4D0a
+RESOURCE=STONE 3 0x89f
+SKILL=11 600 1100
+ADDITEM=0x4D0a
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone weapons /////
+//////////////////////////////////////
+
+[ITEM 4237]
+{
+NAME=stone war sword
+ID=0x4D0a
+RESOURCE=STONE 18 0x89f
+SKILL=11 550 1050
+ADDITEM=stonewarsword
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone walls /////
+//////////////////////////////////////
+
+[ITEM 4238]
+{
+NAME=rough windowless
+ID=0x01D0
+RESOURCE=STONE 10 0x89f
+SKILL=11 600 1100
+ADDITEM=roughwindowless1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4239]
+{
+NAME=rough window
+ID=0x01D3
+RESOURCE=STONE 10 0x89f
+SKILL=11 600 1100
+ADDITEM=roughwindow1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4240]
+{
+NAME=rough arch
+ID=0x01D5
+RESOURCE=STONE 10 0x89f
+SKILL=11 600 1100
+ADDITEM=rougharch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4241]
+{
+NAME=rough pillar
+ID=0x01Da
+RESOURCE=STONE 10 0x89f
+SKILL=11 600 1100
+ADDITEM=roughpillar
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4242]
+{
+NAME=rough rounded arch
+ID=0x01DB
+RESOURCE=STONE 10 0x89f
+SKILL=11 600 1100
+ADDITEM=roughroundedarch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4243]
+{
+NAME=rough small arch
+ID=0x01E0
+RESOURCE=STONE 10 0x89f
+SKILL=11 600 1100
+ADDITEM=roughsmallarch1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4244]
+{
+NAME=rough angled pillar
+ID=0x01E6
+RESOURCE=STONE 10 0x89f
+SKILL=11 600 1100
+ADDITEM=roughangledpillar1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4245]
+{
+NAME=short rough
+ID=0x01E8
+RESOURCE=STONE 10 0x89f
+SKILL=11 600 1100
+ADDITEM=shortrough1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//[ITEM 4246]
+//[ITEM 4247]
+//[ITEM 4248]
+//[ITEM 4249]
+
+//////////////////////////////////////
+//// stone stairs /////
+//////////////////////////////////////
+
+[ITEM 4250]
+{
+NAME=short rough
+ID=0x0788
+RESOURCE=STONE 5 0x89f
+SKILL=11 600 1100
+ADDITEM=roughblock
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4251]
+{
+NAME=rough steps
+ID=0x0789
+RESOURCE=STONE 5 0x89f
+SKILL=11 600 1100
+ADDITEM=roughsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4252]
+{
+NAME=rough corner steps
+ID=0x078d
+RESOURCE=STONE 5 0x89f
+SKILL=11 600 1100
+ADDITEM=roughcornersteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4253]
+{
+NAME=rough rounded corner step
+ID=0x0791
+RESOURCE=STONE 5 0x89f
+SKILL=11 600 1100
+ADDITEM=roughroundedcornerseps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4254]
+{
+NAME=rough inset steps
+ID=0x0795
+RESOURCE=STONE 5 0x89f
+SKILL=11 600 1100
+ADDITEM=roughinsetsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4255]
+{
+NAME=rough rounded inset steps
+ID=0x0799
+RESOURCE=STONE 5 0x89f
+SKILL=11 600 1100
+ADDITEM=roughroundedisetsteps1
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+//////////////////////////////////////
+//// stone floors /////
+//////////////////////////////////////
+
+[ITEM 4256]
+{
+NAME=light paver
+ID=0x0519
+RESOURCE=STONE 5 0x89f
+SKILL=11 600 1100
+ADDITEM=lightpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4257]
+{
+NAME=medium paver
+ID=0x0519
+RESOURCE=STONE 5 0x89f
+SKILL=11 600 1100
+ADDITEM=mediumpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
+
+[ITEM 4258]
+{
+NAME=dark paver
+ID=0x0519
+RESOURCE=STONE 5 0x89f
+SKILL=11 600 1100
+ADDITEM=darkpaver
+MINRANK=1
+MAXRANK=10
+SOUND=0x23D
+}
\ No newline at end of file
diff --git a/data/dfndata/create/resources.dfn b/data/dfndata/create/resources.dfn
index fff348454..0befb09ae 100644
--- a/data/dfndata/create/resources.dfn
+++ b/data/dfndata/create/resources.dfn
@@ -50,6 +50,11 @@ ID=0x1081
ID=0x1082
}
+[RESOURCE SCALE]
+{
+ID=0x26b4
+}
+
[RESOURCE BONE]
{
ID=0x0f7e
@@ -163,4 +168,9 @@ ID=0x423A
{
ID=0x14eb
ID=0x0E34
+}
+
+[RESOURCE STONE]
+{
+ID=0x1779
}
\ No newline at end of file
diff --git a/data/dfndata/create/smithing.dfn b/data/dfndata/create/smithing.dfn
index 21ddee92a..d7f0f3635 100644
--- a/data/dfndata/create/smithing.dfn
+++ b/data/dfndata/create/smithing.dfn
@@ -979,6 +979,65 @@ MAXRANK=10
SOUND=0x002A
}
+[ITEM 367]
+{
+NAME=dragon sleeves
+ID=0x2657
+RESOURCE=SCALE 24
+SKILL=7 763 1120
+ADDITEM=dragonsleeves
+MINRANK=1
+MAXRANK=10
+SOUND=0x002A
+}
+
+[ITEM 368]
+{
+NAME=dragon breast plate
+ID=0x2657
+RESOURCE=SCALE 36
+SKILL=7 850 1120
+ADDITEM=dragonbreastplate
+MINRANK=1
+MAXRANK=10
+SOUND=0x002A
+}
+
+[ITEM 369]
+{
+NAME=dragon gloves
+ID=0x2657
+RESOURCE=SCALE 16
+SKILL=7 689 1120
+ADDITEM=dragongloves
+MINRANK=1
+MAXRANK=10
+SOUND=0x002A
+}
+
+[ITEM 370]
+{
+NAME=dragon helmet
+ID=0x2657
+RESOURCE=SCALE 20
+SKILL=7 722 1120
+ADDITEM=dragonhelm
+MINRANK=1
+MAXRANK=10
+SOUND=0x002A
+}
+
+[ITEM 371]
+{
+NAME=dragon leggings
+ID=0x2657
+RESOURCE=SCALE 28
+SKILL=7 788 1120
+ADDITEM=dragonleggings
+MINRANK=1
+MAXRANK=10
+SOUND=0x002A
+}
// end of blacksmithing section
//*****************************************
\ No newline at end of file
diff --git a/data/dfndata/house/house.dfn b/data/dfndata/house/house.dfn
index 8ef288aae..2fcbf6ccf 100644
--- a/data/dfndata/house/house.dfn
+++ b/data/dfndata/house/house.dfn
@@ -1639,6 +1639,64 @@ HOUSE_ITEM=629
HOUSE_ITEM=630
}
+// House Addon - stone table (east)
+[HOUSE 210]
+{
+ID=0x14F0
+SPACEX=1
+SPACEY=1
+CHARX=0
+CHARY=0
+CHARZ=0
+HOUSE_DEED=mediumstonetableeastdeed
+HOUSE_ITEM=631
+HOUSE_ITEM=632
+}
+
+// House Addon - stone table (south)
+[HOUSE 211]
+{
+ID=0x14F0
+SPACEX=1
+SPACEY=1
+CHARX=0
+CHARY=0
+CHARZ=0
+HOUSE_DEED=mediumstonetablesouthdeed
+HOUSE_ITEM=633
+HOUSE_ITEM=634
+}
+
+// House Addon - large stone table (east)
+[HOUSE 212]
+{
+ID=0x14F0
+SPACEX=1
+SPACEY=1
+CHARX=0
+CHARY=0
+CHARZ=0
+HOUSE_DEED=largestonetableeastdeed
+HOUSE_ITEM=635
+HOUSE_ITEM=636
+HOUSE_ITEM=637
+}
+
+// House Addon - large stone table (south)
+[HOUSE 213]
+{
+ID=0x14F0
+SPACEX=1
+SPACEY=1
+CHARX=0
+CHARY=0
+CHARZ=0
+HOUSE_DEED=largestonetablesouthdeed
+HOUSE_ITEM=638
+HOUSE_ITEM=639
+HOUSE_ITEM=640
+}
+
[HOUSE ITEM 1]
{ Wooden Door (Small house)
ITEM=0x06A5
@@ -3891,6 +3949,86 @@ Y=2
Z=0
}
+[HOUSE ITEM 631]
+{
+ITEM=0x1202
+X=0
+Y=0
+Z=0
+}
+
+[HOUSE ITEM 632]
+{
+ITEM=0x1201
+X=0
+Y=1
+Z=0
+}
+
+[HOUSE ITEM 633]
+{
+ITEM=0x1205
+X=0
+Y=0
+Z=0
+}
+
+[HOUSE ITEM 634]
+{
+ITEM=0x1204
+X=1
+Y=0
+Z=0
+}
+
+[HOUSE ITEM 635]
+{
+ITEM=0x1202
+X=0
+Y=0
+Z=0
+}
+
+[HOUSE ITEM 636]
+{
+ITEM=0x1203
+X=0
+Y=1
+Z=0
+}
+
+[HOUSE ITEM 637]
+{
+ITEM=0x1201
+X=0
+Y=2
+Z=0
+}
+
+[HOUSE ITEM 638]
+{
+ITEM=0x1205
+X=0
+Y=0
+Z=0
+}
+
+[HOUSE ITEM 639]
+{
+ITEM=0x1206
+X=1
+Y=0
+Z=0
+}
+
+[HOUSE ITEM 640]
+{
+ITEM=0x1204
+X=2
+Y=0
+Z=0
+}
+
// Dull Copper Colored Anvil 1/1
[HOUSE ITEM 700]
{
diff --git a/data/dfndata/house/house_addons_ml.dfn b/data/dfndata/house/house_addons_ml.dfn
index 57d6fe89d..3fc3f45e4 100644
--- a/data/dfndata/house/house_addons_ml.dfn
+++ b/data/dfndata/house/house_addons_ml.dfn
@@ -346,6 +346,32 @@ HOUSE_ITEM=1058
HOUSE_ITEM=1059
}
+// House Addon - stone anvil (east)
+[HOUSE 324]
+{
+ID=0x14F0
+SPACEX=1
+SPACEY=1
+CHARX=0
+CHARY=0
+CHARZ=0
+HOUSE_DEED=stoneanvileastdeed
+HOUSE_ITEM=1060
+}
+
+// House Addon - stone anvil (south)
+[HOUSE 325]
+{
+ID=0x14F0
+SPACEX=1
+SPACEY=1
+CHARX=0
+CHARY=0
+CHARZ=0
+HOUSE_DEED=stoneanvilsouthdeed
+HOUSE_ITEM=1061
+}
+
[HOUSE ITEM 1000]
{ alchemist table part 1 (east)
ITEM=0x3077
@@ -825,4 +851,20 @@ ITEM=0x3056
X=0
Y=1
Z=0
+}
+
+[HOUSE ITEM 1060]
+{ stone anvil east
+ITEM=0x2dd6
+X=0
+Y=0
+Z=0
+}
+
+[HOUSE ITEM 1061]
+{ stone anvil south
+ITEM=0x2dd5
+X=0
+Y=0
+Z=0
}
\ No newline at end of file
diff --git a/data/dfndata/house/house_addons_sa.dfn b/data/dfndata/house/house_addons_sa.dfn
new file mode 100644
index 000000000..1c31010bf
--- /dev/null
+++ b/data/dfndata/house/house_addons_sa.dfn
@@ -0,0 +1,293 @@
+// House Addon - ritual table
+[HOUSE 2000]
+{
+ID=0x14F0
+SPACEX=1
+SPACEY=1
+CHARX=0
+CHARY=0
+CHARZ=0
+HOUSE_DEED=ritualtabledeed
+HOUSE_ITEM=2000
+HOUSE_ITEM=2001
+HOUSE_ITEM=2002
+HOUSE_ITEM=2003
+}
+
+// House Addon - large gargish bed (east)
+[HOUSE 2001]
+{
+ID=0x14F0
+SPACEX=1
+SPACEY=1
+CHARX=0
+CHARY=0
+CHARZ=0
+HOUSE_DEED=largegargoylebedeastdeed
+HOUSE_ITEM=2006
+HOUSE_ITEM=2007
+HOUSE_ITEM=2008
+HOUSE_ITEM=2009
+HOUSE_ITEM=2010
+HOUSE_ITEM=2011
+HOUSE_ITEM=2012
+HOUSE_ITEM=2013
+HOUSE_ITEM=2014
+}
+
+// House Addon - large gargish bed (south)
+[HOUSE 2002]
+{
+ID=0x14F0
+SPACEX=1
+SPACEY=1
+CHARX=0
+CHARY=0
+CHARZ=0
+HOUSE_DEED=largegargoylebedsouthdeed
+HOUSE_ITEM=2015
+HOUSE_ITEM=2016
+HOUSE_ITEM=2017
+HOUSE_ITEM=2018
+HOUSE_ITEM=2019
+HOUSE_ITEM=2020
+HOUSE_ITEM=2021
+HOUSE_ITEM=2022
+HOUSE_ITEM=2023
+}
+
+// House Addon - gargish cot (east)
+[HOUSE 2003]
+{
+ID=0x14F0
+SPACEX=1
+SPACEY=1
+CHARX=0
+CHARY=0
+CHARZ=0
+HOUSE_DEED=gargishcoteasteastdeed
+HOUSE_ITEM=2024
+HOUSE_ITEM=2025
+}
+
+// House Addon - gargish cot (south)
+[HOUSE 2004]
+{
+ID=0x14F0
+SPACEX=1
+SPACEY=1
+CHARX=0
+CHARY=0
+CHARZ=0
+HOUSE_DEED=gargishcoteastsouthdeed
+HOUSE_ITEM=2026
+HOUSE_ITEM=2027
+}
+
+[HOUSE ITEM 2000]
+{ // ritual table
+ITEM=0x4985
+X=0
+Y=0
+Z=0
+}
+
+[HOUSE ITEM 2001]
+{ // ritual table
+ITEM=0x4984
+X=0
+Y=1
+Z=0
+}
+
+[HOUSE ITEM 2002]
+{ // ritual table
+ITEM=0x4983
+X=1
+Y=0
+Z=0
+}
+
+[HOUSE ITEM 2003]
+{ // ritual table
+ITEM=0x4982
+X=1
+Y=1
+Z=0
+}
+
+[HOUSE ITEM 2006]
+{ E Left Side Gargoyle Bed
+ITEM=0x4019
+X=0
+Y=0
+Z=0
+}
+
+[HOUSE ITEM 2007]
+{ E Left Side Gargoyle Bed
+ITEM=0x401c
+X=0
+Y=1
+Z=0
+}
+
+[HOUSE ITEM 2008]
+{ E Left Side Gargoyle Bed
+ITEM=0x401f
+X=0
+Y=2
+Z=0
+}
+
+HOUSE ITEM 2009]
+{ E Middle Gargoyle Bed
+ITEM=0x401a
+X=1
+Y=0
+Z=0
+}
+
+HOUSE ITEM 2010]
+{ E Middle Gargoyle Bed
+ITEM=0x401d
+X=1
+Y=1
+Z=0
+}
+
+HOUSE ITEM 2011]
+{ E Middle Gargoyle Bed
+ITEM=0x4020
+X=1
+Y=2
+Z=0
+}
+
+HOUSE ITEM 2012]
+{ E Right Side Gargoyle Bed
+ITEM=0x401b
+X=2
+Y=0
+Z=0
+}
+
+HOUSE ITEM 2013]
+{ E Right Side Gargoyle Bed
+ITEM=0x401e
+X=2
+Y=1
+Z=0
+}
+
+HOUSE ITEM 2014]
+{ E Right Side Gargoyle Bed
+ITEM=0x4021
+X=2
+Y=2
+Z=0
+}
+
+HOUSE ITEM 2015]
+{ S Left Side Gargoyle Bed
+ITEM=0x4010
+X=0
+Y=0
+Z=0
+}
+
+HOUSE ITEM 2016]
+{ S Left Side Gargoyle Bed
+ITEM=0x4013
+X=0
+Y=1
+Z=0
+}
+
+HOUSE ITEM 2017]
+{ S Left Side Gargoyle Bed
+ITEM=0x4016
+X=0
+Y=2
+Z=0
+}
+
+HOUSE ITEM 2018]
+{ S Middle Gargoyle Bed
+ITEM=0x4011
+X=1
+Y=0
+Z=0
+}
+
+HOUSE ITEM 2019]
+{ S Middle Gargoyle Bed
+ITEM=0x4014
+X=1
+Y=1
+Z=0
+}
+
+HOUSE ITEM 2020]
+{ S Middle Gargoyle Bed
+ITEM=0x4017
+X=1
+Y=2
+Z=0
+}
+
+HOUSE ITEM 2021]
+{ S left Side Gargoyle Bed
+ITEM=0x4012
+X=2
+Y=0
+Z=0
+}
+
+HOUSE ITEM 2022]
+{ S left Side Gargoyle Bed
+ITEM=0x4015
+X=2
+Y=1
+Z=0
+}
+
+HOUSE ITEM 2023]
+{ S left Side Gargoyle Bed
+ITEM=0x4018
+X=2
+Y=2
+Z=0
+}
+
+HOUSE ITEM 2024]
+{ E Gargoyle Cot
+ITEM=0x400e
+X=0
+Y=0
+Z=0
+}
+
+HOUSE ITEM 2025]
+{ E Gargoyle Cot
+ITEM=0x400f
+X=1
+Y=0
+Z=0
+}
+
+HOUSE ITEM 2026]
+{ S Gargoyle Cot
+ITEM=0x400d
+X=0
+Y=0
+Z=0
+}
+
+HOUSE ITEM 2027]
+{ S Gargoyle Cot
+ITEM=0x400c
+X=0
+Y=-1
+Z=0
+}
\ No newline at end of file
diff --git a/data/dfndata/items/ItemMenu.bulk.dfn b/data/dfndata/items/ItemMenu.bulk.dfn
index 8d014ae24..c7ccd3daf 100644
--- a/data/dfndata/items/ItemMenu.bulk.dfn
+++ b/data/dfndata/items/ItemMenu.bulk.dfn
@@ -13544,6 +13544,10 @@ ADDITEM=0x10E5
ADDITEM=0x10E6
0x10E7=scorp
ADDITEM=0x10E7
+0x12B3=mallet and chisel
+ADDITEM=malletandchisel
+0x0FBE=Making Valuables With Stonecrafting
+ADDITEM=masonrybook
<=Previous Menu
ITEMMENU=127
}
diff --git a/data/dfndata/items/building/decs/misc_sa.dfn b/data/dfndata/items/building/decs/misc_sa.dfn
new file mode 100644
index 000000000..474a7d88a
--- /dev/null
+++ b/data/dfndata/items/building/decs/misc_sa.dfn
@@ -0,0 +1,101 @@
+[0x403d]
+{
+get=base_item
+name=gargoyle painting
+id=0x403d
+weight=1000
+movable=1
+}
+
+[0x403e]
+{
+get=0x403d
+id=0x403e
+}
+
+[0x403f]
+{
+get=base_item
+name=gargoyle sculpture
+id=0x403f
+weight=100
+movable=1
+}
+
+[0x4040]
+{
+get=0x403f
+id=0x4040
+}
+
+[0x4042]
+{
+get=base_item
+name=gargoyle vase
+id=0x4042
+weight=1000
+movable=1
+}
+
+[0x403e]
+{
+get=0x4042
+id=0x403e
+}
+
+[0x4985]
+{
+get=base_item
+name=ritual table
+id=0x4985
+weight=1000
+movable=2
+}
+
+[0x4984]
+{
+get=0x4985
+id=0x4984
+}
+
+[0x4983]
+{
+get=0x4985
+id=0x4983
+}
+
+[0x4982]
+{
+get=0x4985
+id=0x4982
+}
+
+[0x494e]
+{
+get=base_item
+name=gargoyle statue
+id=0x494e
+weight=100
+movable=2
+}
+
+[0x494d]
+{
+get=0x494e
+id=0x494d
+}
+
+[0x493e]
+{
+get=base_item
+name=gryphon statue
+id=0x493e
+weight=100
+movable=2
+}
+
+[0x493b]
+{
+get=0x493e
+id=0x493b
+}
\ No newline at end of file
diff --git a/data/dfndata/items/building/decs/misc_se.dfn b/data/dfndata/items/building/decs/misc_se.dfn
new file mode 100644
index 000000000..ea016e08d
--- /dev/null
+++ b/data/dfndata/items/building/decs/misc_se.dfn
@@ -0,0 +1,17 @@
+[0x241C]
+{
+get=base_item
+name=Small Urn
+id=0x241C
+weight=100
+movable=1
+}
+
+[0x241A]
+{
+get=base_item
+name=Tower Sculpture
+id=0x241A
+weight=100
+movable=1
+}
\ No newline at end of file
diff --git a/data/dfndata/items/building/decs/misc_tol.dfn b/data/dfndata/items/building/decs/misc_tol.dfn
new file mode 100644
index 000000000..c286e275e
--- /dev/null
+++ b/data/dfndata/items/building/decs/misc_tol.dfn
@@ -0,0 +1,17 @@
+[0x9bc7]
+{
+get=base_item
+name=Tall 18th Anniversary Vase
+id=0x9bc7
+weight=1000
+movable=1
+}
+
+[0x9bca]
+{
+get=base_item
+name=Short 18th Anniversary Vase
+id=0x9bca
+weight=100
+movable=1
+}
\ No newline at end of file
diff --git a/data/dfndata/items/building/decs/stoneart.dfn b/data/dfndata/items/building/decs/stoneart.dfn
index 290036814..efbb39478 100644
--- a/data/dfndata/items/building/decs/stoneart.dfn
+++ b/data/dfndata/items/building/decs/stoneart.dfn
@@ -228,9 +228,9 @@ decay=1
[0x139d]
{ S pegasus
get=base_item
-name=statue
+name=pegasus statuette
id=0x139d
-weight=1000
+weight=100
movable=1
decay=1
}
diff --git a/data/dfndata/items/building/floors/craftable_tiles.dfn b/data/dfndata/items/building/floors/craftable_tiles.dfn
new file mode 100644
index 000000000..cf9b816f4
--- /dev/null
+++ b/data/dfndata/items/building/floors/craftable_tiles.dfn
@@ -0,0 +1,26 @@
+[base_craftable_tile]
+{
+get=base_item
+script=5080
+weight=500
+movable=1
+decay=1
+}
+
+[lightpaver]
+{
+get=base_craftable_tile
+id=0x0519
+}
+
+[mediumpaver]
+{
+get=base_craftable_tile
+id=0x051d
+}
+
+[darkpaver]
+{
+get=base_craftable_tile
+id=0x0521
+}
\ No newline at end of file
diff --git a/data/dfndata/items/building/furniture/beds_sa.dfn b/data/dfndata/items/building/furniture/beds_sa.dfn
new file mode 100644
index 000000000..4ea646876
--- /dev/null
+++ b/data/dfndata/items/building/furniture/beds_sa.dfn
@@ -0,0 +1,116 @@
+[base_bed_sa]
+{
+get=base_item
+name=bed
+weight=10000
+movable=2
+origin=sa
+}
+
+[0x4019]
+{ E gargoyle bed
+get=base_bed_sa
+id=0x4019
+}
+
+[0x401c]
+{ E gargoyle bed
+get=base_bed_sa
+id=0x401c
+}
+
+[0x401f]
+{ E gargoyle bed
+get=base_bed_sa
+id=0x401f
+}
+
+[0x401a]
+{ E gargoyle bed
+get=base_bed_sa
+id=0x401a
+}
+
+[0x401d]
+{ E gargoyle bed
+get=base_bed_sa
+id=0x401d
+}
+
+[0x4020]
+{ E gargoyle bed
+get=base_bed_sa
+id=0x4020
+}
+
+[0x401b]
+{ E gargoyle bed
+get=base_bed_sa
+id=0x401b
+}
+
+[0x401e]
+{ E gargoyle bed
+get=base_bed_sa
+id=0x401e
+}
+
+[0x4021]
+{ E gargoyle bed
+get=base_bed_sa
+id=0x4021
+}
+
+[0x4010]
+{ S gargoyle bed
+get=base_bed_sa
+id=0x4010
+}
+
+[0x4013]
+{ S gargoyle bed
+get=base_bed_sa
+id=0x4013
+}
+
+[0x4016]
+{ S gargoyle bed
+get=base_bed_sa
+id=0x4016
+}
+
+[0x4011]
+{ S gargoyle bed
+get=base_bed_sa
+id=0x4011
+}
+
+[0x4014]
+{ S gargoyle bed
+get=base_bed_sa
+id=0x4014
+}
+
+[0x4017]
+{ S gargoyle bed
+get=base_bed_sa
+id=0x4017
+}
+
+[0x4012]
+{ S gargoyle bed
+get=base_bed_sa
+id=0x4012
+}
+
+[0x4015]
+{ S gargoyle bed
+get=base_bed_sa
+id=0x4015
+}
+
+[0x4018]
+{ S gargoyle bed
+get=base_bed_sa
+id=0x4018
+}
\ No newline at end of file
diff --git a/data/dfndata/items/building/furniture/chairs.dfn b/data/dfndata/items/building/furniture/chairs.dfn
index 21b636a28..9348c29ad 100644
--- a/data/dfndata/items/building/furniture/chairs.dfn
+++ b/data/dfndata/items/building/furniture/chairs.dfn
@@ -607,7 +607,7 @@ movable=2
get=base_item
name=stone chair
id=0x1218
-weight=4000
+weight=1000
value=30 15
restock=10
movable=1
@@ -620,7 +620,7 @@ good=1
get=base_item
name=stone chair
id=0x1219
-weight=4000
+weight=1000
value=30 15
restock=10
movable=1
@@ -633,7 +633,7 @@ good=1
get=base_item
name=stone chair
id=0x121a
-weight=4000
+weight=1000
value=30 15
restock=10
movable=1
@@ -646,7 +646,7 @@ good=1
get=base_item
name=stone chair
id=0x121b
-weight=4000
+weight=1000
value=30 15
restock=10
movable=1
diff --git a/data/dfndata/items/building/furniture/chairs_sa.dfn b/data/dfndata/items/building/furniture/chairs_sa.dfn
new file mode 100644
index 000000000..ce740bb14
--- /dev/null
+++ b/data/dfndata/items/building/furniture/chairs_sa.dfn
@@ -0,0 +1,31 @@
+[base_chair_sa]
+{
+get=base_item
+weight=10000
+movable=2
+origin=sa
+}
+
+[0x400e]
+{ E Gargoyle Cot
+get=base_chair_sa
+id=0x400e
+}
+
+[0x400f]
+{ E Gargoyle Cot
+get=base_chair_sa
+id=0x400f
+}
+
+[0x400d]
+{ S Gargoyle Cot
+get=base_chair_sa
+id=0x400d
+}
+
+[0x400c]
+{ S Gargoyle Cot
+get=base_chair_sa
+id=0x400c
+}
\ No newline at end of file
diff --git a/data/dfndata/items/building/walls/craftablewalls.dfn b/data/dfndata/items/building/walls/craftablewalls.dfn
new file mode 100644
index 000000000..948988ad2
--- /dev/null
+++ b/data/dfndata/items/building/walls/craftablewalls.dfn
@@ -0,0 +1,342 @@
+[base_craftable_wall]
+{
+get=base_item
+script=5080
+weight=1000
+movable=1
+decay=1
+}
+
+[roughwindowless1]
+{
+get=base_craftable_wall
+id=0x01cf
+}
+
+[roughwindowless2]
+{
+get=base_craftable_wall
+id=0x01d0
+}
+
+[roughwindowless3]
+{
+get=base_craftable_wall
+id=0x01d1
+}
+
+[roughwindowless4]
+{
+get=base_craftable_wall
+id=0x01d2
+}
+
+[roughwindow1]
+{
+get=base_craftable_wall
+id=0x01d3
+}
+
+[roughwindow2]
+{
+get=base_craftable_wall
+id=0x01d4
+}
+
+[rougharch1]
+{
+get=base_craftable_wall
+id=0x01d5
+}
+
+[rougharch2]
+{
+get=base_craftable_wall
+id=0x01d6
+}
+
+[rougharch3]
+{
+get=base_craftable_wall
+id=0x01d7
+}
+
+[rougharch4]
+{
+get=base_craftable_wall
+id=0x01d8
+}
+
+[rougharch5]
+{
+get=base_craftable_wall
+id=0x01d9
+}
+
+[roughPillar]
+{
+get=base_craftable_wall
+id=0x01da
+}
+
+[roughroundedarch1]
+{
+get=base_craftable_wall
+id=0x01db
+}
+
+[roughroundedarch2]
+{
+get=base_craftable_wall
+id=0x01dc
+}
+
+[roughroundedarch3]
+{
+get=base_craftable_wall
+id=0x01dd
+}
+
+[roughroundedarch4]
+{
+get=base_craftable_wall
+id=0x01dE
+}
+
+[roughroundedarch5]
+{
+get=base_craftable_wall
+id=0x01df
+}
+
+[roughsmallarch1]
+{
+get=base_craftable_wall
+id=0x01E0
+}
+
+[roughsmallarch2]
+{
+get=base_craftable_wall
+id=0x01E1
+}
+
+[roughsmallarch3]
+{
+get=base_craftable_wall
+id=0x01E2
+}
+
+[roughsmallarch4]
+{
+get=base_craftable_wall
+id=0x01E3
+}
+
+[roughsmallarch5]
+{
+get=base_craftable_wall
+id=0x01E4
+}
+
+[roughsmallarch6]
+{
+get=base_craftable_wall
+id=0x01E5
+}
+
+[roughangledPillar1]
+{
+get=base_craftable_wall
+id=0x01E6
+}
+
+[roughangledPillar2]
+{
+get=base_craftable_wall
+id=0x01E7
+}
+
+[shortrough1]
+{
+get=base_craftable_wall
+id=0x01E8
+}
+
+[shortrough2]
+{
+get=base_craftable_wall
+id=0x01E9
+}
+
+[shortrough3]
+{
+get=base_craftable_wall
+id=0x01Ea
+}
+
+[shortrough4]
+{
+get=base_craftable_wall
+id=0x01Eb
+}
+
+[roughblock]
+{
+get=base_craftable_wall
+id=0x0788
+}
+
+[roughsteps1]
+{
+get=base_craftable_wall
+id=0x0789
+}
+
+[roughsteps2]
+{
+get=base_craftable_wall
+id=0x078a
+}
+
+[roughsteps3]
+{
+get=base_craftable_wall
+id=0x078b
+}
+
+[roughsteps4]
+{
+get=base_craftable_wall
+id=0x078c
+}
+
+[roughcornersteps1]
+{
+get=base_craftable_wall
+id=0x078d
+}
+
+[roughcornersteps2]
+{
+get=base_craftable_wall
+id=0x078E
+}
+
+[roughcornersteps3]
+{
+get=base_craftable_wall
+id=0x078f
+}
+
+[roughcornersteps4]
+{
+get=base_craftable_wall
+id=0x0790
+}
+
+[roughroundedcornerseps1]
+{
+get=base_craftable_wall
+id=0x0791
+}
+
+[roughroundedcornerseps2]
+{
+get=base_craftable_wall
+id=0x0792
+}
+
+[roughroundedcornerseps3]
+{
+get=base_craftable_wall
+id=0x0793
+}
+
+[roughroundedcornerseps4]
+{
+get=base_craftable_wall
+id=0x0794
+}
+
+[roughinsetsteps1]
+{
+get=base_craftable_wall
+id=0x0795
+}
+
+[roughinsetsteps2]
+{
+get=base_craftable_wall
+id=0x0796
+}
+
+[roughinsetsteps3]
+{
+get=base_craftable_wall
+id=0x0797
+}
+
+[roughinsetsteps4]
+{
+get=base_craftable_wall
+id=0x0798
+}
+
+[roughroundedisetsteps1]
+{
+get=base_craftable_wall
+id=0x0799
+}
+
+[roughroundedisetsteps2]
+{
+get=base_craftable_wall
+id=0x079a
+}
+
+[roughroundedisetsteps3]
+{
+get=base_craftable_wall
+id=0x079b
+}
+
+[roughroundedisetsteps4]
+{
+get=base_craftable_wall
+id=0x079c
+}
+
+[base_craftable_door]
+{
+get=base_door
+script=5081
+weight=1000
+movable=1
+decay=1
+type=12
+}
+
+[StoneDoor_S_In]
+{
+get=base_craftable_door
+id=0x0326
+}
+
+[StoneDoor_E_Out]
+{
+get=base_craftable_door
+id=0x032C
+}
+
+[StoneDoor_S_Out]
+{
+get=base_craftable_door
+id=0x032a
+}
+
+[StoneDoor_E_In]
+{
+get=base_craftable_door
+id=0x0330
+}
\ No newline at end of file
diff --git a/data/dfndata/items/deeds/houseaddon_deeds.dfn b/data/dfndata/items/deeds/houseaddon_deeds.dfn
index 9bd5fecbd..524e91099 100644
--- a/data/dfndata/items/deeds/houseaddon_deeds.dfn
+++ b/data/dfndata/items/deeds/houseaddon_deeds.dfn
@@ -486,3 +486,39 @@ origin=uor
{
get=dc_anvil_deed si_anvil_deed c_anvil_deed b_anvil_deed g_anvil_deed a_anvil_deed ve_anvil_deed va_anvil_deed
}
+
+[mediumstonetableeastdeed]
+{
+name=stone table (east)
+id=0x14f0
+morex=210
+value=1000 500
+restock=15
+}
+
+[mediumstonetablesouthdeed]
+{
+name=stone table (south)
+id=0x14f0
+morex=211
+value=1000 500
+restock=15
+}
+
+[largestonetableeastdeed]
+{
+name=large stone table (east)
+id=0x14f0
+morex=212
+value=1000 500
+restock=15
+}
+
+[largestonetablesouthdeed]
+{
+name=large stone table (south)
+id=0x14f0
+morex=213
+value=1000 500
+restock=15
+}
\ No newline at end of file
diff --git a/data/dfndata/items/deeds/houseaddon_deeds_ml.dfn b/data/dfndata/items/deeds/houseaddon_deeds_ml.dfn
index 59ffb2c8e..e5a86e7b5 100644
--- a/data/dfndata/items/deeds/houseaddon_deeds_ml.dfn
+++ b/data/dfndata/items/deeds/houseaddon_deeds_ml.dfn
@@ -212,4 +212,26 @@ name=Tall Elven Bed (South)
id=0x14F0
morex=323
origin=ml
+}
+
+[stoneanvileastdeed]
+{
+get=base_item
+name=stone anvil (east)
+id=0x14f0
+morex=324
+value=1000 500
+restock=15
+origin=ml
+}
+
+[stoneanvilsouthdeed]
+{
+get=base_item
+name=stone anvil (south)
+id=0x14f0
+morex=325
+value=1000 500
+restock=15
+origin=ml
}
\ No newline at end of file
diff --git a/data/dfndata/items/deeds/houseaddon_deeds_sa.dfn b/data/dfndata/items/deeds/houseaddon_deeds_sa.dfn
new file mode 100644
index 000000000..9a5c93d3e
--- /dev/null
+++ b/data/dfndata/items/deeds/houseaddon_deeds_sa.dfn
@@ -0,0 +1,54 @@
+[ritualtabledeed]
+{
+get=base_item
+name=ritual table
+id=0x14f0
+morex=2000
+value=1000 500
+restock=15
+origin=sa
+}
+
+[largegargoylebedeastdeed]
+{
+get=base_item
+name=large gargish bed (east)
+id=0x14f0
+morex=2001
+value=1000 500
+restock=15
+origin=sa
+}
+
+[largegargoylebedsouthdeed]
+{
+get=base_item
+name=large gargish bed (south)
+id=0x14f0
+morex=2002
+value=1000 500
+restock=15
+origin=sa
+}
+
+[gargishcoteasteastdeed]
+{
+get=base_item
+name=gargish cot (east)
+id=0x14f0
+morex=2003
+value=1000 500
+restock=15
+origin=sa
+}
+
+[gargishcoteastsouthdeed]
+{
+get=base_item
+name=gargish cot (south)
+id=0x14f0
+morex=2004
+value=1000 500
+restock=15
+origin=sa
+}
\ No newline at end of file
diff --git a/data/dfndata/items/itemlists/itemlists.dfn b/data/dfndata/items/itemlists/itemlists.dfn
index b9eba6b67..f3c57ea2b 100644
--- a/data/dfndata/items/itemlists/itemlists.dfn
+++ b/data/dfndata/items/itemlists/itemlists.dfn
@@ -1226,7 +1226,7 @@ itemlist=randomgranite
[ITEMLIST randomgranite]
{
-irongranite
+granite
dullcoppergranite
shadowirongranite
coppergranite
diff --git a/data/dfndata/items/misc/crafting_recipes.dfn b/data/dfndata/items/misc/crafting_recipes.dfn
index 157851d7b..26a64fa7e 100644
--- a/data/dfndata/items/misc/crafting_recipes.dfn
+++ b/data/dfndata/items/misc/crafting_recipes.dfn
@@ -16,4 +16,36 @@ origin=ml
get=base_recipe_scroll
custominttag=recipeID 18
customstringtag=recipeName skullcap
+}
+
+[tall_18th_anniversary_vase_recipe]
+{
+get=base_recipe_scroll
+custominttag=recipeID 3500
+customstringtag=recipeName Tall 18th Anniversary Vase
+origin=sa
+}
+
+[short_18th_anniversary_vase_recipe]
+{
+get=base_recipe_scroll
+custominttag=recipeID 3501
+customstringtag=recipeName Short 18th Anniversary Vase
+origin=sa
+}
+
+[stone_anvil_east_recipe]
+{
+get=base_recipe_scroll
+custominttag=recipeID 3520
+customstringtag=recipeName Stone Anvil (East)
+origin=sa
+}
+
+[stone_anvil_south_recipe]
+{
+get=base_recipe_scroll
+custominttag=recipeID 3521
+customstringtag=recipeName Stone Anvil (South)
+origin=sa
}
\ No newline at end of file
diff --git a/data/dfndata/items/misc/jewelry_sa.dfn b/data/dfndata/items/misc/jewelry_sa.dfn
new file mode 100644
index 000000000..f8f338a49
--- /dev/null
+++ b/data/dfndata/items/misc/jewelry_sa.dfn
@@ -0,0 +1,32 @@
+[base_jewelry_sa]
+{
+get=base_item
+weight=100
+value=26 13
+restock=10
+decay=1
+good=37
+}
+
+[0x4D0a]
+{
+get=base_jewelry_sa
+name=gargish stone amulet
+id=0x4D0a
+colour=2500
+layer=0x0a
+str=40
+}
+
+[0x4210]
+{
+get=base_jewelry_sa
+name=gargish amulet
+id=0x4210
+layer=0x0a
+str=40
+hp=30
+maxhp=40
+def=1
+elementresist=2 2 3 2
+}
\ No newline at end of file
diff --git a/data/dfndata/items/skills/resources/mining.dfn b/data/dfndata/items/skills/resources/mining.dfn
index ecb8b78f9..f5ee7084a 100644
--- a/data/dfndata/items/skills/resources/mining.dfn
+++ b/data/dfndata/items/skills/resources/mining.dfn
@@ -85,7 +85,7 @@ decay=1
good=46
}
-[irongranite]
+[granite]
{
get=base_granite
}
diff --git a/data/dfndata/items/skills/tools/alchemy.dfn b/data/dfndata/items/skills/tools/alchemy.dfn
index d46e1a729..3828fbd4c 100644
--- a/data/dfndata/items/skills/tools/alchemy.dfn
+++ b/data/dfndata/items/skills/tools/alchemy.dfn
@@ -789,4 +789,105 @@ decay=1
value=10637 5318
origin=lbr
script=5054
+}
+
+[0x2f5d]
+{
+get=base_item
+name=hollow prism
+id=0x2f5d
+weight=100
+restock=20
+movable=1
+decay=1
+good=51
+}
+
+[hollowprism]
+{
+get=0x2f5d
+}
+
+[0x403a]
+{
+get=base_item
+name=Gargoyle Floor Mirror
+id=0x403a
+weight=1000
+restock=20
+movable=1
+decay=1
+good=51
+}
+
+[0x4046]
+{
+get=0x403a
+id=0x4046
+}
+
+[gargoylefloormirror]
+{
+get=0x403a 0x4046
+}
+
+[0x4044]
+{
+get=base_item
+name=Gargoyle Wall Mirror
+id=0x4044
+weight=1000
+restock=20
+movable=1
+decay=1
+good=51
+}
+
+[0x4045]
+{
+get=0x4044
+id=0x4045
+}
+
+[gargoylewallmirror]
+{
+get=0x4044 0x4045
+}
+
+[emptyvenomvial]
+{
+get=base_item
+name=Empty Venom Vial
+id=0x0E24
+weight=100
+restock=20
+movable=1
+decay=1
+good=51
+}
+
+[emptyoilflask]
+{
+get=base_item
+name=empty oil flask
+id=0x1C18
+weight=100
+restock=20
+pilable=1
+movable=1
+decay=1
+good=51
+}
+
+[workableglass]
+{
+get=base_item
+name=workable glass
+id=0x4B80
+weight=100
+restock=20
+pilable=1
+movable=1
+decay=1
+good=51
}
\ No newline at end of file
diff --git a/data/dfndata/items/skills/tools/blacksmithy.dfn b/data/dfndata/items/skills/tools/blacksmithy.dfn
index debd46232..f99512e04 100644
--- a/data/dfndata/items/skills/tools/blacksmithy.dfn
+++ b/data/dfndata/items/skills/tools/blacksmithy.dfn
@@ -1,5 +1,4 @@
//Blacksmithy Tools
-
[0x0fbb]
{
get=base_item
@@ -51,3 +50,18 @@ id=0x0fb0
get=0x0faf 0x0fb0
}
+[0x2dd6]
+{// stone anvil east
+get=0x0faf
+name=elven anvil
+id=0x2dd6
+origin=ml
+}
+
+[0x2dd5]
+{// stone anvil south
+get=0x0faf
+name=elven anvil
+id=0x2dd5
+origin=ml
+}
\ No newline at end of file
diff --git a/data/dfndata/items/skills/tools/carpenty.dfn b/data/dfndata/items/skills/tools/carpenty.dfn
index 64a7319b6..a12e9e45f 100644
--- a/data/dfndata/items/skills/tools/carpenty.dfn
+++ b/data/dfndata/items/skills/tools/carpenty.dfn
@@ -300,4 +300,34 @@ usesleft=25
script=2200// uses left tooltip
}
+[malletandchisel]
+{
+get=base_item
+name=mallet and chisel
+id=0x12B3
+colour=0x3B9
+weight=100
+value=9 4
+restock=20
+decay=1
+good=53
+maxuses=75
+usesleft=25
+script=2200// uses left tooltip
+script=4031// craft tool
+}
+[masonrybook]
+{
+get=base_item
+name=Making Valuables With Stonecrafting
+id=0xFBE
+weight=500
+movable=1
+pileable=1
+decay=1
+value=10637 5318
+good=53
+origin=lbr
+script=5055
+}
diff --git a/data/dictionaries/dictionary.CSY b/data/dictionaries/dictionary.CSY
index 4c66d7eec..ad9cc0d41 100644
--- a/data/dictionaries/dictionary.CSY
+++ b/data/dictionaries/dictionary.CSY
@@ -5112,6 +5112,85 @@
13609=prázdné lahviÄky
13610=plné lahviÄky
13611=toÄÃcà se pÅ™esýpacà hodiny
+// 14001 - 14500 Zednické řemeslné dovednosti
+14001=Zednické menu
+14002=Dekorace
+14003=Nábytek
+14004=Sochy
+14005=Různé doplňky
+14006=Kamenné brnÄ›nÃ
+14007=Kamenné zbraně
+14008=Kamenné zdi
+14009=Kamenné schody
+14010=Kamenné podlahy
+14011=Žula
+14012=Malá měděná žula
+14013=Žula Shadow Iron
+14014=Měděná žula
+14015=Bronzová žula
+14016=Zlatá žula
+14017=Agapitová žula
+14018=Verite žula
+14019=Valoritová žula
+14050=váza
+14051=velká váza
+14052=malá urna
+14053=věžová socha
+14054=obraz chrliÄe
+14055=socha chrliÄe
+14056=váza chrliÄ
+14057=vysoká váza k 18. výroÄÃ
+14058=nÃzká váza k 18. výroÄÃ
+14059=kamenná židle
+14060=kamenný stůl (východ)
+14061=kamenný stůl (jih)
+14062=velký kamenný stůl (východ)
+14063=velký kamenný stůl (jih)
+14064=rituálnà stůl
+14065=malá socha (jih)
+14066=malá socha (sever)
+14067=malá socha (východ)
+14068=soška pegase
+14069=socha chrliÄe
+14070=socha gryfona
+14071=kamenná kovadlina (východ)
+14072=kamenná kovadlina (jih)
+14073=velká ÄernobÃlá postel (východ)
+14074=velká ÄernobÃlá postel (jih)
+14075=ÄernobÃlá postýlka (východ)
+14076=ÄernobÃlá postýlka (jih)
+14077=ÄernobÃlé kamenné paže
+14078=ÄernobÃlá kamenná truhla
+14079=ÄernobÃlé kamenné legÃny
+14080=ÄernobÃlý kamenný kilt
+14081=ÄernobÃlé kamenné paže
+14082=ÄernobÃlá kamenná truhla
+14083=ÄernobÃlé kamenné legÃny
+14084=ÄernobÃlý kamenný kilt
+14085=velký kamenný Å¡tÃt
+14086=ÄernobÃlý kamenný amulet
+14087=kamenný váleÄný meÄ
+14088=Drůstkový Bez oken
+14089=Drůstkový Okno
+14090=Drůstkový Oblouk
+14091=Hrubý sloupek
+14092=Hrubý zaoblený oblouk
+14093=Hrubý malý oblouk
+14094=Hrubý šikmý sloupek
+14095=Krátký hrubý
+14096=Kamenné dveře (J dovnitř)
+14097=Kamenné dveře (V ven)
+14098=Levé kovové dveře (J dovnitř)
+14099=Pravé kovové dveře (J dovnitř)
+14100=krátký hrubý
+14101=hrubé schody
+14102=hrubé rohové schody
+14103=hrubě zaoblený rohový schod
+14104=hrubé vložené schody
+14105=hrubě zaoblené vložené schody
+14106=světlá dlažba
+14107=střednà dlažba
+14108=tmavá dlažba
// 15000 Dovednosti
15000=Alchymie
15001=Anatomie
diff --git a/data/dictionaries/dictionary.ENG b/data/dictionaries/dictionary.ENG
index 7c6a7ef35..5f521005d 100644
--- a/data/dictionaries/dictionary.ENG
+++ b/data/dictionaries/dictionary.ENG
@@ -3524,6 +3524,9 @@
6278=You have to use your taste identification skill to know more about this potion.
6279=Tip: Skill check success guaranteed due to elevated command level!
6280=Spellcast delay ignored due to elevated command level!
+6297=You havent learned masonry.
+6298=Only a Grandmaster Carpenter can learn from this book.
+6299= You have learned to make items from stone. You will need miners to gather stones for you to make these items.
6300=You havent learned glassblowing.
6301=Only a Grandmaster Alchemist can learn from this book.
6302=You have already learned this information.
@@ -4705,6 +4708,12 @@
10296=You have no knowledge on how to unravel that.
10297=You unravel the item and place 5 resources in your pack.
20298=You aren't skilled enough to unravel this.
+20299=RED SCALES
+20300=YELLOW SCALES
+20301=BLACK SCALES
+20302=GREEN SCALES
+20303=WHITE SCALES
+20304=BLUE SCALES
// [10600-10900] Carpentry Crafting Skill
10600=CARPENTRY MENU
10601=Other
@@ -5111,6 +5120,85 @@
13609=empty vials
13610=full vials
13611=spinning hourglass
+// 14001 - 14500 Masonary Crafting Skill
+14001=Masonry Menu
+14002=Decorations
+14003=Furniture
+14004=Statues
+14005=Misc Addons
+14006=Stone Armor
+14007=Stone Weapons
+14008=Stone Walls
+14009=Stone Stairs
+14010=Stone Floors
+14011=Granite
+14012=Dull Copper Granite
+14013=Shadow Iron Granite
+14014=Copper Granite
+14015=Bronze Granite
+14016=Gold Granite
+14017=Agapite Granite
+14018=Verite Granite
+14019=Valorite Granite
+14050=vase
+14051=large vase
+14052=small urn
+14053=Tower Sculpture
+14054=gargoyle painting
+14055=gargoyle sculpture
+14056=gargoyle vase
+14057=Tall 18th Anniversary Vase
+14058=Short 18th Anniversary Vase
+14059=stone chair
+14060=stone table (east)
+14061=stone table (south)
+14062=large stone table (east)
+14063=large stone table (south)
+14064=ritual table
+14065=small statue (south)
+14066=small statue (north)
+14067=small statue (east)
+14068=pegasus statuette
+14069=gargoyle statue
+14070=gryphon statue
+14071=stone anvil (east)
+14072=stone anvil (south)
+14073=large gargish bed (east)
+14074=large gargish bed (south)
+14075=gargish cot (east)
+14076=gargish cot (south)
+14077=gargish stone arms
+14078=gargish stone chest
+14079=gargish stone leggings
+14080=gargish stone kilt
+14081=gargish stone arms
+14082=gargish stone chest
+14083=gargish stone leggings
+14084=gargish stone kilt
+14085=large stone shield
+14086=gargish stone amulet
+14087=stone war sword
+14088=Rough Windowless
+14089=Rough Window
+14090=Rough Arch
+14091=Rough Pillar
+14092=Rough Rounded Arch
+14093=Rough Small Arch
+14094=Rough Angled Pillar
+14095=Short Rough
+14096=Stone Door (S In)
+14097=Stone Door (E Out)
+14098=Left Metal Door (S In)
+14099=Right Metal Door (S In)
+14100=short rough
+14101=rough steps
+14102=rough corner steps
+14103=rough rounded corner step
+14104=rough inset steps
+14105=rough rounded inset steps
+14106=light paver
+14107=medium paver
+14108=dark paver
// 15000 Skills
15000=Alchemy
15001=Anatomy
diff --git a/data/dictionaries/dictionary.FRE b/data/dictionaries/dictionary.FRE
index 33f64c63b..2ecf9bee8 100644
--- a/data/dictionaries/dictionary.FRE
+++ b/data/dictionaries/dictionary.FRE
@@ -5276,6 +5276,85 @@
13609=flacons vides
13610=flacons pleins
13611=sablier tournant
+// 14001 - 14500 Compétence en maçonnerie
+14001=Menu Maçonnerie
+14002=Décorations
+14003=Mobilier
+14004=Statues
+14005=Accessoires divers
+14006=Armure de pierre
+14007=Armes de pierre
+14008=Murs de pierre
+14009=Escaliers de pierre
+14010=Sols en pierre
+14011=Granite
+14012=Granite de cuivre mat
+14013=Granite de fer de l'ombre
+14014=Granite de cuivre
+14015=Granite de bronze
+14016=Granite d'or
+14017=Granite d'agapite
+14018=Granite de vératie
+14019=Granite de valorite
+14050=Vase
+14051=Grand Vase
+14052=Petite urne
+14053=Sculpture de tour
+14054=Peinture de gargouille
+14055=Sculpture de gargouille
+14056=Vase à gargouille
+14057=Grand vase du 18e anniversaire
+14058=Petit vase du 18e anniversaire
+14059=Chaise en pierre
+14060=Table en pierre (est)
+14061=Table en pierre (sud)
+14062=Grande table en pierre (est)
+14063=Grande table en pierre (sud)
+14064=Table rituelle
+14065=Petite statue (sud)
+14066=Petite statue (nord)
+14067=Petite statue (est)
+14068=Statuette de Pégase
+14069=Statue de gargouille
+14070=Statue de griffon
+14071=Enclume en pierre (est)
+14072=Pierre Enclume (sud)
+14073=Grand lit gargish (est)
+14074=Grand lit gargish (sud)
+14075=Bébé gargish (est)
+14076=Bébé gargish (sud)
+14077=Armes en pierre gargish
+14078=Coffre en pierre gargish
+14079=Jambières en pierre gargish
+14080=Kilt en pierre gargish
+14081=Armes en pierre gargish
+14082=Coffre en pierre gargish
+14083=Jambières en pierre gargish
+14084=Kilt en pierre gargish
+14085=Grand bouclier en pierre
+14086=Amulette en pierre gargish
+14087=Épée de guerre en pierre
+14088=Rugueux sans fenêtre
+14089=Rugueux avec fenêtre
+14090=Arche rugueuse
+14091=Rugueux Pilier
+14092 = Arche arrondie brute
+14093 = Petite arche brute
+14094 = Pilier angulaire brut
+14095 = Courte marche brute
+14096 = Porte en pierre (S vers l'intérieur)
+14097 = Porte en pierre (E vers l'extérieur)
+14098 = Porte métallique gauche (S vers l'intérieur)
+14099 = Porte métallique droite (S vers l'intérieur)
+14100 = Courte marche brute
+14101 = Marches brutes
+14102 = Marches d'angle brutes
+14103 = Marche d'angle grossièrement arrondie
+14104 = Marches encastrées brutes
+14105 = Marches encastrées grossièrement arrondies
+14106 = Pavé clair
+14107 = Pavé moyen
+14108 = Pavé foncé
// 15000 compétences
15000=Alchimie
15001=Anatomie
diff --git a/data/dictionaries/dictionary.GER b/data/dictionaries/dictionary.GER
index 8cec50d60..289c85e03 100644
--- a/data/dictionaries/dictionary.GER
+++ b/data/dictionaries/dictionary.GER
@@ -5112,6 +5112,85 @@
13609=leere Fläschchen
13610=volle Fläschchen
13611=drehende Sanduhr
+// 14001 - 14500 Maurerhandwerksfertigkeit
+14001=Mauerwerksmenü
+14002=Dekorationen
+14003=Möbel
+14004=Statuen
+14005=Sonstige Erweiterungen
+14006=Steinrüstung
+14007=Steinwaffen
+14008=Steinmauern
+14009=Steintreppen
+14010=Steinböden
+14011=Granit
+14012=Mattkupfergranit
+14013=Schatteneisengranit
+14014=Kupfergranit
+14015=Bronzegranit
+14016=Goldgranit
+14017=Agapitgranit
+14018=Veritgranit
+14019=Valorit Granit
+14050=Vase
+14051=Große Vase
+14052=Kleine Urne
+14053=Turmskulptur
+14054=Gemälde mit Wasserspeier
+14055=Wasserspeier-Skulptur
+14056=Wasserspeier-Vase
+14057=Hohe Vase zum 18. Jahrestag
+14058=Kleine Vase zum 18. Jahrestag
+14059=Steinstuhl
+14060=Steintisch (Osten)
+14061=Steintisch (Süden)
+14062=Großer Steintisch (Osten)
+14063=Großer Steintisch (Süden)
+14064=Ritualtisch
+14065=Kleine Statue (Süden)
+14066=Kleine Statue (Norden)
+14067=Kleine Statue (Osten)
+14068=Pegasus-Statuette
+14069=Wasserspeier Statue
+14070=Greifenstatue
+14071=Steinamboss (Osten)
+14072=Steinamboss (Süden)
+14073=Großes, grelles Bett (Osten)
+14074=Großes, grelles Bett (Süden)
+14075=Grelles Feldbett (Osten)
+14076=Grelles Feldbett (Süden)
+14077=Grelle Steinarme
+14078=Grelle Steintruhe
+14079=Grelle Steinbeinlinge
+14080=Grelle Steinkilt
+14081=Grelle Steinarme
+14082=Grelle Steintruhe
+14083=Grelle Steinbeinlinge
+14084=Grelle Steinkilt
+14085=Großer Steinschild
+14086=Grelles Steinamulett
+14087=Steinkrieger Schwert
+14088=Rohbau ohne Fenster
+14089=Rohbau mit Fenster
+14090=Rohbau mit Bogen
+14091=Rohbau mit Pfeiler
+14092=Rohbau mit Rundbogen
+14093=Rohbau mit kleinem Bogen
+14094=Rohbau mit abgewinkeltem Pfeiler
+14095=Kurzbau
+14096=Steintür (Südseite)
+14097=Steintür (Ostseite)
+14098=Linke Metalltür (Südseite)
+14099=Rechte Metalltür (Südseite)
+14100=Kurzbau
+14101=Rohbau mit Stufen
+14102=Rohbau mit Eckstufen
+14103=Rohbau mit abgerundeten Eckstufen
+14104=Rohbau mit Einsatzstufen
+14105=Rohbau mit abgerundeten Einsatzstufen
+14106=Heller Pflasterstein
+14107=Mittelgroßer Pflasterstein
+14108=Dunkler Pflasterstein
// 15000 Fertigkeiten
15000=Alchemie
15001=Anatomie
diff --git a/data/dictionaries/dictionary.ITA b/data/dictionaries/dictionary.ITA
index fded04dd5..d115d4e95 100644
--- a/data/dictionaries/dictionary.ITA
+++ b/data/dictionaries/dictionary.ITA
@@ -5112,6 +5112,85 @@
13609=fialette vuote
13610=flaconi pieni
13611=clessidra rotante
+// 14001 - 14500 Abilità di lavorazione della muratura
+14001=Menu muratura
+14002=Decorazioni
+14003=Mobili
+14004=Statue
+14005=Accessori vari
+14006=Armature di pietra
+14007=Armi di pietra
+14008=Muri di pietra
+14009=Scale di pietra
+14010=Pavimenti di pietra
+14011=Granito
+14012=Granito rame opaco
+14013=Granito ferro ombra
+14014=Granito rame
+14015=Granito bronzo
+14016=Granito oro
+14017=Granito agapite
+14018=Granito Verite
+14019=Valorite Granito
+14050=vaso
+14051=vaso grande
+14052=urna piccola
+14053=scultura a torre
+14054=dipinto a gargoyle
+14055=scultura a gargoyle
+14056=vaso a gargoyle
+14057=vaso alto per il 18° anniversario
+14058=vaso basso per il 18° anniversario
+14059=sedia in pietra
+14060=tavolo in pietra (est)
+14061=tavolo in pietra (sud)
+14062=tavolo in pietra grande (est)
+14063=tavolo in pietra grande (sud)
+14064=tavolo rituale
+14065=statua piccola (sud)
+14066=statua piccola (nord)
+14067=statua piccola (est)
+14068=statuetta di Pegaso
+14069=gargoyle statua
+14070=statua di grifone
+14071=incudine di pietra (est)
+14072=incudine di pietra (sud)
+14073=grande letto di pietra (est)
+14074=grande letto di pietra (sud)
+14075=branda di pietra (est)
+14076=branda di pietra (sud)
+14077=braccia di pietra
+14078=baule di pietra
+14079=gambali di pietra
+14080=kilt di pietra
+14081=braccia di pietra
+14082=baule di pietra
+14083=gambali di pietra
+14084=kilt di pietra
+14085=grande scudo di pietra
+14086=amuleto di pietra
+14087=spada da guerra di pietra
+14088=Grezzo Senza finestre
+14089=Finestra grezza
+14090=Arco grezzo
+14091=Pilastro grezzo
+14092=Arco arrotondato grezzo
+14093=Arco piccolo grezzo
+14094=Pilastro angolare grezzo
+14095=Corto grezzo
+14096=Porta in pietra (Ingresso S)
+14097=Porta in pietra (Uscita E)
+14098=Porta metallica sinistra (Ingresso S)
+14099=Porta metallica destra (Ingresso S)
+14100=Corto grezzo
+14101=Gradini grezzi
+14102=Gradini angolari grezzi
+14103=Gradino angolare arrotondato grezzo
+14104=Gradini con inserto grezzo
+14105=Gradini con inserto arrotondato grezzo
+14106=Pavimentazione chiara
+14107=Pavimentazione media
+14108=Pavimentazione scura
// 15000 AbilitÃ
15000=Alchimia
15001=Anatomia
diff --git a/data/dictionaries/dictionary.POL b/data/dictionaries/dictionary.POL
index 50af51c7c..965e53feb 100644
--- a/data/dictionaries/dictionary.POL
+++ b/data/dictionaries/dictionary.POL
@@ -5112,6 +5112,85 @@
13609=puste fiolki
13610=pełne fiolki
13611=wirujÄ…ca klepsydra
+// 14001 - 14500 Umiejętność rzemiosła murarskiego
+14001=Menu murarskie
+14002=Dekoracje
+14003=Meble
+14004=PosÄ…gi
+14005=Różne dodatki
+14006=Kamienna zbroja
+14007=Kamienna broń
+14008=Kamienne ściany
+14009=Kamienne schody
+14010=Kamienne podłogi
+14011=Granit
+14012=Matowy granit miedziany
+14013=Granit z cienistego żelaza
+14014=Granit miedziany
+14015=BrÄ…zowy granit
+14016=Złoty granit
+14017=Granit agapitowy
+14018=Granit verite
+14019=Wartość Granity
+14050=wazon
+14051=duży wazon
+14052=mała urna
+14053=rzeźba wieży
+14054=obraz gargulca
+14055=rzeźba gargulca
+14056=wazon gargulca
+14057=wysoki wazon na 18. rocznicÄ™
+14058=niski wazon na 18. rocznicÄ™
+14059=kamienne krzesło
+14060=kamienny stół (wschód)
+14061=kamienny stół (południe)
+14062=duży kamienny stół (wschód)
+14063=duży kamienny stół (południe)
+14064=stół rytualny
+14065=mała statua (południe)
+14066=mała statua (północ)
+14067=mała statua (wschód)
+14068=pegaz Statuetki
+14069=posągi gargulców
+14070=posÄ…g gryfa
+14071=kamienne kowadło (wschód)
+14072=kamienne kowadło (południe)
+14073=duże łoże gargish (wschód)
+14074=duże łoże gargish (południe)
+14075=łoże gargish (wschód)
+14076=łoże gargish (południe)
+14077=kamienne ramiona gargish
+14078=skrzynia gargish
+14079=kamienne nagolenniki gargish
+14080=kamienny kilt gargish
+14081=kamienne ramiona gargish
+14082=kamienna skrzynia gargish
+14083=kamienne nagolenniki gargish
+14084=kamienny kilt gargish
+14085=duża kamienna tarcza
+14086=kamień gargish Amulet
+14087=Kamienny miecz wojenny
+14088=Surowy bez okien
+14089=Surowy okno
+14090=Surowy łuk
+14091=Surowy filar
+14092=Surowy łuk zaokrąglony
+14093=Surowy mały łuk
+14094=Surowy filar kÄ…towy
+14095=Krótki surowy
+14096=Kamienne drzwi (południowe wejście)
+14097=Kamienne drzwi (wschodnie wyjście)
+14098=Lewe metalowe drzwi (południowe wejście)
+14099=Prawe metalowe drzwi (południowe wejście)
+14100=Krótki surowy
+14101=Surowe stopnie
+14102=Surowe stopnie narożne
+14103=Surowy stopień z zaokrąglonym narożnikiem
+14104=Surowe stopnie z wstawkami
+14105=Surowe stopnie z zaokrÄ…glonymi wstawkami
+14106=Jasny strach
+14107=średnia kostka brukowa
+14108=ciemny strach
// 15000 Umiejętności
15000=Alchemia
15001=Anatomia
diff --git a/data/dictionaries/dictionary.PTG b/data/dictionaries/dictionary.PTG
index b6987987f..f9f9383f5 100644
--- a/data/dictionaries/dictionary.PTG
+++ b/data/dictionaries/dictionary.PTG
@@ -5112,6 +5112,85 @@
13609=frascos vazios
13610=frascos cheios
13611=ampulheta girando
+// 14001 - 14500 Habilidade em Alvenaria
+14001=Menu de Alvenaria
+14002=Decorações
+14003=Móveis
+14004=Estátuas
+14005=Acessórios Diversos
+14006=Armadura de Pedra
+14007=Armas de Pedra
+14008=Paredes de Pedra
+14009=Escadas de Pedra
+14010=Pisos de Pedra
+14011=Granito
+14012=Granito Cobre Mate
+14013=Granito Ferro Sombrio
+14014=Granito Cobre
+14015=Granito Bronze
+14016=Granito Ouro
+14017=Granito Agapita
+14018=Granito Verita
+14019=Valor Granitos
+14050=vaso
+14051=vaso grande
+14052=urna pequena
+14053=Escultura de Torre
+14054=pintura de gárgula
+14055=escultura de gárgula
+14056=vaso de gárgula
+14057=Vaso Alto do 18º Aniversário
+14058=Vaso Baixo do 18º Aniversário
+14059=cadeira de pedra
+14060=mesa de pedra (nascente)
+14061=mesa em pedra (sul)
+14062=mesa de pedra grande (nascente)
+14063=mesa de pedra grande (sul)
+14064=mesa ritual
+14065=estátua pequena (sul)
+14066=estátua pequena (norte)
+14067=estátua pequena (leste)
+14068=pégaso estatuetas
+14069=estátuas de gárgula
+14070=estátua de grifo
+14071=bigorna de pedra (leste)
+14072=bigorna de pedra (sul)
+14073=cama grande de gárgula (nascente)
+14074=cama grande de gárgulas (sul)
+14075=berço de gárgula (nascente)
+14076=berço de gárgula (sul)
+14077=braços de pedra de gárgula
+14078=baú em pedra de gárgula
+14079=calças de pedra de gárgula
+14080=saia de pedra de gárgula
+14081=braços de pedra de gárgula
+14082=baú em pedra de gárgula
+14083=calças de pedra de gárgula
+14084=saia de pedra de gárgula
+14085=escudo de pedra grande
+14086=gárgula Amuleto de pedra
+14087=Espada de guerra de pedra
+14088=Sem janela rústica
+14089=Janela rústica
+14090=Arco rústico
+14091=Pilar rústico
+14092=Arco arredondado rústico
+14093=Arco pequeno rústico
+14094=Pilar angular rústico
+14095=Rouco curto
+14096=Porta em pedra (entrada sul)
+14097=Porta de pedra (saÃda nascente)
+14098=Porta metálica esquerda (entrada sul)
+14099=Porta metálica direita (entrada sul)
+14100=Rouco curto
+14101=Degraus rústicos
+14102=Degraus de canto rústicos
+14103=Degrau de canto arredondado rústico
+14104=Degraus embutidos rústicos
+14105=Degraus embutidos arredondados rústicos
+14106=Luz medo
+14107=pavimento médio
+14108=medo escuro
// 15000 Competências
15000=Alchemia
15001=Anatomia
diff --git a/data/dictionaries/dictionary.SPA b/data/dictionaries/dictionary.SPA
index 34f5560ab..668270b4f 100644
--- a/data/dictionaries/dictionary.SPA
+++ b/data/dictionaries/dictionary.SPA
@@ -5112,6 +5112,85 @@
13609=frascos vacÃos
13610=frascos llenos
13611=reloj de arena giratorio
+// 14001 - 14500 Habilidad de ArtesanÃa en AlbañilerÃa
+14001=Menú de AlbañilerÃa
+14002=Decoraciones
+14003=Mobiliario
+14004=Estatuas
+14005=Complementos Varios
+14006=Armadura de Piedra
+14007=Armas de Piedra
+14008=Muros de Piedra
+14009=Escaleras de Piedra
+14010=Suelos de Piedra
+14011=Granito
+14012=Granito de Cobre Mate
+14013=Granito de Hierro SombrÃo
+14014=Granito de Cobre
+14015=Granito de Bronce
+14016=Granito de Oro
+14017=Granito de Agapita
+14018=Granito de Verita
+14019=Valor Granitos
+14050=jarrón
+14051=jarrón grande
+14052=urna pequeña
+14053=escultura de torre
+14054=pintura de gárgola
+14055=escultura de gárgola
+14056=jarrón de gárgola
+14057=jarrón alto del 18.º aniversario
+14058=jarrón bajo del 18.º aniversario
+14059=silla de piedra
+14060=mesa de piedra (este)
+14061=mesa de piedra (sur)
+14062=mesa de piedra grande (este)
+14063=mesa de piedra grande (sur)
+14064=mesa ritual
+14065=estatua pequeña (sur)
+14066=estatua pequeña (norte)
+14067=estatua pequeña (este)
+14068=estatuillas de pegaso
+14069=gárgola Estatuas
+14070=Estatua de grifo
+14071=Yunque de piedra (este)
+14072=Yunque de piedra (sur)
+14073=Cama grande de gárgola (este)
+14074=Cama grande de gárgola (sur)
+14075=Cuna de gárgola (este)
+14076=Cuna de gárgola (sur)
+14077=Brazos de piedra de gárgola
+14078=Cofre de piedra de gárgola
+14079=Polainas de piedra de gárgola
+14080=Falda de piedra de gárgola
+14081=Brazos de piedra de gárgola
+14082=Cofre de piedra de gárgola
+14083=Polainas de piedra de gárgola
+14084=Falda de piedra de gárgola
+14085=Escudo grande de piedra
+14086=Amuleto de piedra de gárgola
+14087=Espada de guerra de piedra
+14088=Búsqueda Sin ventanas
+14089=Ventana rústica
+14090=Arco rústico
+14091=Pilar rústico
+14092=Arco redondeado rústico
+14093=Arco pequeño rústico
+14094=Pilar angular rústico
+14095=Rústico corto
+14096=Puerta de piedra (Sin entrada)
+14097=Puerta de piedra (E sin salida)
+14098=Puerta metálica izquierda (Sin entrada)
+14099=Puerta metálica derecha (Sin entrada)
+14100=Rústico corto
+14101=Escalones rústicos
+14102=Escalones de esquina rústicos
+14103=Escalón de esquina redondeado rústico
+14104=Escalones empotrados rústicos
+14105=Escalones empotrados redondeados rústicos
+14106=Piso claro
+14107=AdoquÃn mediano
+14108=Piso oscuro
// 15000 Habilidades
15000=Alquimia
15001=AnatomÃa
diff --git a/data/dictionaries/dictionary.ZRO b/data/dictionaries/dictionary.ZRO
index a49b7576a..80c9e80b6 100644
--- a/data/dictionaries/dictionary.ZRO
+++ b/data/dictionaries/dictionary.ZRO
@@ -4705,6 +4705,12 @@
10296=You have no knowledge on how to unravel that.
10297=You unravel the item and place 5 resources in your pack.
20298=You aren't skilled enough to unravel this.
+20299=RED SCALES
+20300=YELLOW SCALES
+20301=BLACK SCALES
+20302=GREEN SCALES
+20303=WHITE SCALES
+20304=BLUE SCALES
// [10600-10900] Carpentry Crafting Skill
10600=CARPENTRY MENU
10601=Other
@@ -5111,6 +5117,85 @@
13609=empty vials
13610=full vials
13611=spinning hourglass
+// 14001 - 14500 Masonary Crafting Skill
+14001=Masonry Menu
+14002=Decorations
+14003=Furniture
+14004=Statues
+14005=Misc Addons
+14006=Stone Armor
+14007=Stone Weapons
+14008=Stone Walls
+14009=Stone Stairs
+14010=Stone Floors
+14011=Granite
+14012=Dull Copper Granite
+14013=Shadow Iron Granite
+14014=Copper Granite
+14015=Bronze Granite
+14016=Gold Granite
+14017=Agapite Granite
+14018=Verite Granite
+14019=Valorite Granite
+14050=vase
+14051=large vase
+14052=small urn
+14053=Tower Sculpture
+14054=gargoyle painting
+14055=gargoyle sculpture
+14056=gargoyle vase
+14057=Tall 18th Anniversary Vase
+14058=Short 18th Anniversary Vase
+14059=stone chair
+14060=stone table (east)
+14061=stone table (south)
+14062=large stone table (east)
+14063=large stone table (south)
+14064=ritual table
+14065=small statue (south)
+14066=small statue (north)
+14067=small statue (east)
+14068=pegasus statuette
+14069=gargoyle statue
+14070=gryphon statue
+14071=stone anvil (east)
+14072=stone anvil (south)
+14073=large gargish bed (east)
+14074=large gargish bed (south)
+14075=gargish cot (east)
+14076=gargish cot (south)
+14077=gargish stone arms
+14078=gargish stone chest
+14079=gargish stone leggings
+14080=gargish stone kilt
+14081=gargish stone arms
+14082=gargish stone chest
+14083=gargish stone leggings
+14084=gargish stone kilt
+14085=large stone shield
+14086=gargish stone amulet
+14087=stone war sword
+14088=Rough Windowless
+14089=Rough Window
+14090=Rough Arch
+14091=Rough Pillar
+14092=Rough Rounded Arch
+14093=Rough Small Arch
+14094=Rough Angled Pillar
+14095=Short Rough
+14096=Stone Door (S In)
+14097=Stone Door (E Out)
+14098=Left Metal Door (S In)
+14099=Right Metal Door (S In)
+14100=short rough
+14101=rough steps
+14102=rough corner steps
+14103=rough rounded corner step
+14104=rough inset steps
+14105=rough rounded inset steps
+14106=light paver
+14107=medium paver
+14108=dark paver
// 15000 Skills
15000=Alchemy
15001=Anatomy
diff --git a/data/js/item/buildingcraftables.js b/data/js/item/buildingcraftables.js
new file mode 100644
index 000000000..53e2cd690
--- /dev/null
+++ b/data/js/item/buildingcraftables.js
@@ -0,0 +1,181 @@
+///
+// @ts-check
+/** @type { ( thingCreated: BaseObject, thingType: 0 | 1 ) => void } */
+function onCreateDFN( iCreated, dfnSection )
+{
+ setLockedState( iCreated, false );
+ iCreated.SetTag( "BuildingCraftable", 1 );
+}
+
+/** @type { ( user: Character, iUsing: Item ) => boolean } */
+function onUseChecked( pUser, iUsed )
+{
+ var pSocket = pUser.socket;
+
+ if( iUsed.container !== null )
+ {
+ if( iUsed.container === pUser.pack )
+ {
+ if (pSocket)
+ pSocket.SysMessage( "Place the wall in your house, then double-click it to lock it down." );
+ }
+ else
+ {
+ if( pSocket )
+ pSocket.SysMessage( "You must first place this wall in the world." );
+ }
+ return false;
+ }
+
+ if( !isInOwnHouse( pUser ))
+ {
+ if( pSocket )
+ {
+ pSocket.SysMessage( GetDictionaryEntry( 2067, pSocket.language )); // You must be in your house to do this.
+ }
+ return false;
+ }
+
+ var iMulti = iUsed.multi;
+ if( !ValidateObject( iMulti ) || iMulti !== pUser.multi )
+ {
+ if( pSocket )
+ pSocket.SysMessage( "This crafted wall must be inside your house to lock it down." );
+ return false;
+ }
+
+ var locked = iUsed.GetTag("CraftWallLocked");
+
+ if( locked == 1 )
+ {
+ setLockedState( iUsed, false );
+ if( pSocket )
+ pSocket.SysMessage("You unlock the crafted wall. It can now be moved and will decay normally.");
+ }
+ else
+ {
+ setLockedState( iUsed, true );
+ if( pSocket )
+ pSocket.SysMessage("You lock the crafted wall in place. It will no longer decay.");
+ }
+
+ return false;
+}
+
+/** @type { ( myObj: BaseObject, pSocket: Socket ) => string } */
+function onTooltip( myObj, pSocket )
+{
+ var locked = myObj.GetTag( "CraftWallLocked" );
+
+ if( locked == 1 )
+ {
+ // Line 1: House Only
+ // Line 2: unlock hint
+ return "House Only
You must double click this to unlock it.";
+ }
+ else
+ {
+ // Line 1: House Only
+ // Line 2: lock hint
+ return "House Only
You must double click this to lock it down.";
+ }
+}
+
+function onDropItemOnNpc( pDropper, pDroppedOn, iDropped )
+{
+ var targPack = pDroppedOn.pack;
+ if( ValidateObject( targPack ) && pDroppedOn.sectionID == "packhorse" )
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+function onDropItemOnItem( iDropped, cDropper, iDroppedOn )
+{
+ if( !ValidateObject( iDropped ) || !ValidateObject( cDropper ) || !ValidateObject( iDroppedOn ))
+ return 0;
+
+ var isBuilding = iDropped.GetTag( "BuildingCraftable" ) === 1;
+ if( !isBuilding )
+ return 0
+
+ var owner = iDroppedOn.container;
+ if( ValidateObject( owner ) && owner.isChar && owner.sectionID == "packhorse" )
+ {
+ var sock = cDropper.socket;
+ if( sock )
+ sock.SysMessage( "You cannot place building pieces on a pack animal." );
+
+ return 0;
+ }
+
+ return 1;
+}
+
+function onDrop( iDropped, pDropper )
+{
+ if( !ValidateObject( iDropped ) || !ValidateObject( pDropper ))
+ return 0;
+
+ var isBuilding = iDropped.GetTag( "BuildingCraftable" ) === 1;
+ if( !isBuilding )
+ return 0;
+
+ var socket = pDropper.socket;
+ if( !isInOwnHouse( pDropper ))
+ {
+ if( socket != null)
+ socket.SysMessage( "The building piece crumbles when dropped on the ground." );
+
+ iDropped.Delete();
+ return 2;
+ }
+
+ return 1;
+}
+
+
+function isInOwnHouse( pUser )
+{
+ if( !ValidateObject( pUser ))
+ return false;
+
+ var iMulti = pUser.multi;
+ if( !ValidateObject( iMulti ))
+ return false;
+
+ // Owner / co-owner / whatever IsOnOwnerList covers
+ if( iMulti.IsOnOwnerList( pUser ))
+ return true;
+
+ // Optional: co-owned houses on same account
+ if( GetServerSetting( "COOWNHOUSESONSAMEACCOUNT" ))
+ {
+ if( ValidateObject( iMulti.owner ) && iMulti.owner.accountNum === pUser.accountNum )
+ return true;
+ }
+
+ return false;
+}
+
+function setLockedState( item, locked )
+{
+ if( !ValidateObject( item ))
+ return;
+
+ if( locked )
+ {
+ item.SetTag( "CraftWallLocked", 1 );
+ item.movable = 2; // locked down / secure
+ item.decayable = false; // no decay
+ }
+ else
+ {
+ item.SetTag( "CraftWallLocked", 0 );
+ item.movable = 1; // movable
+ item.decayable = true; // normal decay
+ }
+ item.Refresh();
+}
\ No newline at end of file
diff --git a/data/js/item/buildingcraftables_doors.js b/data/js/item/buildingcraftables_doors.js
new file mode 100644
index 000000000..c55a77da4
--- /dev/null
+++ b/data/js/item/buildingcraftables_doors.js
@@ -0,0 +1,91 @@
+///
+// @ts-check
+/** @type { ( thingCreated: BaseObject, thingType: 0 | 1 ) => void } */
+function onCreateDFN( iCreated, dfnSection )
+{
+ iCreated.SetTag( "BuildingCraftableDoor", 1 );
+}
+
+/** @type { ( myObj: BaseObject, pSocket: Socket ) => string } */
+function onTooltip( myObj, pSocket )
+{
+ return "House Only";
+}
+
+function onDropItemOnNpc( pDropper, pDroppedOn, iDropped )
+{
+ var targPack = pDroppedOn.pack;
+ if( ValidateObject( targPack ) && pDroppedOn.sectionID == "packhorse" )
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+function onDropItemOnItem( iDropped, cDropper, iDroppedOn )
+{
+ if( !ValidateObject( iDropped ) || !ValidateObject( cDropper ) || !ValidateObject( iDroppedOn ))
+ return 0;
+
+ var isBuilding = iDropped.GetTag( "BuildingCraftableDoor" ) === 1;
+ if( !isBuilding )
+ return 0
+
+ var owner = iDroppedOn.container;
+ if( ValidateObject( owner ) && owner.isChar && owner.sectionID == "packhorse" )
+ {
+ var sock = cDropper.socket;
+ if( sock )
+ sock.SysMessage( "You cannot place building pieces on a pack animal." );
+
+ return 0;
+ }
+
+ return 1;
+}
+
+function onDrop( iDropped, pDropper )
+{
+ if( !ValidateObject( iDropped ) || !ValidateObject( pDropper ))
+ return 0;
+
+ var isBuilding = iDropped.GetTag( "BuildingCraftableDoor" ) === 1;
+ if( !isBuilding )
+ return 0;
+
+ var socket = pDropper.socket;
+ if( !isInOwnHouse( pDropper ))
+ {
+ if( socket != null)
+ socket.SysMessage( "The building piece crumbles when dropped on the ground." );
+
+ iDropped.Delete();
+ return 2;
+ }
+
+ return 1;
+}
+
+function isInOwnHouse( pUser )
+{
+ if( !ValidateObject( pUser ))
+ return false;
+
+ var iMulti = pUser.multi;
+ if( !ValidateObject( iMulti ))
+ return false;
+
+ // Owner / co-owner / whatever IsOnOwnerList covers
+ if( iMulti.IsOnOwnerList( pUser ))
+ return true;
+
+ // Optional: co-owned houses on same account
+ if( GetServerSetting( "COOWNHOUSESONSAMEACCOUNT" ))
+ {
+ if( ValidateObject( iMulti.owner ) && iMulti.owner.accountNum === pUser.accountNum )
+ return true;
+ }
+
+ return false;
+}
\ No newline at end of file
diff --git a/data/js/item/glassblowingbook.js b/data/js/item/glassblowingbook.js
index acf70e220..57afa8322 100644
--- a/data/js/item/glassblowingbook.js
+++ b/data/js/item/glassblowingbook.js
@@ -14,20 +14,20 @@ function onUseChecked( pUser, iUsed )
}
else if( pUser.skills[0] < 1000 )
{
- socket.SysMessage( GetDictionaryEntry( 6301, socket.Language ) ); // Only a Grandmaster Alchemist can learn from this book.
+ socket.SysMessage( GetDictionaryEntry( 6301, socket.language )); // Only a Grandmaster Alchemist can learn from this book.
}
else if( pUser.GetTag( "GlassBlowing" ) == 1 )
{
- socket.SysMessage( GetDictionaryEntry( 6302, socket.Language ) ); // You have already learned this information.
+ socket.SysMessage( GetDictionaryEntry( 6302, socket.language )); // You have already learned this information.
}
else if( iUsed.movable == 2 || iUsed.movable == 3 )
{
- socket.SysMessage( GetDictionaryEntry( 774, socket.Language )); //That is locked down and you cannot use it
+ socket.SysMessage( GetDictionaryEntry( 774, socket.language )); //That is locked down and you cannot use it
}
else
{
pUser.SetTag( "GlassBlowing", 1 );
- socket.SysMessage( GetDictionaryEntry( 6303, socket.Language )); // You have learned to mine for stones. Target mountains when mining to find stones.
+ socket.SysMessage( GetDictionaryEntry( 6303, socket.language )); // You have learned to make items from glass. You will need to find miners to mine fine
iUsed.Delete();
}
}
diff --git a/data/js/item/interiordecorator.js b/data/js/item/interiordecorator.js
index a3161e947..a2dae4819 100644
--- a/data/js/item/interiordecorator.js
+++ b/data/js/item/interiordecorator.js
@@ -30,6 +30,7 @@ function onUseChecked( pUser, iUsed )
function interiorDecoratorGump( pUser )
{
+ var socket = pUser.socket;
var interiorGump = new Gump;
interiorGump.AddPage( 0 );
interiorGump.AddBackground( 0, 0, 200, 200, 2600 );
@@ -39,7 +40,7 @@ function interiorDecoratorGump( pUser )
interiorGump.AddXMFHTMLGump( 90, 100, 70, 40, 1018324, false, false );//up
interiorGump.AddButton( 50, 145, 0x868, 1, 0, 3 );
interiorGump.AddXMFHTMLGump( 90, 150, 70, 40, 1018325, false, false );//down
- interiorGump.Send( pUser );
+ interiorGump.Send( socket );
interiorGump.Free();
return false;
}
@@ -84,10 +85,18 @@ function onGumpPress( socket, pButton, gumpData )
/** @type { ( tSock: Socket, target: Character | Item | null ) => void } */
function onCallback1( socket, ourObj )
{
+ if( !ValidateObject( ourObj ))
+ {
+ return;
+ }
+
+ var isBuilding = ourObj.GetTag( "BuildingCraftable" ) === 1;
+ var isWallLocked = ourObj.GetTag( "CraftWallLocked" ) === 1;
var pUser = socket.currentChar;
- if( !ValidateObject( ourObj ) || !ourObj.isItem || ourObj.movable != 3 )
+ if( !ourObj.isItem || ( isBuilding && !isWallLocked ) || ( !isBuilding && ourObj.movable != 3 ))
{
socket.SysMessage( GetDictionaryEntry( 2072, socket.language )); // You can only use the interior decorator on locked down items.
+ socket.CustomTarget( 1 );
return;
}
@@ -126,10 +135,18 @@ function onCallback1( socket, ourObj )
/** @type { ( tSock: Socket, target: Character | Item | null ) => void } */
function onCallback2( socket, ourObj )
{
+ if( !ValidateObject( ourObj ))
+ {
+ return;
+ }
+
+ var isBuilding = ourObj.GetTag( "BuildingCraftable" ) === 1;
+ var isWallLocked = ourObj.GetTag( "CraftWallLocked" ) === 1;
var pUser = socket.currentChar;
- if( !ValidateObject( ourObj ) || !ourObj.isItem || ourObj.movable != 3 )
+ if( !ourObj.isItem || ( isBuilding && !isWallLocked ) || ( !isBuilding && ourObj.movable != 3 ))
{
socket.SysMessage( GetDictionaryEntry( 2072, socket.language )); // You can only use the interior decorator on locked down items.
+ socket.CustomTarget( 2 );
return;
}
@@ -168,10 +185,18 @@ function onCallback2( socket, ourObj )
/** @type { ( tSock: Socket, target: Character | Item | null ) => void } */
function onCallback3( socket, ourObj )
{
+ if( !ValidateObject( ourObj ))
+ {
+ return;
+ }
+
+ var isBuilding = ourObj.GetTag( "BuildingCraftable" ) === 1;
+ var isWallLocked = ourObj.GetTag( "CraftWallLocked" ) === 1;
var pUser = socket.currentChar;
- if( !ValidateObject( ourObj ) || !ourObj.isItem || ourObj.movable != 3 )
+ if( !ourObj.isItem || ( isBuilding && !isWallLocked ) || ( !isBuilding && ourObj.movable != 3 ))
{
socket.SysMessage( GetDictionaryEntry( 2072, socket.language )); // You can only use the interior decorator on locked down items.
+ socket.CustomTarget( 3 );
return;
}
diff --git a/data/js/item/masonrybook.js b/data/js/item/masonrybook.js
new file mode 100644
index 000000000..f4b664f3f
--- /dev/null
+++ b/data/js/item/masonrybook.js
@@ -0,0 +1,35 @@
+///
+// @ts-check
+/** @type { ( user: Character, iUsing: Item ) => boolean } */
+function onUseChecked( pUser, iUsed )
+{
+ var socket = pUser.socket;
+ var itemOwner = GetPackOwner( iUsed, 0 );
+
+ if( socket && iUsed && iUsed.isItem )
+ {
+ if( itemOwner == null || itemOwner.serial != pUser.serial )
+ {
+ socket.SysMessage( GetDictionaryEntry( 1763, socket.language )); // That item must be in your backpack before it can be used.
+ }
+ else if( pUser.skills[0] < 1000 )
+ {
+ socket.SysMessage( GetDictionaryEntry( 6298, socket.language )); // Only a Grandmaster Carpenter can learn from this book.
+ }
+ else if( pUser.GetTag( "StoneCrafting" ) == 1 )
+ {
+ socket.SysMessage( GetDictionaryEntry( 6302, socket.language )); // You have already learned this information.
+ }
+ else if( iUsed.movable == 2 || iUsed.movable == 3 )
+ {
+ socket.SysMessage( GetDictionaryEntry( 774, socket.language )); //That is locked down and you cannot use it
+ }
+ else
+ {
+ pUser.SetTag( "StoneCrafting", 1 );
+ socket.SysMessage( GetDictionaryEntry( 6299, socket.language )); // You have learned to make items from stone. You will need miners to gather stones for you to make these items.
+ iUsed.Delete();
+ }
+ }
+ return false;
+}
diff --git a/data/js/item/sandminingbook.js b/data/js/item/sandminingbook.js
index 89f05f69a..7fa887671 100644
--- a/data/js/item/sandminingbook.js
+++ b/data/js/item/sandminingbook.js
@@ -22,7 +22,7 @@ function onUseChecked( pUser, iUsed )
}
else if( iUsed.movable == 2 || iUsed.movable == 3 )
{
- pSocket.SysMessage( GetDictionaryEntry( 774, pSocket.Language )); //That is locked down and you cannot use it
+ pSocket.SysMessage( GetDictionaryEntry( 774, pSocket.language )); //That is locked down and you cannot use it
}
else
{
diff --git a/data/js/item/stoneminingbook.js b/data/js/item/stoneminingbook.js
index 60cbec328..1b9408b6d 100644
--- a/data/js/item/stoneminingbook.js
+++ b/data/js/item/stoneminingbook.js
@@ -22,12 +22,12 @@ function onUseChecked( pUser, iUsed )
}
else if( iUsed.movable == 2 || iUsed.movable == 3 )
{
- pSocket.SysMessage( GetDictionaryEntry( 774, pSocket.Language )); //That is locked down and you cannot use it
+ pSocket.SysMessage( GetDictionaryEntry( 774, pSocket.language )); //That is locked down and you cannot use it
}
else
{
pUser.SetTag( "GatheringStone", 1 );
- pSocket.SysMessage( GetDictionaryEntry( 9411, pSocket.Language )); // You have learned to mine for stones. Target mountains when mining to find stones.
+ pSocket.SysMessage( GetDictionaryEntry( 9411, pSocket.language )); // You have learned to mine for stones. Target mountains when mining to find stones.
iUsed.Delete();
}
}
diff --git a/data/js/jse_fileassociations.scp b/data/js/jse_fileassociations.scp
index 5b993348f..3c800bcec 100644
--- a/data/js/jse_fileassociations.scp
+++ b/data/js/jse_fileassociations.scp
@@ -243,6 +243,7 @@
4034=skill/craft/cooking.js
4035=skill/craft/cartography.js
4036=skill/craft/glassblowing.js
+4037=skill/craft/masonry.js
4050=skill/mining.js
4055=skill/snooping.js
@@ -316,7 +317,7 @@
5052=item/sandminingbook.js
5053=item/stoneminingbook.js
5054=item/glassblowingbook.js
-//reserved for granitebook
+5055=item/masonrybook.js
5056=item/crystalball.js
5057=item/ballofpetsummoning.js
5058=item/bagofsending.js
@@ -328,6 +329,9 @@
5064=item/runicatlas.js
5070=item/dawns_music_box.js
+5080=item/buildingcraftables.js
+5081=item/buildingcraftables_doors.js
+
5300=item/etherealstatuettes.js
5301=npc/pets/dismountetherealmount.js
5302=item/retouchingtool.js
diff --git a/data/js/server/house/houseCommands.js b/data/js/server/house/houseCommands.js
index 1fb81fab9..e3ccf1a58 100644
--- a/data/js/server/house/houseCommands.js
+++ b/data/js/server/house/houseCommands.js
@@ -1125,15 +1125,17 @@ function onCallback8( pSocket, myTarget )
var StrangeByte = pSocket.GetWord( 1 );
if( StrangeByte === 0 && ValidateObject( myTarget ) && myTarget.isItem )
{
+ var isDoor = ( myTarget.GetTag( "BuildingCraftableDoor" ) === 1 );
+
if( myTarget.type == 87 )
{
pSocket.SysMessage( GetDictionaryEntry( 1888, pSocket.language )); // You cannot lock down trash barrels.
}
- else if( myTarget.movable == 3 )
+ else if( !isDoor && ( myTarget.movable == 3 || myTarget.GetTag( "BuildingCraftable" ) == 1 ))
{
pSocket.SysMessage( GetDictionaryEntry( 1889, pSocket.language )); // That is already locked down!
}
- else if( myTarget.type == 12 || myTarget.type == 13 || myTarget.type == 203 || myTarget.id == 0x0BD2 ||
+ else if( !isDoor && myTarget.type == 12 || myTarget.type == 13 || myTarget.type == 203 || myTarget.id == 0x0BD2 ||
myTarget.movable == 2 || myTarget.movable == 3 || myTarget.id == 0x3996 || myTarget.id == 0x398C ||
myTarget.id == 0x3915 || myTarget.id == 0x3920 || myTarget.id == 0x3979 || myTarget.id == 0x3967 ||
myTarget.id == 0x3956 || myTarget.id == 0x3946 )
@@ -1173,7 +1175,7 @@ function onCallback8( pSocket, myTarget )
// Check if item blocks movement and is too close to a door
// 6 = TF_BLOCKING
- if( CheckTileFlag( myTarget.id, 6 ))
+ if( !isDoor && CheckTileFlag( myTarget.id, 6 ))
{
// Check for nearby doors
var foundDoor = AreaItemFunction( "CheckForNearbyDoors", myTarget, 3, pSocket );
diff --git a/data/js/server/misc/furniture_smartturn.js b/data/js/server/misc/furniture_smartturn.js
index 7bcf3e35e..1fd25f1f5 100644
--- a/data/js/server/misc/furniture_smartturn.js
+++ b/data/js/server/misc/furniture_smartturn.js
@@ -59,7 +59,7 @@ function LookForSittingPlayers( srcItem, trgChar, pSock )
function onDrop( iDropped, pDropper )
{
SmartTurn( iDropped );
- return true;
+ return 1;
}
// Turn supported furniture clockwise, based on previous direction
@@ -81,110 +81,302 @@ function SmartTurn( iTurn )
// our partner is listed! this is an implicit subtraction operation
iTurn.id--;
}// Special Turn for glass made items
- else if (iTurn.id == 0x1810 )//spinning hourglass
+ else if( iTurn.id == 0x1810 )//spinning hourglass
{
iTurn.id = 0x1811;
}
- else if (iTurn.id == 0x1811 || iTurn.id == 0x1812 || iTurn.id == 0x1813 || iTurn.id == 0x1814 || iTurn.id == 0x1815 || iTurn.id == 0x1816 || iTurn.id == 0x1817 || iTurn.id == 0x1818 || iTurn.id == 0x1819 || iTurn.id == 0x181A || iTurn.id == 0x181B || iTurn.id == 0x181C)//spinning hourglass animations
+ else if( iTurn.id == 0x1811 || iTurn.id == 0x1812 || iTurn.id == 0x1813 || iTurn.id == 0x1814 || iTurn.id == 0x1815 || iTurn.id == 0x1816 || iTurn.id == 0x1817 || iTurn.id == 0x1818 || iTurn.id == 0x1819 || iTurn.id == 0x181A || iTurn.id == 0x181B || iTurn.id == 0x181C )//spinning hourglass animations
{
iTurn.id = 0x1810;
}
- else if (iTurn.id == 0x185d)// Full Vials
+ else if( iTurn.id == 0x185d )// Full Vials
{
iTurn.id = 0x185e;
}
- else if (iTurn.id == 0x185e)// Full Vials
+ else if( iTurn.id == 0x185e )// Full Vials
{
iTurn.id = 0x185d;
}
- else if (iTurn.id == 0x185b)// Empty Vials
+ else if( iTurn.id == 0x185b )// Empty Vials
{
iTurn.id = 0x185c;
}
- else if (iTurn.id == 0x185c)// Full Vials
+ else if( iTurn.id == 0x185c )// Full Vials
{
iTurn.id = 0x185b;
}
- else if (iTurn.id == 0x182a)// Medium Flask
+ else if( iTurn.id == 0x182a )// Medium Flask
{
iTurn.id = 0x182b;
}
- else if (iTurn.id == 0x182b)// Medium Flask
+ else if( iTurn.id == 0x182b )// Medium Flask
{
iTurn.id = 0x182c;
}
- else if (iTurn.id == 0x182c)// Medium Flask
+ else if( iTurn.id == 0x182c )// Medium Flask
{
iTurn.id = 0x182d;
}
- else if (iTurn.id == 0x182d)// Medium Flask
+ else if( iTurn.id == 0x182d )// Medium Flask
{
iTurn.id = 0x182a;
}
- else if (iTurn.id == 0x182e)// Small Flask
+ else if( iTurn.id == 0x182e )// Small Flask
{
iTurn.id = 0x182f;
}
- else if (iTurn.id == 0x182f)// Small Flask
+ else if( iTurn.id == 0x182f )// Small Flask
{
iTurn.id = 0x1830;
}
- else if (iTurn.id == 0x1830)// Small Flask
+ else if( iTurn.id == 0x1830 )// Small Flask
{
iTurn.id = 0x1831;
}
- else if (iTurn.id == 0x1831)// Small Flask
+ else if( iTurn.id == 0x1831 )// Small Flask
{
iTurn.id = 0x182e;
}
- else if (iTurn.id == 0x1832)// Curved Flask
+ else if( iTurn.id == 0x1832 )// Curved Flask
{
iTurn.id = 0x1833;
}
- else if (iTurn.id == 0x1833)// Curved Flask
+ else if( iTurn.id == 0x1833 )// Curved Flask
{
iTurn.id = 0x1834;
}
- else if (iTurn.id == 0x1834)// Curved Flask
+ else if( iTurn.id == 0x1834 )// Curved Flask
{
iTurn.id = 0x1835;
}
- else if (iTurn.id == 0x1835)// Curved Flask
+ else if( iTurn.id == 0x1835 )// Curved Flask
{
iTurn.id = 0x1836;
}
- else if (iTurn.id == 0x1836)// Curved Flask
+ else if( iTurn.id == 0x1836 )// Curved Flask
{
iTurn.id = 0x1837;
}
- else if (iTurn.id == 0x1837)// Curved Flask
+ else if( iTurn.id == 0x1837 )// Curved Flask
{
iTurn.id = 0x1832;
}
- else if (iTurn.id == 0x1838)// Large Flask
+ else if( iTurn.id == 0x1838 )// Large Flask
{
iTurn.id = 0x1839;
}
- else if (iTurn.id == 0x1839)// Large Flask
+ else if( iTurn.id == 0x1839 )// Large Flask
{
iTurn.id = 0x183a;
}
- else if (iTurn.id == 0x1838)// Large Flask
+ else if( iTurn.id == 0x1838 )// Large Flask
{
iTurn.id = 0x183b;
}
- else if (iTurn.id == 0x183b)// Large Flask 2
+ else if( iTurn.id == 0x183b )// Large Flask 2
{
iTurn.id = 0x183c;
}
- else if (iTurn.id == 0x183c)// Large Flask 2
+ else if( iTurn.id == 0x183c )// Large Flask 2
{
iTurn.id = 0x183d;
}
- else if (iTurn.id == 0x183d)// Large Flask 2
+ else if( iTurn.id == 0x183d )// Large Flask 2
{
iTurn.id = 0x183b;
}
+ else if( iTurn.id == 0x01CF ) // RoughWindowless
+ {
+ iTurn.id = 0x01D0;
+ }
+ else if( iTurn.id == 0x01D0 )
+ {
+ iTurn.id = 0x01D1;
+ }
+ else if( iTurn.id == 0x01D1 )
+ {
+ iTurn.id = 0x01D0;
+ }
+ else if( iTurn.id == 0x01D2 )
+ {
+ iTurn.id = 0x01CF;
+ }
+ else if( iTurn.id == 0x01D3 ) // RoughWindow
+ {
+ iTurn.id = 0x01D4;
+ }
+ else if( iTurn.id == 0x01D4 )
+ {
+ iTurn.id = 0x01D3;
+ }
+ else if( iTurn.id == 0x01D5 ) // RoughArch
+ {
+ iTurn.id = 0x01D6;
+ }
+ else if( iTurn.id == 0x01D6 )
+ {
+ iTurn.id = 0x01D7;
+ }
+ else if( iTurn.id == 0x01D7 )
+ {
+ iTurn.id = 0x01D8;
+ }
+ else if( iTurn.id == 0x01D8 )
+ {
+ iTurn.id = 0x01D9;
+ }
+ else if( iTurn.id == 0x01D9 )
+ {
+ iTurn.id = 0x01D5;
+ }
+ else if( iTurn.id == 0x01DB ) // RoughRoundedArch
+ {
+ iTurn.id = 0x01DC;
+ }
+ else if( iTurn.id == 0x01DC )
+ {
+ iTurn.id = 0x01DD;
+ }
+ else if( iTurn.id == 0x01DD )
+ {
+ iTurn.id = 0x01DE;
+ }
+ else if( iTurn.id == 0x01DE )
+ {
+ iTurn.id = 0x01DF;
+ }
+ else if( iTurn.id == 0x01DF )
+ {
+ iTurn.id = 0x01DB;
+ }
+ else if( iTurn.id == 0x01E0 ) // RoughSmallArch
+ {
+ iTurn.id = 0x01E1;
+ }
+ else if( iTurn.id == 0x01E1 )
+ {
+ iTurn.id = 0x01E2;
+ }
+ else if( iTurn.id == 0x01E2 )
+ {
+ iTurn.id = 0x01E3;
+ }
+ else if( iTurn.id == 0x01E3 )
+ {
+ iTurn.id = 0x01E4;
+ }
+ else if( iTurn.id == 0x01E4 )
+ {
+ iTurn.id = 0x01E5;
+ }
+ else if( iTurn.id == 0x01E5 )
+ {
+ iTurn.id = 0x01E0;
+ }
+ else if( iTurn.id == 0x01E6 ) // RoughAngledPillar
+ {
+ iTurn.id = 0x01E7;
+ }
+ else if( iTurn.id == 0x01E7 )
+ {
+ iTurn.id = 0x01E6;
+ }
+ else if( iTurn.id == 0x01E8 ) // ShortRough
+ {
+ iTurn.id = 0x01E9;
+ }
+ else if( iTurn.id == 0x01E9 )
+ {
+ iTurn.id = 0x01EA;
+ }
+ else if( iTurn.id == 0x01EA )
+ {
+ iTurn.id = 0x01E9;
+ }
+ else if( iTurn.id == 0x01EB )
+ {
+ iTurn.id = 0x01E8;
+ }
+ else if( iTurn.id == 0x0789 ) // Rough steps
+ {
+ iTurn.id = 0x078A;
+ }
+ else if( iTurn.id == 0x078A )
+ {
+ iTurn.id = 0x078B;
+ }
+ else if( iTurn.id == 0x078B )
+ {
+ iTurn.id = 0x078C;
+ }
+ else if( iTurn.id == 0x078C )
+ {
+ iTurn.id = 0x0789;
+ }
+ else if( iTurn.id == 0x078D ) // Rough corner steps
+ {
+ iTurn.id = 0x078E;
+ }
+ else if( iTurn.id == 0x078E )
+ {
+ iTurn.id = 0x078F;
+ }
+ else if( iTurn.id == 0x078F )
+ {
+ iTurn.id = 0x0790;
+ }
+ else if( iTurn.id == 0x0790 )
+ {
+ iTurn.id = 0x078D;
+ }
+ else if( iTurn.id == 0x0791 ) // Rough rounded corner steps
+ {
+ iTurn.id = 0x0792;
+ }
+ else if( iTurn.id == 0x0792 )
+ {
+ iTurn.id = 0x0793;
+ }
+ else if( iTurn.id == 0x0793 )
+ {
+ iTurn.id = 0x0794;
+ }
+ else if( iTurn.id == 0x0794 )
+ {
+ iTurn.id = 0x0791;
+ }
+ else if( iTurn.id == 0x0795 ) // Rough inset steps
+ {
+ iTurn.id = 0x0796;
+ }
+ else if( iTurn.id == 0x0796 )
+ {
+ iTurn.id = 0x0797;
+ }
+ else if( iTurn.id == 0x0797 )
+ {
+ iTurn.id = 0x0798;
+ }
+ else if( iTurn.id == 0x0798 )
+ {
+ iTurn.id = 0x0795;
+ }
+ else if( iTurn.id == 0x0799 ) // Rough rounded inset steps
+ {
+ iTurn.id = 0x079A;
+ }
+ else if( iTurn.id == 0x079A )
+ {
+ iTurn.id = 0x079B;
+ }
+ else if( iTurn.id == 0x079B )
+ {
+ iTurn.id = 0x079C;
+ }
+ else if( iTurn.id == 0x079C )
+ {
+ iTurn.id = 0x0799;
+ }
}
var divorcedPairs = [ // they are paired up, but more than one index apart and out of order, [hex ID, add or subtract distance]
diff --git a/data/js/skill/craft/alchemy.js b/data/js/skill/craft/alchemy.js
index 5a954c492..4069092f9 100644
--- a/data/js/skill/craft/alchemy.js
+++ b/data/js/skill/craft/alchemy.js
@@ -1,241 +1,555 @@
///
// @ts-check
-const textHue = 0x480; // Color of the text.
-const scriptID = 4028; // Use this to tell the gump what script to close.
-const gumpDelay = 2000; // Timer for the gump to reapear after crafting.
-const itemDetailsScriptID = 4026;
-const craftGumpID = 4027;
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// The section below is the tables for each page.
-// All you have to do is add the item to your dictionary
-// and then list the dictionary number in the right page and it will
-// add it to the crafting gump.
-///////////////////////////////////////////////////////////////////////////////////////////
-
-const myPage = [
- // Page 1 - Healing and Curative
- [10908, 10909, 10910, 10911, 10912, 10913, 10914, 10915],
+const textHue = 0x480; // Color of the text.
+const alchemyID = 4028; // Script ID for this alchemy gump
+const gumpDelay = 2000; // Delay (ms) before gump reappears after crafting
+const itemDetailsScriptID = 4026; // Generic item details gump
+const craftGumpID = 4027; // Shared crafting menu frame
+const itemsPerPage = 10; // Items per subpage
+const displayUnlearnedRecipes = true; // For future recipe use
+const coreShardEra = EraStringToNum( GetServerSetting( "CoreShardEra" ));
+const alchemySkillID = 0; // Skill ID: Alchemy
+
+// o--------------------------------------------------------------------------o
+// | AlchemyMap |
+// o--------------------------------------------------------------------------o
+// | Keyed by makeID (create entry ID). |
+// | Each entry: |
+// | dictID - dictionary entry for row text |
+// | page - main category page (1..4) |
+// | timerID - which page timer should reopen |
+// | skill - skill used (default: alchemySkillID) |
+// | recipeID?, minEra?, maxEra?, harvest?[] harvestNames? ["cloth" ] |
+// | can be added later |
+// o--------------------------------------------------------------------------o
+// If you want to add multi-reagent potions later
+// (e.g. harvest: [ garlic_dictNumber, ginseng_dictNumber ] or harvestNames: [ "garlic, "ginseng" ]),
+// the same pattern will just work with no script changes.
+
+const AlchemyMap = {
+ // Page 1 - Curative / Refresh
+ 298: { dictID: 10910, page: 1, timerID: 1, harvest: [ 10022 ] }, // Lesser Heal
+ 299: { dictID: 10911, page: 1, timerID: 1, harvest: [ 10022 ] }, // Heal
+ 300: { dictID: 10912, page: 1, timerID: 1, harvest: [ 10022 ] }, // Greater Heal
+ 305: { dictID: 10908, page: 1, timerID: 1, harvest: [ 10025 ] }, // Refresh
+ 306: { dictID: 10909, page: 1, timerID: 1, harvest: [ 10025 ] }, // Total Refresh
+ 292: { dictID: 10913, page: 1, timerID: 1, harvest: [ 10020 ] }, // Lesser Cure
+ 293: { dictID: 10914, page: 1, timerID: 1, harvest: [ 10020 ] }, // Cure
+ 294: { dictID: 10915, page: 1, timerID: 1, harvest: [ 10020 ] }, // Greater Cure
// Page 2 - Enhancement
- [10916, 10917, 10918, 10919, 10920],
+ 290: { dictID: 10916, page: 2, timerID: 2, harvest: [ 10019 ] }, // Agility
+ 291: { dictID: 10917, page: 2, timerID: 2, harvest: [ 10019 ] }, // Greater Agility
+ 295: { dictID: 10918, page: 2, timerID: 2, harvest: [ 10021 ] }, // Strength
+ 296: { dictID: 10919, page: 2, timerID: 2, harvest: [ 10021 ] }, // Greater Strength
+ 297: { dictID: 10920, page: 2, timerID: 2, harvest: [ 10021 ] }, // Night Sight
+
+ // Page 3 - Poison
+ 301: { dictID: 10921, page: 3, timerID: 3, harvest: [ 10024 ] }, // Lesser Poison
+ 302: { dictID: 10922, page: 3, timerID: 3, harvest: [ 10024 ] }, // Poison
+ 303: { dictID: 10923, page: 3, timerID: 3, harvest: [ 10024 ] }, // Greater Poison
+ 304: { dictID: 10924, page: 3, timerID: 3, harvest: [ 10024 ] }, // Deadly Poison
+
+ // Page 4 - Explosive
+ 307: { dictID: 10925, page: 4, timerID: 4, harvest: [ 10026 ] }, // Explosion
+ 308: { dictID: 10926, page: 4, timerID: 4, harvest: [ 10026 ] }, // Greater Explosion
+ 309: { dictID: 10927, page: 4, timerID: 4, harvest: [ 10023 ] } // Conflagration (or w/e 10927 is)
+};
+
+// Fill in defaults (skill, etc)
+(function initAlchemyMap()
+{
+ for( var key in AlchemyMap )
+ {
+ if( !AlchemyMap.hasOwnProperty( key ))
+ continue;
- // Page 3 - Toxic
- [10921, 10922, 10923, 10924],
+ var entry = AlchemyMap[key];
- // Page 4 - Explosives
- [10925, 10926, 10927]
-];
+ if( entry.skill === undefined )
+ entry.skill = alchemySkillID;
+ // If you ever want to drive item detail resources here:
+ // entry.harvest = [ , , ... ];
+ // For now, the detail gump will infer resources directly from DFN create entries.
+ }
+})();
+
+// o--------------------------------------------------------------------------o
+// | PageX() - build a page of alchemy items |
+// o--------------------------------------------------------------------------o
+/** @type { ( socket: Socket, pUser: Character, pageNum: number ) => void } */
function PageX( socket, pUser, pageNum )
{
- // Pages 1 - 4
- var myGump = new Gump;
- pUser.SetTempTag( "page", pageNum );
- TriggerEvent( craftGumpID, "CraftingGumpMenu", myGump, socket );
- for( var i = 0; i < myPage[pageNum - 1].length; i++ )
+ if( !socket || !ValidateObject( pUser ))
+ return;
+
+ var pageItems;
+
+ // Special Last Ten page
+ if( pageNum == 999 )
+ {
+ var lastTenRaw = pUser.GetTempTag( "LastTenAlchemy" ) || "";
+ var split = lastTenRaw.split( "," );
+ pageItems = [];
+
+ for( var i = 0; i < split.length; i++ )
+ {
+ var val = parseInt( split[i] );
+ if( !isNaN( val ))
+ pageItems.push( val ); // makeID itself
+ }
+ }
+ else
{
- var index = i % 10;
- if( index == 0 )
+ // Collect all makeIDs for this page
+ var makeIDs = [];
+ for( var key in AlchemyMap )
+ {
+ if( !AlchemyMap.hasOwnProperty( key ))
+ continue;
+
+ var makeID = parseInt( key );
+ var data = AlchemyMap[makeID];
+ if( !data || data.page != pageNum )
+ continue;
+
+ makeIDs.push( makeID );
+ }
+
+ // Sort by dictID so order matches dictionary sequence
+ makeIDs.sort( function( a, b )
+ {
+ var ea = AlchemyMap[a];
+ var eb = AlchemyMap[b];
+ if( ea && eb )
+ return ( ea.dictID || 0 ) - ( eb.dictID || 0 );
+ return a - b;
+ });
+
+ // Era / recipe filtering (no recipes yet, but keep hook)
+ pageItems = [];
+ for( var k = 0; k < makeIDs.length; k++ )
+ {
+ var id = makeIDs[k];
+ var data2 = AlchemyMap[id];
+ if( !data2 )
+ continue;
+
+ var needsRecipe = data2.recipeID;
+ var showAll = displayUnlearnedRecipes;
+
+ if( eraOK( data2 ) && ( !needsRecipe || showAll || HasLearnedRecipe( pUser, needsRecipe )))
+ pageItems.push( id );
+ }
+
+ // Fallback: if no items on this page and it's not page 1, go to page 1
+ if( pageItems.length == 0 && pageNum != 1 )
{
- if( i > 0 )
+ pageNum = 1;
+
+ makeIDs = [];
+ for( var key2 in AlchemyMap )
+ {
+ if( !AlchemyMap.hasOwnProperty( key2 ))
+ continue;
+
+ var mid2 = parseInt( key2 );
+ var d3 = AlchemyMap[mid2];
+ if( !d3 || d3.page != 1 )
+ continue;
+
+ makeIDs.push( mid2 );
+ }
+
+ makeIDs.sort( function( a, b )
+ {
+ var ea2 = AlchemyMap[a];
+ var eb2 = AlchemyMap[b];
+ if( ea2 && eb2 )
+ return ( ea2.dictID || 0 ) - ( eb2.dictID || 0 );
+ return a - b;
+ });
+
+ pageItems = [];
+ for( var m = 0; m < makeIDs.length; m++ )
{
- myGump.AddButton( 370, 260, 4005, 4007, 0, ( i / 10 ) + 1, 0 );
- myGump.AddHTMLGump( 405, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10100, socket.language ) + "" );// NEXT PAGE
+ var id2 = makeIDs[m];
+ var data4 = AlchemyMap[id2];
+ if( !data4 )
+ continue;
+
+ var needsRecipe2 = data4.recipeID;
+ var showAll2 = displayUnlearnedRecipes;
+
+ if( eraOK( data4 ) && ( !needsRecipe2 || showAll2 || HasLearnedRecipe( pUser, needsRecipe2 )))
+ pageItems.push( id2 );
}
+ }
+ }
+
+ // Subpage handling
+ var subPage = pUser.GetTempTag( "subPage" );
+ var totalSubPages = Math.ceil( pageItems.length / itemsPerPage );
+
+ if( totalSubPages < 1 )
+ totalSubPages = 1;
+ if( subPage < 1 )
+ subPage = 1;
+ if( subPage > totalSubPages )
+ subPage = totalSubPages;
+
+ pUser.SetTempTag( "page", pageNum );
+ pUser.SetTempTag( "subPage", subPage );
+
+ var startIndex = ( subPage - 1 ) * itemsPerPage;
+ var endIndex = Math.min( startIndex + itemsPerPage, pageItems.length );
+
+ if( startIndex >= pageItems.length )
+ {
+ subPage = 1;
+ startIndex = 0;
+ endIndex = Math.min( itemsPerPage, pageItems.length );
+ pUser.SetTempTag( "subPage", subPage );
+ }
- myGump.AddPage(( i / 10 ) + 1 );
+ var alchGump = new Gump;
+ TriggerEvent( craftGumpID, "CraftingGumpMenu", alchGump, socket );
+ alchGump.AddPage( 1 );
- if( i > 0 )
+ for( var j = startIndex; j < endIndex; j++ )
+ {
+ var index = j - startIndex;
+ var makeID = pageItems[j];
+ var entryText;
+ var buttonID = makeID; // use makeID directly as buttonID
+
+ var data5 = AlchemyMap[makeID];
+
+ if( !data5 )
+ {
+ entryText = "[Missing MakeID: " + makeID + "]";
+ }
+ else
+ {
+ if( data5.customName )
+ {
+ entryText = data5.customName;
+ }
+ else if( data5.dictID )
+ {
+ entryText = GetDictionaryEntry( data5.dictID, socket.language );
+ if( !entryText || entryText === "" )
+ entryText = "[Missing EntryID: " + data5.dictID + "]";
+ }
+ else
{
- myGump.AddButton( 220, 260, 4014, 4015, 0, i / 10, 0 );
- myGump.AddHTMLGump( 255, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10101, socket.language ) + "" );// PREV PAGE
+ entryText = "[Unnamed Item: " + makeID + "]";
}
}
- myGump.AddButton( 220, 60 + ( index * 20 ), 4005, 4007, 1, 0, ( 100 * pageNum ) + i );
- myGump.AddText( 255, 60 + ( index * 20 ), textHue, GetDictionaryEntry( myPage[pageNum - 1][i], socket.language ));
+ // Craft button uses makeID
+ alchGump.AddButton( 220, 60 + ( index * 20 ), 4005, 4007, 1, 0, buttonID );
+ alchGump.AddText( 255, 60 + ( index * 20 ), textHue, entryText );
+
+ // Detail button: 20000 + makeID (same pattern as glassblowing)
+ alchGump.AddButton( 480, 60 + ( index * 20 ), 4011, 4012, 1, 0, 20000 + buttonID );
+ }
+
+ // Prev subpage
+ if( subPage > 1 )
+ {
+ alchGump.AddButton( 220, 260, 4014, 4015, 1, 0, 8000 + ( subPage - 1 ));
+ alchGump.AddHTMLGump( 255, 263, 100, 18, false, false,
+ "" + GetDictionaryEntry( 10101, socket.language ) + "" ); // PREV PAGE
+ }
- myGump.AddButton( 480, 60 + ( index * 20 ), 4011, 4012, 1, 0, ( 2000 + ( 100 * pageNum )) + i );
+ // Next subpage
+ if( subPage < totalSubPages )
+ {
+ alchGump.AddButton( 370, 260, 4005, 4007, 1, 0, 9000 + ( subPage + 1 ));
+ alchGump.AddHTMLGump( 405, 263, 100, 18, false, false,
+ "" + GetDictionaryEntry( 10100, socket.language ) + "" ); // NEXT PAGE
}
- myGump.Send( socket );
- myGump.Free();
+
+ alchGump.Send( socket );
+ alchGump.Free();
}
-/** @type { ( tObject: BaseObject, timerId: number ) => void } */
+// o--------------------------------------------------------------------------o
+// | onTimer - reopen last page after crafting |
+// o--------------------------------------------------------------------------o
+/** @type { ( pUser: Character, timerID: number ) => void } */
function onTimer( pUser, timerID )
{
if( !ValidateObject( pUser ))
return;
- var socket = pUser.socket;
+ var pSocket = pUser.socket;
+ if( pSocket == null )
+ return;
- switch( timerID )
+ if( timerID >= 1 && timerID <= 8 )
{
- case 1: // Page 1
- case 2: // Page 2
- case 3: // Page 3
- case 4: // Page 4
- TriggerEvent( scriptID, "PageX", socket, pUser, timerID );
- break;
- default:
- break;
+ PageX( pSocket, pUser, timerID );
+ }
+ else if( timerID == 999 )
+ {
+ PageX( pSocket, pUser, 999 );
}
}
-/** @type { ( myObj: Socket, pressed: number, gump: GumpData ) => void } */
-function onGumpPress( pSock, pButton, gumpData )
+// o--------------------------------------------------------------------------o
+// | onGumpPress - navigation, Make Last, craft & details |
+// o--------------------------------------------------------------------------o
+/** @type { ( socket: Socket, pButton: number, gumpData: GumpData ) => void } */
+function onGumpPress( socket, pButton, gumpData )
{
- var pUser = pSock.currentChar;
+ if( socket == null )
+ return;
- // Don't continue if character is invalid, or worse... dead!
+ var pUser = socket.currentChar;
if( !ValidateObject( pUser ) || pUser.dead )
return;
- // Don't continue if player no longer has access to the crafting tool
- var bItem = pSock.tempObj;
- if( !ValidateObject( bItem ) || !pUser.InRange( bItem, 3 ))
+ var tool = socket.tempObj;
+ if( !ValidateObject( tool ) || !pUser.InRange( tool, 3 ))
{
- pSock.SysMessage( GetDictionaryEntry( 461, pSock.language )); // You are too far away.
+ socket.SysMessage( GetDictionaryEntry( 461, socket.language )); // You are too far away.
return;
}
- var gumpID = scriptID + 0xffff;
- var makeID = 0;
- var itemDetailsID = 0;
- var timerID = 0;
+ if( tool.movable == 3 )
+ {
+ socket.SysMessage( GetDictionaryEntry( 6031, socket.language )); // That is locked down and cannot be used.
+ return;
+ }
- if(( pButton >= 100 && pButton <= 402 ) || pButton == 5000 )
+ var packOwner = GetPackOwner( tool, 0 );
+ if( ValidateObject( packOwner ))
{
- if( pButton == 5000 )
+ if( packOwner.serial != pUser.serial )
{
- // Make Last button
- pButton = pUser.GetTempTag( "MAKELAST" );
- }
- else
- {
- pUser.SetTempTag( "MAKELAST", pButton );
+ socket.SysMessage( GetDictionaryEntry( 6032, socket.language )); // That is not in your pack.
+ return;
}
}
+ else
+ {
+ socket.SysMessage( GetDictionaryEntry( 6022, socket.language )); // You must have that item in your pack to use it.
+ return;
+ }
+
+ var gumpID = alchemyID + 0xffff;
- switch( pButton )
+ // Subpage back / forward
+ if( pButton >= 8001 && pButton < 9000 )
{
- case 0:
- pUser.SetTempTag( "MAKELAST", null );
- pUser.SetTempTag( "CRAFT", null )
- pSock.CloseGump( gumpID, 0 );
- break;// abort and do nothing
- case 1: // Page 1
- case 2: // Page 2
- case 3: // Page 3
- case 4: // Page 4
- pSock.CloseGump( gumpID, 0 );
- TriggerEvent( scriptID, "PageX", pSock, pUser, pButton );
- break;
- // Make Items
- case 100: // Refresh
- makeID = 305; timerID = 1; break;
- case 101: // Greater Refresh
- makeID = 306; timerID = 1; break;
- case 102: // Lesser Heal
- makeID = 298; timerID = 1; break;
- case 103: // Heal
- makeID = 299; timerID = 1; break;
- case 104: // Greater Heal
- makeID = 300; timerID = 1; break;
- case 105: // Lesser Cure
- makeID = 292; timerID = 1; break;
- case 106: // Cure
- makeID = 293; timerID = 1; break;
- case 107: // Greater Cure
- makeID = 294; timerID = 1; break;
- case 200: // Agility
- makeID = 290; timerID = 2; break;
- case 201: // Greater Agility
- makeID = 291; timerID = 2; break;
- case 202: // Night Sight
- makeID = 309; timerID = 2; break;
- case 203: // Strength
- makeID = 307; timerID = 2; break;
- case 204: // Greater Strength
- makeID = 308; timerID = 2; break;
- case 300: // Lesser Poison
- makeID = 301; timerID = 3; break;
- case 301: // Poison
- makeID = 302; timerID = 3; break;
- case 302: // Greater Poison
- makeID = 303; timerID = 3; break;
- case 303: // Deadly Poison
- makeID = 304; timerID = 3; break;
- case 400: // Lesser Explosion
- makeID = 295; timerID = 4; break;
- case 401: // Explosion
- makeID = 296; timerID = 4; break;
- case 402: // Greater Explosion
- makeID = 297; timerID = 4; break;
- // Show Item Details
- case 2100: // Item Details - Refresh
- itemDetailsID = 305; break;
- case 2101: // Item Details - Greater Refreshment
- itemDetailsID = 306; break;
- case 2102: // Item Details - Lesser Heal
- itemDetailsID = 298; break;
- case 2103: // Item Details - Heal
- itemDetailsID = 299; break;
- case 2104: // Item Details - Greater Heal
- itemDetailsID = 300; break;
- case 2105: // Item Details - Lesser Cure
- itemDetailsID = 292; break;
- case 2106: // Item Details - Cure
- itemDetailsID = 293; break;
- case 2107: // Item Details - Greater Cure
- itemDetailsID = 294; break;
- case 2200: // Item Details - Agility
- itemDetailsID = 290; break;
- case 2201: // Item Details - Greater Agility
- itemDetailsID = 291; break;
- case 2202: // Item Details - Night Sight
- itemDetailsID = 309; break;
- case 2203: // Item Details - Strength
- itemDetailsID = 307; break;
- case 2204: // Item Details - Greater Strength
- itemDetailsID = 308; break;
- case 2300: // Item Details - Lesser Poison
- itemDetailsID = 301; break;
- case 2301: // Item Details - Poison
- itemDetailsID = 302; break;
- case 2302: // Item Details - Greater Poison
- itemDetailsID = 303; break;
- case 2303: // Item Details - Deadly Poison
- itemDetailsID = 304; break;
- case 2400: // Item Details - Lesser Explosion
- itemDetailsID = 295; break;
- case 2401: // Item Details - Explosion
- itemDetailsID = 296; break;
- case 2402: // Item Details - Greater Explosion
- itemDetailsID = 297; break;
- default:
- break;
+ var subPage = pButton - 8000;
+ var pageNum = pUser.GetTempTag( "page" );
+ pUser.SetTempTag( "subPage", subPage );
+ PageX( socket, pUser, pageNum );
+ return;
+ }
+
+ if( pButton >= 9001 && pButton < 10000 )
+ {
+ var subPage2 = pButton - 9000;
+ var pageNum2 = pUser.GetTempTag( "page" );
+ pUser.SetTempTag( "subPage", subPage2 );
+ PageX( socket, pUser, pageNum2 );
+ return;
+ }
+
+ // Page tabs (Alchemy has 4 categories, but using <=8 is harmless)
+ if( pButton >= 1 && pButton <= 8 )
+ {
+ pUser.SetTempTag( "page", pButton );
+ pUser.SetTempTag( "subPage", 1 );
+ PageX( socket, pUser, pButton );
+ return;
+ }
+
+ // Last Ten
+ if( pButton == 11000 )
+ {
+ pUser.SetTempTag( "page", 999 );
+ pUser.SetTempTag( "subPage", 1 );
+ PageX( socket, pUser, 999 );
+ return;
}
- if( makeID != 0 )
+ // Close gump
+ if( pButton == 0 )
{
- MakeItem( pSock, pUser, makeID );
+ pUser.SetTempTag( "MakeLast_Alchemy", null );
+ pUser.SetTempTag( "CRAFT", null );
+ socket.CloseGump( gumpID, 0 );
+ return;
+ }
+
+ var makeID = 0;
+ var timerID = 0;
+
+ // Make Last
+ if( pButton == 5000 )
+ {
+ var last = pUser.GetTempTag( "MakeLast_Alchemy" );
+ if( last )
+ pButton = last;
+ else
+ return;
+ }
+
+ // Craft buttons use makeID directly
+ if( AlchemyMap[pButton] != undefined )
+ {
+ makeID = pButton;
+ var data = AlchemyMap[makeID];
+ timerID = data.timerID || 1;
+
+ if( !eraOK( data ))
+ {
+ socket.SysMessage( "That item is not available in this era." );
+ return;
+ }
+
+ if( data.recipeID && !TriggerEvent( 4022, "NeedRecipe", pUser, data.recipeID ))
+ {
+ socket.SysMessage( "You must learn that recipe from a scroll." );
+ return;
+ }
+
+ pUser.SetTempTag( "MakeLast_Alchemy", makeID );
+
+ MakeItem( socket, pUser, makeID );
+ AddToLastTen( pUser, makeID );
+
if( GetServerSetting( "ToolUseLimit" ))
{
- bItem.usesLeft -= 1;
- if( bItem.usesLeft == 0 && GetServerSetting( "ToolUseBreak" ))
+ tool.usesLeft -= 1;
+ if( tool.usesLeft == 0 && GetServerSetting( "ToolUseBreak" ))
{
- bItem.Delete();
- pSock.SysMessage( GetDictionaryEntry( 10202, pSock.language )); // You have worn out your tool!
- // Play sound effect of tool breaking
+ tool.Delete();
+ socket.SysMessage( GetDictionaryEntry( 10202, socket.language )); // Your tool wears out.
}
- }
- pUser.StartTimer( gumpDelay, timerID, true );
+ }
+
+ pUser.StartTimer( gumpDelay, timerID, alchemyID );
+ return;
}
- else if( itemDetailsID != 0 )
+
+ // Detail buttons: 20000 + makeID
+ if( pButton >= 20000 && pButton < 30000 )
{
- pUser.SetTempTag( "ITEMDETAILS", itemDetailsID );
- TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
+ var detailMakeID = pButton - 20000;
+ var entry = AlchemyMap[detailMakeID];
+
+ if( entry )
+ {
+ // Which item details to show
+ pUser.SetTempTag( "ITEMDETAILS", detailMakeID );
+
+ // Skill used
+ pUser.SetTempTag( "Skill", entry.skill || alchemySkillID );
+
+ // Clear old harvest tags to avoid cross-contamination
+ pUser.SetTempTag( "Harvest", null );
+ pUser.SetTempTag( "Harvest2", null );
+ pUser.SetTempTag( "Harvest3", null );
+ pUser.SetTempTag( "Harvest4", null );
+
+ // Clear old harvest names
+ pUser.SetTempTag( "HarvestName", null );
+ pUser.SetTempTag( "Harvest2Name", null );
+ pUser.SetTempTag( "Harvest3Name", null );
+ pUser.SetTempTag( "Harvest4Name", null );
+
+ // If you later add entry.harvest = [dictID1, dictID2,...], you can push them here
+ if( entry.harvest && entry.harvest.length > 0 )
+ {
+ if( entry.harvest.length >= 1 )
+ pUser.SetTempTag( "Harvest", entry.harvest[0] );
+ if( entry.harvest.length >= 2 )
+ pUser.SetTempTag( "Harvest2", entry.harvest[1] );
+ if( entry.harvest.length >= 3 )
+ pUser.SetTempTag( "Harvest3", entry.harvest[2] );
+ if( entry.harvest.length >= 4 )
+ pUser.SetTempTag( "Harvest4", entry.harvest[3] );
+ }
+
+ // OPTIONAL custom names – these override the dictionary string
+ if( entry.harvestNames && entry.harvestNames.length > 0 )
+ {
+ if( entry.harvestNames.length >= 1 )
+ pUser.SetTempTag( "HarvestName", entry.harvestNames[0] );
+ if( entry.harvestNames.length >= 2 )
+ pUser.SetTempTag( "Harvest2Name", entry.harvestNames[1] );
+ if( entry.harvestNames.length >= 3 )
+ pUser.SetTempTag( "Harvest3Name", entry.harvestNames[2] );
+ if( entry.harvestNames.length >= 4 )
+ pUser.SetTempTag( "Harvest4Name", entry.harvestNames[3] );
+ }
+
+ if( entry.recipeID && entry.recipeID > 0 )
+ pUser.SetTempTag( "needRecipeID", entry.recipeID );
+ else
+ pUser.SetTempTag( "needRecipeID", 0 );
+
+ TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
+ }
+ return;
}
}
+
+// o--------------------------------------------------------------------------o
+// | Last Ten handling |
+// o--------------------------------------------------------------------------o
+function AddToLastTen( pUser, makeID )
+{
+ var raw = pUser.GetTempTag( "LastTenAlchemy" ) || "";
+ var list = raw.split( "," );
+
+ for( var i = 0; i < list.length; i++ )
+ {
+ if( parseInt( list[i] ) == makeID )
+ {
+ list.splice( i, 1 );
+ break;
+ }
+ }
+
+ var newList = [ makeID ];
+ for( var j = 0; j < list.length && newList.length < 10; j++ )
+ {
+ var entry = parseInt( list[j] );
+ if( !isNaN( entry ) && entry > 0 )
+ newList.push( entry );
+ }
+
+ pUser.SetTempTag( "LastTenAlchemy", newList.join( "," ) );
+}
+
+function HasLearnedRecipe( pUser, recipeID )
+{
+ var myData = TriggerEvent( 4022, "ReadRecipeID", pUser );
+ if( !myData || myData.length == 0 )
+ return false;
+
+ for( var i = 0; i < myData.length; i++ )
+ {
+ var data = myData[i].split( "," );
+ if( data[0] == recipeID )
+ return true;
+ }
+ return false;
+}
+
+function eraOK( entry )
+{
+ if( entry.minEra && coreShardEra < EraStringToNum( entry.minEra ))
+ return false;
+ if( entry.maxEra && coreShardEra > EraStringToNum( entry.maxEra ))
+ return false;
+ return true;
+}
\ No newline at end of file
diff --git a/data/js/skill/craft/blacksmithing.js b/data/js/skill/craft/blacksmithing.js
index 8ffa0eba4..ad4c220ee 100644
--- a/data/js/skill/craft/blacksmithing.js
+++ b/data/js/skill/craft/blacksmithing.js
@@ -1,36 +1,161 @@
///
// @ts-check
-const textHue = 0x480; // Color of the text.
-const blacksmithID = 4023; // Use this to tell the gump what script to close.
-const bronzeID = 4015;
-const copperID = 4016;
-const agapiteID = 4017;
-const dullcopperID = 4018;
-const goldID = 4019;
-const shadowironID = 4020;
-const valoriteID = 4021;
-const veriteID = 4022;
-const gumpDelay = 2000; // Timer for the gump to reappear after crafting.
-const ingotDelay = 200; // Timer for the gump to reappear after selecting a ingot.
-const repairDelay = 200; // Timer for the gump to reappear after repairing an item
+const textHue = 0x480; // Color of the text.
+const blacksmithID = 4023; // Script ID used to identify and close this gump
+const gumpDelay = 2000; // Timer for the gump to reappear after crafting.
+const ingotDelay = 200; // Timer for the gump to reappear after selecting an ingot.
+const repairDelay = 200; // Timer for the gump to reappear after repairing an item
const craftGumpID = 4027;
const itemDetailsScriptID = 4026;
- // If enabled, players can craft coloured variants of weapons, though unless the craftItems array
- // is updated with specific create entries for the coloured weapon variants, they'll just be
- // regular weapons with ore colour applied
+const itemsPerPage = 10; // Number of craftable items shown per gump subpage
+const displayUnlearnedRecipes = true; // Show recipes player has not learned (if we add any later)
+const coreShardEra = EraStringToNum( GetServerSetting( "CoreShardEra" ));
+
+// If enabled, players can craft coloured variants of weapons, though unless the craftItems array
+// is updated with specific create entries for the coloured weapon variants, they will just be
+// regular weapons with ore colour applied
const allowColouredWeapons = GetServerSetting( "CraftColouredWeapons" );
-//////////////////////////////////////////////////////////////////////////////////////////
-// The section below is the tables for each page.
-// All you have to do is add the armor or weapon to your dictionary
-// and then list the dictionary number in the right page and it will
-// add it to the crafting gump.
-///////////////////////////////////////////////////////////////////////////////////////////
+const OreTypes = [
+ { nameDict: 10291, hue: 0x0000, minMining: 0 }, // Iron
+ { nameDict: 10203, hue: 0x0973, minMining: 650 }, // Dull Copper
+ { nameDict: 10204, hue: 0x0966, minMining: 700 }, // Shadow Iron
+ { nameDict: 10205, hue: 0x07dd, minMining: 750 }, // Copper
+ { nameDict: 10206, hue: 0x06d6, minMining: 800 }, // Bronze
+ { nameDict: 10207, hue: 0x08a5, minMining: 850 }, // Gold
+ { nameDict: 10208, hue: 0x0979, minMining: 900 }, // Agapite
+ { nameDict: 10209, hue: 0x089f, minMining: 950 }, // Verite
+ { nameDict: 10210, hue: 0x08ab, minMining: 990 } // Valorite
+];
+
+const ScaleTypes = [
+ { nameDict: 20299, hue: 0x0663, minSkill: 0 }, // Red
+ { nameDict: 20300, hue: 0x084d, minSkill: 0 }, // Yellow
+ { nameDict: 20301, hue: 0x0455, minSkill: 0 }, // Black
+ { nameDict: 20302, hue: 0x0851, minSkill: 0 }, // Green
+ { nameDict: 20303, hue: 0x02c2, minSkill: 0 }, // White
+ { nameDict: 20304, hue: 0x0006, minSkill: 0 } // Blue
+ // add more as required
+];
+
+// Optional: if you later decide to make some blacksmith items recipe-locked, we will use this map:
+// BlacksmithMap[buttonID] = { dictID, page, timerID, oreMake: [makeIDByOre], recipeID?, minEra?, maxEra? }
+// o--------------------------------------------------------------------------o
+// | Script - blacksmithing.js |
+// | System - Blacksmith Crafting Gump |
+// o--------------------------------------------------------------------------o
+// | Purpose - |
+// | Provides the blacksmith crafting menu using the same data-driven |
+// | system used by the tailoring script. |
+// | |
+// | All craftable items are defined in tables (myPage, craftItems) and |
+// | then mapped into a MasonryMap structure that controls: |
+// | - Which dictionary entry is shown per row |
+// | - Which "makeID" entry is used for each ore type |
+// | - Which page and timer ID to use when reopening the gump |
+// | - Optional per-item recipe and era requirements |
+// | - Optional per-item custom names for display |
+// | |
+// | The script also handles: |
+// | - iron selection (iron / colored ores) with skill requirements |
+// | - Smelting iron items back into Granite |
+// | - Repairing stone armor and weapons at an anvil |
+// | - Tool wear and runic hammer handling |
+// | - A "Make Last" feature |
+// | - A "Last Ten Masonry" list (optional) |
+// o--------------------------------------------------------------------------o
+// | Data Tables |
+// o--------------------------------------------------------------------------o
+// | myPage |
+// | myPage[pageIndex] = [ dictID1, dictID2, ... ] |
+// | pageIndex 0 => Page 1: Metal Armor |
+// | pageIndex 1 => Page 2: Helmets |
+// | pageIndex 2 => Page 3: Shields |
+// | pageIndex 3 => Page 4: Bladed weapons |
+// | pageIndex 4 => Page 5: Axes |
+// | pageIndex 5 => Page 6: Polearms |
+// | pageIndex 6 => Page 7: Bashing weapons |
+// | |
+// | Each entry is a dictionary ID that will be used to look up the text |
+// | for that row, unless a customName is defined for that button in |
+// | MasonryMap. |
+// | |
+// | craftItems |
+// | craftItems[ironIndex][pageIndex][itemIndex] = makeID |
+// | oreIndex 0 = Iron |
+// | oreIndex 1 = Dull Copper |
+// | oreIndex 2 = Shadow Iron |
+// | oreIndex 3 = Copper |
+// | oreIndex 4 = Bronze |
+// | oreIndex 5 = Gold |
+// | oreIndex 6 = Agapite |
+// | oreIndex 7 = Verite |
+// | oreIndex 8 = Valorite |
+// | |
+// | For each granite type and page, this holds the createEntry ID used by |
+// | MakeItem when the player crafts that item. The same index positions |
+// | on each page line up with the matching entries in myPage. |
+// o--------------------------------------------------------------------------o
+// | BlacksmithMap |
+// o--------------------------------------------------------------------------o
+// | BlacksmithMap is built automatically from myPage and craftItems. |
+// | |
+// | BlacksmithMap[buttonID] = { |
+// | dictID : number, // Base dictionary entry for the row |
+// | page : number, // Main page (1..7, or 999 for Last Ten) |
+// | timerID : number, // Timer ID to reopen same page |
+// | oreMake : number[], // oreMake[oreIndex] = makeID |
+// | customName: string?, // Optional override for display text |
+// | recipeID : number?, // Optional recipe requirement |
+// | minEra : string?, // Optional minimum shard era |
+// | maxEra : string? // Optional maximum shard era |
+// | }; |
+// | |
+// | Button ID mapping (same as original script): |
+// | Page 1 (Metal Armors) : 100..108 |
+// | Page 2 (Helmets) : 200..205 |
+// | Page 3 (Shields) : 300..305 |
+// | Page 4 (Bladed) : 400..407 |
+// | Page 5 (Axes) : 500..506 |
+// | Page 6 (PoleArms) : 600..604 |
+// | Page 7 (Bashing): 700..704 |
+// | |
+// | Custom Names |
+// | To override the display name for a specific row, set customName after |
+// | the BlacksmithMap has been initialized, for example: |
+// | |
+// | BlacksmithMap[100].customName = "Hump vase"; |
+// | |
+// | PageX() will use this order of preference for text: |
+// | 1. entry.customName (if set) |
+// | 2. GetDictionaryEntry(entry.dictID) |
+// | 3. A fallback "[Unnamed Item: buttonID]" |
+// | |
+// | Recipes |
+// | If recipeID is set on a MasonryMap entry, onGumpPress will call: |
+// | TriggerEvent(4022, "NeedRecipe", pUser, recipeID) |
+// | to check if the player has learned that recipe. If not, the craft |
+// | attempt is blocked and a message is shown. |
+// | |
+// | Era Gating |
+// | The script reads the shard era using: |
+// | const coreShardEra = EraStringToNum(GetServerSetting("CoreShardEra")); |
+// | |
+// | If an entry defines minEra or maxEra (strings like "lbr","aos","ml", |
+// | "sa","hs","tol"), eraOK(entry) will ensure the current server era is |
+// | within that range before allowing craft or display. |
+// o--------------------------------------------------------------------------o
+// | Notes |
+// o--------------------------------------------------------------------------o
+// | - To add new blacksmith items, update myPage and craftItems, then |
+// | optionally decorate their BlacksmithMap entries with customName, |
+// | recipeID, minEra, and maxEra. |
+// o--------------------------------------------------------------------------o
const myPage = [
// Page 1 - Metal Armors
- [10217, 10218, 10219, 10220, 10221, 10222, 10223, 10224, 10225, 10226, 10227, 10228, 10229],
+ [10217, 10218, 10219, 10220, 10221, 10222, 10223, 10224, 10225, 10226, 10227, 10228, 10229, 0, 0, 0, 0 ,0],
// Page 2 - Helmets
[10230, 10231, 10232, 10233, 10234],
// Page 3 - Shields
@@ -49,7 +174,7 @@ const craftItems = [
// Iron
[
// Metal Armors
- [ 7, 9, 8, 10, 11, 12, 13, 16, 15, 14, 17, 18, 19 ],
+ [ 7, 9, 8, 10, 11, 12, 13, 16, 15, 14, 17, 18, 19, 367, 368, 369, 370, 371 ],
// Helmets
[ 46, 48, 45, 47, 49 ],
// Shields
@@ -209,99 +334,336 @@ const craftItems = [
]
];
-function PageX( socket, pUser, pageNum )
-{
- // Pages 1 - 7
- var myGump = new Gump;
- pUser.SetTempTag( "page", pageNum );
- TriggerEvent( craftGumpID, "CraftingGumpMenu", myGump, socket );
- var resourceHue = pUser.GetTempTag( "resourceHue" );
+// BlacksmithMap[buttonID] = {
+// dictID: ,
+// page: ,
+// timerID: ,
+// oreMake: [ makeIDForIron, makeIDForDullCopper, ... ], // index is oreID (0..8)
+// // Optional later:
+// // recipeID: ,
+// // minEra: "lbr" / "aos" / "ml" / "sa" / "hs" / "tol",
+// // maxEra: ...
+// };
+
+const BlacksmithMap = {};
- for( var i = 0; i < myPage[pageNum - 1].length; i++ )
+(function initBlacksmithMap()
+{
+ // oreIndex: 0 = iron, 1 = dull copper, ... 8 = valorite
+ for( var oreIndex = 0; oreIndex < craftItems.length; oreIndex++ )
{
- // Don't show weapon entries if player has coloured ingots selected
- if( !allowColouredWeapons && resourceHue > 0 && pageNum > 3 )
- continue;
+ var oreRows = craftItems[oreIndex];
- var index = i % 10;
- if( index == 0 )
+ // pageIdx: 0..6 => pages 1..7
+ for( var pageIdx = 0; pageIdx < myPage.length; pageIdx++ )
{
- if( i > 0 )
+ var dictList = myPage[pageIdx];
+ var makeList = oreRows[pageIdx];
+
+ for( var i = 0; i < dictList.length && i < makeList.length; i++ )
{
- myGump.AddButton( 370, 260, 4005, 4007, 0, ( i / 10 ) + 1, 0 );
- myGump.AddHTMLGump( 405, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10100, socket.language ) + "" );// NEXT PAGE
+ // Old script uses:
+ // page 1 => 100..112
+ // page 2 => 200..204
+ // page 3 => 300..305
+ // etc.
+ var buttonID = ( ( pageIdx + 1 ) * 100 ) + i;
+ var dictID = dictList[i];
+ var makeID = makeList[i];
+
+ if( !BlacksmithMap[buttonID] )
+ {
+ BlacksmithMap[buttonID] = {
+ dictID: dictID,
+ page: pageIdx + 1,
+ timerID: pageIdx + 1,
+ oreMake: [],
+ // recipeID: undefined,
+ // minEra: undefined,
+ // maxEra: undefined
+ skill: 7, // blacksmithing skill ID
+ harvest: [10015], // ingots dict
+ harvest2: [], // optional second resource
+ harvest3: [], // optional second resource
+ harvest4: [] // optional second resource
+ };
+ }
+
+ BlacksmithMap[buttonID].oreMake[oreIndex] = makeID;
}
+ }
+ }
+})();
+
+// 3) AFTER initBlacksmithMap, you can override entries:
+// BlacksmithMap[400].customName = "Elven Broadsword";
+// BlacksmithMap[400].recipeID = 5101; // if you want it recipe-locked
+// BlacksmithMap[400].minEra = "ml"; // if you want it ML and later only
+BlacksmithMap[113].customName = "Dragon Sleeves";
+BlacksmithMap[113].useScales = true;
+BlacksmithMap[114].customName = "Dragon Breast Plate";
+BlacksmithMap[114].useScales = true;
+BlacksmithMap[115].customName = "Dragon Gloves";
+BlacksmithMap[115].useScales = true;
+BlacksmithMap[116].customName = "Dragon Helmet";
+BlacksmithMap[116].useScales = true;
+BlacksmithMap[117].customName = "Dragon leggings";
+BlacksmithMap[117].useScales = true;
- myGump.AddPage(( i / 10) + 1 );
+function PageX( socket, pUser, pageNum )
+{
+ if( !ValidateObject( pUser ))
+ return;
- if( i > 0 )
+ // Pages 1 - 7: normal crafting pages
+ // Page 999: optional "Last Ten Blacksmith" (if you decide to use it later)
+
+ var subPage = pUser.GetTempTag( "subPage" ) || 1;
+ var pageItems = [];
+
+ if( pageNum == 999 )
+ {
+ var lastTenRaw = pUser.GetTempTag( "LastTenBlacksmith" ) || "";
+ var split = lastTenRaw.split( "," );
+ for( var i = 0; i < split.length; i++ )
+ {
+ var val = parseInt( split[i] );
+ if( !isNaN( val ) && BlacksmithMap[val] )
+ pageItems.push( val ); // here val is the buttonID itself
+ }
+ }
+ else
+ {
+ // Build list of buttonIDs for this page from BlacksmithMap
+ for( var buttonID in BlacksmithMap )
+ {
+ var data = BlacksmithMap[buttonID];
+ if( data.page == pageNum && eraOK( data ))
{
- myGump.AddButton( 220, 260, 4014, 4015, 0, i / 10, 0 );
- myGump.AddHTMLGump( 255, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10101, socket.language ) + "" );// PREV PAGE
+ // If we later add recipes and want to hide unknown ones:
+ var needsRecipe = data.recipeID;
+ var showAll = displayUnlearnedRecipes;
+ if( !needsRecipe || showAll || HasLearnedRecipe( pUser, needsRecipe ))
+ {
+ pageItems.push( parseInt( buttonID ) );
+ }
}
}
- myGump.AddButton( 220, 60 + ( index * 20 ), 4005, 4007, 1, 0, ( 100 * pageNum ) + i );
- myGump.AddText( 255, 60 + ( index * 20 ), textHue, GetDictionaryEntry( myPage[pageNum - 1][i], socket.language ));
+ // Sort by buttonID to keep consistent ordering
+ pageItems.sort( function( a, b ){ return a - b; } );
+ }
+
+ if( pageItems.length == 0 )
+ {
+ var emptyGump = new Gump;
+ TriggerEvent( craftGumpID, "CraftingGumpMenu", emptyGump, socket );
+ emptyGump.AddPage( 1 );
+ emptyGump.AddText( 220, 60, textHue, "No items available on this page." );
+ emptyGump.Send( socket );
+ emptyGump.Free();
+ return;
+ }
+
+ var totalSubPages = Math.ceil( pageItems.length / itemsPerPage );
+
+ if( subPage < 1 )
+ subPage = 1;
+ if( subPage > totalSubPages )
+ subPage = totalSubPages;
+
+ pUser.SetTempTag( "page", pageNum );
+ pUser.SetTempTag( "subPage", subPage );
+
+ var startIndex = ( subPage - 1 ) * itemsPerPage;
+ var endIndex = Math.min( startIndex + itemsPerPage, pageItems.length );
- myGump.AddButton( 480, 60 + ( index * 20 ), 4011, 4012, 1, 0, ( 2000 + ( 100 * pageNum )) + i );
+ if( startIndex >= pageItems.length )
+ {
+ subPage = 1;
+ startIndex = 0;
+ endIndex = Math.min( itemsPerPage, pageItems.length );
+ pUser.SetTempTag( "subPage", subPage );
}
- myGump.Send( socket );
- myGump.Free();
-}
-function Page8( socket, pUser )
-{
- //Ingot Choices
- var myGump = new Gump;
- pUser.SetTempTag( "page", 8 );
- TriggerEvent( craftGumpID, "CraftingGumpMenu", myGump, socket );
- var iron = pUser.ResourceCount( 0x1BF2 );
- var bronze = pUser.ResourceCount( 0x1BF2, 0x06d6 );
- var copper = pUser.ResourceCount( 0x1BF2, 0x07dd );
- var agapite = pUser.ResourceCount( 0x1BF2, 0x0979 );
- var dullcopper = pUser.ResourceCount( 0x1BF2, 0x0973 );
- var gold = pUser.ResourceCount( 0x1BF2, 0x08a5 );
- var shadowiron = pUser.ResourceCount( 0x1BF2, 0x0966 );
- var valorite = pUser.ResourceCount( 0x1BF2, 0x08ab );
- var verite = pUser.ResourceCount( 0x1BF2, 0x089f );
- var myPage8 = [
- GetDictionaryEntry( 10291, socket.language ) + " (" + iron.toString() + ")",
- GetDictionaryEntry( 10203, socket.language ) + " (" + dullcopper.toString() + ")",
- GetDictionaryEntry( 10204, socket.language ) + " (" + shadowiron.toString() + ")",
- GetDictionaryEntry( 10205, socket.language ) + " (" + copper.toString() + ")",
- GetDictionaryEntry( 10206, socket.language ) + " (" + bronze.toString() + ")",
- GetDictionaryEntry( 10207, socket.language ) + " (" + gold.toString() + ")",
- GetDictionaryEntry( 10208, socket.language ) + " (" + agapite.toString() + ")",
- GetDictionaryEntry( 10209, socket.language ) + " (" + verite.toString() + ")",
- GetDictionaryEntry( 10210, socket.language ) + " (" + valorite.toString() + ")"
- ];
-
- for( var i = 0; i < myPage8.length; i++ )
+ var resourceHue = pUser.GetTempTag( "resourceHue" ) | 0;
+ var blacksmithMenu = new Gump;
+ TriggerEvent( craftGumpID, "CraftingGumpMenu", blacksmithMenu, socket );
+ blacksmithMenu.AddPage( 1 );
+
+ var drawIndex = 0;
+
+ for( var i = startIndex; i < endIndex; i++ )
{
- var index = i % 10;
- if( index == 0 )
- {
- if( i > 0 )
- {
- myGump.AddButton( 370, 260, 4005, 4007, 0, ( i / 10 ) + 1, 0 );
- myGump.AddHTMLGump( 405, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10100, socket.language ) + "" );// NEXT PAGE
- }
+ var buttonID = pageItems[i];
+ var data = BlacksmithMap[buttonID];
- myGump.AddPage(( i / 10 ) + 1 );
+ // Do not show weapons when colored ingots selected and colored weapons are disabled
+ if( !allowColouredWeapons && resourceHue > 0 && data.page > 3 )
+ continue;
- if( i > 0 )
- {
- myGump.AddButton( 220, 260, 4014, 4015, 0, i / 10, 0 );
- myGump.AddHTMLGump( 255, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10101, socket.language ) + "" );// PREV PAGE
- }
+ var entryText = "";
+ if( data.customName )
+ {
+ entryText = data.customName;
+ }
+ else if( data.dictID )
+ {
+ entryText = GetDictionaryEntry( data.dictID, socket.language );
+ if( !entryText || entryText === "" )
+ entryText = "[Missing EntryID: " + data.dictID + "]";
+ }
+ else
+ {
+ entryText = "[Unnamed Item: " + buttonID + "]";
}
- myGump.AddButton( 220, 60 + ( index * 20), 4005, 4007, 1, 0, 1000 + i );
- myGump.AddText( 255, 60 + ( index * 20), textHue, myPage8[i] );
+ // Same layout as tailoring: button, text, details button
+ blacksmithMenu.AddButton( 220, 60 + ( drawIndex * 20 ), 4005, 4007, 1, 0, buttonID );
+ blacksmithMenu.AddText( 255, 60 + ( drawIndex * 20 ), textHue, entryText );
+ blacksmithMenu.AddButton( 480, 60 + ( drawIndex * 20 ), 4011, 4012, 1, 0, 2000 + buttonID );
+
+ drawIndex++;
+ }
+
+ // Prev subpage
+ if( subPage > 1 )
+ {
+ blacksmithMenu.AddButton( 220, 260, 4014, 4015, 1, 0, 8000 + ( subPage - 1 ));
+ blacksmithMenu.AddHTMLGump( 255, 263, 100, 18, 0, 0,
+ "" + GetDictionaryEntry( 10101, socket.language ) + "" );
}
- myGump.Send( socket );
- myGump.Free();
+
+ // Next subpage
+ if( subPage < totalSubPages )
+ {
+ blacksmithMenu.AddButton( 370, 260, 4005, 4007, 1, 0, 9000 + ( subPage + 1 ));
+ blacksmithMenu.AddHTMLGump( 405, 263, 100, 18, 0, 0,
+ "" + GetDictionaryEntry( 10100, socket.language ) + "" );
+ }
+
+ blacksmithMenu.Send( socket );
+ blacksmithMenu.Free();
+}
+
+function Page8( socket, pUser )
+{
+ // Ingot choices page
+ var myGump = new Gump();
+ pUser.SetTempTag( "page", 8 );
+
+ // Draw standard crafting frame (title, buttons, notices etc.)
+ TriggerEvent( craftGumpID, "CraftingGumpMenu", myGump, socket );
+
+ // Count ingots by hue
+ var iron = pUser.ResourceCount( 0x1BF2 );
+ var dullcopper = pUser.ResourceCount( 0x1BF2, 0x0973 );
+ var shadowiron = pUser.ResourceCount( 0x1BF2, 0x0966 );
+ var copper = pUser.ResourceCount( 0x1BF2, 0x07dd );
+ var bronze = pUser.ResourceCount( 0x1BF2, 0x06d6 );
+ var gold = pUser.ResourceCount( 0x1BF2, 0x08a5 );
+ var agapite = pUser.ResourceCount( 0x1BF2, 0x0979 );
+ var verite = pUser.ResourceCount( 0x1BF2, 0x089f );
+ var valorite = pUser.ResourceCount( 0x1BF2, 0x08ab );
+
+ var counts = [ iron, dullcopper, shadowiron, copper, bronze, gold, agapite, verite, valorite ];
+
+ var lines = [];
+ for( var i = 0; i < OreTypes.length; i++ )
+ {
+ var oreInfo = OreTypes[i];
+ var label = GetDictionaryEntry( oreInfo.nameDict, socket.language );
+ lines.push( label + " (" + counts[i].toString() + ")" );
+ }
+
+ for( var j = 0; j < lines.length; j++ )
+ {
+ var index = j % 10;
+
+ if( index == 0 )
+ {
+ if( j > 0 )
+ {
+ myGump.AddButton( 370, 260, 4005, 4007, 0, ( j / 10 ) + 1, 0 );
+ myGump.AddHTMLGump( 405, 263, 100, 18, false, false, "" + GetDictionaryEntry( 10100, socket.language ) + "" ); // NEXT PAGE
+ }
+
+ myGump.AddPage(( j / 10 ) + 1 );
+
+ if( j > 0 )
+ {
+ myGump.AddButton( 220, 260, 4014, 4015, 0, j / 10, 0 );
+ myGump.AddHTMLGump( 255, 263, 100, 18, false, false, "" + GetDictionaryEntry( 10101, socket.language ) + "" ); // PREV PAGE
+ }
+ }
+
+ myGump.AddButton( 220, 60 + ( index * 20 ), 4005, 4007, 1, 0, 1000 + j );
+ myGump.AddText( 255, 60 + ( index * 20 ), textHue, lines[j] );
+ }
+
+ myGump.Send( socket );
+ myGump.Free();
+}
+
+function Page30( socket, pUser )
+{
+ var myGump = new Gump();
+ pUser.SetTempTag( "page", 30 ); // page index for scales (use any unused index)
+
+ // Base crafting frame (same as other pages)
+ TriggerEvent( craftGumpID, "CraftingGumpMenu", myGump, socket );
+
+ // Count scales in backpack by hue (update IDs/hues as needed)
+ // If all scales share the same item ID but different hues, adjust this.
+ var counts = [];
+
+ for( var i = 0; i < ScaleTypes.length; i++ )
+ {
+ var scaleInfo = ScaleTypes[i];
+
+ // Example: if all scales share a single ID (replace 0x26B4 with your scale item ID)
+ // counts[i] = pUser.ResourceCount( 0x26B4, scaleInfo.hue );
+
+ // If you have different IDs per scale type, change the above line as needed.
+ counts[i] = pUser.ResourceCount( 0x26b4, scaleInfo.hue );; // placeholder so script parses; you’ll replace with real logic
+ }
+
+ var lines = [];
+ for( var j = 0; j < ScaleTypes.length; j++ )
+ {
+ var sInfo = ScaleTypes[j];
+ var label = GetDictionaryEntry( sInfo.nameDict, socket.language );
+ lines.push( label + " (" + counts[j].toString() + ")" );
+ }
+
+ for( var k = 0; k < lines.length; k++ )
+ {
+ var index = k % 10;
+
+ if( index == 0 )
+ {
+ if( k > 0 )
+ {
+ myGump.AddButton( 370, 260, 4005, 4007, 0, ( k / 10 ) + 1, 0 );
+ myGump.AddHTMLGump( 405, 263, 100, 18, false, false, "" + GetDictionaryEntry( 10100, socket.language ) + "" ); // NEXT PAGE
+ }
+
+ myGump.AddPage(( k / 10 ) + 1 );
+
+ if( k > 0 )
+ {
+ myGump.AddButton( 220, 260, 4014, 4015, 0, k / 10, 0 );
+ myGump.AddHTMLGump( 255, 263, 100, 18, false, false, "" + GetDictionaryEntry( 10101, socket.language ) + "" ); // PREV PAGE
+ }
+ }
+
+ // Use a separate button range from ores to avoid conflicts: 1100+
+ myGump.AddButton( 220, 60 + ( index * 20 ), 4005, 4007, 1, 0, 1100 + k );
+ myGump.AddText( 255, 60 + ( index * 20 ), textHue, lines[k] );
+ }
+
+ myGump.Send( socket );
+ myGump.Free();
}
function FindNearbyAnvils( pUser, trgItem, pSock )
@@ -681,22 +1043,21 @@ function onTimer( pUser, timerID )
var socket = pUser.socket;
- switch( timerID )
+ if( timerID >= 1 && timerID <= 7 )
{
- case 1: // Page 1
- case 2: // Page 2
- case 3: // Page 3
- case 4: // Page 4
- case 5: // Page 5
- case 6: // Page 6
- case 7: // Page 7
- PageX( socket, pUser, timerID );
- break;
- case 8: // Page 8
- Page8( socket, pUser );
- break;
- default:
- break;
+ PageX( socket, pUser, timerID ); // Pages 1 - 7
+ }
+ else if( timerID == 8 )
+ {
+ Page8( socket, pUser ); // Ingot selection
+ }
+ else if( timerID == 30 )
+ {
+ Page30( socket, pUser ); // Scale selection
+ }
+ else if( timerID == 999 )
+ {
+ PageX( socket, pUser, 999 ); // Last Ten Blacksmith (if used)
}
}
@@ -704,12 +1065,11 @@ function onTimer( pUser, timerID )
function onGumpPress( pSock, pButton, gumpData )
{
var pUser = pSock.currentChar;
+ var usedMakeLast = false;
- // Don't continue if character is invalid, or worse... dead!
if( !ValidateObject( pUser ) || pUser.dead )
return;
- // Don't continue if player no longer has access to the crafting tool
var bItem = pSock.tempObj;
if( !ValidateObject( bItem ) || !pUser.InRange( bItem, 3 ))
{
@@ -724,9 +1084,9 @@ function onGumpPress( pSock, pButton, gumpData )
}
var iPackOwner = GetPackOwner( bItem, 0 );
- if( ValidateObject( iPackOwner )) // Is the item in a backpack?
+ if( ValidateObject( iPackOwner ))
{
- if( iPackOwner.serial != pUser.serial ) // And if so does the pack belong to the user?
+ if( iPackOwner.serial != pUser.serial )
{
pSock.SysMessage( GetDictionaryEntry( 6032, pSock.language )); // That resource is in someone else's backpack!
return;
@@ -739,422 +1099,393 @@ function onGumpPress( pSock, pButton, gumpData )
}
var gumpID = blacksmithID + 0xffff;
- var makeID = 0;
- var itemDetailsID = 0;
- var oreID = pUser.GetTempTag( "ORE" );
- var resourceHue = pUser.GetTempTag( "resourceHue" );
- var newOreID = -1;
- var newResourceHue = -1;
- var timerID = 0;
-
- // Check for nearby anvil
- var anvil = 0;
-
- // If button pressed is one of the crafting buttons (or "make last"), check that anvil was found
- if(( pButton >= 100 && pButton <= 704 ) || pButton == 5000 )
+
+ // Close / Exit
+ if( pButton == 0 )
+ {
+ pUser.SetTempTag( "prevActionResult", null );
+ pUser.SetTempTag( "MAKELAST", null );
+ pSock.CloseGump( gumpID, 0 );
+ return;
+ }
+
+ // Repair Item
+ if( pButton == 49 )
+ {
+ RepairTarget( pSock );
+ return;
+ }
+
+ // Select Materials (ingots)
+ if( pButton == 50 )
{
- anvil = AreaItemFunction( "FindNearbyAnvils", pUser, 2, pSock );
- if( anvil > 0 )
+ pSock.CloseGump( gumpID, 0 );
+ Page8( pSock, pUser );
+ return;
+ }
+
+ // Select Materials (scales)
+ if( pButton == 51 )
+ {
+ pSock.CloseGump( gumpID, 0 );
+ Page30( pSock, pUser );
+ return;
+ }
+
+ // Smelt Item
+ if( pButton == 52 )
+ {
+ SmeltTarget( pSock );
+ return;
+ }
+
+ // Page buttons (1..7)
+ if( pButton >= 1 && pButton <= 7 )
+ {
+ pUser.SetTempTag( "page", pButton );
+ pUser.SetTempTag( "subPage", 1 );
+ pSock.CloseGump( gumpID, 0 );
+ PageX( pSock, pUser, pButton );
+ return;
+ }
+
+ // Handle ore selection buttons (1000–1008) BEFORE the main switch
+ if( pButton >= 1000 && pButton <= 1008 )
+ {
+ var index = pButton - 1000; // 0..8
+ var oreInfo = OreTypes[index];
+ var miningSkill = pUser.skills.mining; // you are already using skills.mining in smelting logic
+
+ if( miningSkill < oreInfo.minMining )
{
- if( pButton == 5000 )
- {
- // Make Last button
- pButton = pUser.GetTempTag( "MAKELAST" );
- }
- else
- {
- // Update Make Last entry
- pUser.SetTempTag( "MAKELAST", pButton );
- }
+ // Not enough mining skill for this ore
+ pSock.CloseGump( gumpID, 0 );
+ pUser.SetTempTag( "prevActionResult", "FAILED" );
+ // Re-open ore page after a short delay
+ pUser.StartTimer( ingotDelay, 8, true );
+ return;
+ }
+
+ // Set selected ore and hue
+ pUser.SetTempTag( "ORE", index );
+ pUser.SetTempTag( "resourceHue", oreInfo.hue );
+ pUser.SetTempTag( "MAKELAST", null );
+ pUser.SetTempTag( "prevActionResult", null );
+
+ // Re-open ore page so they see the new counts/selection
+ pSock.CloseGump( gumpID, 0 );
+ pUser.StartTimer( ingotDelay, 8, true );
+ return;
+ }
+
+ // Handle scale selection buttons (1100–11XX) BEFORE the main switch
+ if (pButton >= 1100 && pButton < 1100 + ScaleTypes.length)
+ {
+ var sIndex = pButton - 1100; // 0..ScaleTypes.length-1
+ var sInfo = ScaleTypes[sIndex];
+ var smithSkill = pUser.skills.blacksmithing; // or mining / whatever you want to gate on
+
+ if( smithSkill < sInfo.minSkill )
+ {
+ // Not enough skill for this scale type
+ pSock.CloseGump( gumpID, 0 );
+ pUser.SetTempTag( "prevActionResult", "FAILED" );
+ pUser.StartTimer( ingotDelay, 30, true ); // reopen scale page after delay
+ return;
+ }
+
+ // Store selection
+ pUser.SetTempTag( "Scale", sIndex );
+ pUser.SetTempTag( "resourceHue", sInfo.hue );
+ pUser.SetTempTag( "MAKELAST", null );
+ pUser.SetTempTag( "prevActionResult", null );
+
+ // If you want a separate "make last" for scales, you could also SetTempTag("SCALEMAKELAST", ...)
+ // For now we just store the current selection.
+
+ pSock.CloseGump( gumpID, 0 );
+ pUser.StartTimer( ingotDelay, 30, true ); // reopen PageScales to show updated selection/notice
+ return;
+ }
+
+ // Last Ten page (if you wire it into the gump)
+ if( pButton == 11000 )
+ {
+ pUser.SetTempTag( "page", 999 );
+ pUser.SetTempTag( "subPage", 1 );
+ PageX( pSock, pUser, 999 );
+ return;
+ }
+
+ // Subpage navigation (8000 = prev, 9000 = next)
+ if( pButton >= 8000 && pButton < 9000 )
+ {
+ var prevSub = pButton - 8000;
+ var curPage = pUser.GetTempTag( "page" ) || 1;
+ pUser.SetTempTag( "subPage", prevSub );
+ PageX( pSock, pUser, curPage );
+ return;
+ }
+
+ if( pButton >= 9000 && pButton < 10000 )
+ {
+ var nextSub = pButton - 9000;
+ var curPage2 = pUser.GetTempTag( "page" ) || 1;
+ pUser.SetTempTag( "subPage", nextSub );
+ PageX( pSock, pUser, curPage2 );
+ return;
+ }
+
+ // Handle "Make Last"
+ if(( pButton >= 100 && pButton <= 799 ) || pButton == 5000 )
+ {
+ if( pButton == 5000 )
+ {
+ pButton = pUser.GetTempTag( "MAKELAST" );
+ usedMakeLast = true;
}
else
{
- // No anvil found nearby!
- pUser.SetTempTag( "prevActionResult", "NOANVIL" );
+ pUser.SetTempTag( "MAKELAST", pButton );
}
}
- switch( pButton )
+ // Item detail buttons (2000 + buttonID)
+ if( pButton >= 2000 && pButton < 3000 )
{
- case 0:
- pUser.SetTempTag( "prevActionResult", null );
- pUser.SetTempTag( "MAKELAST", null );
- pSock.CloseGump( gumpID, 0 );
- break;// abort and do nothing
- case 1: // Page 1
- case 2: // Page 2
- case 3: // Page 3
- case 4: // Page 4
- case 5: // Page 5
- case 6: // Page 6
- case 7: // Page 7
- pSock.CloseGump( gumpID, 0 );
- PageX( pSock, pUser, pButton );
- break;
- case 49: // Repair Item
- RepairTarget( pSock );
- break;
- case 50: // Select Materials
- pSock.CloseGump( gumpID, 0 );
- Page8( pSock, pUser );
- break;
- case 52: // Smelt Item
- SmeltTarget( pSock );
- break;
- // [100-199]
- case 100: // Ringmail Gloves
- makeID = craftItems[oreID][0][0]; timerID = 1; break;
- case 101: // Ringmail Legs
- makeID = craftItems[oreID][0][1]; timerID = 1; break;
- case 102: // Ringmail Arms
- makeID = craftItems[oreID][0][2]; timerID = 1; break;
- case 103: // Ringmail Chest
- makeID = craftItems[oreID][0][3]; timerID = 1; break;
- case 104: // Chain Coif
- makeID = craftItems[oreID][0][4]; timerID = 1; break;
- case 105: // Chain Legs
- makeID = craftItems[oreID][0][5]; timerID = 1; break;
- case 106: // Chain Chest
- makeID = craftItems[oreID][0][6]; timerID = 1; break;
- case 107: // Plate Arms
- makeID = craftItems[oreID][0][7]; timerID = 1; break;
- case 108: // Plate Gloves
- makeID = craftItems[oreID][0][8]; timerID = 1; break;
- case 109: // Plate Gorget
- makeID = craftItems[oreID][0][9]; timerID = 1; break;
- case 110: // Plate Legs
- makeID = craftItems[oreID][0][10]; timerID = 1; break;
- case 111: // Plate Chest
- makeID = craftItems[oreID][0][11]; timerID = 1; break;
- case 112: // Female Plate Chest
- makeID = craftItems[oreID][0][12]; timerID = 1; break;
- // [200-299]
- case 200: // Bascinet
- makeID = craftItems[oreID][1][0]; timerID = 2; break;
- case 201: // Close Helm
- makeID = craftItems[oreID][1][1]; timerID = 2; break;
- case 202: // Helmet
- makeID = craftItems[oreID][1][2]; timerID = 2; break;
- case 203: // Norse Helm
- makeID = craftItems[oreID][1][3]; timerID = 2; break;
- case 204: // Plate Helm
- makeID = craftItems[oreID][1][4]; timerID = 2; break;
- // [300-399]
- case 300: // Buckler
- makeID = craftItems[oreID][2][0]; timerID = 3; break;
- case 301: // Bronze Shield
- makeID = craftItems[oreID][2][1]; timerID = 3; break;
- case 302: // Heater Shield
- makeID = craftItems[oreID][2][2]; timerID = 3; break;
- case 303: // Metal Shield
- makeID = craftItems[oreID][2][3]; timerID = 3; break;
- case 304: // Metal Kite Shield
- makeID = craftItems[oreID][2][4]; timerID = 3; break;
- case 305: // Tear Kite Shield
- makeID = craftItems[oreID][2][5]; timerID = 3; break;
- // [400-499]
- case 400: // Broadsword
- makeID = craftItems[oreID][3][0]; timerID = 4; break;
- case 401: // Cutlass
- makeID = craftItems[oreID][3][1]; timerID = 4; break;
- case 402: // Dagger
- makeID = craftItems[oreID][3][2]; timerID = 4; break;
- case 403: // Katana
- makeID = craftItems[oreID][3][3]; timerID = 4; break;
- case 404: // Kryss
- makeID = craftItems[oreID][3][4]; timerID = 4; break;
- case 405: // Longsword
- makeID = craftItems[oreID][3][5]; timerID = 4; break;
- case 406: // Scimitar
- makeID = craftItems[oreID][3][6]; timerID = 4; break;
- case 407: // Viking Sword
- makeID = craftItems[oreID][3][7]; timerID = 4; break;
- // [500-599]
- case 500: // Axe
- makeID = craftItems[oreID][4][0]; timerID = 5; break;
- case 501: // Battle Axe
- makeID = craftItems[oreID][4][1]; timerID = 5; break;
- case 502: // Double Axe
- makeID = craftItems[oreID][4][2]; timerID = 5; break;
- case 503: // Executioners Axe
- makeID = craftItems[oreID][4][3]; timerID = 5; break;
- case 504: // Large Battle Axe
- makeID = craftItems[oreID][4][4]; timerID = 5; break;
- case 505: // Two Handed Axe
- makeID = craftItems[oreID][4][5]; timerID = 5; break;
- case 506: // War Axe
- makeID = craftItems[oreID][4][6]; timerID = 5; break;
- // [600-699]
- case 600: // Bardiche
- makeID = craftItems[oreID][5][0]; timerID = 6; break;
- case 601: // Halberd
- makeID = craftItems[oreID][5][1]; timerID = 6; break;
- case 602: // Short Spear
- makeID = craftItems[oreID][5][2]; timerID = 6; break;
- case 603: // Spear
- makeID = craftItems[oreID][5][3]; timerID = 6; break;
- case 604: // War Fork
- makeID = craftItems[oreID][5][4]; timerID = 6; break;
- // [700-799]
- case 700: // Hammer Pick
- makeID = craftItems[oreID][6][0]; timerID = 7; break;
- case 701: // Mace
- makeID = craftItems[oreID][6][1]; timerID = 7; break;
- case 702: // Maul
- makeID = craftItems[oreID][6][2]; timerID = 7; break;
- case 703: // War Mace
- makeID = craftItems[oreID][6][3]; timerID = 7; break;
- case 704: // War Hammer
- makeID = craftItems[oreID][6][4]; timerID = 7; break;
- // Select Ore Type
- case 1000: // Iron
- newOreID = (( newOreID == -1 ) ? 0 : newOreID );
- newResourceHue = 0; // Update manually if color changes in skills.dfn!
- case 1001: // Dull Copper
- if( pButton == 1001 && pUser.skills[7] < 650 )
- {
- pSock.CloseGump( gumpID, 0 );
- pUser.StartTimer( ingotDelay, 8, true);
- pUser.SetTempTag( "prevActionResult", "FAILED" );
- break;
- }
- newResourceHue = (( newResourceHue == - 1 ) ? 0x0973 : newResourceHue ); // Update manually if color changes in skills.dfn!
- newOreID = (( newOreID == -1 ) ? 1 : newOreID );
- case 1002: // Shadow Iron
- if( pButton == 1002 && pUser.skills[7] < 700 )
- {
- pSock.CloseGump( gumpID, 0 );
- pUser.StartTimer( ingotDelay, 8, true);
- pUser.SetTempTag( "prevActionResult", "FAILED" );
- break;
- }
- newResourceHue = (( newResourceHue == - 1 ) ? 0x0966 : newResourceHue ); // Update manually if color changes in skills.dfn!
- newOreID = (( newOreID == -1 ) ? 2 : newOreID );
- case 1003: // Copper
- if( pButton == 1003 && pUser.skills[7] < 750 )
- {
- pSock.CloseGump( gumpID, 0 );
- pUser.StartTimer( ingotDelay, 8, true);
- pUser.SetTempTag( "prevActionResult", "FAILED" );
- break;
- }
- newResourceHue = (( newResourceHue == - 1 ) ? 0x07dd : newResourceHue ); // Update manually if color changes in skills.dfn!
- newOreID = (( newOreID == -1 ) ? 3 : newOreID );
- case 1004: // Bronze
- if( pButton == 1004 && pUser.skills[7] < 800 )
- {
- pSock.CloseGump( gumpID, 0 );
- pUser.StartTimer( ingotDelay, 8, true);
- pUser.SetTempTag( "prevActionResult", "FAILED" );
- break;
- }
- newResourceHue = (( newResourceHue == - 1 ) ? 0x06d6 : newResourceHue ); // Update manually if color changes in skills.dfn!
- newOreID = (( newOreID == -1 ) ? 4 : newOreID );
- case 1005: // Gold
- if( pButton == 1005 && pUser.skills[7] < 850 )
- {
- pSock.CloseGump( gumpID, 0 );
- pUser.StartTimer( ingotDelay, 8, true);
- pUser.SetTempTag( "prevActionResult", "FAILED" );
- break;
- }
- newResourceHue = (( newResourceHue == - 1 ) ? 0x08a5 : newResourceHue ); // Update manually if color changes in skills.dfn!
- newOreID = (( newOreID == -1 ) ? 5 : newOreID );
- case 1006: // Agapite
- if( pButton == 1006 && pUser.skills[7] < 900 )
- {
- pSock.CloseGump( gumpID, 0 );
- pUser.StartTimer( ingotDelay, 8, true);
- pUser.SetTempTag( "prevActionResult", "FAILED" );
- break;
- }
- newResourceHue = (( newResourceHue == - 1 ) ? 0x0979 : newResourceHue );; // Update manually if color changes in skills.dfn!
- newOreID = (( newOreID == -1 ) ? 6 : newOreID );
- case 1007: // Verite
- if( pButton == 1007 && pUser.skills[7] < 950 )
- {
- pSock.CloseGump( gumpID, 0 );
- pUser.StartTimer( ingotDelay, 8, true);
- pUser.SetTempTag( "prevActionResult", "FAILED" );
- break;
- }
- newResourceHue = (( newResourceHue == - 1 ) ? 0x089f : newResourceHue );; // Update manually if color changes in skills.dfn!
- newOreID = (( newOreID == -1 ) ? 7 : newOreID );
- case 1008: // Valorite
- if( pButton == 1008 && pUser.skills[7] < 990 )
+ var detailButtonID = pButton - 2000;
+ var entry = BlacksmithMap[detailButtonID];
+ if( entry )
+ {
+ // For details we just pass the iron version (ore index 0) to 4026
+ var ironMakeID = entry.oreMake[0] || 0;
+ if( ironMakeID > 0 )
{
- pSock.CloseGump( gumpID, 0 );
- pUser.StartTimer( ingotDelay, 8, true);
- pUser.SetTempTag( "prevActionResult", "FAILED" );
- break;
- }
- newResourceHue = (( newResourceHue == - 1 ) ? 0x08ab : newResourceHue );; // Update manually if color changes in skills.dfn!
- newOreID = (( newOreID == -1 ) ? 8 : newOreID );
+ // Masonry uses Carpentry skill
+ pUser.SetTempTag( "Skill", entry.skill | 0 );
+
+ // Clear old harvests
+ pUser.SetTempTag( "Harvest", null );
+ pUser.SetTempTag( "Harvest2", null );
+ pUser.SetTempTag( "Harvest3", null );
+ pUser.SetTempTag( "Harvest4", null );
+
+ // Clear old harvest names
+ pUser.SetTempTag( "HarvestName", null );
+ pUser.SetTempTag( "Harvest2Name", null );
+ pUser.SetTempTag( "Harvest3Name", null );
+ pUser.SetTempTag( "Harvest4Name", null );
+
+ // If this entry uses dragon scales, override resource label
+ if( entry.useScales )
+ {
+ // Use currently selected scale type (from Page30 selection)
+ var sIndex = pUser.GetTempTag( "Scale" );
+ if( sIndex < 0 || sIndex >= ScaleTypes.length )
+ sIndex = 0;
+
+ var sInfo = ScaleTypes[sIndex];
+ var sLabel = GetDictionaryEntry( sInfo.nameDict, pSock.language );
+ pUser.SetTempTag( "HarvestName", sLabel );
+ }
+ else
+ {
+ // Normal ingot-based items: use the harvest setup as before
+ if( entry.harvest && entry.harvest.length > 0 )
+ {
+ if( entry.harvest.length >= 1 )
+ pUser.SetTempTag( "Harvest", entry.harvest[0] );
+ if( entry.harvest.length >= 2 )
+ pUser.SetTempTag( "Harvest2", entry.harvest[1] );
+ if( entry.harvest.length >= 3 )
+ pUser.SetTempTag( "Harvest3", entry.harvest[2] );
+ if( entry.harvest.length >= 4 )
+ pUser.SetTempTag( "Harvest4", entry.harvest[3] );
+ }
+
+ // OPTIONAL custom names – these override the dictionary string
+ if( entry.harvestNames && entry.harvestNames.length > 0 )
+ {
+ if( entry.harvestNames.length >= 1 )
+ pUser.SetTempTag( "HarvestName", entry.harvestNames[0] );
+ if( entry.harvestNames.length >= 2 )
+ pUser.SetTempTag( "Harvest2Name", entry.harvestNames[1] );
+ if( entry.harvestNames.length >= 3 )
+ pUser.SetTempTag( "Harvest3Name", entry.harvestNames[2] );
+ if( entry.harvestNames.length >= 4 )
+ pUser.SetTempTag( "Harvest4Name", entry.harvestNames[3] );
+ }
+ }
+
+ if( entry.recipeID && entry.recipeID > 0 )
+ pUser.SetTempTag( "needRecipeID", entry.recipeID );
+ else
+ pUser.SetTempTag( "needRecipeID", 0 );
- // Run common code for this group of buttons
- pSock.CloseGump( gumpID, 0 );
- pUser.SetTempTag( "MAKELAST", null );
- pUser.SetTempTag( "prevActionResult", null );
- pUser.SetTempTag( "ORE", newOreID );
- pUser.SetTempTag( "resourceHue", newResourceHue );
- Page8( pSock, pUser );
- break;
- // Show Item Details
- case 2100: // Ringmail Gloves
- itemDetailsID = 7; break;
- case 2101: // Ringmail Legs
- itemDetailsID = 9; break;
- case 2102: // Ringmail Arms
- itemDetailsID = 8; break;
- case 2103: // Ringmail Chest
- itemDetailsID = 10; break;
- case 2104: // Chain Coif
- itemDetailsID = 11; break;
- case 2105: // Chain Legs
- itemDetailsID = 12; break;
- case 2106: // Chain Chest
- itemDetailsID = 13; break;
- case 2107: // Plate Arms
- itemDetailsID = 16; break;
- case 2108: // Plate Gloves
- itemDetailsID = 15; break;
- case 2109: // Plate Gorget
- itemDetailsID = 14; break;
- case 2110: // Plate Legs
- itemDetailsID = 17; break;
- case 2111: // Plate Chest
- itemDetailsID = 18; break;
- case 2112: // Female Plate Chest
- itemDetailsID = 19; break;
- // [200-299]
- case 2200: // Bascinet
- itemDetailsID = 46; break;
- case 2201: // Close Helm
- itemDetailsID = 48; break;
- case 2202: //Helmet
- itemDetailsID = 45; break;
- case 2203: // Norse Helm
- itemDetailsID = 47; break;
- case 2204: // Plate Helm
- itemDetailsID = 49; break;
- // [300-399]
- case 2300: // Buckler
- itemDetailsID = 1; break;
- case 2301: // Bronze Shield
- itemDetailsID = 2; break;
- case 2302: // Heater Shield
- itemDetailsID = 6; break;
- case 2303: // Metal Shield
- itemDetailsID = 3; break;
- case 2304: // MetalKiteShield
- itemDetailsID = 5; break;
- case 2305: // tear kite shield
- itemDetailsID = 4; break;
- // [400-499]
- case 2400: // Broadsword
- itemDetailsID = 25; break;
- case 2401: // Cutlass
- itemDetailsID = 21; break;
- case 2402: // Dagger
- itemDetailsID = 20; break;
- case 2403: // Katana
- itemDetailsID = 22; break;
- case 2404: // Kryss
- itemDetailsID = 23; break;
- case 2405: // Longsword
- itemDetailsID = 26; break;
- case 2406: // Scimitar
- itemDetailsID = 24; break;
- case 2407: // Viking Sword
- itemDetailsID = 27; break;
- // [500-599]
- case 2500: // Axe
- itemDetailsID = 29; break;
- case 2501: // Battle Axe
- itemDetailsID = 28; break;
- case 2502: // Double Axe
- itemDetailsID = 32; break;
- case 2503: // Executioners Axe
- itemDetailsID = 30; break;
- case 2504: // Large Battle Axe
- itemDetailsID = 33; break;
- case 2505: // Two Handed Axe
- itemDetailsID = 31; break;
- case 2506: // War Axe
- itemDetailsID = 34; break;
- // [600-699]
- case 2600: // Bardiche
- itemDetailsID = 38; break;
- case 2601: // Halberd
- itemDetailsID = 39; break;
- case 2602: // Short Spear
- itemDetailsID = 35; break;
- case 2603: // Spear
- itemDetailsID = 36; break;
- case 2604: // WarFork
- itemDetailsID = 37; break;
- // [700-799]
- case 2700: // Hammer Pick
- itemDetailsID = 44; break;
- case 2701: // Mace
- itemDetailsID = 40; break;
- case 2702: // Maul
- itemDetailsID = 41; break;
- case 2703: // War Mace
- itemDetailsID = 42; break;
- case 2704: // War Hammer
- itemDetailsID = 43; break;
- default:
- break;
+ pUser.SetTempTag( "ITEMDETAILS", ironMakeID );
+ TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
+ }
+ }
+ return;
}
- if( makeID != 0 )
+ // If this is a craft button in our map:
+ if( BlacksmithMap[pButton] != undefined )
{
- if( anvil > 0 )
+ var entry2 = BlacksmithMap[pButton];
+ var oreID;
+ if( entry2.useScales )
{
- // crafting_complete.js for applying special bonuses for exceptional equipment/from runic hammers
- pUser.AddScriptTrigger( 4033 );
- MakeItem( pSock, pUser, makeID, resourceHue );
- var toolUseLimit = GetServerSetting( "ToolUseLimit" );
- var toolUseBreak = GetServerSetting( "ToolUseBreak" );
-
- // Check if player had runic hammer equipped while crafting
- var runicHammer = pUser.FindItemLayer( 0x01 ); // Right Hand
- if( ValidateObject( runicHammer ) && runicHammer.GetTag( "runicHammer" ) && runicHammer.usesLeft > 0 )
- {
- // Store some temp tags on player to get info on runic hammer used in crafting_complete.js
- pUser.SetTempTag( "usedRunicHammer", true );
- pUser.SetTempTag( "runicHammerType", runicHammer.color );
+ // For dragon armor, index into oreMake[] by selected scale index
+ oreID = pUser.GetTempTag( "Scale" );
+ }
+ else
+ {
+ oreID = pUser.GetTempTag( "ORE" );
+ }
+ var resourceHue = pUser.GetTempTag( "resourceHue" );
+
+ // Ensure oreID within range
+ if( oreID < 0 || oreID >= craftItems.length )
+ oreID = 0;
- // Wear and tear for equipped runic hammer, even if another tool was used to craft
- if( toolUseLimit && runicHammer != bItem )
+ // Era / recipe gating
+ if( !eraOK( entry2 ))
+ {
+ pSock.SysMessage( "That item is not available in this era." );
+ return;
+ }
+
+ if( entry2.recipeID && !TriggerEvent( 4022, "NeedRecipe", pUser, entry2.recipeID ))
+ {
+ pSock.SysMessage( "You must learn that recipe from a scroll." );
+ return;
+ }
+
+ // Check for nearby anvil
+ var anvil = AreaItemFunction( "FindNearbyAnvils", pUser, 2, pSock );
+ if( anvil == 0 )
+ {
+ pUser.SetTempTag( "prevActionResult", "NOANVIL" );
+ pUser.StartTimer( gumpDelay, entry2.timerID, true );
+ return;
+ }
+
+ // No colored weapons if disabled and using non-iron ingots
+ if( !allowColouredWeapons && resourceHue > 0 && entry2.page > 3 )
+ {
+ pSock.SysMessage( "You cannot use colored ingots for weapons on this shard." );
+ return;
+ }
+
+ var makeID = entry2.oreMake[oreID];
+ if( !makeID || makeID == 0 )
+ {
+ // Fallback to iron version if for some reason we did not get a specific ore entry
+ makeID = entry2.oreMake[0];
+ }
+
+ if( !makeID || makeID == 0 )
+ {
+ pSock.SysMessage( "That item is not properly configured." );
+ return;
+ }
+
+ // Runic hammer bonus logic (unchanged from your original)
+ pUser.AddScriptTrigger( 4033 );
+
+ MakeItem( pSock, pUser, makeID, resourceHue );
+
+ // Tool wear
+ var toolUseLimit = GetServerSetting( "ToolUseLimit" );
+ var toolUseBreak = GetServerSetting( "ToolUseBreak" );
+
+ var runicHammer = pUser.FindItemLayer( 0x01 ); // Right Hand
+ if( ValidateObject( runicHammer ) && runicHammer.GetTag( "runicHammer" ) && runicHammer.usesLeft > 0 )
+ {
+ pUser.SetTempTag( "usedRunicHammer", true );
+ pUser.SetTempTag( "runicHammerType", runicHammer.color );
+
+ if( toolUseLimit && runicHammer != bItem )
+ {
+ runicHammer.usesLeft -= 1;
+ if( runicHammer.usesLeft == 0 && toolUseBreak )
{
- runicHammer.usesLeft -= 1;
- if( runicHammer.usesLeft == 0 && toolUseBreak )
- {
- runicHammer.Delete();
- pSock.SysMessage( GetDictionaryEntry( 10202, pSock.language )); // You have worn out your tool!
- // Play sound effect of tool breaking
- }
+ runicHammer.Delete();
+ pSock.SysMessage( GetDictionaryEntry( 10202, pSock.language ));
}
}
+ }
- if( toolUseLimit )
+ if( toolUseLimit )
+ {
+ bItem.usesLeft -= 1;
+ if( bItem.usesLeft == 0 && toolUseBreak )
{
- bItem.usesLeft -= 1;
- if( bItem.usesLeft == 0 && toolUseBreak )
- {
- bItem.Delete();
- pSock.SysMessage( GetDictionaryEntry( 10202, pSock.language )); // You have worn out your tool!
- // Play sound effect of tool breaking
- }
+ bItem.Delete();
+ pSock.SysMessage( GetDictionaryEntry( 10202, pSock.language ));
}
}
- pUser.StartTimer( gumpDelay, timerID, true );
+
+ // Track in last ten list for blacksmith
+ AddToLastTenBlacksmith( pUser, pButton );
+
+ // Reopen page after delay
+ pUser.StartTimer( gumpDelay, entry2.timerID, true );
+ return;
+ }
+
+ // Any other buttons fall through and do nothing
+}
+
+function AddToLastTenBlacksmith( pUser, buttonID )
+{
+ var raw = pUser.GetTempTag( "LastTenBlacksmith" ) || "";
+ var list = raw.split( "," );
+
+ // Remove if already in list
+ for( var i = 0; i < list.length; i++ )
+ {
+ if( parseInt( list[i] ) == buttonID )
+ {
+ list.splice( i, 1 );
+ break;
+ }
+ }
+
+ var newList = [buttonID];
+ for( var j = 0; j < list.length && newList.length < 10; j++ )
+ {
+ var entry = parseInt( list[j] );
+ if( !isNaN( entry ) && entry > 0 )
+ newList.push( entry );
}
- else if( itemDetailsID != 0 )
+
+ pUser.SetTempTag( "LastTenBlacksmith", newList.join( "," ) );
+}
+
+function HasLearnedRecipe( pUser, recipeID )
+{
+ var myData = TriggerEvent( 4022, "ReadRecipeID", pUser );
+ if( !myData || myData.length == 0 )
+ return false;
+
+ for( var i = 0; i < myData.length; i++ )
{
- pUser.SetTempTag( "ITEMDETAILS", itemDetailsID );
- TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
+ var data = myData[i].split( "," );
+ if( data[0] == recipeID )
+ return true;
}
+ return false;
}
+
+function eraOK( entry )
+{
+ // Optional per-entry gates. Strings like "lbr","aos","ml","sa","hs","tol".
+ // If not present, the entry is valid for all eras.
+ if( entry.minEra && coreShardEra < EraStringToNum( entry.minEra ))
+ return false;
+ if( entry.maxEra && coreShardEra > EraStringToNum( entry.maxEra ))
+ return false;
+ return true;
+}
\ No newline at end of file
diff --git a/data/js/skill/craft/carpentry.js b/data/js/skill/craft/carpentry.js
index abf9fb725..49eaeef76 100644
--- a/data/js/skill/craft/carpentry.js
+++ b/data/js/skill/craft/carpentry.js
@@ -1,18 +1,20 @@
///
// @ts-check
-const textHue = 0x480; // Color of the text.
-const scriptID = 4025; // Use this to tell the gump what script to close.
-const gumpDelay = 2000; // Timer for the gump to reappear after crafting.
-const repairDelay = 200; // Timer for the gump to reappear after repairing an item
-const itemDetailsScriptID = 4026;
-const craftGumpID = 4027;
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// The section below is the tables for each page.
-// All you have to do is add the item to your dictionary
-// and then list the dictionary number in the right page and it will
-// add it to the crafting gump.
-///////////////////////////////////////////////////////////////////////////////////////////
+const textHue = 0x480; // Color of the text.
+const carpentryID = 4025; // Use this to tell the gump what script to close.
+const gumpDelay = 2000; // Timer for the gump to reappear after crafting.
+const repairDelay = 200; // Timer for the gump to reappear after repairing an item.
+const itemDetailsScriptID = 4026; // Generic item detail gump
+const craftGumpID = 4027; // Crafting frame gump
+const itemsPerPage = 10; // Number of craftable items shown per gump subpage
+const displayUnlearnedRecipes = true; // For future recipe use
+const coreShardEra = EraStringToNum( GetServerSetting( "CoreShardEra" ) );
+const carpentrySkillID = 11; // Carpentry skill ID
+const harvestDict = 10014; // Dictionary entry for "wood" (boards/logs)
+
+//======================================================================================
+// Data tables
+//======================================================================================
const myPage = [
@@ -47,39 +49,264 @@ const myPage = [
[10683, 10684, 10685, 10686]
];
+const craftItems = [
+ // Page 1 (button 100..110)
+ [ 73, 74, 89, 90, 91, 92, 76, 77, 78, 79, 72 ],
+
+ // Page 2 (200..212)
+ [ 50, 51, 52, 53, 57, 58, 54, 55, 56, 59, 60, 61, 62 ],
+
+ // Page 3 (300..308)
+ [ 63, 64, 65, 67, 68, 69, 70, 71, 66 ],
+
+ // Page 4 (400..404)
+ [ 80, 81, 82, 123, 124 ],
+
+ // Page 5 (500)
+ [ 75 ],
+
+ // Page 6 (600..605)
+ [ 83, 84, 85, 86, 87, 88 ],
+
+ // Page 7 (700..710)
+ [ 93, 93, 94, 93, 96, 95, 97, 98, 99, 100, 101 ],
+
+ // Page 8 (800..812)
+ [ 115, 116, 107, 108, 109, 110, 117, 118, 119, 120, 121, 122 ],
+
+ // Page 9 (900..904)
+ [ 102, 103, 104, 105, 106 ],
+
+ // Page 10 (1000..1003)
+ [ 111, 112, 113, 114 ]
+];
+
+// Map: buttonID carpentry entry
+// buttonID: 100..110, 200..212, ... 1000..1003
+// CarpentryMap[buttonID] = {
+// dictID: number,
+// page: number,
+// timerID: number,
+// makeID: number,
+// customName?: string,
+// recipeID?: number,
+// minEra?: string,
+// maxEra?: string,
+// skill: number,
+// harvest: number[] // dictionary IDs for resources; labels can be overridden in ItemDetailGump
+// };
+const CarpentryMap = {};
+
+(function initCarpentryMap()
+{
+ for( var pageIdx = 0; pageIdx < myPage.length; pageIdx++ )
+ {
+ var dictList = myPage[pageIdx];
+ var makeList = craftItems[pageIdx];
+
+ for( var i = 0; i < dictList.length && i < makeList.length; i++ )
+ {
+ // Button layout:
+ // page 1 => 100..110
+ // page 2 => 200..212
+ // page 3 => 300..308
+ // ...
+ var buttonID = ( ( pageIdx + 1 ) * 100 ) + i;
+ var dictID = dictList[i];
+ var makeID = makeList[i];
+
+ CarpentryMap[buttonID] = {
+ dictID: dictID,
+ page: pageIdx + 1,
+ timerID: pageIdx + 1,
+ makeID: makeID,
+ // recipeID: undefined,
+ // minEra: undefined,
+ // maxEra: undefined
+ skill: carpentrySkillID,
+ harvest: [ harvestDict ] // default: wood
+ };
+ }
+ }
+
+ // If you need per-entry overrides in future (recipes, extra resources, era gating),
+ // you can do:
+ // CarpentryMap[704].harvest = [ harvestDict, 11402 ]; // wood + cloth
+ // CarpentryMap[109].harvest = [ harvestDict ]; // Fishing Pole wood + cloth
+ // CarpentryMap[109].harvestNames = [ "", "Cloth" ]; // Fishing Pole wood + cloth
+ // CarpentryMap[308].harvest = [ 0 ]; // open keg Barrel Staves Barrel Hoops Barrel Lid
+ // CarpentryMap[308].harvestNames = [ "Barrel Staves", "Barrel Hoops", "Barrel Lid" ]; // open keg Barrel Staves Barrel Hoops Barrel Lid
+ // CarpentryMap[709].minEra = "ml";
+})();
+
+CarpentryMap[109].harvest = [ harvestDict, 10016 ]; // Fishing Pole wood + cloth
+CarpentryMap[308].harvest = [ 10611, 10612, 11860 ]; // open keg Barrel Staves Barrel Hoops Barrel Lid
+CarpentryMap[600].harvest = [ harvestDict, 10016 ]; // lap harp wood + cloth
+CarpentryMap[601].harvest = [ harvestDict, 10016 ]; // Standing Harp wood + cloth
+CarpentryMap[602].harvest = [ harvestDict, 10016 ]; // Drum wood + cloth
+CarpentryMap[603].harvest = [ harvestDict, 10016 ]; // Lute wood + cloth
+CarpentryMap[604].harvest = [ harvestDict, 10016 ]; // Tambourine wood + cloth
+CarpentryMap[605].harvest = [ harvestDict, 10016 ]; // Tambourine wood + cloth
+CarpentryMap[700].harvest = [ harvestDict, 10016 ]; // Small Bed (S) wood + cloth
+CarpentryMap[701].harvest = [ harvestDict, 10016 ]; // Small Bed (E) wood + cloth
+CarpentryMap[702].harvest = [ harvestDict, 10016 ]; // Large Bed (S) wood + cloth
+CarpentryMap[703].harvest = [ harvestDict, 10016 ]; // Large Bed (E) wood + cloth
+CarpentryMap[709].harvest = [ harvestDict, 10015 ]; // Pentagram wood + ingots
+CarpentryMap[710].harvest = [ harvestDict, 10015 ]; // Abbatoir wood + ingots
+CarpentryMap[800].harvest = [ harvestDict, 10016 ]; // Dressform wood + cloth
+CarpentryMap[801].harvest = [ harvestDict, 10016 ]; // Dressform wood + cloth
+CarpentryMap[802].harvest = [ harvestDict, 10016 ]; // Spin Wheel (E) wood + cloth
+CarpentryMap[803].harvest = [ harvestDict, 10016 ]; // Spin Wheel (S) wood + cloth
+CarpentryMap[804].harvest = [ harvestDict, 10016 ]; // Loom (E) wood + cloth
+CarpentryMap[805].harvest = [ harvestDict, 10016 ]; // Loom (S) wood + cloth
+CarpentryMap[806].harvest = [ harvestDict, 10015 ]; // Stone Oven (E) wood + ingots
+CarpentryMap[807].harvest = [ harvestDict, 10015 ]; // Stone Oven (S) wood + ingots
+CarpentryMap[808].harvest = [ harvestDict, 10015 ]; // Flour Mill (E) wood + ingots
+CarpentryMap[809].harvest = [ harvestDict, 10015 ]; // Flour Mill (S) wood + ingots
+CarpentryMap[900].harvest = [ harvestDict, 10015 ]; // Small Forge wood + ingots
+CarpentryMap[901].harvest = [ harvestDict, 10015 ]; // Large Forge (E) wood + ingots
+CarpentryMap[902].harvest = [ harvestDict, 10015 ]; // Large Forge (S) wood + ingots
+CarpentryMap[903].harvest = [ harvestDict, 10015 ]; // Anvil (E) wood + ingots
+CarpentryMap[904].harvest = [ harvestDict, 10015 ]; // Anvil (S) wood + ingots
+CarpentryMap[1000].harvest = [ harvestDict, 10016 ]; // Dummy (E) wood + cloth
+CarpentryMap[1001].harvest = [ harvestDict, 10016 ]; // Dummy (S) wood + cloth
+CarpentryMap[1002].harvest = [ harvestDict, 10016 ]; // Pickpocket (E) wood + cloth
+CarpentryMap[1003].harvest = [ harvestDict, 10016 ]; // Pickpocket (S) wood + cloth
+
+
function PageX( socket, pUser, pageNum )
{
- // Pages 1 - 10
- var myGump = new Gump;
- pUser.SetTempTag( "page", pageNum );
- TriggerEvent( craftGumpID, "CraftingGumpMenu", myGump, socket );
- for( var i = 0; i < myPage[pageNum - 1].length; i++ )
+ if( !socket || !ValidateObject( pUser ))
+ return;
+
+ var pageItems = [];
+
+ if( pageNum == 999 )
{
- var index = i % 10;
- if( index == 0 )
+ var lastRaw = pUser.GetTempTag( "LastTenCarpentry" ) || "";
+ var split = lastRaw.split( "," );
+ for( var i = 0; i < split.length; i++ )
{
- if( i > 0 )
- {
- myGump.AddButton( 370, 260, 4005, 4007, 0, ( i / 10 ) + 1, 0 );
- myGump.AddHTMLGump( 405, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10100, socket.language ) + "" );// NEXT PAGE
- }
+ var val = parseInt( split[i] );
+ if( !isNaN( val ) && CarpentryMap[val] )
+ pageItems.push( val );
+ }
+ }
+ else
+ {
+ for( var buttonIDStr in CarpentryMap )
+ {
+ if( !CarpentryMap.hasOwnProperty( buttonIDStr ))
+ continue;
- myGump.AddPage(( i / 10) + 1 );
+ var buttonID = parseInt( buttonIDStr );
+ var data = CarpentryMap[buttonID];
- if( i > 0 )
+ if( data.page == pageNum && eraOK( data ))
{
- myGump.AddButton( 220, 260, 4014, 4015, 0, i / 10, 0 );
- myGump.AddHTMLGump( 255, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10101, socket.language ) + "" );// PREV PAGE
+ var needsRecipe = data.recipeID;
+ var showAll = displayUnlearnedRecipes;
+
+ if( !needsRecipe || showAll || HasLearnedRecipe( pUser, needsRecipe ))
+ {
+ pageItems.push( buttonID );
+ }
}
}
- myGump.AddButton( 220, 60 + ( index * 20 ), 4005, 4007, 1, 0, ( 100 * pageNum ) + i );
- myGump.AddText( 255, 60 + ( index * 20 ), textHue, GetDictionaryEntry( myPage[pageNum - 1][i], socket.language ));
+ pageItems.sort(function(a, b){ return a - b; });
+ }
- myGump.AddButton( 480, 60 + ( index * 20 ), 4011, 4012, 1, 0, ( 2000 + ( 100 * pageNum )) + i );
+ if( pageItems.length == 0 )
+ {
+ var emptyGump = new Gump;
+ TriggerEvent( craftGumpID, "CraftingGumpMenu", emptyGump, socket );
+ emptyGump.AddPage( 1 );
+ emptyGump.AddText( 220, 60, textHue, "No items available on this page." );
+ emptyGump.Send( socket );
+ emptyGump.Free();
+ return;
}
- myGump.Send( socket );
- myGump.Free();
+
+ var subPage = pUser.GetTempTag( "subPage" );
+ var totalSubPages = Math.ceil( pageItems.length / itemsPerPage );
+
+ if( totalSubPages < 1 )
+ totalSubPages = 1;
+
+ if( subPage < 1 )
+ subPage = 1;
+
+ if( subPage > totalSubPages )
+ subPage = totalSubPages;
+
+ pUser.SetTempTag( "page", pageNum );
+ pUser.SetTempTag( "subPage", subPage );
+
+ var startIndex = ( subPage - 1 ) * itemsPerPage;
+ var endIndex = Math.min( startIndex + itemsPerPage, pageItems.length );
+
+ if( startIndex >= pageItems.length )
+ {
+ subPage = 1;
+ startIndex = 0;
+ endIndex = Math.min( itemsPerPage, pageItems.length );
+ pUser.SetTempTag( "subPage", subPage );
+ }
+
+ var carpGump = new Gump;
+ TriggerEvent( craftGumpID, "CraftingGumpMenu", carpGump, socket );
+ carpGump.AddPage( 1 );
+
+ var drawIndex = 0;
+
+ for( var i = startIndex; i < endIndex; i++ )
+ {
+ var buttonID = pageItems[i];
+ var data = CarpentryMap[buttonID];
+
+ var entryText = "";
+ if( data.customName )
+ {
+ entryText = data.customName;
+ }
+ else if( data.dictID )
+ {
+ entryText = GetDictionaryEntry( data.dictID, socket.language );
+ if( !entryText || entryText === "" )
+ entryText = "[Missing EntryID: " + data.dictID + "]";
+ }
+ else
+ {
+ entryText = "[Unnamed Item: " + buttonID + "]";
+ }
+
+ carpGump.AddButton( 220, 60 + ( drawIndex * 20 ), 4005, 4007, 1, 0, buttonID );
+ carpGump.AddText( 255, 60 + ( drawIndex * 20 ), textHue, entryText );
+
+ carpGump.AddButton( 480, 60 + ( drawIndex * 20 ), 4011, 4012, 1, 0, 2000 + buttonID );
+
+ drawIndex++;
+ }
+
+ // Prev subpage
+ if( subPage > 1 )
+ {
+ carpGump.AddButton( 220, 260, 4014, 4015, 1, 0, 8000 + ( subPage - 1 ));
+ carpGump.AddHTMLGump( 255, 263, 100, 18, 0, 0,
+ "" + GetDictionaryEntry( 10101, socket.language ) + "" ); // PREV PAGE
+ }
+
+ // Next subpage
+ if( subPage < totalSubPages )
+ {
+ carpGump.AddButton( 370, 260, 4005, 4007, 1, 0, 9000 + ( subPage + 1 ));
+ carpGump.AddHTMLGump( 405, 263, 100, 18, 0, 0,
+ "" + GetDictionaryEntry( 10100, socket.language ) + "" ); // NEXT PAGE
+ }
+
+ carpGump.Send( socket );
+ carpGump.Free();
}
function RepairTarget( pSock )
@@ -97,8 +324,8 @@ function onCallback2( pSock, ourObj )
if( !ValidateObject( mChar ) || mChar.dead )
return;
- var bItem = pSock.tempObj;
- var gumpID = scriptID + 0xffff;
+ var bItem = pSock.tempObj;
+ var gumpID = carpentryID + 0xffff;
pSock.tempObj = null;
if( ValidateObject( mChar ) && mChar.isChar && ValidateObject( bItem ) && bItem.isItem )
@@ -116,102 +343,64 @@ function onCallback2( pSock, ourObj )
}
var itemDurabilityLossEnabled = GetServerSetting( "ItemRepairDurabilityLoss" );
- var repairID = ourObj.id;
var ownerObj = GetPackOwner( ourObj, 0 );
+
if( ownerObj && mChar.serial == ownerObj.serial )
{
- var maxHitpoints = ourObj.maxhp;
+ var maxHitpoints = ourObj.maxhp;
var currentHitpoints = ourObj.health;
+
if( currentHitpoints < maxHitpoints )
{
- // Get base repair difficulty based on amount of HP missing and max hitpoints
- var deltaHP = maxHitpoints - currentHitpoints;
+ var deltaHP = maxHitpoints - currentHitpoints;
var repairDifficulty = (( deltaHP / maxHitpoints ) * 1000 );
- var minDifficulty = repairDifficulty - 250;
- var skillBonus = 0;
- var repairSkill = mChar.skills.carpentry;
+ var minDifficulty = repairDifficulty - 250;
+ var skillBonus = 0;
+ var repairSkill = mChar.skills.carpentry;
+
if( minDifficulty < 0 )
{
- // If minDifficulty is negative, add the negative value as a bonus to player's skill
- skillBonus = minDifficulty * -1;
+ skillBonus = minDifficulty * -1;
minDifficulty = 0;
}
else if( minDifficulty > repairSkill )
{
- // Player skill below minimum repair difficulty, Too difficult to make the attempt!
pSock.tempObj = bItem;
pSock.CloseGump( gumpID, 0 );
mChar.SetTempTag( "prevActionResult", "CANTREPAIR" );
mChar.StartTimer( repairDelay, 1, true );
return;
}
+
var maxDifficulty = Math.min( repairDifficulty + 250, mChar.skillCaps.carpentry );
- // Allow repair if random number between min and base difficulty is under player's skill
if( RandomNumber( minDifficulty, 1000 ) < ( Math.max( repairSkill + skillBonus, 999 )))
{
- // Give player a chance every now and then to gain skill from repairing
if( RandomNumber( 1, 5 ) == 1 )
- {
- // Run a skill-check, which might trigger a skill-gain if player passes
- mChar.CheckSkill( 11, minDifficulty, maxDifficulty ); // Skill 11 = carpentry
- }
+ mChar.CheckSkill( 11, minDifficulty, maxDifficulty ); // carpentry
- // Reduce object's max durability by 1
if( itemDurabilityLossEnabled )
- {
ourObj.maxhp -= 1;
- }
- // Repair item here
ourObj.health = ourObj.maxhp;
pSock.SoundEffect( 0x023D, true );
- // Reopen gump after a short delay
pSock.CloseGump( gumpID, 0 );
mChar.SetTempTag( "prevActionResult", "REPAIRSUCCESS" );
mChar.StartTimer( repairDelay, 1, true );
-
- // GM skill (100.0 skillpoints)
- // Item with 51 HP max
- // item with 2 hp left - 99.65% chance to repair
- // item with 25 hp left - 99.86% chance to repair
- // item with 40 hp left - 99.9% chance to repair
-
- // Expert Smith (71.5 skill points)
- // Item with 51 HP max
- // item with 2 hp left - 1.45% chance to repair
- // item with 25 hp left - 61.49% chance to repair
- // item with 40 hp left - 74.9% chance to repair
- // item with 48 hp left - 90.6% chance to repair
-
- // Apprentice Smith (51.5 skill points)
- // Item with 51 HP max
- // item with 2 hp left - 0% chance to repair
- // item with 25 hp left - 34.5% chance to repair
- // item with 40 hp left - 54.9% chance to repair
- // item with 48 hp left - 70.6% chance to repair
}
else
{
- // Failed to repair item - decrease item health!
- if( repairSkill >= 1000 ) // GM Smith
- {
+ if( repairSkill >= 1000 )
ourObj.health -= 1;
- }
- else if( repairSkill >= 715 ) // Expert Smith
- {
+ else if( repairSkill >= 715 )
ourObj.health -= 2;
- }
- else // Below Expert Smith
- {
+ else
ourObj.health -= 3;
- }
if( ourObj.health <= 0 )
{
- // Item has been destroyed!
- pSock.SysMessage( GetDictionaryEntry( 311, pSock.language ).replace(/%s/gi, ourObj.name )); // Your %s has been destroyed.
+ pSock.SysMessage( GetDictionaryEntry( 311, pSock.language ).replace(/%s/gi, ourObj.name )); // destroyed
ourObj.Delete();
}
@@ -246,436 +435,260 @@ function onTimer( pUser, timerID )
return;
var socket = pUser.socket;
+ if( socket == null )
+ return;
- switch ( timerID )
+ if( timerID >= 1 && timerID <= 10 )
{
- case 1: // Page 1
- case 2: // Page 2
- case 3: // Page 3
- case 4: // Page 4
- case 5: // Page 5
- case 6: // Page 6
- case 7: // Page 7
- case 8: // Page 8
- case 9: // Page 9
- case 10: // Page 10
- PageX( socket, pUser, timerID );
- break;
+ PageX( socket, pUser, timerID );
+ }
+ else if( timerID == 999 )
+ {
+ PageX( socket, pUser, 999 );
}
}
/** @type { ( myObj: Socket, pressed: number, gump: GumpData ) => void } */
function onGumpPress( pSock, pButton, gumpData )
{
- var pUser = pSock.currentChar;
+ if( pSock == null )
+ return;
- // Don't continue if character is invalid, or worse... dead!
+ var pUser = pSock.currentChar;
if( !ValidateObject( pUser ) || pUser.dead )
return;
- // Don't continue if player no longer has access to the crafting tool
var bItem = pSock.tempObj;
if( !ValidateObject( bItem ) || !pUser.InRange( bItem, 3 ))
{
- pSock.SysMessage( GetDictionaryEntry( 461, pSock.language )); // You are too far away.
+ pSock.SysMessage( GetDictionaryEntry( 461, pSock.language )); // too far
return;
}
if( bItem.movable == 3 )
{
- pSock.SysMessage( GetDictionaryEntry( 6031, pSock.language )); // Locked down resources cannot be used!
+ pSock.SysMessage( GetDictionaryEntry( 6031, pSock.language )); // locked down
return;
}
var iPackOwner = GetPackOwner( bItem, 0 );
- if( ValidateObject( iPackOwner )) // Is the item in a backpack?
+ if( ValidateObject( iPackOwner ))
{
- if( iPackOwner.serial != pUser.serial ) // And if so does the pack belong to the user?
+ if( iPackOwner.serial != pUser.serial )
{
- pSock.SysMessage( GetDictionaryEntry( 6032, pSock.language )); // That resource is in someone else's backpack!
+ pSock.SysMessage( GetDictionaryEntry( 6032, pSock.language )); // someone else's pack
return;
}
}
else
{
- pSock.SysMessage( GetDictionaryEntry( 6022, pSock.language )); // This has to be in your backpack before you can use it.
+ pSock.SysMessage( GetDictionaryEntry( 6022, pSock.language )); // must be in pack
return;
}
-
- var gumpID = scriptID + 0xffff;
- var makeID = 0;
- var itemDetailsID = 0;
- var timerID = 0;
-
- // If button pressed is one of the crafting buttons (or "make last")
- if(( pButton >= 100 && pButton <= 1003 ) || pButton == 5000 )
+
+ var gumpID = carpentryID + 0xffff;
+
+ // Subpage back / forward
+ if( pButton >= 8001 && pButton < 9000 )
{
- if( pButton == 5000 )
- {
- // Make Last button
- pButton = pUser.GetTempTag( "MAKELAST" );
- }
- else
- {
- // Update Make Last entry
- pUser.SetTempTag( "MAKELAST", pButton );
- }
+ var subPage = pButton - 8000;
+ var pageNum = pUser.GetTempTag( "page" );
+ pUser.SetTempTag( "subPage", subPage );
+ PageX( pSock, pUser, pageNum );
+ return;
}
- switch( pButton )
+ if( pButton >= 9001 && pButton < 10000 )
{
- case 0:
- pUser.SetTempTag( "MAKELAST", null );
- pUser.SetTempTag( "CRAFT", null )
- pSock.CloseGump( gumpID, 0 );
- break;// abort and do nothing
- case 1: // Page 1
- case 2: // Page 2
- case 3: // Page 3
- case 4: // Page 4
- case 5: // Page 5
- case 6: // Page 6
- case 7: // Page 7
- case 8: // Page 8
- case 9: // Page 9
- case 10: // Page 10
- pSock.CloseGump( gumpID, 0 );
- TriggerEvent( scriptID, "PageX", pSock, pUser, pButton );
- break;
- case 51:
- RepairTarget( pSock );
- break;
- case 100: // Barrel Staves
- makeID = 73; timerID = 1; break;
- case 101: // Barrel Lid
- makeID = 74; timerID = 1; break;
- case 102: // short music stand ( left )
- makeID = 89; timerID = 1; break;
- case 103: // short music stand ( right )
- makeID = 90; timerID = 1; break;
- case 104: // tall music stand ( left )
- makeID = 91; timerID = 1; break;
- case 105: // tall music stand ( left )
- makeID = 92; timerID = 1; break;
- case 106: // Easel ( S )
- makeID = 76; timerID = 1; break;
- case 107: // Easel ( E )
- makeID = 77; timerID = 1; break;
- case 108: // Easel ( N )
- makeID = 78; timerID = 1; break;
- case 109: // Fishing Pole
- makeID = 79; timerID = 1; break;
- case 110: // Boards
- makeID = 72; timerID = 1; break;
- case 200: // Barstool
- makeID = 50; timerID = 2; break;
- case 201: // Stool
- makeID = 51; timerID = 2; break;
- case 202: // Straw Chair
- makeID = 52; timerID = 2; break;
- case 203: // Wooden Chair
- makeID = 53; timerID = 2; break;
- case 204: // Vesper Style
- makeID = 57; timerID = 2; break;
- case 205: // Trinsic Style
- makeID = 58; timerID = 2; break;
- case 206: // Bench
- makeID = 54; timerID = 2; break;
- case 207: // Wooden Throne
- makeID = 55; timerID = 2; break;
- case 208: // Magincian Throne
- makeID = 56; timerID = 2; break;
- case 209: // Small Table
- makeID = 59; timerID = 2; break;
- case 210: // Writing Table
- makeID = 60; timerID = 2; break;
- case 211: // Large Table
- makeID = 61; timerID = 2; break;
- case 212: // Yew-wood Table
- makeID = 62; timerID = 2; break;
- case 300: // Small Chest
- makeID = 63; timerID = 3; break;
- case 301: // Small Crate
- makeID = 64; timerID = 3; break;
- case 302: // Medium Crate
- makeID = 65; timerID = 3; break;
- case 303: // Large Crate
- makeID = 67; timerID = 3; break;
- case 304: // Chest
- makeID = 68; timerID = 3; break;
- case 305: // Bookshelf
- makeID = 69; timerID = 3; break;
- case 306: // Armoire
- makeID = 70; timerID = 3; break;
- case 307: // Armoire
- makeID = 71; timerID = 3; break;
- case 308: // Open Keg
- makeID = 66; timerID = 3; break;
- case 400: // Shepard's Crook
- makeID = 80; timerID = 4; break;
- case 401: // Quarter Staff
- makeID = 81; timerID = 4; break;
- case 402: // Gnarled Staff
- makeID = 82; timerID = 4; break;
- case 403: // Club
- makeID = 123; timerID = 4; break;
- case 404: // Black Staff
- makeID = 124; timerID = 4; break;
- case 500: // Wooden Shield
- makeID = 75; timerID = 5; break;
- case 600: // Lap Harp
- makeID = 83; timerID = 6; break;
- case 601: // Standing Harp
- makeID = 84; timerID = 6; break;
- case 602: // Drum
- makeID = 85; timerID = 6; break;
- case 603: // Lute
- makeID = 86; timerID = 6; break;
- case 604: // Tambourine
- makeID = 87; timerID = 6; break;
- case 605: // Tambourine ( tassel )
- makeID = 88; timerID = 6; break;
- case 700: // bulletin board ( east )
- makeID = 93; timerID = 7; break;
- case 701: // bulletin board ( south )
- makeID = 93; timerID = 7; break;
- case 702: // Sm Bed ( E )
- makeID = 94; timerID = 7; break;
- case 703: // Sm Bed ( S )
- makeID = 93; timerID = 7; break;
- case 704: // Lg Bed ( E )
- makeID = 96; timerID = 7; break;
- case 705: // Lg Bed ( S )
- makeID = 95; timerID = 7; break;
- case 706: // Dart Board ( S )
- makeID = 97; timerID = 7; break;
- case 707: // Dart Board ( E )
- makeID = 98; timerID = 7; break;
- case 708: // Ballot Box
- makeID = 99; timerID = 7; break;
- case 709: // Pentagram
- makeID = 100; timerID = 7; break;
- case 710: // Abbatoir
- makeID = 101; timerID = 7; break;
- case 800: // Dressform
- makeID = 115; timerID = 8; break;
- case 801: // Dressform
- makeID = 116; timerID = 8; break;
- case 802: // Spin Wheel ( E )
- makeID = 107; timerID = 8; break;
- case 803: // Spin Wheel ( S )
- makeID = 108; timerID = 8; break;
- case 804: // Loom ( E )
- makeID = 109; timerID = 8; break;
- case 805: // Loom ( S )
- makeID = 110; timerID = 8; break;
- case 806: // Stone Oven ( E )
- makeID = 117; timerID = 8; break;
- case 807: // Stone Oven ( S )
- makeID = 118; timerID = 8; break;
- case 808: // Flour Mill ( E )
- makeID = 119; timerID = 8; break;
- case 809: // Flour Mill ( S )
- makeID = 120; timerID = 8; break;
- case 810: // Water Trough( E )
- makeID = 121; timerID = 8; break;
- case 811: // Water Trough( S )
- makeID = 122; timerID = 8; break;
- case 900: // Small Forge
- makeID = 102; timerID = 9; break;
- case 901: // Lg Forge ( E )
- makeID = 103; timerID = 9; break;
- case 902: // Lg Forge ( S )
- makeID = 104; timerID = 9; break;
- case 903: // Anvil ( E )
- makeID = 105; timerID = 9; break;
- case 904: // Anvil ( S )
- makeID = 106; timerID = 9; break;
- case 1000: // Dummy ( E )
- makeID = 111; timerID = 10; break;
- case 1001: // Dummy ( S )
- makeID = 112; timerID = 10; break;
- case 1002: // Pickpocket ( E )
- makeID = 113; timerID = 10; break;
- case 1003: // Pickpocket ( S )
- makeID = 114; timerID = 10; break;
- case 2100: // Barrel Staves
- itemDetailsID = 73; break;
- case 2101: // Barrel Lid
- itemDetailsID = 74; break;
- case 2102: // short music stand ( left )
- itemDetailsID = 89; break;
- case 2103: // short music stand ( right )
- itemDetailsID = 90; break;
- case 2104: // tall music stand ( left )
- itemDetailsID = 91; break;
- case 2105: // tall music stand ( left )
- itemDetailsID = 92; break;
- case 2106: // Easel ( S )
- itemDetailsID = 76; break;
- case 2107: // Easel ( E )
- itemDetailsID = 77; break;
- case 2108: // Easel ( N )
- itemDetailsID = 78; break;
- case 2109: // Fishing Pole
- itemDetailsID = 79; break;
- case 2110: // Boards
- itemDetailsID = 72; break;
- case 2200: // Barstool
- itemDetailsID = 50; break;
- case 2201: // Stool
- itemDetailsID = 51; break;
- case 2202: // Straw Chair
- itemDetailsID = 52; break;
- case 2203: // Wooden Chair
- itemDetailsID = 53; break;
- case 2204: // Vesper Style
- itemDetailsID = 57; break;
- case 2205: // Trinsic Style
- itemDetailsID = 58; break;
- case 2206: // Bench
- itemDetailsID = 54; break;
- case 2207: // Wooden Throne
- itemDetailsID = 55; break;
- case 2208: // Magincian Throne
- itemDetailsID = 56; break;
- case 2209:
- itemDetailsID = 59; break;
- case 2210: // Writing Table
- itemDetailsID = 60; break;
- case 2211: // Large Table
- itemDetailsID = 61; break;
- case 2212: // Yew-wood Table
- itemDetailsID = 62; break;
- case 2300: // Small Chest
- itemDetailsID = 63; break;
- case 2301: // Small Crate
- itemDetailsID = 64; break;
- case 2302: // Medium Crate
- itemDetailsID = 65; break;
- case 2303: // Large Crate
- itemDetailsID = 67; break;
- case 2304: // Chest
- itemDetailsID = 68; break;
- case 2305: // Bookshelf
- itemDetailsID = 69; break;
- case 2306: // Armoire
- itemDetailsID = 70; break;
- case 2307: // Armoire
- itemDetailsID = 71; break;
- case 2308: // Open Keg
- itemDetailsID = 66; break;
- case 2400: // Shepard's Crook
- itemDetailsID = 80; break;
- case 2401: // Quarter Staff
- itemDetailsID = 81; break;
- case 2402: // Gnarled Staff
- itemDetailsID = 82; break;
- case 2403: // Club
- itemDetailsID = 123; break;
- case 2404: // Black Staff
- itemDetailsID = 124; break;
- case 2500: // Wooden Shield
- itemDetailsID = 75; break;
- case 2600: // Lap Harp
- itemDetailsID = 83; break;
- case 2601: // Standing Harp
- itemDetailsID = 84; break;
- case 2602: // Drum
- itemDetailsID = 85; break;
- case 2603: // Lute
- itemDetailsID = 86; break;
- case 2604: // Tambourine
- itemDetailsID = 87; break;
- case 2605: // Tambourine ( tassel )
- itemDetailsID = 88; break;
- case 2700: // bulletin board ( east )
- itemDetailsID = 93; break;
- case 2701: // bulletin board ( south )
- itemDetailsID = 93; break;
- case 2702: // Sm Bed ( E )
- itemDetailsID = 94; break;
- case 2703: // Sm Bed ( S )
- itemDetailsID = 93; break;
- case 2704: // Lg Bed ( E )
- itemDetailsID = 96; break;
- case 2705: // Lg Bed ( S )
- itemDetailsID = 95; break;
- case 2706: // Dart Board ( S )
- itemDetailsID = 97; break;
- case 2707: // Dart Board ( E )
- itemDetailsID = 98; break;
- case 2708: // Ballot Box
- itemDetailsID = 99; break;
- case 2709: // Pentagram
- itemDetailsID = 100; break;
- case 2710: // Abbatoir
- itemDetailsID = 101; break;
- case 2800: // Dressform
- itemDetailsID = 115; break;
- case 2801: // Dressform
- itemDetailsID = 116; break;
- case 2802: // Spin Wheel ( E )
- itemDetailsID = 107; break;
- case 2803: // Spin Wheel ( S )
- itemDetailsID = 108; break;
- case 2804: // Loom ( E )
- itemDetailsID = 109; break;
- case 2805: // Loom ( S )
- itemDetailsID = 110; break;
- case 2806: // Stone Oven ( E )
- itemDetailsID = 117; break;
- case 2807: // Stone Oven ( S )
- itemDetailsID = 118; break;
- case 2808: // Flour Mill ( E )
- itemDetailsID = 119; break;
- case 2809: // Flour Mill ( S )
- itemDetailsID = 120; break;
- case 2810: // Water Trough( E )
- itemDetailsID = 121; break;
- case 2811: // Water Trough( S )
- itemDetailsID = 122; break;
- case 2900: // Small Forge
- itemDetailsID = 102; break;
- case 2901: // Lg Forge ( E )
- itemDetailsID = 103; break;
- case 2902: // Lg Forge ( S )
- itemDetailsID = 104; break;
- case 2903: // Anvil ( E )
- itemDetailsID = 105; break;
- case 2904: // Anvil ( S )
- itemDetailsID = 106; break;
- case 3000: // Dummy ( E )
- itemDetailsID = 111; break;
- case 3001: // Dummy ( S )
- itemDetailsID = 112; break;
- case 3002: // Pickpocket ( E )
- itemDetailsID = 113; break;
- case 3003: // Pickpocket ( S )
- itemDetailsID = 114; break;
- default:
- break;
+ var subPage2 = pButton - 9000;
+ var pageNum2 = pUser.GetTempTag( "page" );
+ pUser.SetTempTag( "subPage", subPage2 );
+ PageX( pSock, pUser, pageNum2 );
+ return;
+ }
+
+ // Page tabs 1..10
+ if( pButton >= 1 && pButton <= 10 )
+ {
+ pUser.SetTempTag( "page", pButton );
+ pUser.SetTempTag( "subPage", 1 );
+ PageX( pSock, pUser, pButton );
+ return;
}
- if( makeID != 0 )
+ // Last Ten
+ if( pButton == 11000 )
{
- pUser.AddScriptTrigger( 4033 ); // crafting_complete.js for applying locks to crafted containers
+ pUser.SetTempTag( "page", 999 );
+ pUser.SetTempTag( "subPage", 1 );
+ PageX( pSock, pUser, 999 );
+ return;
+ }
+
+ // Close gump
+ if( pButton == 0 )
+ {
+ pUser.SetTempTag( "MakeLast_Carpentry", null );
+ pUser.SetTempTag( "CRAFT", null );
+ pSock.CloseGump( gumpID, 0 );
+ return;
+ }
+
+ // Make Last
+ if( pButton == 5000 )
+ {
+ var last = pUser.GetTempTag( "MakeLast_Carpentry" );
+ if( last )
+ pButton = last;
+ else
+ return;
+ }
+
+ if( CarpentryMap[pButton] !== undefined )
+ {
+ var entry = CarpentryMap[pButton];
+ var makeID = entry.makeID;
+ var timerID = entry.timerID || entry.page || 1;
+
+ if( !eraOK( entry ))
+ {
+ pSock.SysMessage( "That item is not available in this era." );
+ return;
+ }
+
+ if( entry.recipeID && !TriggerEvent( 4022, "NeedRecipe", pUser, entry.recipeID ))
+ {
+ pSock.SysMessage( "You must learn that recipe from a scroll." );
+ return;
+ }
+
+ pUser.SetTempTag( "MakeLast_Carpentry", pButton );
+
+ // Let crafting_complete.js hook things like locks on containers
+ pUser.AddScriptTrigger( 4033 );
+
MakeItem( pSock, pUser, makeID );
+ AddToLastTen( pUser, pButton );
+
if( GetServerSetting( "ToolUseLimit" ))
{
bItem.usesLeft -= 1;
if( bItem.usesLeft == 0 && GetServerSetting( "ToolUseBreak" ))
{
bItem.Delete();
- pSock.SysMessage( GetDictionaryEntry( 10202, pSock.language )); // You have worn out your tool!
- // Play sound effect of tool breaking
+ pSock.SysMessage( GetDictionaryEntry( 10202, pSock.language )); // tool worn out
+ }
+ }
+
+ pUser.StartTimer( gumpDelay, timerID, carpentryID );
+ return;
+ }
+
+ // Item detail buttons: 2000 + buttonID
+ if( pButton >= 2000 && pButton < 4000 )
+ {
+ var detailButtonID = pButton - 2000;
+ var entry = CarpentryMap[detailButtonID];
+
+ if( entry )
+ {
+ pUser.SetTempTag( "ITEMDETAILS", entry.makeID );
+
+ pUser.SetTempTag( "Skill", entry.skill || carpentrySkillID );
+
+ // Clear old harvests
+ pUser.SetTempTag( "Harvest", null );
+ pUser.SetTempTag( "Harvest2", null );
+ pUser.SetTempTag( "Harvest3", null );
+ pUser.SetTempTag( "Harvest4", null );
+
+ // Clear old harvest names
+ pUser.SetTempTag( "HarvestName", null );
+ pUser.SetTempTag( "Harvest2Name", null );
+ pUser.SetTempTag( "Harvest3Name", null );
+ pUser.SetTempTag( "Harvest4Name", null );
+
+ if( entry.harvest && entry.harvest.length > 0 )
+ {
+ if( entry.harvest.length >= 1 )
+ pUser.SetTempTag( "Harvest", entry.harvest[0] );
+ if( entry.harvest.length >= 2 )
+ pUser.SetTempTag( "Harvest2", entry.harvest[1] );
+ if( entry.harvest.length >= 3 )
+ pUser.SetTempTag( "Harvest3", entry.harvest[2] );
+ if( entry.harvest.length >= 4 )
+ pUser.SetTempTag( "Harvest4", entry.harvest[3] );
}
- }
- pUser.StartTimer( gumpDelay, timerID, true );
+
+ // OPTIONAL custom names – these override the dictionary string
+ if( entry.harvestNames && entry.harvestNames.length > 0 )
+ {
+ if( entry.harvestNames.length >= 1 )
+ pUser.SetTempTag( "HarvestName", entry.harvestNames[0] );
+ if( entry.harvestNames.length >= 2 )
+ pUser.SetTempTag( "Harvest2Name", entry.harvestNames[1] );
+ if( entry.harvestNames.length >= 3 )
+ pUser.SetTempTag( "Harvest3Name", entry.harvestNames[2] );
+ if( entry.harvestNames.length >= 4 )
+ pUser.SetTempTag( "Harvest4Name", entry.harvestNames[3] );
+ }
+
+ if( entry.recipeID && entry.recipeID > 0 )
+ pUser.SetTempTag( "needRecipeID", entry.recipeID );
+ else
+ pUser.SetTempTag( "needRecipeID", 0 );
+
+ TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
+ }
+ return;
+ }
+}
+
+function AddToLastTen( pUser, buttonID )
+{
+ var raw = pUser.GetTempTag( "LastTenCarpentry" ) || "";
+ var list = raw.split( "," );
+
+ for( var i = 0; i < list.length; i++ )
+ {
+ if( parseInt( list[i] ) == buttonID )
+ {
+ list.splice( i, 1 );
+ break;
+ }
}
- else if( itemDetailsID != 0 )
+
+ var newList = [ buttonID ];
+ for( var j = 0; j < list.length && newList.length < 10; j++ )
{
- pUser.SetTempTag( "ITEMDETAILS", itemDetailsID );
- TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
+ var entry = parseInt( list[j] );
+ if( !isNaN( entry ) && entry > 0 )
+ newList.push( entry );
}
+
+ pUser.SetTempTag( "LastTenCarpentry", newList.join( "," ) );
}
+
+function HasLearnedRecipe( pUser, recipeID )
+{
+ var myData = TriggerEvent( 4022, "ReadRecipeID", pUser );
+ if( !myData || myData.length == 0 )
+ return false;
+
+ for( var i = 0; i < myData.length; i++ )
+ {
+ var data = myData[i].split( "," );
+ if( data[0] == recipeID )
+ return true;
+ }
+ return false;
+}
+
+function eraOK( entry )
+{
+ if( entry.minEra && coreShardEra < EraStringToNum( entry.minEra ))
+ return false;
+ if( entry.maxEra && coreShardEra > EraStringToNum( entry.maxEra ))
+ return false;
+ return true;
+}
\ No newline at end of file
diff --git a/data/js/skill/craft/cartography.js b/data/js/skill/craft/cartography.js
index 38f8bc563..0a92dbd90 100644
--- a/data/js/skill/craft/cartography.js
+++ b/data/js/skill/craft/cartography.js
@@ -1,193 +1,549 @@
///
// @ts-check
-const LabelHue = 0x480; // Color of the text.
-const LabelColor = 0x7FFF; // Second Color of text.
-const scriptID = 4035; // Use this to tell the gump what script to close.
-const gumpDelay = 2000; // Timer for the gump to reapear after crafting.
-const itemDetailsScriptID = 4026;
-const craftGumpID = 4027;
+const textHue = 0x480; // Color of the text.
+const cartographyID = 4035; // Script ID for this cartography gump
+const gumpDelay = 2000; // Delay (ms) before gump reappears after crafting
+const itemDetailsScriptID = 4026; // Generic item details gump
+const craftGumpID = 4027; // Shared crafting menu frame
+const itemsPerPage = 10; // Items per subpage
+const displayUnlearnedRecipes = true; // For future recipe use
+const coreShardEra = EraStringToNum( GetServerSetting( "CoreShardEra" ));
+const cartographySkillID = 12; // Skill index for "cartography" in ItemDetailGump
//////////////////////////////////////////////////////////////////////////////////////////
-// The section below is the tables for each page.
-// All you have to do is add the item to your dictionary
-// and then list the dictionary number in the right page and it will
-// add it to the crafting gump.
-///////////////////////////////////////////////////////////////////////////////////////////
+// CartographyMap
+// Keyed by makeID (create entry ID).
+// Each entry:
+// dictID - dictionary entry for row text (13100..13103)
+// page - main category page (1..N)
+// timerID - which page timer should reopen
+// skill - skill used (default: cartographySkillID)
+// recipeID? - optional recipe ID
+// minEra/maxEra? - optional era gating
+// harvest?[] - optional material dictionary IDs
+// harvestNames?[] - optional custom material names
+//
+// NOTE: For the World Map row, we use base makeID 2003.
+// At craft time, we still adjust to 2003..2007 depending on worldnumber,
+// just like the original script did.
+//
+//////////////////////////////////////////////////////////////////////////////////////////
-const myPage = [
+const CartographyMap = {
// Page 1 - Maps
- [ 13100, 13101, 13102, 13103 ]
-];
+ 2000: { dictID: 13100, page: 1, timerID: 1, harvest: [ 13004 ] }, // Local Map
+ 2001: { dictID: 13101, page: 1, timerID: 1, harvest: [ 13004 ] }, // City Map
+ 2002: { dictID: 13102, page: 1, timerID: 1, harvest: [ 13004 ] }, // Sea Chart
+ 2003: { dictID: 13103, page: 1, timerID: 1, harvest: [ 13004 ] } // World Map (base; world-specific in onGumpPress)
+};
+
+// Fill in defaults (skill, etc)
+(function initCartographyMap()
+{
+ for( var key in CartographyMap )
+ {
+ if( !CartographyMap.hasOwnProperty( key ))
+ continue;
+
+ var entry = CartographyMap[key];
+ if( entry.skill === undefined )
+ entry.skill = cartographySkillID;
+
+ // If you later want harvest info for detail gump, you can do:
+ // CartographyMap[2000].harvest = [ ];
+ // CartographyMap[2000].harvestNames = [ "Blank scroll" ];
+ }
+})();
+
+/** @type { ( socket: Socket, pUser: Character, pageNum: number ) => void } */
function PageX( socket, pUser, pageNum )
{
- // Pages 1
- var myGump = new Gump;
- pUser.SetTempTag( "page", pageNum );
- TriggerEvent( craftGumpID, "CraftingGumpMenu", myGump, socket );
- for ( var i = 0; i < myPage[pageNum - 1].length; i++ )
+ if( !socket || !ValidateObject( pUser ))
+ return;
+
+ var pageItems;
+
+ // Special Last Ten page (if you ever want it for Cartography)
+ if( pageNum == 999 )
+ {
+ var lastTenRaw = pUser.GetTempTag( "LastTenCartography" ) || "";
+ var split = lastTenRaw.split( "," );
+ pageItems = [];
+
+ for( var i = 0; i < split.length; i++ )
+ {
+ var val = parseInt( split[i] );
+ if( !isNaN( val ))
+ pageItems.push( val ); // makeID itself
+ }
+ }
+ else
{
- var index = i % 10;
- if ( index == 0 )
+ // Collect all makeIDs for this page
+ var makeIDs = [];
+ for( var key in CartographyMap )
{
- if ( i > 0 )
+ if( !CartographyMap.hasOwnProperty( key ))
+ continue;
+
+ var makeID = parseInt( key );
+ var data = CartographyMap[makeID];
+ if( !data || data.page != pageNum )
+ continue;
+
+ makeIDs.push( makeID );
+ }
+
+ // Sort by dictID so order matches dictionary sequence
+ makeIDs.sort( function( a, b )
+ {
+ var ea = CartographyMap[a];
+ var eb = CartographyMap[b];
+ if( ea && eb )
+ return ( ea.dictID || 0 ) - ( eb.dictID || 0 );
+ return a - b;
+ });
+
+ // Era / recipe filtering (hooks for future use)
+ pageItems = [];
+ for( var k = 0; k < makeIDs.length; k++ )
+ {
+ var id = makeIDs[k];
+ var data2 = CartographyMap[id];
+ if( !data2 )
+ continue;
+
+ var needsRecipe = data2.recipeID;
+ var showAll = displayUnlearnedRecipes;
+
+ if( eraOK( data2 ) && ( !needsRecipe || showAll || HasLearnedRecipe( pUser, needsRecipe )) )
+ pageItems.push( id );
+ }
+
+ // Fallback: if no items on this page and it's not page 1, go to page 1
+ if( pageItems.length == 0 && pageNum != 1 )
+ {
+ pageNum = 1;
+
+ makeIDs = [];
+ for( var key2 in CartographyMap )
{
- myGump.AddButton( 370, 260, 4005, 4007, 0, ( i / 10 ) + 1, 0 );
- myGump.AddHTMLGump( 405, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10100, socket.language ) + "" );// NEXT PAGE
+ if( !CartographyMap.hasOwnProperty( key2 ))
+ continue;
+
+ var mid2 = parseInt( key2 );
+ var d3 = CartographyMap[mid2];
+ if( !d3 || d3.page != 1 )
+ continue;
+
+ makeIDs.push( mid2 );
}
- myGump.AddPage( ( i / 10 ) + 1 );
+ makeIDs.sort( function( a, b )
+ {
+ var ea2 = CartographyMap[a];
+ var eb2 = CartographyMap[b];
+ if( ea2 && eb2 )
+ return ( ea2.dictID || 0 ) - ( eb2.dictID || 0 );
+ return a - b;
+ });
- if ( i > 0 )
+ pageItems = [];
+ for( var m = 0; m < makeIDs.length; m++ )
{
- myGump.AddButton( 220, 260, 4014, 4015, 0, i / 10, 0 );
- myGump.AddHTMLGump( 255, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10101, socket.language ) + "" );// PREV PAGE
+ var id2 = makeIDs[m];
+ var data4 = CartographyMap[id2];
+ if( !data4 )
+ continue;
+
+ var needsRecipe2 = data4.recipeID;
+ var showAll2 = displayUnlearnedRecipes;
+
+ if( eraOK( data4 ) && ( !needsRecipe2 || showAll2 || HasLearnedRecipe( pUser, needsRecipe2 )) )
+ pageItems.push( id2 );
}
}
- myGump.AddButton( 220, 60 + ( index * 20 ), 4005, 4007, 1, 0, ( 100 * pageNum ) + i );
+ }
- myGump.AddText( 255, 60 + ( index * 20 ), LabelHue, GetDictionaryEntry( myPage[pageNum - 1][i], socket.language ) );
+ // Subpage handling (future-proof; only 1 subpage needed right now)
+ var subPage = pUser.GetTempTag( "subPage" );
+ var totalSubPages = Math.ceil( pageItems.length / itemsPerPage );
- myGump.AddButton( 480, 60 + ( index * 20 ), 4011, 4012, 1, 0, 2000 + ( 100 * pageNum ) + i );
+ if( totalSubPages < 1 )
+ totalSubPages = 1;
+ if( subPage < 1 )
+ subPage = 1;
+ if( subPage > totalSubPages )
+ subPage = totalSubPages;
+
+ pUser.SetTempTag( "page", pageNum );
+ pUser.SetTempTag( "subPage", subPage );
+
+ var startIndex = ( subPage - 1 ) * itemsPerPage;
+ var endIndex = Math.min( startIndex + itemsPerPage, pageItems.length );
+
+ if( startIndex >= pageItems.length )
+ {
+ subPage = 1;
+ startIndex = 0;
+ endIndex = Math.min( itemsPerPage, pageItems.length );
+ pUser.SetTempTag( "subPage", subPage );
+ }
+
+ var cartGump = new Gump;
+ TriggerEvent( craftGumpID, "CraftingGumpMenu", cartGump, socket );
+ cartGump.AddPage( 1 );
+
+ for( var j = startIndex; j < endIndex; j++ )
+ {
+ var index = j - startIndex;
+ var makeID = pageItems[j];
+ var entryText;
+ var buttonID = makeID; // use makeID directly as buttonID
+
+ var data5 = CartographyMap[makeID];
+
+ if( !data5 )
+ {
+ entryText = "[Missing MakeID: " + makeID + "]";
+ }
+ else
+ {
+ if( data5.customName )
+ {
+ entryText = data5.customName;
+ }
+ else if( data5.dictID )
+ {
+ entryText = GetDictionaryEntry( data5.dictID, socket.language );
+ if( !entryText || entryText === "" )
+ entryText = "[Missing EntryID: " + data5.dictID + "]";
+ }
+ else
+ {
+ entryText = "[Unnamed Item: " + makeID + "]";
+ }
+ }
+
+ // Craft button uses makeID
+ cartGump.AddButton( 220, 60 + ( index * 20 ), 4005, 4007, 1, 0, buttonID );
+ cartGump.AddText( 255, 60 + ( index * 20 ), textHue, entryText );
+
+ // Detail button: 20000 + makeID
+ cartGump.AddButton( 480, 60 + ( index * 20 ), 4011, 4012, 1, 0, 20000 + buttonID );
+ }
+
+ // Prev subpage
+ if( subPage > 1 )
+ {
+ cartGump.AddButton( 220, 260, 4014, 4015, 1, 0, 8000 + ( subPage - 1 ));
+ cartGump.AddHTMLGump( 255, 263, 100, 18, false, false,
+ "" + GetDictionaryEntry( 10101, socket.language ) + "" ); // PREV PAGE
}
- myGump.Send( socket );
- myGump.Free();
+
+ // Next subpage
+ if( subPage < totalSubPages )
+ {
+ cartGump.AddButton( 370, 260, 4005, 4007, 1, 0, 9000 + ( subPage + 1 ));
+ cartGump.AddHTMLGump( 405, 263, 100, 18, false, false,
+ "" + GetDictionaryEntry( 10100, socket.language ) + "" ); // NEXT PAGE
+ }
+
+ cartGump.Send( socket );
+ cartGump.Free();
}
-/** @type { ( tObject: BaseObject, timerId: number ) => void } */
+/** @type { ( pUser: Character, timerID: number ) => void } */
function onTimer( pUser, timerID )
{
if( !ValidateObject( pUser ))
return;
- var socket = pUser.socket;
+ var pSocket = pUser.socket;
+ if( pSocket == null )
+ return;
- switch ( timerID )
+ if( timerID >= 1 && timerID <= 8 )
{
- case 1: // Page 1 - Maps
- PageX( socket, pUser, timerID );
- break;
+ PageX( pSocket, pUser, timerID );
+ }
+ else if( timerID == 999 )
+ {
+ PageX( pSocket, pUser, 999 );
}
}
-/** @type { ( myObj: Socket, pressed: number, gump: GumpData ) => void } */
-function onGumpPress( pSock, pButton, gumpData )
+/** @type { ( socket: Socket, pButton: number, gumpData: GumpData ) => void } */
+function onGumpPress( socket, pButton, gumpData )
{
- var pUser = pSock.currentChar;
+ if( socket == null )
+ return;
- // Don't continue if character is invalid, or worse... dead!
+ var pUser = socket.currentChar;
if( !ValidateObject( pUser ) || pUser.dead )
return;
- // Don't continue if player no longer has access to the crafting tool
- var bItem = pSock.tempObj;
- if( !ValidateObject( bItem ) || !pUser.InRange( bItem, 3 ))
+ var tool = socket.tempObj;
+ if( !ValidateObject( tool ) || !pUser.InRange( tool, 3 ))
{
- pSock.SysMessage( GetDictionaryEntry( 461, pSock.language )); // You are too far away.
+ socket.SysMessage( GetDictionaryEntry( 461, socket.language )); // You are too far away.
return;
}
- if( bItem.movable == 3 )
+ if( tool.movable == 3 )
{
- pSock.SysMessage( GetDictionaryEntry( 6031, pSock.language )); // Locked down resources cannot be used!
+ socket.SysMessage( GetDictionaryEntry( 6031, socket.language )); // Locked down resources cannot be used!
return;
}
- var iPackOwner = GetPackOwner( bItem, 0 );
- if( ValidateObject( iPackOwner )) // Is the item in a backpack?
+ var packOwner = GetPackOwner( tool, 0 );
+ if( ValidateObject( packOwner ))
{
- if( iPackOwner.serial != pUser.serial ) // And if so does the pack belong to the user?
+ if( packOwner.serial != pUser.serial )
{
- pSock.SysMessage( GetDictionaryEntry( 6032, pSock.language )); // That resource is in someone else's backpack!
+ socket.SysMessage( GetDictionaryEntry( 6032, socket.language )); // That is in someone else's pack.
return;
}
}
else
{
- pSock.SysMessage( GetDictionaryEntry( 6022, pSock.language )); // This has to be in your backpack before you can use it.
+ socket.SysMessage( GetDictionaryEntry( 6022, socket.language )); // Must be in your pack to use it.
+ return;
+ }
+
+ var gumpID = cartographyID + 0xffff;
+
+ // Subpage back / forward
+ if( pButton >= 8001 && pButton < 9000 )
+ {
+ var subPage = pButton - 8000;
+ var pageNum = pUser.GetTempTag( "page" );
+ pUser.SetTempTag( "subPage", subPage );
+ PageX( socket, pUser, pageNum );
+ return;
+ }
+
+ if( pButton >= 9001 && pButton < 10000 )
+ {
+ var subPage2 = pButton - 9000;
+ var pageNum2 = pUser.GetTempTag( "page" );
+ pUser.SetTempTag( "subPage", subPage2 );
+ PageX( socket, pUser, pageNum2 );
+ return;
+ }
+
+ // Page tabs (Cartography only uses page 1 for now, but up to 8 is harmless)
+ if( pButton >= 1 && pButton <= 8 )
+ {
+ pUser.SetTempTag( "page", pButton );
+ pUser.SetTempTag( "subPage", 1 );
+ PageX( socket, pUser, pButton );
+ return;
+ }
+
+ // Last Ten
+ if( pButton == 11000 )
+ {
+ pUser.SetTempTag( "page", 999 );
+ pUser.SetTempTag( "subPage", 1 );
+ PageX( socket, pUser, 999 );
+ return;
+ }
+
+ // Close gump
+ if( pButton == 0 )
+ {
+ pUser.SetTempTag( "MakeLast_Cartography", null );
+ pUser.SetTempTag( "CRAFT", null );
+ socket.CloseGump( gumpID, 0 );
return;
}
- var gumpID = scriptID + 0xffff;
- var makeID = 0;
- var itemDetailsID = 0;
+ var makeID = 0;
var timerID = 0;
- if(( pButton >= 100 && pButton <= 305 ) || pButton == 5000 )
+ // Make Last
+ if( pButton == 5000 )
{
- if( pButton == 5000 )
+ var last = pUser.GetTempTag( "MakeLast_Cartography" );
+ if( last )
+ pButton = last;
+ else
+ return;
+ }
+
+ // Craft buttons use makeID directly
+ if( CartographyMap[pButton] != undefined )
+ {
+ makeID = pButton;
+ var data = CartographyMap[makeID];
+ timerID = data.timerID || 1;
+
+ if( !eraOK( data ))
{
- // Make Last button
- pButton = pUser.GetTempTag( "MAKELAST" );
+ socket.SysMessage( "That item is not available in this era." );
+ return;
}
- else
+
+ if( data.recipeID && !TriggerEvent( 4022, "NeedRecipe", pUser, data.recipeID ))
{
- pUser.SetTempTag( "MAKELAST", pButton );
+ socket.SysMessage( "You must learn that recipe from a scroll." );
+ return;
}
- }
- switch ( pButton )
- {
- case 0: // Abort and do nothing
- pUser.SetTempTag( "MAKELAST", null );
- pUser.SetTempTag( "CRAFT", null )
- pSock.CloseGump( gumpID, 0 );
- break;
- case 1: // Page 1 - Maps
- pSock.CloseGump( gumpID, 0 );
- PageX( pSock, pUser, pButton );
- break;
- // Make Items
- case 100: // Local Map
- makeID = 2000; timerID = 1; break;
- case 101: // City Map
- makeID = 2001; timerID = 1; break;
- case 102: // Sea Chart
- makeID = 2002; timerID = 1; break;
- case 103: // World map
- mapID = 0;
+ // Save Make Last
+ pUser.SetTempTag( "MakeLast_Cartography", makeID );
+
+ // Special case: World Map uses different create entries per world
+ var makeToCraft = makeID;
+ if( makeID == 2003 ) // world map row
+ {
switch( pUser.worldnumber )
{
- case 0: mapID = 2003; break;//fel
- case 1: mapID = 2003; break;//tram
- case 2: mapID = 2004; break;//ilsh
- case 3: mapID = 2005; break;//malas
- case 4: mapID = 2006; break;//Tokuno
- case 5: mapID = 2007; break;//TerMur
+ case 0: // Felucca
+ case 1: // Trammel
+ makeToCraft = 2003; break;
+ case 2: // Ilshenar
+ makeToCraft = 2004; break;
+ case 3: // Malas
+ makeToCraft = 2005; break;
+ case 4: // Tokuno
+ makeToCraft = 2006; break;
+ case 5: // TerMur
+ makeToCraft = 2007; break;
}
- makeID = mapID; timerID = 1; break;
- // Show Item Details
- case 2100: // Local Map
- itemDetailsID = 2000; break;
- case 2101: // City Map
- itemDetailsID = 2001; break;
- case 2102: // Sea Chart
- itemDetailsID = 2002; break;
- case 2103: // World map
- itemDetailsID = 2003; break;
- default:
- break;
+ }
+
+ // Let crafting_complete.js handle map-specific setup
+ pUser.AddScriptTrigger( 4033 );
+
+ MakeItem( socket, pUser, makeToCraft );
+ AddToLastTen( pUser, makeID );
+
+ if( GetServerSetting( "ToolUseLimit" ))
+ {
+ tool.usesLeft -= 1;
+ if( tool.usesLeft == 0 && GetServerSetting( "ToolUseBreak" ))
+ {
+ tool.Delete();
+ socket.SysMessage( GetDictionaryEntry( 10202, socket.language )); // You have worn out your tool!
+ }
+ }
+
+ pUser.StartTimer( gumpDelay, timerID, cartographyID );
+ return;
}
- if( makeID != 0 )
+ // Detail buttons: 20000 + makeID
+ if( pButton >= 20000 && pButton < 30000 )
{
- pUser.AddScriptTrigger(4033); // crafting_complete.js for applying map settings
- MakeItem( pSock, pUser, makeID );
- if( GetServerSetting( "ToolUseLimit" ))
+ var detailMakeID = pButton - 20000;
+ var entry = CartographyMap[detailMakeID];
+
+ if( entry )
{
- bItem.usesLeft -= 1;
- if( bItem.usesLeft == 0 && GetServerSetting( "ToolUseBreak" ))
+ // Which item details to show
+ pUser.SetTempTag( "ITEMDETAILS", detailMakeID );
+
+ // Skill used
+ pUser.SetTempTag( "Skill", entry.skill || cartographySkillID );
+
+ // Clear old harvest tags
+ pUser.SetTempTag( "Harvest", null );
+ pUser.SetTempTag( "Harvest2", null );
+ pUser.SetTempTag( "Harvest3", null );
+ pUser.SetTempTag( "Harvest4", null );
+
+ // Clear old custom harvest names
+ pUser.SetTempTag( "HarvestName", null );
+ pUser.SetTempTag( "Harvest2Name", null );
+ pUser.SetTempTag( "Harvest3Name", null );
+ pUser.SetTempTag( "Harvest4Name", null );
+
+ // If you later add entry.harvest = [ ... ], push them into tags
+ if( entry.harvest && entry.harvest.length > 0 )
+ {
+ if( entry.harvest.length >= 1 )
+ pUser.SetTempTag( "Harvest", entry.harvest[0] );
+ if( entry.harvest.length >= 2 )
+ pUser.SetTempTag( "Harvest2", entry.harvest[1] );
+ if( entry.harvest.length >= 3 )
+ pUser.SetTempTag( "Harvest3", entry.harvest[2] );
+ if( entry.harvest.length >= 4 )
+ pUser.SetTempTag( "Harvest4", entry.harvest[3] );
+ }
+
+ // OPTIONAL custom names – override / supplement dictionary labels
+ if( entry.harvestNames && entry.harvestNames.length > 0 )
{
- bItem.Delete();
- pSock.SysMessage( GetDictionaryEntry( 10202, pSock.language )); // You have worn out your tool!
- // Play sound effect of tool breaking
+ if( entry.harvestNames.length >= 1 )
+ pUser.SetTempTag( "HarvestName", entry.harvestNames[0] );
+ if( entry.harvestNames.length >= 2 )
+ pUser.SetTempTag( "Harvest2Name", entry.harvestNames[1] );
+ if( entry.harvestNames.length >= 3 )
+ pUser.SetTempTag( "Harvest3Name", entry.harvestNames[2] );
+ if( entry.harvestNames.length >= 4 )
+ pUser.SetTempTag( "Harvest4Name", entry.harvestNames[3] );
}
- }
- pUser.StartTimer( gumpDelay, timerID, true );
+
+ if( entry.recipeID && entry.recipeID > 0 )
+ pUser.SetTempTag( "needRecipeID", entry.recipeID );
+ else
+ pUser.SetTempTag( "needRecipeID", 0 );
+
+ TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
+ }
+ return;
}
- else if( itemDetailsID != 0 )
+}
+
+// Last Ten handling
+function AddToLastTen( pUser, makeID )
+{
+ var raw = pUser.GetTempTag( "LastTenCartography" ) || "";
+ var list = raw.split( "," );
+
+ for( var i = 0; i < list.length; i++ )
+ {
+ if( parseInt( list[i] ) == makeID )
+ {
+ list.splice( i, 1 );
+ break;
+ }
+ }
+
+ var newList = [ makeID ];
+ for( var j = 0; j < list.length && newList.length < 10; j++ )
{
- pUser.SetTempTag( "ITEMDETAILS", itemDetailsID );
- TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
+ var entry = parseInt( list[j] );
+ if( !isNaN( entry ) && entry > 0 )
+ newList.push( entry );
}
+
+ pUser.SetTempTag( "LastTenCartography", newList.join( "," ) );
}
+
+function HasLearnedRecipe( pUser, recipeID )
+{
+ var myData = TriggerEvent( 4022, "ReadRecipeID", pUser );
+ if( !myData || myData.length == 0 )
+ return false;
+
+ for( var i = 0; i < myData.length; i++ )
+ {
+ var data = myData[i].split( "," );
+ if( data[0] == recipeID )
+ return true;
+ }
+ return false;
+}
+
+function eraOK( entry )
+{
+ if( entry.minEra && coreShardEra < EraStringToNum( entry.minEra ))
+ return false;
+ if( entry.maxEra && coreShardEra > EraStringToNum( entry.maxEra ))
+ return false;
+ return true;
+}
\ No newline at end of file
diff --git a/data/js/skill/craft/cooking.js b/data/js/skill/craft/cooking.js
index 393b4306a..236b78826 100644
--- a/data/js/skill/craft/cooking.js
+++ b/data/js/skill/craft/cooking.js
@@ -1,389 +1,475 @@
///
// @ts-check
-const LabelHue = 0x480; // Color of the text.
-const LabelColor = 0x7FFF; // Second Color of text.
-const scriptID = 4034; // Use this to tell the gump what script to close.
-const gumpDelay = 2000; // Timer for the gump to reapear after crafting.
-const itemDetailsScriptID = 4026;
-const craftGumpID = 4027;
-const manualMillTarget = true; // If true, player must manually target mills when grinding wheat
+const textHue = 0x480; // Color of the text.
+const cookingID = 4034; // Script ID for this cooking gump
+const gumpDelay = 2000; // Delay (ms) before gump reappears after crafting
+const itemDetailsScriptID = 4026; // Generic item details gump
+const craftGumpID = 4027; // Shared crafting menu frame
+const itemsPerPage = 10; // Items per subpage
+const displayUnlearnedRecipes = true; // For future recipe use
+const coreShardEra = EraStringToNum( GetServerSetting( "CoreShardEra" ));
+const cookingSkillID = 13; // Index for "cooking" in ItemDetailGump skillNames[]
+const manualMillTarget = true; // If true, player must target mills when grinding wheat
+
+// Mills / ovens / heat sources for AreaItemFunction + StaticInRange checks
+const mills = [
+ 0x188b, 0x1893, 0x1920, 0x1922, 0x192c, 0x192e
+];
+
+const ovens = [
+ 0x0461, 0x046f, 0x092b, 0x093f
+];
+
+const heatSources = [
+ 0x0461, 0x0480, 0x092B, 0x0933, 0x0937, 0x0942, 0x0945, 0x0950, 0x0953,
+ 0x095e, 0x0961, 0x096c, 0x0de3, 0x0de8, 0x0fac
+];
+
+function FindNearbyMills( pUser, trgItem, pSock )
+{
+ if( !ValidateObject( trgItem ) || !trgItem.isItem )
+ return false;
+
+ return ( mills.indexOf( trgItem.id ) != -1 );
+}
+function FindNearbyOvens( pUser, trgItem, pSock )
+{
+ if( !ValidateObject( trgItem ) || !trgItem.isItem )
+ return false;
+
+ return ( ovens.indexOf( trgItem.id ) != -1 );
+}
+
+function FindNearbyHeatSources( pUser, trgItem, pSock )
+{
+ if( !ValidateObject( trgItem ) || !trgItem.isItem )
+ return false;
+
+ return ( heatSources.indexOf( trgItem.id ) != -1 );
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// CookingMap
+// Keyed by makeID (create entry ID).
+// Each entry:
+// dictID - dictionary entry for the row text (11606..11635)
+// page - main category page (1..4)
+// timerID - which page timer should reopen
+// skill - skill used (default: cookingSkillID)
+// recipeID? - optional recipe ID (for recipe system)
+// minEra/maxEra? - optional era gating
+// harvest?[] - optional dictIDs for MATERIALS list in ItemDetailGump
+// harvestNames?[] - optional custom material names (works with your new custom-name system)
+//
//////////////////////////////////////////////////////////////////////////////////////////
-// The section below is the tables for each page.
-// All you have to do is add the item to your dictionary
-// and then list the dictionary number in the right page and it will
-// add it to the crafting gump.
-///////////////////////////////////////////////////////////////////////////////////////////
-const myPage = [
+const CookingMap = {
// Page 1 - Ingredients
- [ 11606, 11607, 11608, 11609, 11610 ],
+ 1500: { dictID: 11606, page: 1, timerID: 1, harvest: [ 11636 ] }, // Sack of Flour
+ 1501: { dictID: 11607, page: 1, timerID: 1, harvest: [ 11637, 11638 ] }, // Dough
+ 1502: { dictID: 11608, page: 1, timerID: 1, harvest: [ 11607, 11639 ] }, // Sweet Dough
+ 1503: { dictID: 11609, page: 1, timerID: 1, harvest: [ 11637, 11608 ] }, // Cake Mix
+ 1504: { dictID: 11610, page: 1, timerID: 1, harvest: [ 11639, 11608 ] }, // Cookie Mix
// Page 2 - Preparations
- [ 11611, 11612, 11613, 11614, 11615, 11616, 11617, 11618 ],
+ 1550: { dictID: 11611, page: 2, timerID: 2, harvest: [ 11607, 11640 ] }, // Unbaked Quiche
+ 1551: { dictID: 11612, page: 2, timerID: 2, harvest: [ 11607, 11641 ] }, // Unbaked Meat Pie
+ 1552: { dictID: 11613, page: 2, timerID: 2, harvest: [ 11607, 11642 ] }, // Uncooked Sausage Pizza
+ 1553: { dictID: 11614, page: 2, timerID: 2, harvest: [ 11607, 11643 ] }, // Uncooked Cheese Pizza
+ 1554: { dictID: 11615, page: 2, timerID: 2, harvest: [ 11607, 11644 ] }, // Unbaked Fruit Pie
+ 1555: { dictID: 11616, page: 2, timerID: 2, harvest: [ 11607, 11645 ] }, // Unbaked Peach Cobbler
+ 1556: { dictID: 11617, page: 2, timerID: 2, harvest: [ 11607, 11646 ] }, // Unbaked Apple Pie
+ 1557: { dictID: 11618, page: 2, timerID: 2, harvest: [ 11607, 11647 ] }, // Unbaked Pumpkin Pie
// Page 3 - Baking
- [11619, 11620, 11621, 11657, 11622, 11623, 11624, 11625, 11626, 11627, 11628, 11629 ],
+ 1600: { dictID: 11619, page: 3, timerID: 3, harvest: [ 11607 ] }, // Bread Loaf
+ 1601: { dictID: 11620, page: 3, timerID: 3, harvest: [ 11610 ] }, // Pan of Cookies
+ 1602: { dictID: 11621, page: 3, timerID: 3, harvest: [ 11609 ] }, // Cake
+ 1603: { dictID: 11657, page: 3, timerID: 3, harvest: [ 11608 ] }, // Muffins
+ 1604: { dictID: 11622, page: 3, timerID: 3, harvest: [ 11611 ] }, // Baked Quiche
+ 1605: { dictID: 11623, page: 3, timerID: 3, harvest: [ 11612 ] }, // Baked Meat Pie
+ 1606: { dictID: 11624, page: 3, timerID: 3, harvest: [ 11613 ] }, // Sausage Pizza
+ 1607: { dictID: 11625, page: 3, timerID: 3, harvest: [ 11614 ] }, // Cheese Pizza
+ 1608: { dictID: 11626, page: 3, timerID: 3, harvest: [ 11615 ] }, // Baked Fruit Pie
+ 1609: { dictID: 11627, page: 3, timerID: 3, harvest: [ 11616 ] }, // Baked Peach Cobbler
+ 1610: { dictID: 11628, page: 3, timerID: 3, harvest: [ 11617 ] }, // Baked Apple Pie
+ 1611: { dictID: 11629, page: 3, timerID: 3, harvest: [ 11618 ] }, // Baked Pumpkin Pie
// Page 4 - Barbecue
- [ 11630, 11631, 11632, 11633, 11634, 11635 ]
+ 1650: { dictID: 11630, page: 4, timerID: 4, harvest: [ 11648 ] }, // Cooked Bird
+ 1651: { dictID: 11631, page: 4, timerID: 4, harvest: [ 11649 ] }, // Chicken Leg
+ 1652: { dictID: 11632, page: 4, timerID: 4, harvest: [ 11650 ] }, // Fish Steak
+ 1653: { dictID: 11633, page: 4, timerID: 4, harvest: [ 11651 ] }, // Fried Eggs
+ 1654: { dictID: 11634, page: 4, timerID: 4, harvest: [ 11652 ] }, // Leg of Lamb
+ 1655: { dictID: 11635, page: 4, timerID: 4, harvest: [ 11653 ] } // Cut of Ribs
+};
+
+// Fill in defaults (skill, etc)
+(function initCookingMap()
+{
+ for( var key in CookingMap )
+ {
+ if( !CookingMap.hasOwnProperty( key ))
+ continue;
-];
+ var entry = CookingMap[key];
+
+ if( entry.skill === undefined )
+ entry.skill = cookingSkillID;
+
+ // In future you can add:
+ // entry.harvest = [ dictID_for_flour, dictID_for_water ];
+ // entry.harvestNames = [ "Flour", "Water" ];
+ }
+})();
+/** @type { ( socket: Socket, pUser: Character, pageNum: number ) => void } */
function PageX( socket, pUser, pageNum )
{
- // Pages 1 - 4
- var myGump = new Gump;
- pUser.SetTempTag( "page", pageNum );
- TriggerEvent( craftGumpID, "CraftingGumpMenu", myGump, socket );
- for ( var i = 0; i < myPage[pageNum - 1].length; i++ )
+ if( !socket || !ValidateObject( pUser ))
+ return;
+
+ var pageItems;
+
+ // No "Last Ten" page here (but we keep the infrastructure if you want it later)
+ if( pageNum == 999 )
+ {
+ var lastTenRaw = pUser.GetTempTag( "LastTenCooking" ) || "";
+ var split = lastTenRaw.split( "," );
+ pageItems = [];
+
+ for( var i = 0; i < split.length; i++ )
+ {
+ var val = parseInt( split[i] );
+ if( !isNaN( val ))
+ pageItems.push( val );
+ }
+ }
+ else
{
- var index = i % 10;
- if ( index == 0 )
+ // Collect all makeIDs for this page
+ var makeIDs = [];
+ for( var key in CookingMap )
+ {
+ if( !CookingMap.hasOwnProperty( key ))
+ continue;
+
+ var makeID = parseInt( key );
+ var data = CookingMap[makeID];
+ if( !data || data.page != pageNum )
+ continue;
+
+ makeIDs.push( makeID );
+ }
+
+ // Sort by dictID so order matches dictionary sequence
+ makeIDs.sort( function( a, b )
{
- if ( i > 0 )
+ var ea = CookingMap[a];
+ var eb = CookingMap[b];
+ if( ea && eb )
+ return ( ea.dictID || 0 ) - ( eb.dictID || 0 );
+ return a - b;
+ });
+
+ // Era / recipe filtering (hooks for future use)
+ pageItems = [];
+ for( var k = 0; k < makeIDs.length; k++ )
+ {
+ var id = makeIDs[k];
+ var data2 = CookingMap[id];
+ if( !data2 )
+ continue;
+
+ var needsRecipe = data2.recipeID;
+ var showAll = displayUnlearnedRecipes;
+
+ if( eraOK( data2 ) && ( !needsRecipe || showAll || HasLearnedRecipe( pUser, needsRecipe )) )
+ pageItems.push( id );
+ }
+
+ // Fallback: if no items on this page and it's not page 1, go to page 1
+ if( pageItems.length == 0 && pageNum != 1 )
+ {
+ pageNum = 1;
+
+ makeIDs = [];
+ for( var key2 in CookingMap )
{
- myGump.AddButton( 370, 260, 4005, 4007, 0, ( i / 10 ) + 1, 0 );
- myGump.AddHTMLGump( 405, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10100, socket.language ) + "" );// NEXT PAGE
- }
+ if( !CookingMap.hasOwnProperty( key2 ))
+ continue;
+
+ var mid2 = parseInt( key2 );
+ var d3 = CookingMap[mid2];
+ if( !d3 || d3.page != 1 )
+ continue;
- myGump.AddPage( ( i / 10 ) + 1 );
+ makeIDs.push( mid2 );
+ }
- if ( i > 0 )
+ makeIDs.sort( function( a, b )
+ {
+ var ea2 = CookingMap[a];
+ var eb2 = CookingMap[b];
+ if( ea2 && eb2 )
+ return ( ea2.dictID || 0 ) - ( eb2.dictID || 0 );
+ return a - b;
+ });
+
+ pageItems = [];
+ for( var m = 0; m < makeIDs.length; m++ )
{
- myGump.AddButton( 220, 260, 4014, 4015, 0, i / 10, 0 );
- myGump.AddHTMLGump( 255, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10101, socket.language ) + "" );// PREV PAGE
+ var id2 = makeIDs[m];
+ var data4 = CookingMap[id2];
+ if( !data4 )
+ continue;
+
+ var needsRecipe2 = data4.recipeID;
+ var showAll2 = displayUnlearnedRecipes;
+
+ if( eraOK( data4 ) && ( !needsRecipe2 || showAll2 || HasLearnedRecipe( pUser, needsRecipe2 )) )
+ pageItems.push( id2 );
}
}
- myGump.AddButton( 220, 60 + ( index * 20 ), 4005, 4007, 1, 0, ( 100 * pageNum ) + i );
+ }
+
+ // Subpage handling (future-proof; currently only 1 subpage per page)
+ var subPage = pUser.GetTempTag( "subPage" );
+ var totalSubPages = Math.ceil( pageItems.length / itemsPerPage );
- myGump.AddText( 255, 60 + ( index * 20 ), LabelHue, GetDictionaryEntry( myPage[pageNum - 1][i], socket.language ));
+ if( totalSubPages < 1 )
+ totalSubPages = 1;
+ if( subPage < 1 )
+ subPage = 1;
+ if( subPage > totalSubPages )
+ subPage = totalSubPages;
- myGump.AddButton( 480, 60 + ( index * 20 ), 4011, 4012, 1, 0, 2000 + ( 100 * pageNum ) + i );
+ pUser.SetTempTag( "page", pageNum );
+ pUser.SetTempTag( "subPage", subPage );
+
+ var startIndex = ( subPage - 1 ) * itemsPerPage;
+ var endIndex = Math.min( startIndex + itemsPerPage, pageItems.length );
+
+ if( startIndex >= pageItems.length )
+ {
+ subPage = 1;
+ startIndex = 0;
+ endIndex = Math.min( itemsPerPage, pageItems.length );
+ pUser.SetTempTag( "subPage", subPage );
}
- myGump.Send( socket );
- myGump.Free();
-}
-const mills = [
- 0x188b, 0x1893, 0x1920, 0x1922, 0x192c, 0x192e
-];
-function FindNearbyMills( pUser, trgItem, pSock )
-{
- if( !ValidateObject( trgItem ) || !trgItem.isItem )
- return false;
+ var cookGump = new Gump;
+ TriggerEvent( craftGumpID, "CraftingGumpMenu", cookGump, socket );
+ cookGump.AddPage( 1 );
- return ( mills.indexOf( trgItem.id ) != -1 );
-}
+ for( var j = startIndex; j < endIndex; j++ )
+ {
+ var index = j - startIndex;
+ var makeID = pageItems[j];
+ var entryText;
+ var buttonID = makeID; // use makeID directly as buttonID
-const ovens = [
- 0x461, 0x46F, 0x92B, 0x93F
-];
-function FindNearbyOvens( pUser, trgItem, pSock )
-{
- if( !ValidateObject( trgItem ) || !trgItem.isItem )
- return false;
+ var data5 = CookingMap[makeID];
- return ( ovens.indexOf( trgItem.id ) != -1 );
-}
+ if( !data5 )
+ {
+ entryText = "[Missing MakeID: " + makeID + "]";
+ }
+ else
+ {
+ if( data5.customName )
+ {
+ entryText = data5.customName;
+ }
+ else if( data5.dictID )
+ {
+ entryText = GetDictionaryEntry( data5.dictID, socket.language );
+ if( !entryText || entryText === "" )
+ entryText = "[Missing EntryID: " + data5.dictID + "]";
+ }
+ else
+ {
+ entryText = "[Unnamed Item: " + makeID + "]";
+ }
+ }
-const heatSources = [
- 0x0461, 0x0480, 0x092B, 0x0933, 0x0937, 0x0942, 0x0945, 0x0950, 0x0953,
- 0x095e, 0x0961, 0x096c, 0x0de3, 0x0de8, 0x0fac
-];
-function FindNearbyHeatSources( pUser, trgItem, pSock )
-{
- if( !ValidateObject( trgItem ) || !trgItem.isItem )
- return false;
+ // Craft button uses makeID
+ cookGump.AddButton( 220, 60 + ( index * 20 ), 4005, 4007, 1, 0, buttonID );
+ cookGump.AddText( 255, 60 + ( index * 20 ), textHue, entryText );
- return ( heatSources.indexOf( trgItem.id ) != -1 );
+ // Detail button: 20000 + makeID
+ cookGump.AddButton( 480, 60 + ( index * 20 ), 4011, 4012, 1, 0, 20000 + buttonID );
+ }
+
+ // Prev subpage
+ if( subPage > 1 )
+ {
+ cookGump.AddButton( 220, 260, 4014, 4015, 1, 0, 8000 + ( subPage - 1 ));
+ cookGump.AddHTMLGump( 255, 263, 100, 18, false, false,
+ "" + GetDictionaryEntry( 10101, socket.language ) + "" ); // PREV PAGE
+ }
+
+ // Next subpage
+ if( subPage < totalSubPages )
+ {
+ cookGump.AddButton( 370, 260, 4005, 4007, 1, 0, 9000 + ( subPage + 1 ));
+ cookGump.AddHTMLGump( 405, 263, 100, 18, false, false,
+ "" + GetDictionaryEntry( 10100, socket.language ) + "" ); // NEXT PAGE
+ }
+
+ cookGump.Send( socket );
+ cookGump.Free();
}
-/** @type { ( tObject: BaseObject, timerId: number ) => void } */
+/** @type { ( pUser: Character, timerID: number ) => void } */
function onTimer( pUser, timerID )
{
if( !ValidateObject( pUser ))
return;
- var socket = pUser.socket;
+ var pSocket = pUser.socket;
+ if( pSocket == null )
+ return;
- switch ( timerID )
+ if( timerID >= 1 && timerID <= 4 )
{
- case 1: // Page 1 - Ingredients
- case 2: // Page 2 - Preparation
- case 3: // Page 3 - Baking
- case 4: // Page 4 - Barbecue
- PageX( socket, pUser, timerID );
- break;
+ PageX( pSocket, pUser, timerID );
+ }
+ else if( timerID == 999 )
+ {
+ PageX( pSocket, pUser, 999 );
}
}
-/** @type { ( myObj: Socket, pressed: number, gump: GumpData ) => void } */
-function onGumpPress( pSock, pButton, gumpData )
+/** @type { ( socket: Socket, pButton: number, gumpData: GumpData ) => void } */
+function onGumpPress( socket, pButton, gumpData )
{
- var pUser = pSock.currentChar;
+ if( socket == null )
+ return;
- // Don't continue if character is invalid, or worse... dead!
+ var pUser = socket.currentChar;
if( !ValidateObject( pUser ) || pUser.dead )
return;
- // Don't continue if player no longer has access to the crafting tool
- var bItem = pSock.tempObj;
- if( !ValidateObject( bItem ) || !pUser.InRange( bItem, 3 ))
+ // Don't continue if player no longer has access to the cooking tool
+ var tool = socket.tempObj;
+ if( !ValidateObject( tool ) || !pUser.InRange( tool, 3 ))
{
- pSock.SysMessage( GetDictionaryEntry( 461, pSock.language )); // You are too far away.
+ socket.SysMessage( GetDictionaryEntry( 461, socket.language )); // You are too far away.
return;
}
- if( bItem.movable == 3 )
+ if( tool.movable == 3 )
{
- pSock.SysMessage( GetDictionaryEntry( 6031, pSock.language )); // Locked down resources cannot be used!
+ socket.SysMessage( GetDictionaryEntry( 6031, socket.language )); // Locked down resources cannot be used!
return;
}
- var iPackOwner = GetPackOwner( bItem, 0 );
- if( ValidateObject( iPackOwner )) // Is the item in a backpack?
+ var packOwner = GetPackOwner( tool, 0 );
+ if( ValidateObject( packOwner ))
{
- if( iPackOwner.serial != pUser.serial ) // And if so does the pack belong to the user?
+ if( packOwner.serial != pUser.serial )
{
- pSock.SysMessage( GetDictionaryEntry( 6032, pSock.language )); // That resource is in someone else's backpack!
+ socket.SysMessage( GetDictionaryEntry( 6032, socket.language )); // That is in someone else's backpack!
return;
}
}
else
{
- pSock.SysMessage( GetDictionaryEntry( 6022, pSock.language )); // This has to be in your backpack before you can use it.
+ socket.SysMessage( GetDictionaryEntry( 6022, socket.language )); // This must be in your pack to use.
return;
}
- var gumpID = scriptID + 0xffff;
- var makeID = 0;
- var itemDetailsID = 0;
- var timerID = 0;
+ var gumpID = cookingID + 0xffff;
- // Check for nearby heatsource
- var nearbyHeatSource = 0;
- var nearbyMill = 0;
- var nearbyOven = 0;
+ // Subpage back / forward
+ if( pButton >= 8001 && pButton < 9000 )
+ {
+ var subPage = pButton - 8000;
+ var pageNum = pUser.GetTempTag( "page" );
+ pUser.SetTempTag( "subPage", subPage );
+ PageX( socket, pUser, pageNum );
+ return;
+ }
- // Make Last Button
- if( pButton == 5000 )
+ if( pButton >= 9001 && pButton < 10000 )
{
- pButton = pUser.GetTempTag( "MAKELAST" );
+ var subPage2 = pButton - 9000;
+ var pageNum2 = pUser.GetTempTag( "page" );
+ pUser.SetTempTag( "subPage", subPage2 );
+ PageX( socket, pUser, pageNum2 );
+ return;
}
- if( pButton >= 300 && pButton <= 311 )
+ // Page tabs (1–4: Ingredients, Preparation, Baking, Barbecue)
+ if( pButton >= 1 && pButton <= 4 )
{
- // Baking - Requires Oven
- nearbyOven = AreaItemFunction( "FindNearbyOvens", pUser, 2, pSock );
- if( nearbyOven > 0 )
- {
- pUser.SetTempTag( "MAKELAST", pButton );
- }
- else
- {
- // No dynamic oven found nearby! Look for a static one?
- var staticFound = false;
- for( var i = 0; i < ovens.length; i++ )
- {
- if( StaticInRange( pUser.x, pUser.y, pUser.worldnumber, 2, ovens[i] ))
- {
- staticFound = true;
- break;
- }
- }
- if( !staticFound )
- {
- pUser.SetTempTag( "prevActionResult", "NOOVEN" );
- }
- }
+ pUser.SetTempTag( "page", pButton );
+ pUser.SetTempTag( "subPage", 1 );
+ PageX( socket, pUser, pButton );
+ return;
}
- else if( pButton >= 400 && pButton <= 405 )
+
+ // Last Ten (if you ever wire it into the main craft gump)
+ if( pButton == 11000 )
{
- // Barbecue - Requires Fire/Generic Heat Source
- nearbyHeatSource = AreaItemFunction( "FindNearbyHeatSources", pUser, 2, pSock );
- if( nearbyHeatSource > 0 )
- {
- pUser.SetTempTag( "MAKELAST", pButton );
- }
- else
- {
- // No dynamic heat source found nearby! Look for a static one?
- var staticFound = false;
- for( var i = 0; i < heatSources.length; i++ )
- {
- if( StaticInRange( pUser.x, pUser.y, pUser.worldnumber, 2, heatSources[i] ))
- {
- staticFound = true;
- break;
- }
- }
- if( !staticFound )
- {
- pUser.SetTempTag( "prevActionResult", "NOHEATSOURCE" );
- }
- }
+ pUser.SetTempTag( "page", 999 );
+ pUser.SetTempTag( "subPage", 1 );
+ PageX( socket, pUser, 999 );
+ return;
}
- else if( pButton >= 101 && pButton <= 207 )
+
+ // Close gump
+ if( pButton == 0 )
{
- // Ingredients and Preparation - no heatsource required
- pUser.SetTempTag( "MAKELAST", pButton );
+ pUser.SetTempTag( "MakeLast_Cooking", null );
+ pUser.SetTempTag( "CRAFT", null );
+ socket.CloseGump( gumpID, 0 );
+ return;
}
- switch ( pButton )
+ // Make Last
+ if( pButton == 5000 )
{
- case 0: // Abort and do nothing
- pUser.SetTempTag( "MAKELAST", null );
- pUser.SetTempTag( "CRAFT", null )
- pSock.CloseGump( gumpID, 0 );
- break;
- case 1: // Page 1 - Ingredients
- case 2: // Page 2 - Preparation
- case 3: // Page 3 - Baking
- case 4: // Page 4 - Barbecue
- pSock.CloseGump( gumpID, 0 );
- PageX( pSock, pUser, pButton );
- break;
- // Make Items
- // Page 1 - Ingredients
- case 100: // Sack of Flour
- makeID = 1500; timerID = 1; break;
- case 101: // Dough
- makeID = 1501; timerID = 1; break;
- case 102: // Sweet Dough
- makeID = 1502; timerID = 1; break;
- case 103: // Cake Mix
- makeID = 1503; timerID = 1; break;
- case 104: // Cookie Mix
- makeID = 1504; timerID = 1; break;
- // Page 2 - Preparation
- case 200: // Unbaked Quiche
- makeID = 1550; timerID = 2; break;
- case 201: // Unbaked Meat Pie
- makeID = 1551; timerID = 2; break;
- case 202: // Uncooked Sausage Pizza
- makeID = 1552; timerID = 2; break;
- case 203: // Uncooked Cheese Pizza
- makeID = 1553; timerID = 2; break;
- case 204: // Unbaked Fruit Pie
- makeID = 1554; timerID = 2; break;
- case 205: // Unbaked Peach Cobbler
- makeID = 1555; timerID = 2; break;
- case 206: // Unbaked Applie Pie
- makeID = 1556; timerID = 2; break;
- case 207: // Unbaked Pumpkin Pie
- makeID = 1557; timerID = 2; break;
- // Page 3 - Baking
- case 300: // Bread Loaf
- makeID = 1600; timerID = 3; break;
- case 301: // Pan of Cookies
- makeID = 1601; timerID = 3; break;
- case 302: // Cake
- makeID = 1602; timerID = 3; break;
- case 303: // Muffins
- makeID = 1603; timerID = 3; break;
- case 304: // Baked Quiche
- makeID = 1604; timerID = 3; break;
- case 305: // Baked Meat Pie
- makeID = 1605; timerID = 3; break;
- case 306: // Sausage Pizza
- makeID = 1606; timerID = 3; break;
- case 307: // Cheese Pizza
- makeID = 1607; timerID = 3; break;
- case 308: // Baked Fruit Pie
- makeID = 1608; timerID = 3; break;
- case 309: // Baked Peach Cobbler
- makeID = 1609; timerID = 3; break;
- case 310: // Baked Applie Pie
- makeID = 1610; timerID = 3; break;
- case 311: // Baked Pumpkin Pie
- makeID = 1611; timerID = 3; break;
- // Page 4 - Barbecue
- case 400: // Cooked Bird
- makeID = 1650; timerID = 4; break;
- case 401: // Chicken Leg
- makeID = 1651; timerID = 4; break;
- case 402: // Fish Steak
- makeID = 1652; timerID = 4; break;
- case 403: // Fried Eggs
- makeID = 1653; timerID = 4; break;
- case 404: // Leg of Lamb
- makeID = 1654; timerID = 4; break;
- case 405: // Cut of Ribs
- makeID = 1655; timerID = 4; break;
- // Show Item Details
- case 2100: // Sack of Flour
- itemDetailsID = 1500; break;
- case 2101: // Dough
- itemDetailsID = 1501; break;
- case 2102: // Sweet Dough
- itemDetailsID = 1502; break;
- case 2103: // Cake Mix
- itemDetailsID = 1503; break;
- case 2104: // Cookie Mix
- itemDetailsID = 1504; break;
- case 2200: // Unbaked Quiche
- itemDetailsID = 1550; break;
- case 2201: // Unbaked Meat Pie
- itemDetailsID = 1551; break;
- case 2202: // Uncooked Sausage Pizza
- itemDetailsID = 1552; break;
- case 2203: // Uncooked Cheese Pizza
- itemDetailsID = 1553; break;
- case 2204: // Unbaked Fruit Pie
- itemDetailsID = 1554; break;
- case 2205: // Unbaked Peach Cobbler
- itemDetailsID = 1555; break;
- case 2206: // Unbaked Apple Pie
- itemDetailsID = 1556; break;
- case 2207: // Unbaked Pumpkin Pie
- itemDetailsID = 1557; break;
- case 2300: // Bread Loaf
- itemDetailsID = 1600; break;
- case 2301: // Pan of Cookies
- itemDetailsID = 1601; break;
- case 2302: // Cake
- itemDetailsID = 1602; break;
- case 2303: // Muffins
- itemDetailsID = 1603; break;
- case 2304: // Baked Quiche
- itemDetailsID = 1604; break;
- case 2305: // Baked Meat Pie
- itemDetailsID = 1605; break;
- case 2306: // Sausage Pizza
- itemDetailsID = 1606; break;
- case 2307: // Cheese Pizza
- itemDetailsID = 1607; break;
- case 2308: // Baked Fruit Pie
- itemDetailsID = 1608; break;
- case 2309: // Baked Peach Cobbler
- itemDetailsID = 1609; break;
- case 2310: // Baked Apple Pie
- itemDetailsID = 1610; break;
- case 2311: // Baked Pumpkin Pie
- itemDetailsID = 1611; break;
- case 2400: // Cooked Bird
- itemDetailsID = 1650; break;
- case 2401: // Chicken Leg
- itemDetailsID = 1651; break;
- case 2402: // Fish Steak
- itemDetailsID = 1652; break;
- case 2403: // Fried Eggs
- itemDetailsID = 1653; break;
- case 2404: // Leg of Lamb
- itemDetailsID = 1654; break;
- case 2405: // Cut of Ribs
- itemDetailsID = 1655; break;
- default:
- break;
+ var last = pUser.GetTempTag( "MakeLast_Cooking" );
+ if( last )
+ pButton = last;
+ else
+ return;
}
- if( makeID != 0 )
+ var makeID = 0;
+ var timerID = 0;
+
+ // Craft buttons use makeID directly
+ if( CookingMap[pButton] != undefined )
{
+ makeID = pButton;
+ var data = CookingMap[makeID];
+ timerID = data.timerID || 1;
+
+ // Era / recipe checks
+ if( !eraOK( data ))
+ {
+ socket.SysMessage( "That item is not available in this era." );
+ return;
+ }
+
+ if( data.recipeID && !TriggerEvent( 4022, "NeedRecipe", pUser, data.recipeID ))
+ {
+ socket.SysMessage( "You must learn that recipe from a scroll." );
+ return;
+ }
+
+ pUser.SetTempTag( "MakeLast_Cooking", makeID );
+
+ // Environment checks
var makeItem = false;
+
if( makeID == 1500 )
{
- // Grind wheat to flour
+ // Sack of Flour – wheat grinding
if( manualMillTarget )
{
// Require player to manually target mill
@@ -393,34 +479,38 @@ function onGumpPress( pSock, pButton, gumpData )
{
if( ValidateObject( packItem ) && packItem.id == 0x1ebd )
{
- // Found wheat item in inventory, ask user where to grind it
+ // Found wheat; call its onUseChecked
TriggerEvent( 101, "onUseChecked", pUser, packItem ); // 101 = wheat.js
return;
}
}
+
+ // No wheat found – just fail silently and let DFN/msg handle it
+ return;
}
else
{
- // Look for nearby dynamic mill
- var nearbyMill = AreaItemFunction( "FindNearbyMills", pUser, 2, pSock );
+ // Auto mill lookup (dynamic first, then static)
+ var nearbyMill = AreaItemFunction( "FindNearbyMills", pUser, 2, socket );
if( nearbyMill == 0 )
{
- // No dynamic oven found nearby! Look for a static one?
var staticFound = false;
- for( var i = 0; i < ovens.length; i++ )
+ for( var i = 0; i < mills.length; i++ )
{
- if( StaticInRange( pUser.x, pUser.y, pUser.worldnumber, 2, ovens[i] ))
+ if( StaticInRange( pUser.x, pUser.y, pUser.worldnumber, 2, mills[i] ))
{
- nearbyMill = 1;
+ staticFound = true;
break;
}
}
+
+ if( staticFound )
+ nearbyMill = 1;
}
if( nearbyMill > 0 )
{
makeItem = true;
- pUser.SetTempTag( "MAKELAST", pButton );
}
else
{
@@ -428,41 +518,191 @@ function onGumpPress( pSock, pButton, gumpData )
}
}
}
- else if( makeID >= 1650 && makeID <= 1655 && nearbyHeatSource > 0 )
+ else if( makeID >= 1600 && makeID <= 1611 )
{
- // Barbecue
- makeItem = true;
+ // Baking – requires oven
+ var nearbyOven = AreaItemFunction( "FindNearbyOvens", pUser, 2, socket );
+ if( nearbyOven > 0 )
+ {
+ makeItem = true;
+ }
+ else
+ {
+ var staticFoundOven = false;
+ for( var o = 0; o < ovens.length; o++ )
+ {
+ if( StaticInRange( pUser.x, pUser.y, pUser.worldnumber, 2, ovens[o] ))
+ {
+ staticFoundOven = true;
+ break;
+ }
+ }
+ if( !staticFoundOven )
+ pUser.SetTempTag( "prevActionResult", "NOOVEN" );
+ else
+ makeItem = true;
+ }
}
- else if( makeID >= 1600 && makeID <= 1611 && nearbyOven > 0 )
+ else if( makeID >= 1650 && makeID <= 1655 )
{
- // Baking
- makeItem = true;
+ // Barbecue – requires generic heat source
+ var nearbyHeat = AreaItemFunction( "FindNearbyHeatSources", pUser, 2, socket );
+ if( nearbyHeat > 0 )
+ {
+ makeItem = true;
+ }
+ else
+ {
+ var staticFoundHeat = false;
+ for( var h = 0; h < heatSources.length; h++ )
+ {
+ if( StaticInRange( pUser.x, pUser.y, pUser.worldnumber, 2, heatSources[h] ))
+ {
+ staticFoundHeat = true;
+ break;
+ }
+ }
+ if( !staticFoundHeat )
+ pUser.SetTempTag( "prevActionResult", "NOHEATSOURCE" );
+ else
+ makeItem = true;
+ }
}
- else if( makeID >= 1501 && makeID <= 1557 )
+ else if( ( makeID >= 1501 && makeID <= 1557 ) )
{
- // Ingredients/Baking - no heatsource or mill required
+ // Ingredients & preparations – no heat source or mill required
makeItem = true;
}
if( makeItem )
{
- MakeItem( pSock, pUser, makeID );
+ MakeItem( socket, pUser, makeID );
+ AddToLastTen( pUser, makeID );
+
if( GetServerSetting( "ToolUseLimit" ))
{
- bItem.usesLeft -= 1;
- if( bItem.usesLeft == 0 && GetServerSetting( "ToolUseBreak" ))
+ tool.usesLeft -= 1;
+ if( tool.usesLeft == 0 && GetServerSetting( "ToolUseBreak" ))
{
- bItem.Delete();
- pSock.SysMessage( GetDictionaryEntry( 10202, pSock.language )); // You have worn out your tool!
- // Play sound effect of tool breaking
+ tool.Delete();
+ socket.SysMessage( GetDictionaryEntry( 10202, socket.language )); // You have worn out your tool!
}
}
}
- pUser.StartTimer( gumpDelay, timerID, true );
+
+ pUser.StartTimer( gumpDelay, timerID, cookingID );
+ return;
+ }
+
+ // Detail buttons: 20000 + makeID
+ if( pButton >= 20000 && pButton < 30000 )
+ {
+ var detailMakeID = pButton - 20000;
+ var entry = CookingMap[detailMakeID];
+
+ if( entry )
+ {
+ // Which item details to show
+ pUser.SetTempTag( "ITEMDETAILS", detailMakeID );
+
+ // Skill used
+ pUser.SetTempTag( "Skill", entry.skill || cookingSkillID );
+
+ // Clear old harvest tags
+ pUser.SetTempTag( "Harvest", null );
+ pUser.SetTempTag( "Harvest2", null );
+ pUser.SetTempTag( "Harvest3", null );
+ pUser.SetTempTag( "Harvest4", null );
+
+ // Clear old custom harvest names
+ pUser.SetTempTag( "HarvestName", null );
+ pUser.SetTempTag( "Harvest2Name", null );
+ pUser.SetTempTag( "Harvest3Name", null );
+ pUser.SetTempTag( "Harvest4Name", null );
+
+ // Optional harvest dictIDs
+ if( entry.harvest && entry.harvest.length > 0 )
+ {
+ if( entry.harvest.length >= 1 )
+ pUser.SetTempTag( "Harvest", entry.harvest[0] );
+ if( entry.harvest.length >= 2 )
+ pUser.SetTempTag( "Harvest2", entry.harvest[1] );
+ if( entry.harvest.length >= 3 )
+ pUser.SetTempTag( "Harvest3", entry.harvest[2] );
+ if( entry.harvest.length >= 4 )
+ pUser.SetTempTag( "Harvest4", entry.harvest[3] );
+ }
+
+ // Optional custom names – plug into your new ItemDetail custom text logic
+ if( entry.harvestNames && entry.harvestNames.length > 0 )
+ {
+ if( entry.harvestNames.length >= 1 )
+ pUser.SetTempTag( "HarvestName", entry.harvestNames[0] );
+ if( entry.harvestNames.length >= 2 )
+ pUser.SetTempTag( "Harvest2Name", entry.harvestNames[1] );
+ if( entry.harvestNames.length >= 3 )
+ pUser.SetTempTag( "Harvest3Name", entry.harvestNames[2] );
+ if( entry.harvestNames.length >= 4 )
+ pUser.SetTempTag( "Harvest4Name", entry.harvestNames[3] );
+ }
+
+ if( entry.recipeID && entry.recipeID > 0 )
+ pUser.SetTempTag( "needRecipeID", entry.recipeID );
+ else
+ pUser.SetTempTag( "needRecipeID", 0 );
+
+ TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
+ }
+ return;
+ }
+}
+
+// Last Ten handling
+function AddToLastTen( pUser, makeID )
+{
+ var raw = pUser.GetTempTag( "LastTenCooking" ) || "";
+ var list = raw.split( "," );
+
+ for( var i = 0; i < list.length; i++ )
+ {
+ if( parseInt( list[i] ) == makeID )
+ {
+ list.splice( i, 1 );
+ break;
+ }
}
- else if( itemDetailsID != 0 )
+
+ var newList = [ makeID ];
+ for( var j = 0; j < list.length && newList.length < 10; j++ )
{
- pUser.SetTempTag( "ITEMDETAILS", itemDetailsID );
- TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
+ var entry = parseInt( list[j] );
+ if( !isNaN( entry ) && entry > 0 )
+ newList.push( entry );
}
+
+ pUser.SetTempTag( "LastTenCooking", newList.join( "," ) );
}
+
+function HasLearnedRecipe( pUser, recipeID )
+{
+ var myData = TriggerEvent( 4022, "ReadRecipeID", pUser );
+ if( !myData || myData.length == 0 )
+ return false;
+
+ for( var i = 0; i < myData.length; i++ )
+ {
+ var data = myData[i].split( "," );
+ if( data[0] == recipeID )
+ return true;
+ }
+ return false;
+}
+
+function eraOK( entry )
+{
+ if( entry.minEra && coreShardEra < EraStringToNum( entry.minEra ))
+ return false;
+ if( entry.maxEra && coreShardEra > EraStringToNum( entry.maxEra ))
+ return false;
+ return true;
+}
\ No newline at end of file
diff --git a/data/js/skill/craft/craftgump.js b/data/js/skill/craft/craftgump.js
index 2e894f588..f65735918 100644
--- a/data/js/skill/craft/craftgump.js
+++ b/data/js/skill/craft/craftgump.js
@@ -14,11 +14,13 @@ const Tinkering = 4032;
const Cooking = 4034;
const Cartography = 4035;
const Glassblowing = 4036;
+const Masonry = 4037;
// If enabled, players can craft coloured variants of weapons using Blacksmithing skill, though
// unless the craftItems array in blacksmithing.js is updated with specific create entries for the
// coloured weapon variants, they'll just be regular weapons with ore colour applied
const allowColouredWeapons = GetServerSetting( "CraftColouredWeapons" );
+const coreShardEra = EraStringToNum( GetServerSetting( "CoreShardEra" ));
function CraftingGumpMenu( myGump, socket )
{
@@ -43,8 +45,27 @@ function CraftingGumpMenu( myGump, socket )
var hides = pUser.ResourceCount( 0x1078 );
var hides1 = pUser.ResourceCount( 0x1079 );
+ var granite = pUser.ResourceCount( 0x1779 );
+ var dullcoppergranite = pUser.ResourceCount( 0x1779, 0x0973 );
+ var shadowirongranite = pUser.ResourceCount( 0x1779, 0x0966 );
+ var coppergranite = pUser.ResourceCount( 0x1779, 0x07dd );
+ var bronzegranite = pUser.ResourceCount( 0x1779, 0x06d6 );
+ var goldgranite = pUser.ResourceCount( 0x1779, 0x08a5 );
+ var agapitegranite = pUser.ResourceCount( 0x1779, 0x0979 );
+ var veritegranite = pUser.ResourceCount( 0x1779, 0x089f );
+ var valoritegranite = pUser.ResourceCount( 0x1779, 0x08ab );
+
+ var redScales = pUser.ResourceCount( 0x26b4, 0x0663 ); // red scales
+ var yellowScales = pUser.ResourceCount( 0x26b4, 0x084d); // yellow scales
+ var blackScales = pUser.ResourceCount( 0x26b4, 0x0455); // Black scales
+ var greenScales = pUser.ResourceCount( 0x26b4, 0x0851); // Green scales
+ var whiteScales = pUser.ResourceCount( 0x26b4, 0x02c2 ); // White scales
+ var blueScales = pUser.ResourceCount( 0x26b4, 0x0006); // Blue scales
+
var resourcename = 10291;
+ var resourcename2 = 10291;
var resource = iron;
+ var resource2 = whiteScales;
var groupList;
var gumpMenuName = "";
var repair = 51;
@@ -123,6 +144,37 @@ function CraftingGumpMenu( myGump, socket )
resource = iron;
break;
}
+ switch( pUser.GetTempTag( "Scale" ))
+ {
+ case 0:
+ resourcename2 = 20299;
+ resource2 = redScales;
+ break;
+ case 1:
+ resourcename2 = 20300;
+ resource2 = yellowScales;
+ break;
+ case 2:
+ resourcename2 = 20301;
+ resource2 = blackScales;
+ break;
+ case 3:
+ resourcename2 = 20302;
+ resource2 = greenScales;
+ break;
+ case 4:
+ resourcename2 = 20303;
+ resource2 = whiteScales;
+ break;
+ case 5:
+ resourcename2 = 20304;
+ resource2 = blueScales;
+ break;
+ default:
+ resourcename2 = 20299;
+ resource2 = redScales;
+ break;
+ }
repair = 49;
break;
case 6: // Cooking
@@ -139,8 +191,61 @@ function CraftingGumpMenu( myGump, socket )
break;
case 9: // Glassblowing
grouplist = [13502]; //CATEGORIES
+ // Only show Glass Weapons (page 2) in SA+ era
+ if( coreShardEra >= EraStringToNum( "sa" ))
+ {
+ grouplist.push( 13503 ); // Glass Weapons
+ }
gumpMenuName = 13501;//Cartography Menu
- break;
+ break;
+ case 10: // Masonry
+ grouplist = [14002, 14003, 14004, 14005, 14006, 14007, 14008, 14009, 14010] //CATEGORIES
+ gumpMenuName = 14001;//Masonry Menu
+ switch( pUser.GetTempTag( "Granite" ))
+ {
+ case 0: // Granite
+ resourcename = 14011;
+ resource = granite;
+ break;
+ case 1: // Dull Copper
+ resourcename = 14012;
+ resource = dullcoppergranite;
+ break;
+ case 2: // Shadow Iron
+ resourcename = 14013;
+ resource = shadowirongranite;
+ break;
+ case 3: // Copper
+ resourcename = 14014;
+ resource = coppergranite;
+ break;
+ case 4: // Bronze
+ resourcename = 14015;
+ resource = bronzegranite;
+ break;
+ case 5: // Gold
+ resourcename = 14016;
+ resource = goldgranite;
+ break;
+ case 6: // Agapite
+ resourcename = 14017;
+ resource = agapitegranite;
+ break;
+ case 7: // Verite
+ resourcename = 14018;
+ resource = veritegranite;
+ break;
+ case 8: // Valorite
+ resourcename = 14019;
+ resource = valoritegranite;
+ break;
+ default: // Iron
+ resourcename = 14011;
+ resource = irongranite;
+ break;
+ }
+ repair = 49;
+ break;
}
myGump.AddPage( 0 );
@@ -249,7 +354,7 @@ function CraftingGumpMenu( myGump, socket )
break;
}
- if( craftingSkillUsed != 2 && craftingSkillUsed != 6 && craftingSkillUsed != 8 )
+ if( craftingSkillUsed != 2 && craftingSkillUsed != 6 && craftingSkillUsed != 8 && craftingSkillUsed != 9 )
{
myGump.AddText( 50, 362, textHue, GetDictionaryEntry( resourcename, socket.language ) + " (" + resource.toString() + ")" );
@@ -261,6 +366,8 @@ function CraftingGumpMenu( myGump, socket )
if( craftingSkillUsed == 5 )
{
+ myGump.AddText( 50, 380, textHue, GetDictionaryEntry( resourcename2, socket.language ) + " (" + resource2.toString() + ")" );
+ myGump.AddButton(15, 380, 4005, 4007, 1, 0, 51); // Material Selection Button
// Blacksmithing
myGump.AddButton(270, 342, 0xfa5, 1, 0, repair); // Repair Button
myGump.AddHTMLGump(305, 345, 150, 18, 0, 0, "" + GetDictionaryEntry( 10212, socket.language ) + "" );// REPAIR ITEM
@@ -346,8 +453,12 @@ function onGumpPress( pSock, pButton, gumpData )
break;
case 9:
TriggerEvent( Glassblowing, "PageX", pSock, pUser, 1 );
- break;
- default:
+ break;
+ case 10:
+ if( pUser.GetTempTag( "Granite" ) >= 0 && pUser.GetTempTag( "Granite" ) <= 8 )
+ {
+ TriggerEvent( Masonry, "PageX", pSock, pUser, 1 );
+ }
break;
}
case 2:
@@ -377,8 +488,11 @@ function onGumpPress( pSock, pButton, gumpData )
break;
case 7:
TriggerEvent( Tinkering, "PageX", pSock, pUser, 2 );
- break;
- default:
+ case 10:
+ if( pUser.GetTempTag( "Granite" ) >= 0 && pUser.GetTempTag( "Granite" ) <= 8 )
+ {
+ TriggerEvent( Masonry, "PageX", pSock, pUser, 2 );
+ }
break;
}
case 3:
@@ -409,7 +523,11 @@ function onGumpPress( pSock, pButton, gumpData )
case 7:
TriggerEvent( Tinkering, "PageX", pSock, pUser, 3 );
break;
- default:
+ case 10:
+ if( pUser.GetTempTag( "Granite" ) >= 0 && pUser.GetTempTag( "Granite" ) <= 8 )
+ {
+ TriggerEvent( Masonry, "PageX", pSock, pUser, 3 );
+ }
break;
}
case 4:
@@ -437,7 +555,11 @@ function onGumpPress( pSock, pButton, gumpData )
case 7:
TriggerEvent( Tinkering, "PageX", pSock, pUser, 4 );
break;
- default:
+ case 10:
+ if( pUser.GetTempTag( "Granite" ) >= 0 && pUser.GetTempTag( "Granite" ) <= 8 )
+ {
+ TriggerEvent( Masonry, "PageX", pSock, pUser, 4 );
+ }
break;
}
case 5:
@@ -459,7 +581,11 @@ function onGumpPress( pSock, pButton, gumpData )
case 7:
TriggerEvent( Tinkering, "PageX", pSock, pUser, 5 );
break;
- default:
+ case 10:
+ if( pUser.GetTempTag( "Granite" ) >= 0 && pUser.GetTempTag( "Granite" ) <= 8 )
+ {
+ TriggerEvent( Masonry, "PageX", pSock, pUser, 5 );
+ }
break;
}
case 6:
@@ -481,7 +607,11 @@ function onGumpPress( pSock, pButton, gumpData )
case 7:
TriggerEvent( Tinkering, "PageX", pSock, pUser, 6 );
break;
- default:
+ case 10:
+ if( pUser.GetTempTag( "Granite" ) >= 0 && pUser.GetTempTag( "Granite" ) <= 8 )
+ {
+ TriggerEvent( Masonry, "PageX", pSock, pUser, 6 );
+ }
break;
}
case 7:
@@ -503,7 +633,11 @@ function onGumpPress( pSock, pButton, gumpData )
case 7:
TriggerEvent( Tinkering, "PageX", pSock, pUser, 7 );
break;
- default:
+ case 10:
+ if( pUser.GetTempTag( "Granite" ) >= 0 && pUser.GetTempTag( "Granite" ) <= 8 )
+ {
+ TriggerEvent( Masonry, "PageX", pSock, pUser, 7 );
+ }
break;
}
case 8:
@@ -519,6 +653,12 @@ function onGumpPress( pSock, pButton, gumpData )
case 7:
TriggerEvent( Tinkering, "PageX", pSock, pUser, 8 );
break;
+ case 10:
+ if( pUser.GetTempTag( "Granite" ) >= 0 && pUser.GetTempTag( "Granite" ) <= 8 )
+ {
+ TriggerEvent( Masonry, "PageX", pSock, pUser, 8 );
+ }
+ break;
}
break;
case 9:
@@ -534,6 +674,12 @@ function onGumpPress( pSock, pButton, gumpData )
case 7:
TriggerEvent( Tinkering, "PageX", pSock, pUser, 9 );
break;
+ case 10:
+ if( pUser.GetTempTag( "Granite" ) >= 0 && pUser.GetTempTag( "Granite" ) <= 8 )
+ {
+ TriggerEvent( Masonry, "PageX", pSock, pUser, 9 );
+ }
+ break;
}
break;
case 10:
diff --git a/data/js/skill/craft/crafttool.js b/data/js/skill/craft/crafttool.js
index 99410252a..34dc95c0f 100644
--- a/data/js/skill/craft/crafttool.js
+++ b/data/js/skill/craft/crafttool.js
@@ -1,274 +1,308 @@
///
// @ts-check
-const enableUOX3Craft = 0; // Disable or enable to use old uox3 menus.
-const blacksmithID = 4023; // Use this to tell the gump what script to close.
-const Carpentry = 4025;
-const Alchemy = 4028;
-const Fletching = 4029;
-const Tailoring = 4030;
-const Tinkering = 4032;
-const Cooking = 4034;
-const Cartography = 4035;
-const Glassblowing = 4036;
-
-/** @type { ( user: Character, iUsing: Item ) => boolean } */
-function onUseChecked( pUser, iUsed )
+const enableUOX3Craft = 0; // 0 = new system, 1 = old UOX3 menus
+const BlacksmithingID = 4023;
+const CarpentryID = 4025;
+const AlchemyID = 4028;
+const FletchingID = 4029;
+const TailoringID = 4030;
+const TinkeringID = 4032;
+const CookingID = 4034;
+const CartographyID = 4035;
+const GlassblowingID = 4036;
+const MasonryID = 4037;
+
+/**
+ * Ensure the tool is usable: charges > 0, in range, not locked down,
+ * and in the user's own backpack.
+ * @param {Character} pUser
+ * @param {Item} iUsed
+ * @returns {boolean}
+ */
+function checkToolUsable(pUser, iUsed)
{
var socket = pUser.socket;
- var gumpID = Carpentry + 0xffff;
- var gumpID2 = Alchemy + 0xffff;
- var gumpID3 = Fletching + 0xffff;
- var gumpID4 = Tailoring + 0xffff;
- var gumpID5 = blacksmithID + 0xffff;
- var gumpID6 = Cooking + 0xffff;
- var gumpID7 = Tinkering + 0xffff;
- var gumpID8 = Cartography + 0xffff;
-
- if( socket && ValidateObject( iUsed ) && iUsed.isItem )
+ if( !socket || !ValidateObject( iUsed ) || !iUsed.isItem )
+ return false;
+
+ if( GetServerSetting( "ToolUseLimit" ) && iUsed.usesLeft == 0 )
{
- if( GetServerSetting( "ToolUseLimit" ) && iUsed.usesLeft == 0 )
+ socket.SysMessage( GetDictionaryEntry( 9262, socket.language )); // This has no more charges.
+ return false;
+ }
+
+ if( !pUser.InRange( iUsed, 3 ))
+ {
+ socket.SysMessage( GetDictionaryEntry( 461, socket.language )); // You are too far away.
+ return false;
+ }
+
+ if( iUsed.movable == 3 )
+ {
+ socket.SysMessage( GetDictionaryEntry( 6031, socket.language )); // Locked down resources cannot be used!
+ return false;
+ }
+
+ var iPackOwner = GetPackOwner( iUsed, 0 );
+ if( ValidateObject( iPackOwner ))
+ {
+ if( iPackOwner.serial != pUser.serial )
{
- // Tool has no uses left
- socket.SysMessage( GetDictionaryEntry( 9262, socket.language )); // This has no more charges.
+ socket.SysMessage( GetDictionaryEntry( 6032, socket.language )); // That resource is in someone else's backpack!
return false;
}
+ }
+ else
+ {
+ socket.SysMessage( GetDictionaryEntry( 6022, socket.language )); // This has to be in your backpack before you can use it.
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Open a paged crafting gump using a standard "PageX" entry point, with optional
+ * special page handlers (e.g. Page8, Page20).
+ *
+ * @param {Character} pUser
+ * @param {Socket} socket
+ * @param {number} scriptID Script ID of the crafting handler
+ * @param {number} craftIndex Value to store in "CRAFT" temp tag
+ * @param {number} maxPage Highest normal page number (handled by "PageX")
+ * @param {{[page: number]: string}=} specialPages Map: page -> function name
+ */
+function openCraftMenu(pUser, socket, scriptID, craftIndex, maxPage, specialPages)
+{
+ if( !socket )
+ return;
+
+ var gumpID = scriptID + 0xffff;
+ var tempPage = pUser.GetTempTag( "page" );
+
+ socket.CloseGump( gumpID, 0 );
+ pUser.SetTempTag( "CRAFT", craftIndex );
+
+ if( typeof tempPage !== "number" || tempPage < 1 )
+ tempPage = 1;
+
+ // Custom page handlers (e.g. Page8, Page20)
+ if( specialPages && specialPages[tempPage] )
+ {
+ TriggerEvent( scriptID, specialPages[tempPage], socket, pUser );
+ return;
+ }
+
+ // Normal pages handled by PageX, up to maxPage
+ if( tempPage >= 1 && tempPage <= maxPage )
+ {
+ TriggerEvent( scriptID, "PageX", socket, pUser, tempPage );
+ }
+ else
+ {
+ TriggerEvent( scriptID, "PageX", socket, pUser, 1 );
+ }
+}
+
+// Tool ID helpers for readability
+function isCarpentryTool( id )
+{
+ return (( id >= 0x1026 && id <= 0x1029 ) ||
+ ( id >= 0x102C && id <= 0x102F ) ||
+ ( id >= 0x1030 && id <= 0x1035 ) ||
+ ( id >= 0x10E4 && id <= 0x10E6 ));
+}
+
+function isFletchingTool( id )
+{
+ return ( id == 0x1022 || id == 0x1BD1 || id == 0x1BD4 );
+}
+
+function isBlacksmithTool( id )
+{
+ return ( id == 0x0FBB || id == 0x0FBC || id == 0x13E3 || id == 0x13E4 );
+}
+
+function isCookingTool( id )
+{
+ return ( id == 0x1043 || id == 0x097F || id == 0x09E2 || id == 0x103E );
+}
+
+// ---------------------------------------------------------------------------
+// Main entry
+// ---------------------------------------------------------------------------
+
+/** @type { ( pUser: Character, iUsed: Item ) => boolean } */
+function onUseChecked( pUser, iUsed )
+{
+ var socket = pUser.socket;
+ if( !socket )
+ return false;
+
+ if( !checkToolUsable( pUser, iUsed ))
+ return false;
+
+ // Save tool on socket so skill gumps can reference it
+ socket.tempObj = iUsed;
+ var id = iUsed.id;
- if( !pUser.InRange( iUsed, 3 ))
+ // -------------------------------------------------------------------
+ // Carpentry
+ // -------------------------------------------------------------------
+ if( isCarpentryTool( id ))
+ {
+ if( enableUOX3Craft == 1 )
{
- socket.SysMessage( GetDictionaryEntry( 461, socket.language )); // You are too far away.
+ // Old UOX3 carpentry gump
+ TriggerEvent( 4006, "onUseChecked", pUser, iUsed );
return false;
}
- if( iUsed.movable == 3 )
+ // New carpentry menu – Pages 1–10
+ openCraftMenu( pUser, socket, CarpentryID, 1, 10 );
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ // Alchemy (mortar and pestle)
+ // -------------------------------------------------------------------
+ if( id == 0x0E9B )
+ {
+ if( enableUOX3Craft == 1 )
{
- socket.SysMessage( GetDictionaryEntry( 6031, socket.language )); // Locked down resources cannot be used!
+ TriggerEvent( 4007, "onUseChecked", pUser, iUsed );
return false;
}
- var iPackOwner = GetPackOwner( iUsed, 0 );
- if( ValidateObject( iPackOwner )) // Is the item in a backpack?
- {
- if( iPackOwner.serial != pUser.serial ) //And if so does the pack belong to the user?
- {
- socket.SysMessage( GetDictionaryEntry( 6032, socket.language )); // That resource is in someone else's backpack!
- return false;
- }
- }
- else
+ // New alchemy menu – Pages 1–4
+ openCraftMenu( pUser, socket, AlchemyID, 2, 4 );
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ // Bowcraft / Fletching
+ // -------------------------------------------------------------------
+ if( isFletchingTool( id ))
+ {
+ if( enableUOX3Craft == 1 )
{
- socket.SysMessage( GetDictionaryEntry( 6022, socket.language )); // This has to be in your backpack before you can use it.
+ TriggerEvent( 4005, "onUseChecked", pUser, iUsed );
return false;
}
- socket.tempObj = iUsed;
- var tempPage = pUser.GetTempTag( "page" );
- if( iUsed.id >= 0x1026 && iUsed.id <= 0x1029 || iUsed.id >= 0x102C && iUsed.id <= 0x102F || iUsed.id >= 0x1030 && iUsed.id <= 0x1035 || iUsed.id >= 0x10E4 && iUsed.id <= 0x10E6 )
- {
- // Carpentry
- if( enableUOX3Craft == 1 )
- {
- TriggerEvent( 4006, "onUseChecked", pUser, iUsed );
- return;
- }
- socket.CloseGump( gumpID, 0 );
- pUser.SetTempTag( "CRAFT", 1 )
- switch( tempPage )
- {
- case 1: // Page 1
- case 2: // Page 2
- case 3: // Page 3
- case 4: // Page 4
- case 5: // Page 5
- case 6: // Page 6
- case 7: // Page 7
- case 8: // Page 8
- case 9: // Page 9
- case 10: // Page 10
- TriggerEvent( Carpentry, "PageX", socket, pUser, tempPage );
- break;
- default: TriggerEvent( Carpentry, "PageX", socket, pUser, 1 );
- break;
- }
- }
- else if( iUsed.id == 0x0E9B ) // mortar and pestle
- {
- // Alchemy
- if( enableUOX3Craft == 1 )
- {
- TriggerEvent( 4007, "onUseChecked", pUser, iUsed );
- return;
- }
- socket.CloseGump( gumpID2, 0 );
- pUser.SetTempTag( "CRAFT", 2 )
- switch( tempPage )
- {
- case 1: // Page 1
- case 2: // Page 2
- case 3: // Page 3
- case 4: // Page 4
- TriggerEvent( Alchemy, "PageX", socket, pUser, tempPage );
- break;
- default: TriggerEvent( Alchemy, "PageX", socket, pUser, 1 );
- break;
- }
- }
- else if( iUsed.id == 0x1022 || iUsed.id == 0x1BD1 || iUsed.id == 0x1BD4 )
- {
- // Bowcraft/Fletching
- if( enableUOX3Craft == 1 )
- {
- TriggerEvent( 4005, "onUseChecked", pUser, iUsed );
- return;
- }
- socket.CloseGump( gumpID3, 0 );
- pUser.SetTempTag( "CRAFT", 3 )
- switch( tempPage )
- {
- case 1: // Page 1
- case 2: // Page 2
- case 3: // Page 3
- TriggerEvent( Fletching, "PageX", socket, pUser, tempPage );
- break;
- default: TriggerEvent( Fletching, "PageX", socket, pUser, 1 );
- break;
- }
- }
- else if( iUsed.id == 0x0F9D ) // Sewing Kit
+ // New fletching menu – Pages 1–3
+ openCraftMenu( pUser, socket, FletchingID, 3, 3 );
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ // Tailoring (sewing kit)
+ // -------------------------------------------------------------------
+ if( id == 0x0F9D )
+ {
+ if( enableUOX3Craft == 1 )
{
- // Tailoring
- if( enableUOX3Craft == 1 )
- {
- TriggerEvent( 4004, "onUseChecked", pUser, iUsed );
- return;
- }
- socket.CloseGump( gumpID4, 0 );
- pUser.SetTempTag( "CRAFT", 4 )
- switch( tempPage )
- {
- case 1: // Page 1
- case 2: // Page 2
- case 3: // Page 3
- case 4: // Page 4
- case 5: // Page 5
- case 6: // Page 6
- case 7: // Page 7
- case 8: // Page 8
- TriggerEvent( Tailoring, "PageX", socket, pUser, tempPage );
- break;
- default: TriggerEvent( Tailoring, "PageX", socket, pUser, 1 );
- break;
- }
+ TriggerEvent( 4004, "onUseChecked", pUser, iUsed );
+ return false;
}
- else if( iUsed.id == 0x0FBB || iUsed.id == 0x0FBC || iUsed.id == 0x13E3 || iUsed.id == 0x13E4 )
+
+ // New tailoring menu – Pages 1–8
+ openCraftMenu( pUser, socket, TailoringID, 4, 8 );
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ // Blacksmithing (tongs, smith hammers)
+ // -------------------------------------------------------------------
+ if( isBlacksmithTool( id ))
+ {
+ if( enableUOX3Craft == 1 )
{
- // Blacksmithing
- if( enableUOX3Craft == 1 )
- {
- return true;
- }
- socket.CloseGump( gumpID5, 0 );
- pUser.SetTempTag( "CRAFT", 5 )
- switch( tempPage )
- {
- case 1: // Page 1
- case 2: // Page 2
- case 3: // Page 3
- case 4: // Page 4
- case 5: // Page 5
- case 6: // Page 6
- case 7: // Page 7
- TriggerEvent( blacksmithID, "PageX", socket, pUser, tempPage );
- break;
- case 8:
- TriggerEvent( blacksmithID, "Page8", socket, pUser );
- break;
- default: TriggerEvent( blacksmithID, "PageX", socket, pUser, 1 );
- break;
- }
+ // Fall back to original blacksmith behavior when enabled
+ return true;
}
- else if( iUsed.id == 0x1043 || iUsed.id == 0x097f || iUsed.id == 0x09e2 || iUsed.id == 0x103e )
+
+ // New blacksmithing menu – Pages 1–7 = PageX, page 8 = Page8
+ openCraftMenu( pUser, socket, BlacksmithingID, 5, 7, { 8: "Page8" } );
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ // Cooking (skillet, flour sifter, rolling pin, etc.)
+ // -------------------------------------------------------------------
+ if( isCookingTool( id ))
+ {
+ if( enableUOX3Craft == 1 )
{
- // Cooking
- if( enableUOX3Craft == 1 )
- {
- //socket.SysMessage( "Old-school crafting gumps have not been implemented for Cooking. Use raw food with heat sources to cook!" );
- TriggerEvent( 104, "onUseChecked", pUser, iUsed );
- return;
- }
- socket.CloseGump( gumpID6, 0 );
- pUser.SetTempTag( "CRAFT", 6 )
- switch( tempPage )
- {
- case 1: // Page 1
- case 2: // Page 2
- case 3: // Page 3
- case 4: // Page 4
- TriggerEvent( Cooking, "PageX", socket, pUser, tempPage );
- break;
- default: TriggerEvent( Cooking, "PageX", socket, pUser, 1 );
- break;
- }
+ // Old-school cooking: use raw food with heat sources, or legacy script
+ TriggerEvent( 104, "onUseChecked", pUser, iUsed );
+ return false;
}
- else if( iUsed.id == 0x1eb8 || iUsed.id == 0x1eb9 || iUsed.id == 0x1eba || iUsed.id == 0x1ebb || iUsed.id == 0x1ebc ) // Tinker's tools
+
+ // New cooking menu – Pages 1–4
+ openCraftMenu( pUser, socket, CookingID, 6, 4 );
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ // Tinkering (tinker's tools)
+ // -------------------------------------------------------------------
+ if( iUsed.sectionID == "tinkerstools" || // optional if you use sectionID
+ id == 0x1EB8 || id == 0x1EB9 || id == 0x1EBA || id == 0x1EBB || id == 0x1EBC )
+ {
+ if( enableUOX3Craft == 1 )
{
- // Tinkering
- if( enableUOX3Craft == 1 )
- {
- TriggerEvent( 4003, "onUseChecked", pUser, iUsed );
- return;
- }
- socket.CloseGump( gumpID7, 0 );
- pUser.SetTempTag( "CRAFT", 7 )
- switch( tempPage )
- {
- case 1: // Page 1
- case 2: // Page 2
- case 3: // Page 3
- case 4: // Page 4
- case 5: // Page 5
- case 6: // Page 6
- case 7: // Page 7
- case 8: // Page 8
- case 9: // Page 9
- TriggerEvent( Tinkering, "PageX", socket, pUser, tempPage );
- break;
- default: TriggerEvent( Tinkering, "PageX", socket, pUser, 1 );
- break;
- }
+ TriggerEvent( 4003, "onUseChecked", pUser, iUsed );
+ return false;
}
- else if( iUsed.sectionID == "mapmakerspen" )
+
+ // New tinkering menu – Pages 1–9
+ openCraftMenu( pUser, socket, TinkeringID, 7, 9 );
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ // Cartography (mapmaker's pen)
+ // -------------------------------------------------------------------
+ if( iUsed.sectionID == "mapmakerspen" )
+ {
+ // Only one page currently – PageX with page 1
+ openCraftMenu( pUser, socket, CartographyID, 8, 1 );
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ // Glassblowing (blow pipe)
+ // -------------------------------------------------------------------
+ if( iUsed.sectionID == "blowpipe" )
+ {
+ if( pUser.GetTag( "GlassBlowing" ) == 0 )
{
- // Cartography
- socket.CloseGump( gumpID8, 0 );
- pUser.SetTempTag( "CRAFT", 8 );
- switch( tempPage )
- {
- case 1: // Page 1
- TriggerEvent( Cartography, "PageX", socket, pUser, tempPage );
- break;
- default: TriggerEvent( Cartography, "PageX", socket, pUser, 1 );
- break;
- }
+ // NOTE: fixed .Language -> .language here
+ socket.SysMessage( GetDictionaryEntry( 6300, socket.language )); // You haven't learned glassblowing.
+ return false;
}
- else if( iUsed.sectionID == "blowpipe" )
+
+ // New glassblowing menu – Page 1 only for now
+ openCraftMenu( pUser, socket, GlassblowingID, 9, 1 );
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ // Masonry (mallet and chisel)
+ // -------------------------------------------------------------------
+ if( iUsed.sectionID == "malletandchisel" )
+ {
+ if( pUser.GetTag( "StoneCrafting" ) == 0 )
{
- if( pUser.GetTag( "GlassBlowing" ) == 0 )
- {
- socket.SysMessage( GetDictionaryEntry( 6300, socket.Language ));// You havent learned glassblowing.
- return;
- }
- // Cartography
- socket.CloseGump( gumpID8, 0 );
- pUser.SetTempTag( "CRAFT", 9 );
- switch( tempPage )
- {
- case 1: // Page 1
- TriggerEvent( Glassblowing, "PageX", socket, pUser, tempPage);
- break;
- default: TriggerEvent( Glassblowing, "PageX", socket, pUser, 1);
- break;
- }
+ socket.SysMessage( GetDictionaryEntry( 6297, socket.language )); // You haven't learned masonry.
+ return false;
}
+
+ // Masonry: Pages 1–9 = PageX, Page 20 = Page20
+ openCraftMenu( pUser, socket, MasonryID, 10, 9, { 20: "Page20" } );
+ return false;
}
+
return false;
-}
+}
\ No newline at end of file
diff --git a/data/js/skill/craft/fletching.js b/data/js/skill/craft/fletching.js
index d93541437..51b73486c 100644
--- a/data/js/skill/craft/fletching.js
+++ b/data/js/skill/craft/fletching.js
@@ -1,240 +1,541 @@
///
// @ts-check
-const LabelHue = 0x480; // Color of the text.
-const LabelColor = 0x7FFF; // Second Color of text.
-const scriptID = 4029; // Use this to tell the gump what script to close.
-const gumpDelay = 2000; // Timer for the gump to reapear after crafting.
-const itemDetailsScriptID = 4026;
-const craftGumpID = 4027;
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// The section below is the tables for each page.
-// All you have to do is add the item to your dictionary
-// and then list the dictionary number in the right page and it will
-// add it to the crafting gump.
-///////////////////////////////////////////////////////////////////////////////////////////
-
-const myPage = [
+const textHue = 0x480; // Color of the text.
+const fletchingID = 4029; // Script ID for this fletching gump
+const gumpDelay = 2000; // Delay (ms) before gump reappears after crafting
+const itemDetailsScriptID = 4026; // Generic item details gump
+const craftGumpID = 4027; // Shared crafting menu frame
+const itemsPerPage = 10; // Items per subpage
+const displayUnlearnedRecipes = true; // For future recipe use
+const coreShardEra = EraStringToNum( GetServerSetting( "CoreShardEra" ));
+const fletchingSkillID = 8; // Index of "bowcraft" in ItemDetailGump skillNames[]
+
+// o--------------------------------------------------------------------------o
+// | FletchingMap |
+// o--------------------------------------------------------------------------o
+// | Keyed by makeID (create entry ID). |
+// | Each entry: |
+// | dictID - dictionary entry for row text (11205..11220) |
+// | page - main category page (1..3) |
+// | timerID - which page timer should reopen |
+// | skill - skill used (default: fletchingSkillID) |
+// | recipeID? - optional recipe ID |
+// | minEra?, maxEra? - optional era gating |
+// | harvest?[] - optional dictIDs for MATERIALS list |
+// | harvestNames?[] - optional custom material names |
+// o--------------------------------------------------------------------------o
+
+const FletchingMap = {
// Page 1 - Materials
- [ 11205, 11206, 11207, 11208, 11209 ],
+ 190: { dictID: 11205, page: 1, timerID: 1, harvest: [ 10014 ] }, // Kindling
+ 194: { dictID: 11206, page: 1, timerID: 1, harvest: [ 10014 ] }, // Shaft
+ 195: { dictID: 11207, page: 1, timerID: 1, harvest: [ 10014 ] }, // Five Shafts
+ 196: { dictID: 11208, page: 1, timerID: 1, harvest: [ 10014 ] }, // Twenty Shafts
+ 197: { dictID: 11209, page: 1, timerID: 1, harvest: [ 10014 ] }, // Fifty Shafts
// Page 2 - Ammunition
- [ 11210, 11211, 11212, 11213, 11214, 11215, 11216, 11217 ],
+ 198: { dictID: 11210, page: 2, timerID: 2, harvest: [ 10029, 10028 ] }, // Arrow
+ 199: { dictID: 11211, page: 2, timerID: 2, harvest: [ 10029, 10028 ] }, // Five Arrows
+ 200: { dictID: 11212, page: 2, timerID: 2, harvest: [ 10029, 10028 ] }, // Twenty Arrows
+ 201: { dictID: 11213, page: 2, timerID: 2, harvest: [ 10029, 10028 ] }, // Fifty Arrows
+ 202: { dictID: 11214, page: 2, timerID: 2, harvest: [ 10029, 10028 ] }, // Bolt
+ 203: { dictID: 11215, page: 2, timerID: 2, harvest: [ 10029, 10028 ] }, // Five Bolts
+ 204: { dictID: 11216, page: 2, timerID: 2, harvest: [ 10029, 10028 ] }, // Twenty Bolts
+ 205: { dictID: 11217, page: 2, timerID: 2, harvest: [ 10029, 10028 ] }, // Fifty Bolts
// Page 3 - Weapons
- [ 11218, 11219, 11220 ]
-];
+ 191: { dictID: 11218, page: 3, timerID: 3, harvest: [ 10014 ] }, // Bow
+ 192: { dictID: 11219, page: 3, timerID: 3, harvest: [ 10014 ] }, // Crossbow
+ 193: { dictID: 11220, page: 3, timerID: 3, harvest: [ 10014 ] } // Heavy Crossbow
+};
+
+// Fill in defaults (skill, etc)
+(function initFletchingMap()
+{
+ for( var key in FletchingMap )
+ {
+ if( !FletchingMap.hasOwnProperty( key ))
+ continue;
+ var entry = FletchingMap[key];
+
+ if( entry.skill === undefined )
+ entry.skill = fletchingSkillID;
+
+ // In future you can do:
+ // entry.harvest = [ woodDictID, featherDictID ];
+ // entry.harvestNames = [ "Wood", "Feathers" ];
+ }
+})();
+
+// o--------------------------------------------------------------------------o
+// | PageX() - build a page of fletching items |
+// o--------------------------------------------------------------------------o
+/** @type { ( socket: Socket, pUser: Character, pageNum: number ) => void } */
function PageX( socket, pUser, pageNum )
{
- // Pages 1 - 3
- var myGump = new Gump;
- pUser.SetTempTag( "page", pageNum );
- TriggerEvent( craftGumpID, "CraftingGumpMenu", myGump, socket );
- for ( var i = 0; i < myPage[pageNum - 1].length; i++ )
+ if( !socket || !ValidateObject( pUser ))
+ return;
+
+ var pageItems;
+
+ // Last Ten page (only if you wire a tab to 999 later)
+ if( pageNum == 999 )
+ {
+ var lastTenRaw = pUser.GetTempTag( "LastTenFletching" ) || "";
+ var split = lastTenRaw.split( "," );
+ pageItems = [];
+
+ for( var i = 0; i < split.length; i++ )
+ {
+ var val = parseInt( split[i] );
+ if( !isNaN( val ))
+ pageItems.push( val );
+ }
+ }
+ else
{
- var index = i % 10;
- if ( index == 0 )
+ // Collect all makeIDs for this page
+ var makeIDs = [];
+ for( var key in FletchingMap )
{
- if ( i > 0 )
+ if( !FletchingMap.hasOwnProperty( key ))
+ continue;
+
+ var makeID = parseInt( key );
+ var data = FletchingMap[makeID];
+ if( !data || data.page != pageNum )
+ continue;
+
+ makeIDs.push( makeID );
+ }
+
+ // Sort by dictID so order matches dictionary sequence
+ makeIDs.sort( function( a, b )
+ {
+ var ea = FletchingMap[a];
+ var eb = FletchingMap[b];
+ if( ea && eb )
+ return ( ea.dictID || 0 ) - ( eb.dictID || 0 );
+ return a - b;
+ });
+
+ // Era / recipe filtering
+ pageItems = [];
+ for( var k = 0; k < makeIDs.length; k++ )
+ {
+ var id = makeIDs[k];
+ var data2 = FletchingMap[id];
+ if( !data2 )
+ continue;
+
+ var needsRecipe = data2.recipeID;
+ var showAll = displayUnlearnedRecipes;
+
+ if( eraOK( data2 ) && ( !needsRecipe || showAll || HasLearnedRecipe( pUser, needsRecipe )) )
+ pageItems.push( id );
+ }
+
+ // Fallback: if no items on this page and it's not page 1, go to page 1
+ if( pageItems.length == 0 && pageNum != 1 )
+ {
+ pageNum = 1;
+
+ makeIDs = [];
+ for( var key2 in FletchingMap )
+ {
+ if( !FletchingMap.hasOwnProperty( key2 ))
+ continue;
+
+ var mid2 = parseInt( key2 );
+ var d3 = FletchingMap[mid2];
+ if( !d3 || d3.page != 1 )
+ continue;
+
+ makeIDs.push( mid2 );
+ }
+
+ makeIDs.sort( function( a, b )
+ {
+ var ea2 = FletchingMap[a];
+ var eb2 = FletchingMap[b];
+ if( ea2 && eb2 )
+ return ( ea2.dictID || 0 ) - ( eb2.dictID || 0 );
+ return a - b;
+ });
+
+ pageItems = [];
+ for( var m = 0; m < makeIDs.length; m++ )
{
- myGump.AddButton( 370, 260, 4005, 4007, 0, ( i / 10 ) + 1, 0 );
- myGump.AddHTMLGump( 405, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10100, socket.language ) + "" );// NEXT PAGE
+ var id2 = makeIDs[m];
+ var data4 = FletchingMap[id2];
+ if( !data4 )
+ continue;
+
+ var needsRecipe2 = data4.recipeID;
+ var showAll2 = displayUnlearnedRecipes;
+
+ if( eraOK( data4 ) && ( !needsRecipe2 || showAll2 || HasLearnedRecipe( pUser, needsRecipe2 )) )
+ pageItems.push( id2 );
}
+ }
+ }
- myGump.AddPage( ( i / 10 ) + 1 );
+ // Subpage handling (future-proof; currently only 1 subpage per page)
+ var subPage = pUser.GetTempTag( "subPage" );
+ var totalSubPages = Math.ceil( pageItems.length / itemsPerPage );
- if ( i > 0 )
+ if( totalSubPages < 1 )
+ totalSubPages = 1;
+ if( subPage < 1 )
+ subPage = 1;
+ if( subPage > totalSubPages )
+ subPage = totalSubPages;
+
+ pUser.SetTempTag( "page", pageNum );
+ pUser.SetTempTag( "subPage", subPage );
+
+ var startIndex = ( subPage - 1 ) * itemsPerPage;
+ var endIndex = Math.min( startIndex + itemsPerPage, pageItems.length );
+
+ if( startIndex >= pageItems.length )
+ {
+ subPage = 1;
+ startIndex = 0;
+ endIndex = Math.min( itemsPerPage, pageItems.length );
+ pUser.SetTempTag( "subPage", subPage );
+ }
+
+ var fletchGump = new Gump;
+ TriggerEvent( craftGumpID, "CraftingGumpMenu", fletchGump, socket );
+ fletchGump.AddPage( 1 );
+
+ for( var j = startIndex; j < endIndex; j++ )
+ {
+ var index = j - startIndex;
+ var makeID = pageItems[j];
+ var entryText;
+ var buttonID = makeID; // use makeID directly as buttonID
+
+ var data5 = FletchingMap[makeID];
+
+ if( !data5 )
+ {
+ entryText = "[Missing MakeID: " + makeID + "]";
+ }
+ else
+ {
+ if( data5.customName )
+ {
+ entryText = data5.customName;
+ }
+ else if( data5.dictID )
+ {
+ entryText = GetDictionaryEntry( data5.dictID, socket.language );
+ if( !entryText || entryText === "" )
+ entryText = "[Missing EntryID: " + data5.dictID + "]";
+ }
+ else
{
- myGump.AddButton( 220, 260, 4014, 4015, 0, i / 10, 0 );
- myGump.AddHTMLGump( 255, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10101, socket.language ) + "" );// PREV PAGE
+ entryText = "[Unnamed Item: " + makeID + "]";
}
}
- myGump.AddButton( 220, 60 + ( index * 20 ), 4005, 4007, 1, 0, ( 100 * pageNum ) + i );
- myGump.AddText( 255, 60 + ( index * 20 ), LabelHue, GetDictionaryEntry( myPage[pageNum - 1][i], socket.language ) );
+ // Craft button uses makeID
+ fletchGump.AddButton( 220, 60 + ( index * 20 ), 4005, 4007, 1, 0, buttonID );
+ fletchGump.AddText( 255, 60 + ( index * 20 ), textHue, entryText );
- myGump.AddButton( 480, 60 + ( index * 20 ), 4011, 4012, 1, 0, 2000 + ( 100 * pageNum ) + i );
+ // Detail button: 20000 + makeID
+ fletchGump.AddButton( 480, 60 + ( index * 20 ), 4011, 4012, 1, 0, 20000 + buttonID );
}
- myGump.Send( socket );
- myGump.Free();
+
+ // Prev subpage
+ if( subPage > 1 )
+ {
+ fletchGump.AddButton( 220, 260, 4014, 4015, 1, 0, 8000 + ( subPage - 1 ));
+ fletchGump.AddHTMLGump( 255, 263, 100, 18, false, false,
+ "" + GetDictionaryEntry( 10101, socket.language ) + "" ); // PREV PAGE
+ }
+
+ // Next subpage
+ if( subPage < totalSubPages )
+ {
+ fletchGump.AddButton( 370, 260, 4005, 4007, 1, 0, 9000 + ( subPage + 1 ));
+ fletchGump.AddHTMLGump( 405, 263, 100, 18, false, false,
+ "" + GetDictionaryEntry( 10100, socket.language ) + "" ); // NEXT PAGE
+ }
+
+ fletchGump.Send( socket );
+ fletchGump.Free();
}
-/** @type { ( tObject: BaseObject, timerId: number ) => void } */
+/** @type { ( pUser: Character, timerID: number ) => void } */
function onTimer( pUser, timerID )
{
if( !ValidateObject( pUser ))
return;
- var socket = pUser.socket;
+ var pSocket = pUser.socket;
+ if( pSocket == null )
+ return;
- switch ( timerID )
+ if( timerID >= 1 && timerID <= 3 )
{
- case 1: // Page 1 - Materials
- case 2: // Page 2 - Ammunition
- case 3: // Page 3 - Weapons
- PageX( socket, pUser, timerID );
- break;
+ PageX( pSocket, pUser, timerID );
+ }
+ else if( timerID == 999 )
+ {
+ PageX( pSocket, pUser, 999 );
}
}
-/** @type { ( myObj: Socket, pressed: number, gump: GumpData ) => void } */
-function onGumpPress( pSock, pButton, gumpData )
+/** @type { ( socket: Socket, pButton: number, gumpData: GumpData ) => void } */
+function onGumpPress( socket, pButton, gumpData )
{
- var pUser = pSock.currentChar;
+ if( socket == null )
+ return;
- // Don't continue if character is invalid, or worse... dead!
+ var pUser = socket.currentChar;
if( !ValidateObject( pUser ) || pUser.dead )
return;
// Don't continue if player no longer has access to the crafting tool
- var bItem = pSock.tempObj;
- if( !ValidateObject( bItem ) || !pUser.InRange( bItem, 3 ))
+ var tool = socket.tempObj;
+ if( !ValidateObject( tool ) || !pUser.InRange( tool, 3 ))
{
- pSock.SysMessage( GetDictionaryEntry( 461, pSock.language )); // You are too far away.
+ socket.SysMessage( GetDictionaryEntry( 461, socket.language )); // You are too far away.
return;
}
- if( bItem.movable == 3 )
+ if( tool.movable == 3 )
{
- pSock.SysMessage( GetDictionaryEntry( 6031, pSock.language )); // Locked down resources cannot be used!
+ socket.SysMessage( GetDictionaryEntry( 6031, socket.language )); // Locked down resources cannot be used!
return;
}
- var iPackOwner = GetPackOwner( bItem, 0 );
- if( ValidateObject( iPackOwner )) // Is the item in a backpack?
+ var packOwner = GetPackOwner( tool, 0 );
+ if( ValidateObject( packOwner ))
{
- if( iPackOwner.serial != pUser.serial ) // And if so does the pack belong to the user?
+ if( packOwner.serial != pUser.serial )
{
- pSock.SysMessage( GetDictionaryEntry( 6032, pSock.language )); // That resource is in someone else's backpack!
+ socket.SysMessage( GetDictionaryEntry( 6032, socket.language )); // That resource is in someone else's backpack!
return;
}
}
else
{
- pSock.SysMessage( GetDictionaryEntry( 6022, pSock.language )); // This has to be in your backpack before you can use it.
+ socket.SysMessage( GetDictionaryEntry( 6022, socket.language )); // This has to be in your backpack before you can use it.
+ return;
+ }
+
+ var gumpID = fletchingID + 0xffff;
+
+ // Subpage back / forward
+ if( pButton >= 8001 && pButton < 9000 )
+ {
+ var subPage = pButton - 8000;
+ var pageNum = pUser.GetTempTag( "page" );
+ pUser.SetTempTag( "subPage", subPage );
+ PageX( socket, pUser, pageNum );
+ return;
+ }
+
+ if( pButton >= 9001 && pButton < 10000 )
+ {
+ var subPage2 = pButton - 9000;
+ var pageNum2 = pUser.GetTempTag( "page" );
+ pUser.SetTempTag( "subPage", subPage2 );
+ PageX( socket, pUser, pageNum2 );
+ return;
+ }
+
+ // Page tabs (1–3: Materials, Ammunition, Weapons)
+ if( pButton >= 1 && pButton <= 3 )
+ {
+ pUser.SetTempTag( "page", pButton );
+ pUser.SetTempTag( "subPage", 1 );
+ PageX( socket, pUser, pButton );
+ return;
+ }
+
+ // Last Ten (if you wire a tab to this)
+ if( pButton == 11000 )
+ {
+ pUser.SetTempTag( "page", 999 );
+ pUser.SetTempTag( "subPage", 1 );
+ PageX( socket, pUser, 999 );
+ return;
+ }
+
+ // Close gump
+ if( pButton == 0 )
+ {
+ pUser.SetTempTag( "MakeLast_Fletching", null );
+ pUser.SetTempTag( "CRAFT", null );
+ socket.CloseGump( gumpID, 0 );
return;
}
- var gumpID = scriptID + 0xffff;
- var makeID = 0;
- var itemDetailsID = 0;
+ // Make Last
+ if( pButton == 5000 )
+ {
+ var last = pUser.GetTempTag( "MakeLast_Fletching" );
+ if( last )
+ pButton = last;
+ else
+ return;
+ }
+
+ var makeID = 0;
var timerID = 0;
- if(( pButton >= 100 && pButton <= 302 ) || pButton == 5000 )
+ // Craft buttons use makeID directly
+ if( FletchingMap[pButton] != undefined )
{
- if( pButton == 5000 )
+ makeID = pButton;
+ var data = FletchingMap[makeID];
+ timerID = data.timerID || 1;
+
+ // Era / recipe checks
+ if( !eraOK( data ))
{
- // Make Last button
- pButton = pUser.GetTempTag( "MAKELAST" );
+ socket.SysMessage( "That item is not available in this era." );
+ return;
}
- else
+
+ if( data.recipeID && !TriggerEvent( 4022, "NeedRecipe", pUser, data.recipeID ))
{
- pUser.SetTempTag( "MAKELAST", pButton );
+ socket.SysMessage( "You must learn that recipe from a scroll." );
+ return;
}
- }
- switch ( pButton )
- {
- case 0: // Abort and do nothing
- pUser.SetTempTag( "MAKELAST", null );
- pUser.SetTempTag( "CRAFT", null )
- pSock.CloseGump( gumpID, 0 );
- break;
- case 1: // Page 1 - Materials
- case 2: // Page 2 - Ammunition
- case 3: // Page 3 - Weapons
- pSock.CloseGump( gumpID, 0 );
- PageX( pSock, pUser, pButton );
- break;
- // Make Items
- case 100: // Kindling
- makeID = 190; timerID = 1; break;
- case 101: // Shaft
- makeID = 194; timerID = 1; break;
- case 102: // Five Shafts
- makeID = 195; timerID = 1; break;
- case 103: // Twenty Shafts
- makeID = 196; timerID = 1; break;
- case 104: // Fifty Shafts
- makeID = 197; timerID = 1; break;
- case 200: // Arrow
- makeID = 198; timerID = 2; break;
- case 201: // Five Arrows
- makeID = 199; timerID = 2; break;
- case 202: // Twenty Arrows
- makeID = 200; timerID = 2; break;
- case 203: // Fifty Arrows
- makeID = 201; timerID = 2; break;
- case 204: // Bolt
- makeID = 202; timerID = 2; break;
- case 205: // Five Bolts
- makeID = 203; timerID = 2; break;
- case 206: // Twenty Bolts
- makeID = 204; timerID = 2; break;
- case 207: // Fifty Bolts
- makeID = 205; timerID = 2; break;
- case 300: // Bow
- makeID = 191; timerID = 3; break;
- case 301: // Crossbow
- makeID = 192; timerID = 3; break;
- case 302: // Heavy Crossbow
- makeID = 193; timerID = 3; break;
- // Show Item Details
- case 2100: // Kindling
- itemDetailsID = 190; break;
- case 2101: // Shaft
- itemDetailsID = 194; break;
- case 2102: // Five Shafts
- itemDetailsID = 195; break;
- case 2103: // Twenty Shafts
- itemDetailsID = 196; break;
- case 2104: // Fifty Shafts
- itemDetailsID = 197; break;
- case 2200: // Arrow
- itemDetailsID = 198; break;
- case 2201: // Five Arrows
- itemDetailsID = 199; break;
- case 2202: // Twenty Arrows
- itemDetailsID = 200; break;
- case 2203: // Fifty Arrows
- itemDetailsID = 201; break;
- case 2204: // Bolt
- itemDetailsID = 202; break;
- case 2205: // Five Bolts
- itemDetailsID = 203; break;
- case 2206: // Twenty Bolts
- itemDetailsID = 204; break;
- case 2207: // Fifty Bolts
- itemDetailsID = 205; break;
- case 2300: // Bow
- itemDetailsID = 191; break;
- case 2301: // Crossbow
- itemDetailsID = 192; break;
- case 2302: // Heavy Crossbow
- itemDetailsID = 193; break;
- default:
- break;
+ pUser.SetTempTag( "MakeLast_Fletching", makeID );
+
+ MakeItem( socket, pUser, makeID );
+ AddToLastTen( pUser, makeID );
+
+ if( GetServerSetting( "ToolUseLimit" ))
+ {
+ tool.usesLeft -= 1;
+ if( tool.usesLeft == 0 && GetServerSetting( "ToolUseBreak" ))
+ {
+ tool.Delete();
+ socket.SysMessage( GetDictionaryEntry( 10202, socket.language )); // You have worn out your tool!
+ }
+ }
+
+ pUser.StartTimer( gumpDelay, timerID, fletchingID );
+ return;
}
- if( makeID != 0 )
+ // Detail buttons: 20000 + makeID
+ if( pButton >= 20000 && pButton < 30000 )
{
- MakeItem( pSock, pUser, makeID );
- if( GetServerSetting( "ToolUseLimit" ))
+ var detailMakeID = pButton - 20000;
+ var entry = FletchingMap[detailMakeID];
+
+ if( entry )
{
- bItem.usesLeft -= 1;
- if( bItem.usesLeft == 0 && GetServerSetting( "ToolUseBreak" ))
+ // Which item details to show
+ pUser.SetTempTag( "ITEMDETAILS", detailMakeID );
+
+ // Skill used
+ pUser.SetTempTag( "Skill", entry.skill || fletchingSkillID );
+
+ // Clear old harvest tags
+ pUser.SetTempTag( "Harvest", null );
+ pUser.SetTempTag( "Harvest2", null );
+ pUser.SetTempTag( "Harvest3", null );
+ pUser.SetTempTag( "Harvest4", null );
+
+ // Clear old custom harvest names
+ pUser.SetTempTag( "HarvestName", null );
+ pUser.SetTempTag( "Harvest2Name", null );
+ pUser.SetTempTag( "Harvest3Name", null );
+ pUser.SetTempTag( "Harvest4Name", null );
+
+ // Optional harvest dictIDs
+ if( entry.harvest && entry.harvest.length > 0 )
{
- bItem.Delete();
- pSock.SysMessage( GetDictionaryEntry( 10202, pSock.language )); // You have worn out your tool!
- // Play sound effect of tool breaking
+ if( entry.harvest.length >= 1 )
+ pUser.SetTempTag( "Harvest", entry.harvest[0] );
+ if( entry.harvest.length >= 2 )
+ pUser.SetTempTag( "Harvest2", entry.harvest[1] );
+ if( entry.harvest.length >= 3 )
+ pUser.SetTempTag( "Harvest3", entry.harvest[2] );
+ if( entry.harvest.length >= 4 )
+ pUser.SetTempTag( "Harvest4", entry.harvest[3] );
}
- }
- pUser.StartTimer( gumpDelay, timerID, true );
+
+ // Optional custom names – plugs into your ItemDetail custom harvest name logic
+ if( entry.harvestNames && entry.harvestNames.length > 0 )
+ {
+ if( entry.harvestNames.length >= 1 )
+ pUser.SetTempTag( "HarvestName", entry.harvestNames[0] );
+ if( entry.harvestNames.length >= 2 )
+ pUser.SetTempTag( "Harvest2Name", entry.harvestNames[1] );
+ if( entry.harvestNames.length >= 3 )
+ pUser.SetTempTag( "Harvest3Name", entry.harvestNames[2] );
+ if( entry.harvestNames.length >= 4 )
+ pUser.SetTempTag( "Harvest4Name", entry.harvestNames[3] );
+ }
+
+ if( entry.recipeID && entry.recipeID > 0 )
+ pUser.SetTempTag( "needRecipeID", entry.recipeID );
+ else
+ pUser.SetTempTag( "needRecipeID", 0 );
+
+ TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
+ }
+ return;
+ }
+}
+
+function AddToLastTen( pUser, makeID )
+{
+ var raw = pUser.GetTempTag( "LastTenFletching" ) || "";
+ var list = raw.split( "," );
+
+ for( var i = 0; i < list.length; i++ )
+ {
+ if( parseInt( list[i] ) == makeID )
+ {
+ list.splice( i, 1 );
+ break;
+ }
}
- else if( itemDetailsID != 0 )
+
+ var newList = [ makeID ];
+ for( var j = 0; j < list.length && newList.length < 10; j++ )
{
- pUser.SetTempTag( "ITEMDETAILS", itemDetailsID );
- TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
+ var entry = parseInt( list[j] );
+ if( !isNaN( entry ) && entry > 0 )
+ newList.push( entry );
}
+
+ pUser.SetTempTag( "LastTenFletching", newList.join( "," ) );
+}
+
+function HasLearnedRecipe( pUser, recipeID )
+{
+ var myData = TriggerEvent( 4022, "ReadRecipeID", pUser );
+ if( !myData || myData.length == 0 )
+ return false;
+
+ for( var i = 0; i < myData.length; i++ )
+ {
+ var data = myData[i].split( "," );
+ if( data[0] == recipeID )
+ return true;
+ }
+ return false;
+}
+
+function eraOK( entry )
+{
+ if( entry.minEra && coreShardEra < EraStringToNum( entry.minEra ))
+ return false;
+ if( entry.maxEra && coreShardEra > EraStringToNum( entry.maxEra ))
+ return false;
+ return true;
}
diff --git a/data/js/skill/craft/glassblowing.js b/data/js/skill/craft/glassblowing.js
index 304b41a31..02ac1a063 100644
--- a/data/js/skill/craft/glassblowing.js
+++ b/data/js/skill/craft/glassblowing.js
@@ -1,215 +1,526 @@
///
// @ts-check
-const LabelHue = 0x480; // Color of the text.
-const LabelColor = 0x7FFF; // Second Color of text.
-const scriptID = 4036; // Use this to tell the gump what script to close.
-const gumpDelay = 2000; // Timer for the gump to reapear after crafting.
-const itemDetailsScriptID = 4026;
-const craftGumpID = 4027;
+const textHue = 0x480; // Color hue for all text in the crafting gump
+const glassblowingID = 4036; // Script ID used to identify and close this gump
+const gumpDelay = 2000; // Delay (ms) before gump reappears after crafting
+const itemDetailsScriptID = 4026; // Script ID used to show item detail tooltips
+const craftGumpID = 4027; // TriggerEvent ID used to build the crafting gump UI
+const itemsPerPage = 10; // Number of craftable items shown per gump subpage
+const displayUnlearnedRecipes = true; // Show recipes player has not learned
+const coreShardEra = EraStringToNum( GetServerSetting( "CoreShardEra" ) );
+const glassSkillID = 0; // alchmey skill
+const glassHarvestDict = 13504; // sand
//////////////////////////////////////////////////////////////////////////////////////////
-// The section below is the tables for each page.
-// All you have to do is add the item to your dictionary
-// and then list the dictionary number in the right page and it will
-// add it to the crafting gump.
-///////////////////////////////////////////////////////////////////////////////////////////
+// Glassblowing GlassBlowingMap
+//////////////////////////////////////////////////////////////////////////////////////////
+
+const GlassBlowingMap = {
+ // Page 1 - Misc Glassware
+ 3000: { dictID: 13600, page: 1, timerID: 1 }, // empty bottle
+ 3001: { dictID: 13601, page: 1, timerID: 1 }, // flask (small)
+ 3002: { dictID: 13602, page: 1, timerID: 1 }, // flask (medium)
+ 3003: { dictID: 13603, page: 1, timerID: 1 }, // flask (curved)
+ 3004: { dictID: 13604, page: 1, timerID: 1 }, // flask (large #1)
+ 3005: { dictID: 13605, page: 1, timerID: 1 }, // flask (large #2)
+ 3006: { dictID: 13606, page: 1, timerID: 1 }, // flask (bubbling blue)
+ 3007: { dictID: 13607, page: 1, timerID: 1 }, // flask (bubbling purple)
+ 3008: { dictID: 13608, page: 1, timerID: 1 }, // flask (bubbling red)
+ 3009: { dictID: 13609, page: 1, timerID: 1 }, // empty vials
+ 3010: { dictID: 13610, page: 1, timerID: 1 }, // full vials
+ 3011: { dictID: 13611, page: 1, timerID: 1 }, // spinning hourglass
+ 3012: { customName: "hollow prism", page: 1, timerID: 1, minEra: "ml" }, // hollow prism
+ 3013: { customName: "gargoyle floor mirror", page: 1, timerID: 1, minEra: "sa" }, // Gargoyle Floor Mirror
+ 3014: { customName: "gargoyle wall mirror", page: 1, timerID: 1, minEra: "sa" }, // Gargoyle Wall Mirror
+ 3015: { customName: "empty venom vial", page: 1, timerID: 1, minEra: "sa" }, // Empty Venom Vial
+ 3016: { customName: "empty oil flask", page: 1, timerID: 1, minEra: "sa" }, // Empty Oil Flask
+ 3017: { customName: "workable glass", page: 1, timerID: 1, minEra: "sa" }, // Workable Glass
+ // Page 2 Glass Weapons
+ 3018: { customName: "glass sword", page: 2, timerID: 2, minEra: "sa" }, // Glass sword
+ 3019: { customName: "glass staff", page: 2, timerID: 2, minEra: "sa" } // Glass staff
+};
+
+// After the GlassBlowingMap literal
+(function initGlassBlowingMap()
+{
+ for( var key in GlassBlowingMap )
+ {
+ if( !GlassBlowingMap.hasOwnProperty( key ))
+ continue;
+
+ var entry = GlassBlowingMap[key];
+
+ // Default skill (if not already set)
+ if( entry.skill === undefined )
+ entry.skill = glassSkillID;
+
+ // Default harvest list (if not already set)
+ if( !entry.harvest )
+ entry.harvest = [ glassHarvestDict ];
+
+ // If you have special cases, you can override here, e.g.:
+ // if( key == "3017" ) { // workable glass
+ // entry.harvest = [ glassHarvestDict, 10016 ]; // sand + cloth
+ // }
+ }
+})();
-const myPage = [
- // Page 1 - Misc
- [13600, 13601, 13602, 13603, 13604, 13605, 13606, 13607, 13608, 13609, 13610, 13611 ]
-];
+// If you ever need a specific item to use 2 or more resources, just override its harvest array:
+// GlassBlowingMap[3017].harvest = [ glassHarvestDict, 10016 ]; // two harvest resources
function PageX( socket, pUser, pageNum )
{
- // Pages 1
- var myGump = new Gump;
- pUser.SetTempTag( "page", pageNum );
- TriggerEvent( craftGumpID, "CraftingGumpMenu", myGump, socket );
- for ( var i = 0; i < myPage[pageNum - 1].length; i++ )
+ if( !socket || !ValidateObject( pUser ))
+ return;
+
+ var pageItems;
+
+ // Special "Last Ten" page uses stored makeIDs directly
+ if( pageNum == 999 )
+ {
+ var lastTenRaw = pUser.GetTempTag( "LastTenGlassblowing" ) || "";
+ var split = lastTenRaw.split( "," );
+ pageItems = [];
+
+ for( var i = 0; i < split.length; i++ )
+ {
+ var val = parseInt( split[i] );
+ if( !isNaN( val ))
+ pageItems.push( val );
+ }
+ }
+ else
{
- var index = i % 10;
- if ( index == 0 )
+ // Build list of makeIDs for this page from GlassBlowingMap
+ var makeIDs = [];
+ for( var makeIDStr in GlassBlowingMap )
+ {
+ if( !GlassBlowingMap.hasOwnProperty( makeIDStr ))
+ continue;
+
+ var makeID = parseInt( makeIDStr );
+ var data = GlassBlowingMap[makeID];
+ if( !data || data.page != pageNum )
+ continue;
+
+ makeIDs.push( makeID );
+ }
+
+ // Sort numeric so order is stable: 3000,3001,...,3012
+ makeIDs.sort(function(a, b){ return a - b; });
+
+ // Filter by era / recipe in that order
+ pageItems = [];
+ for( var k = 0; k < makeIDs.length; k++ )
{
- if ( i > 0 )
+ var id = makeIDs[k];
+ var data2 = GlassBlowingMap[id];
+ if( !data2 )
+ continue;
+
+ var needsRecipe = data2.recipeID;
+ var showAll = displayUnlearnedRecipes;
+
+ if( eraOK( data2 ) && ( !needsRecipe || showAll || HasLearnedRecipe( pUser, needsRecipe )))
{
- myGump.AddButton( 370, 260, 4005, 4007, 0, ( i / 10 ) + 1, 0 );
- myGump.AddHTMLGump( 405, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10100, socket.language ) + "" );// NEXT PAGE
+ pageItems.push( id );
}
+ }
- myGump.AddPage( ( i / 10 ) + 1 );
+ // Fallback if page empty and not page 1
+ if( pageItems.length == 0 && pageNum != 1 )
+ {
+ pageNum = 1;
- if ( i > 0 )
+ makeIDs = [];
+ for( var makeIDStr2 in GlassBlowingMap )
{
- myGump.AddButton( 220, 260, 4014, 4015, 0, i / 10, 0 );
- myGump.AddHTMLGump( 255, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10101, socket.language ) + "" );// PREV PAGE
+ if( !GlassBlowingMap.hasOwnProperty( makeIDStr2 ))
+ continue;
+
+ var makeID2 = parseInt( makeIDStr2 );
+ var data3 = GlassBlowingMap[makeID2];
+ if( !data3 || data3.page != 1 )
+ continue;
+
+ makeIDs.push( makeID2 );
}
- }
- myGump.AddButton( 220, 60 + ( index * 20 ), 4005, 4007, 1, 0, ( 100 * pageNum ) + i );
+ makeIDs.sort(function(a, b){ return a - b; });
+
+ pageItems = [];
+ for( var m = 0; m < makeIDs.length; m++ )
+ {
+ var id2 = makeIDs[m];
+ var data4 = GlassBlowingMap[id2];
+ if( !data4 )
+ continue;
- myGump.AddText( 255, 60 + ( index * 20 ), LabelHue, GetDictionaryEntry( myPage[pageNum - 1][i], socket.language ) );
+ var needsRecipe2 = data4.recipeID;
+ var showAll2 = displayUnlearnedRecipes;
- myGump.AddButton( 480, 60 + ( index * 20 ), 4011, 4012, 1, 0, 2000 + ( 100 * pageNum ) + i );
+ if( eraOK( data4 ) && ( !needsRecipe2 || showAll2 || HasLearnedRecipe( pUser, needsRecipe2 )))
+ {
+ pageItems.push( id2 );
+ }
+ }
+ }
}
- myGump.Send( socket );
- myGump.Free();
-}
-/** @type { ( tObject: BaseObject, timerId: number ) => void } */
-function onTimer( pUser, timerID )
-{
- if( !ValidateObject( pUser ))
- return;
+ // Subpage handling
+ var subPage = pUser.GetTempTag( "subPage" );
+ var totalSubPages = Math.ceil( pageItems.length / itemsPerPage );
- var socket = pUser.socket;
+ if( totalSubPages < 1 )
+ totalSubPages = 1;
- switch( timerID )
- {
- case 1: // Page 1 - Maps
- PageX( socket, pUser, timerID );
- break;
- }
-}
+ if( subPage < 1 )
+ subPage = 1;
-/** @type { ( myObj: Socket, pressed: number, gump: GumpData ) => void } */
-function onGumpPress( pSock, pButton, gumpData )
-{
- var pUser = pSock.currentChar;
+ if( subPage > totalSubPages )
+ subPage = totalSubPages;
- // Don't continue if character is invalid, or worse... dead!
- if( !ValidateObject( pUser ) || pUser.dead )
- return;
+ pUser.SetTempTag( "page", pageNum );
+ pUser.SetTempTag( "subPage", subPage );
- // Don't continue if player no longer has access to the crafting tool
- var bItem = pSock.tempObj;
- if( !ValidateObject( bItem ) || !pUser.InRange( bItem, 3 ))
- {
- pSock.SysMessage( GetDictionaryEntry( 461, pSock.language )); // You are too far away.
- return;
- }
+ var startIndex = ( subPage - 1 ) * itemsPerPage;
+ var endIndex = Math.min( startIndex + itemsPerPage, pageItems.length );
- if( bItem.movable == 3 )
+ if( startIndex >= pageItems.length )
{
- pSock.SysMessage( GetDictionaryEntry( 6031, pSock.language )); // Locked down resources cannot be used!
- return;
+ subPage = 1;
+ startIndex = 0;
+ endIndex = Math.min( itemsPerPage, pageItems.length );
+ pUser.SetTempTag( "subPage", subPage );
}
- var iPackOwner = GetPackOwner( bItem, 0 );
- if( ValidateObject( iPackOwner )) // Is the item in a backpack?
- {
- if( iPackOwner.serial != pUser.serial ) // And if so does the pack belong to the user?
- {
- pSock.SysMessage( GetDictionaryEntry( 6032, pSock.language )); // That resource is in someone else's backpack!
- return;
- }
- }
- else
+ var glassGump = new Gump;
+ TriggerEvent( craftGumpID, "CraftingGumpMenu", glassGump, socket );
+ glassGump.AddPage( 1 );
+
+ for( var j = startIndex; j < endIndex; j++ )
{
- pSock.SysMessage( GetDictionaryEntry( 6022, pSock.language )); // This has to be in your backpack before you can use it.
- return;
- }
+ var index = j - startIndex;
+ var makeID = pageItems[j];
+ var entryText;
+ var buttonID = makeID;
- var gumpID = scriptID + 0xffff;
- var makeID = 0;
- var itemDetailsID = 0;
- var timerID = 0;
+ var data5 = GlassBlowingMap[makeID];
- if(( pButton >= 100 && pButton <= 305 ) || pButton == 5000 )
- {
- if( pButton == 5000 )
+ if( !data5 )
{
- // Make Last button
- pButton = pUser.GetTempTag( "MAKELAST" );
+ entryText = "[Missing MakeID: " + makeID + "]";
}
else
{
- pUser.SetTempTag( "MAKELAST", pButton );
+ if( data5.customName )
+ {
+ entryText = data5.customName;
+ }
+ else if( data5.dictID )
+ {
+ entryText = GetDictionaryEntry( data5.dictID, socket.language );
+ if( !entryText || entryText === "" )
+ entryText = "[Missing EntryID: " + data5.dictID + "]";
+ }
+ else
+ {
+ entryText = "[Unnamed Item: " + makeID + "]";
+ }
}
+
+ // Craft button uses makeID
+ glassGump.AddButton( 220, 60 + ( index * 20 ), 4005, 4007, 1, 0, buttonID );
+ glassGump.AddText( 255, 60 + ( index * 20 ), textHue, entryText );
+
+ // Detail button: 20000 + makeID (matches your current onGumpPress)
+ glassGump.AddButton( 480, 60 + ( index * 20 ), 4011, 4012, 1, 0, 20000 + buttonID );
}
- switch ( pButton )
+ // Prev subpage
+ if( subPage > 1 )
{
- case 0: // Abort and do nothing
- pUser.SetTempTag( "MAKELAST", null );
- pUser.SetTempTag( "CRAFT", null )
- pSock.CloseGump( gumpID, 0 );
- break;
- case 1: // Page 1 - Maps
- pSock.CloseGump( gumpID, 0 );
- PageX( pSock, pUser, pButton );
- break;
- // Make Items
- case 100: // empty bottle
- makeID = 3000; timerID = 1; break;
- case 101: // flask (small)
- makeID = 3001; timerID = 1; break;
- case 102: // flask (medium)
- makeID = 3002; timerID = 1; break;
- case 103: // flask (curved)
- makeID = 3003; timerID = 1; break;
- case 103: // flask (large #1)
- makeID = 3004; timerID = 1; break;
- case 103: // flask (large #2)
- makeID = 3005; timerID = 1; break;
- case 103: // flask (bubbling blue)
- makeID = 3006; timerID = 1; break;
- case 103: // flask (bubbling purple)
- makeID = 3007; timerID = 1; break;
- case 103: // flask (bubbling red)
- makeID = 3008; timerID = 1; break;
- case 103: // empty vials
- makeID = 3009; timerID = 1; break;
- case 103: // full vials
- makeID = 3010; timerID = 1; break;
- case 103: // spinning hourglass
- makeID = 3011; timerID = 1; break;
- // Show Item Details
- case 2100: // empty bottle
- itemDetailsID = 3000; break;
- case 2101: // flask (small)
- itemDetailsID = 3001; break;
- case 2102: // flask (medium)
- itemDetailsID = 3002; break;
- case 2103: // flask (curved)
- itemDetailsID = 3003; break;
- case 2104: // flask (large #1)
- itemDetailsID = 3004; break;
- case 2105: // flask (large #2)
- itemDetailsID = 3005; break;
- case 2106: // flask (bubbling blue)
- itemDetailsID = 3006; break;
- case 2107: // flask (bubbling purple)
- itemDetailsID = 3007; break;
- case 2108: // flask (bubbling red)
- itemDetailsID = 3008; break;
- case 2109: // empty vials
- itemDetailsID = 3009; break;
- case 2110: // full vials
- itemDetailsID = 3010; break;
- case 2111: // spinning hourglass
- itemDetailsID = 3011; break;
- default:
- break;
+ glassGump.AddButton( 220, 260, 4014, 4015, 1, 0, 8000 + ( subPage - 1 ));
+ glassGump.AddHTMLGump( 255, 263, 100, 18, false, false, "" + GetDictionaryEntry( 10101, socket.language ) + "" );
}
- if( makeID != 0 )
+ // Next subpage
+ if( subPage < totalSubPages )
+ {
+ glassGump.AddButton( 370, 260, 4005, 4007, 1, 0, 9000 + ( subPage + 1 ));
+ glassGump.AddHTMLGump( 405, 263, 100, 18, false, false, "" + GetDictionaryEntry( 10100, socket.language ) + "" );
+ }
+
+ glassGump.Send( socket );
+ glassGump.Free();
+}
+
+/** @type { ( tObject: BaseObject, timerId: number ) => void } */
+function onTimer( pUser, timerID )
+{
+ if( !ValidateObject( pUser ))
+ return;
+
+ var pSocket = pUser.socket;
+ if( pSocket == null )
+ return;
+
+ if( timerID >= 1 && timerID <= 8 )
+ {
+ PageX( pSocket, pUser, timerID );
+ }
+ else if( timerID == 999 )
+ {
+ PageX( pSocket, pUser, 999 );
+ }
+}
+
+/** @type { ( myObj: Socket, pressed: number, gump: GumpData ) => void } */
+function onGumpPress( socket, pButton, gumpData )
+{
+ if( socket == null )
+ return;
+
+ var pUser = socket.currentChar;
+
+ if( !ValidateObject( pUser ) || pUser.dead )
+ return;
+
+ var bItem = socket.tempObj;
+ if( !ValidateObject( bItem ) || !pUser.InRange( bItem, 3 ))
+ {
+ socket.SysMessage( GetDictionaryEntry( 461, socket.language ));
+ return;
+ }
+
+ if( bItem.movable == 3 )
+ {
+ socket.SysMessage( GetDictionaryEntry( 6031, socket.language ));
+ return;
+ }
+
+ var iPackOwner = GetPackOwner( bItem, 0 );
+ if( ValidateObject( iPackOwner ))
+ {
+ if( iPackOwner.serial != pUser.serial )
+ {
+ socket.SysMessage( GetDictionaryEntry( 6032, socket.language ));
+ return;
+ }
+ }
+ else
+ {
+ socket.SysMessage( GetDictionaryEntry( 6022, socket.language ));
+ return;
+ }
+
+ var gumpID = glassblowingID + 0xffff;
+
+ // Subpage back / forward
+ if( pButton >= 8001 && pButton < 9000 )
+ {
+ var subPage = pButton - 8000;
+ var pageNum = pUser.GetTempTag( "page" );
+ pUser.SetTempTag( "subPage", subPage );
+ PageX( socket, pUser, pageNum );
+ return;
+ }
+
+ if( pButton >= 9001 && pButton < 10000 )
+ {
+ var subPage2 = pButton - 9000;
+ var pageNum2 = pUser.GetTempTag( "page" );
+ pUser.SetTempTag( "subPage", subPage2 );
+ PageX( socket, pUser, pageNum2 );
+ return;
+ }
+
+ // Page tabs
+ if( pButton >= 1 && pButton <= 8 )
+ {
+ pUser.SetTempTag( "page", pButton );
+ pUser.SetTempTag( "subPage", 1 );
+ PageX( socket, pUser, pButton );
+ return;
+ }
+
+ // Last Ten
+ if( pButton == 11000 )
+ {
+ pUser.SetTempTag( "page", 999 );
+ pUser.SetTempTag( "subPage", 1 );
+ PageX( socket, pUser, 999 );
+ return;
+ }
+
+ // Close gump
+ if( pButton == 0 )
+ {
+ pUser.SetTempTag( "MakeLast_Glass", null );
+ pUser.SetTempTag( "CRAFT", null );
+ socket.CloseGump( gumpID, 0 );
+ return;
+ }
+
+ var makeID = 0;
+ var timerID = 0;
+
+ // Make Last
+ if( pButton == 5000 )
+ {
+ var last = pUser.GetTempTag( "MakeLast_Glass" );
+ if( last )
+ pButton = last;
+ else
+ return;
+ }
+
+ // Craft buttons use makeID directly
+ if( GlassBlowingMap[pButton] != undefined )
+ {
+ makeID = pButton;
+ var data = GlassBlowingMap[makeID];
+ timerID = data.timerID || 1;
+
+ if( !eraOK( data ))
+ {
+ socket.SysMessage( "That item is not available in this era." );
+ return;
+ }
+
+ if( data.recipeID && !TriggerEvent( 4022, "NeedRecipe", pUser, data.recipeID ))
+ {
+ socket.SysMessage( "You must learn that recipe from a scroll." );
+ return;
+ }
+
+ pUser.SetTempTag( "MakeLast_Glass", makeID );
+
+ MakeItem( socket, pUser, makeID );
+ AddToLastTen( pUser, makeID );
+
+ if( GetServerSetting( "ToolUseLimit" ))
+ {
+ bItem.usesLeft -= 1;
+ if( bItem.usesLeft == 0 && GetServerSetting( "ToolUseBreak" ))
+ {
+ bItem.Delete();
+ socket.SysMessage( GetDictionaryEntry( 10202, socket.language ));
+ }
+ }
+
+ pUser.StartTimer( gumpDelay, timerID, glassblowingID );
+ return;
+ }
+
+ // Detail buttons: 20000 + makeID
+ if( pButton >= 20000 && pButton < 30000 )
{
- pUser.AddScriptTrigger(4033); // crafting_complete.js for applying map settings
- MakeItem( pSock, pUser, makeID );
- if( GetServerSetting( "ToolUseLimit" ))
+ var detailMakeID = pButton - 20000;
+ var entry = AlchemyMap[detailMakeID];
+
+ if( entry )
{
- bItem.usesLeft -= 1;
- if( bItem.usesLeft == 0 && GetServerSetting( "ToolUseBreak" ))
+ // Which item details to show
+ pUser.SetTempTag( "ITEMDETAILS", detailMakeID );
+
+ // Skill used
+ pUser.SetTempTag( "Skill", entry.skill || glassSkillID );
+
+ // Clear old harvest tags to avoid cross-contamination
+ pUser.SetTempTag( "Harvest", null );
+ pUser.SetTempTag( "Harvest2", null );
+ pUser.SetTempTag( "Harvest3", null );
+ pUser.SetTempTag( "Harvest4", null );
+
+ // Clear old harvest names
+ pUser.SetTempTag( "HarvestName", null );
+ pUser.SetTempTag( "Harvest2Name", null );
+ pUser.SetTempTag( "Harvest3Name", null );
+ pUser.SetTempTag( "Harvest4Name", null );
+
+ // If you later add entry.harvest = [dictID1, dictID2,...], you can push them here
+ if( entry.harvest && entry.harvest.length > 0 )
{
- bItem.Delete();
- pSock.SysMessage( GetDictionaryEntry( 10202, pSock.language )); // You have worn out your tool!
- // Play sound effect of tool breaking
+ if( entry.harvest.length >= 1 )
+ pUser.SetTempTag( "Harvest", entry.harvest[0] );
+ if( entry.harvest.length >= 2 )
+ pUser.SetTempTag( "Harvest2", entry.harvest[1] );
+ if( entry.harvest.length >= 3 )
+ pUser.SetTempTag( "Harvest3", entry.harvest[2] );
+ if( entry.harvest.length >= 4 )
+ pUser.SetTempTag( "Harvest4", entry.harvest[3] );
}
+
+ // OPTIONAL custom names – these override the dictionary string
+ if( entry.harvestNames && entry.harvestNames.length > 0 )
+ {
+ if( entry.harvestNames.length >= 1 )
+ pUser.SetTempTag( "HarvestName", entry.harvestNames[0] );
+ if( entry.harvestNames.length >= 2 )
+ pUser.SetTempTag( "Harvest2Name", entry.harvestNames[1] );
+ if( entry.harvestNames.length >= 3 )
+ pUser.SetTempTag( "Harvest3Name", entry.harvestNames[2] );
+ if( entry.harvestNames.length >= 4 )
+ pUser.SetTempTag( "Harvest4Name", entry.harvestNames[3] );
+ }
+
+ if( entry.recipeID && entry.recipeID > 0 )
+ pUser.SetTempTag( "needRecipeID", entry.recipeID );
+ else
+ pUser.SetTempTag( "needRecipeID", 0 );
+
+ TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
}
- pUser.StartTimer( gumpDelay, timerID, true );
- }
- else if( itemDetailsID != 0 )
- {
- pUser.SetTempTag( "ITEMDETAILS", itemDetailsID );
- TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
+ return;
}
}
+
+// Last Ten
+function AddToLastTen( pUser, makeID )
+{
+ var raw = pUser.GetTempTag( "LastTenGlassblowing" ) || "";
+ var list = raw.split( "," );
+
+ for( var i = 0; i < list.length; i++ )
+ {
+ if( parseInt( list[i] ) == makeID )
+ {
+ list.splice( i, 1 );
+ break;
+ }
+ }
+
+ var newList = [ makeID ];
+ for( var j = 0; j < list.length && newList.length < 10; j++ )
+ {
+ var entry = parseInt( list[j] );
+ if( !isNaN( entry ) && entry > 0 )
+ newList.push( entry );
+ }
+
+ pUser.SetTempTag( "LastTenGlassblowing", newList.join( "," ) );
+}
+
+function HasLearnedRecipe( pUser, recipeID )
+{
+ var myData = TriggerEvent( 4022, "ReadRecipeID", pUser );
+ if( !myData || myData.length == 0 )
+ return false;
+
+ for( var i = 0; i < myData.length; i++ )
+ {
+ var data = myData[i].split( "," );
+ if( data[0] == recipeID )
+ return true;
+ }
+ return false;
+}
+
+function eraOK( entry )
+{
+ if( entry.minEra && coreShardEra < EraStringToNum( entry.minEra ))
+ return false;
+ if( entry.maxEra && coreShardEra > EraStringToNum( entry.maxEra ))
+ return false;
+ return true;
+}
\ No newline at end of file
diff --git a/data/js/skill/craft/itemdetailgump.js b/data/js/skill/craft/itemdetailgump.js
index 78a460347..28fda39e6 100644
--- a/data/js/skill/craft/itemdetailgump.js
+++ b/data/js/skill/craft/itemdetailgump.js
@@ -7,1766 +7,153 @@ const Alchemy = 4028;
const Fletching = 4029;
const Tailoring = 4030;
const Tinkering = 4032;
-const scriptID = 4026; // This script
+const itemDetailID = 4026; // This script
const Cooking = 4034;
const Cartography = 4035;
const Glassblowing = 4036;
+const Masonry = 4037;
const exceptionalWearablesOnly = true;
function ItemDetailGump( pUser )
{
+ var skillNames = [
+ "alchemy",
+ "anatomy",
+ "animallore",
+ "itemid",
+ "armslore",
+ "parrying",
+ "begging",
+ "blacksmithing",
+ "bowcraft",
+ "peacemaking",
+ "camping",
+ "carpentry",
+ "cartography",
+ "cooking",
+ "detectinghidden",
+ "enticement",
+ "evaluatingintel",
+ "healing",
+ "fishing",
+ "forensics",
+ "herding",
+ "hiding",
+ "provocation",
+ "inscription",
+ "lockpicking",
+ "magery",
+ "magicresistance",
+ "tactics",
+ "snooping",
+ "musicianship",
+ "poisoning",
+ "archery",
+ "spiritspeak",
+ "stealing",
+ "tailoring",
+ "taming",
+ "tasteid",
+ "tinkering",
+ "tracking",
+ "veterinary",
+ "swordsmanship",
+ "macefighting",
+ "fencing",
+ "wrestling",
+ "lumberjacking",
+ "mining",
+ "meditation",
+ "stealth",
+ "removetrap",
+ "necromancy",
+ "focus",
+ "chivalry",
+ "bushido",
+ "ninjitsu",
+ "spellweaving ",
+ "mysticism ",
+ "imbuing",
+ "throwing "
+ ];
+
var socket = pUser.socket;
var itemGump = new Gump;
var createEntry = null;
- var HARVEST;
+ // Now supports both dict-based and custom harvest names
+ // harvestResource[i] = { id: number, name: string }
+ var harvestResource = [];
var mainSkill;
- switch( pUser.GetTempTag( "ITEMDETAILS" ))
+
+ var detailTag = pUser.GetTempTag( "ITEMDETAILS" );
+ var skillTag = pUser.GetTempTag( "Skill" );
+ var harvestTag = pUser.GetTempTag( "Harvest" );
+ var harvest2Tag = pUser.GetTempTag( "Harvest2" );
+ var harvest3Tag = pUser.GetTempTag( "Harvest3" );
+ var harvest4Tag = pUser.GetTempTag( "Harvest4" );
+
+ // NEW: optional custom harvest names
+ var harvestNameTag = pUser.GetTempTag( "HarvestName" );
+ var harvest2NameTag = pUser.GetTempTag( "Harvest2Name" );
+ var harvest3NameTag = pUser.GetTempTag( "Harvest3Name" );
+ var harvest4NameTag = pUser.GetTempTag( "Harvest4Name" );
+
+ var recipeID = pUser.GetTempTag( "needRecipeID" );
+
+ if( detailTag !== null )
{
- //Start Blacksmith
- case 1:
- createEntry = CreateEntries[1];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 2:
- createEntry = CreateEntries[2];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 3:
- createEntry = CreateEntries[3];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 4:
- createEntry = CreateEntries[4];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 5:
- createEntry = CreateEntries[5];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 6:
- createEntry = CreateEntries[6];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 7:
- createEntry = CreateEntries[7];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 8:
- createEntry = CreateEntries[8];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 9:
- createEntry = CreateEntries[9];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 10:
- createEntry = CreateEntries[10];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 11:
- createEntry = CreateEntries[11];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 12:
- createEntry = CreateEntries[12];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 13:
- createEntry = CreateEntries[13];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 14:
- createEntry = CreateEntries[14];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 15:
- createEntry = CreateEntries[15];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 16:
- createEntry = CreateEntries[16];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 17:
- createEntry = CreateEntries[17];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 18:
- createEntry = CreateEntries[18];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 19:
- createEntry = CreateEntries[19];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 20:
- createEntry = CreateEntries[20];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 21:
- createEntry = CreateEntries[21];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 22:
- createEntry = CreateEntries[22];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 23:
- createEntry = CreateEntries[23];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 24:
- createEntry = CreateEntries[24];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 25:
- createEntry = CreateEntries[25];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 26:
- createEntry = CreateEntries[26];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 27:
- createEntry = CreateEntries[27];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 28:
- createEntry = CreateEntries[28];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 29:
- createEntry = CreateEntries[29];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 30:
- createEntry = CreateEntries[30];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 31:
- createEntry = CreateEntries[31];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 32:
- createEntry = CreateEntries[32];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 33:
- createEntry = CreateEntries[33];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 34:
- createEntry = CreateEntries[34];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 35:
- createEntry = CreateEntries[35];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 36:
- createEntry = CreateEntries[36];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 37:
- createEntry = CreateEntries[37];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 38:
- createEntry = CreateEntries[38];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 39:
- createEntry = CreateEntries[39];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 40:
- createEntry = CreateEntries[40];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 41:
- createEntry = CreateEntries[41];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 42:
- createEntry = CreateEntries[42];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 43:
- createEntry = CreateEntries[43];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 44:
- createEntry = CreateEntries[44];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 45:
- createEntry = CreateEntries[45];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 46:
- createEntry = CreateEntries[46];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 47:
- createEntry = CreateEntries[47];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 48:
- createEntry = CreateEntries[48];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- case 49:
- createEntry = CreateEntries[49];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.blacksmithing );
- break;
- //Start Carpentry
- case 50:
- createEntry = CreateEntries[50];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 51:
- createEntry = CreateEntries[51];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 52:
- createEntry = CreateEntries[52];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 53:
- createEntry = CreateEntries[53];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 54:
- createEntry = CreateEntries[54];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 55:
- createEntry = CreateEntries[55];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 56:
- createEntry = CreateEntries[56];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 57:
- createEntry = CreateEntries[57];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 58:
- createEntry = CreateEntries[58];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 59:
- createEntry = CreateEntries[59];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 60:
- createEntry = CreateEntries[60];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 61:
- createEntry = CreateEntries[61];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 62:
- createEntry = CreateEntries[62];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 63:
- createEntry = CreateEntries[63];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 64:
- createEntry = CreateEntries[64];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 65:
- createEntry = CreateEntries[65];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 66:
- createEntry = CreateEntries[66];
- HARVEST = [10611, 10612];//missing loops
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 67:
- createEntry = CreateEntries[67];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 68:
- createEntry = CreateEntries[68];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 69:
- createEntry = CreateEntries[69];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 70:
- createEntry = CreateEntries[70];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 71:
- createEntry = CreateEntries[71];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 72:
- createEntry = CreateEntries[72];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 73:
- createEntry = CreateEntries[73];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 74:
- createEntry = CreateEntries[74];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 75:
- createEntry = CreateEntries[75];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 76:
- createEntry = CreateEntries[76];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 77:
- createEntry = CreateEntries[77];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 78:
- createEntry = CreateEntries[78];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 79:
- createEntry = CreateEntries[79];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 80:
- createEntry = CreateEntries[80];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 81:
- createEntry = CreateEntries[81];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 82:
- createEntry = CreateEntries[82];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 83:
- createEntry = CreateEntries[83];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 84:
- createEntry = CreateEntries[84];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 85:
- createEntry = CreateEntries[85];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 86:
- createEntry = CreateEntries[86];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 87:
- createEntry = CreateEntries[87];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 88:
- createEntry = CreateEntries[8];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 89:
- createEntry = CreateEntries[89];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 90:
- createEntry = CreateEntries[90];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 91:
- createEntry = CreateEntries[91];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 92:
- createEntry = CreateEntries[92];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 93:
- createEntry = CreateEntries[93];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 94:
- createEntry = CreateEntries[94];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 95:
- createEntry = CreateEntries[95];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 96:
- createEntry = CreateEntries[96];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 97:
- createEntry = CreateEntries[97];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 98:
- createEntry = CreateEntries[98];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 99:
- createEntry = CreateEntries[99];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 100:
- createEntry = CreateEntries[100];
- HARVEST = [10014, 10015];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 101:
- createEntry = CreateEntries[101];
- HARVEST = [10014, 10015];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 102:
- createEntry = CreateEntries[102];
- HARVEST = [10014, 10015];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 103:
- createEntry = CreateEntries[103];
- HARVEST = [10014, 10015];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 104:
- createEntry = CreateEntries[104];
- HARVEST = [10014, 10015];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 105:
- createEntry = CreateEntries[105];
- HARVEST = [10014, 10015];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 106:
- createEntry = CreateEntries[106];
- HARVEST = [10014, 10015];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 107:
- createEntry = CreateEntries[107];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 108:
- createEntry = CreateEntries[108];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 109:
- createEntry = CreateEntries[109];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 110:
- pUser.TextMessage( "Case 110!" );
- createEntry = CreateEntries[110];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 111:
- createEntry = CreateEntries[111];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 112:
- createEntry = CreateEntries[112];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 113:
- createEntry = CreateEntries[113];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 114:
- createEntry = CreateEntries[114];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 115:
- createEntry = CreateEntries[115];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 116:
- createEntry = CreateEntries[116];
- HARVEST = [10014, 10016];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 117:
- createEntry = CreateEntries[117];
- HARVEST = [10014, 10015];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 118:
- createEntry = CreateEntries[118];
- HARVEST = [10014, 10015];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 119:
- createEntry = CreateEntries[119];
- HARVEST = [10014, 10015];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 120:
- createEntry = CreateEntries[120];
- HARVEST = [10014, 10015];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 121:
- createEntry = CreateEntries[121];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 122:
- createEntry = CreateEntries[122];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 123:
- createEntry = CreateEntries[123];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- case 124:
- createEntry = CreateEntries[124];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.carpentry );
- break;
- // Start Tailoring
- case 130:
- createEntry = CreateEntries[130];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 131:
- createEntry = CreateEntries[131];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 132:
- createEntry = CreateEntries[132];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 133:
- createEntry = CreateEntries[133];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 134:
- createEntry = CreateEntries[134];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 135:
- createEntry = CreateEntries[135];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 136:
- createEntry = CreateEntries[136];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 137:
- createEntry = CreateEntries[137];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 138:
- createEntry = CreateEntries[138];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 139:
- createEntry = CreateEntries[139];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 140:
- createEntry = CreateEntries[140];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 141:
- createEntry = CreateEntries[141];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 142:
- createEntry = CreateEntries[142];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 143:
- createEntry = CreateEntries[143];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 144:
- createEntry = CreateEntries[144];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 145:
- createEntry = CreateEntries[145];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 146:
- createEntry = CreateEntries[146];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 147:
- createEntry = CreateEntries[147];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 148:
- createEntry = CreateEntries[148];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 149:
- createEntry = CreateEntries[149];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 150:
- createEntry = CreateEntries[150];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 151:
- createEntry = CreateEntries[151];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 152:
- createEntry = CreateEntries[152];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 153:
- createEntry = CreateEntries[153];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 154:
- createEntry = CreateEntries[154];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 155:
- createEntry = CreateEntries[155];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 156:
- createEntry = CreateEntries[156];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 157:
- createEntry = CreateEntries[157];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 158:
- createEntry = CreateEntries[158];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break
- case 159:
- createEntry = CreateEntries[159];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 160:
- createEntry = CreateEntries[160];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 161:
- createEntry = CreateEntries[161];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 162:
- createEntry = CreateEntries[162];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 163:
- createEntry = CreateEntries[163];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 164:
- createEntry = CreateEntries[164];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 165:
- createEntry = CreateEntries[165];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 166:
- createEntry = CreateEntries[166];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 167:
- createEntry = CreateEntries[167];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 168:
- createEntry = CreateEntries[168];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 169:
- createEntry = CreateEntries[169];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 170:
- createEntry = CreateEntries[170];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 171:
- createEntry = CreateEntries[171];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 172:
- createEntry = CreateEntries[172];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 173:
- createEntry = CreateEntries[173];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 174:
- createEntry = CreateEntries[174];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 175:
- createEntry = CreateEntries[175];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 176:
- createEntry = CreateEntries[176];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 177:
- createEntry = CreateEntries[177];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 178:
- createEntry = CreateEntries[178];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 179:
- createEntry = CreateEntries[179];
- HARVEST = [10007];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 180:
- createEntry = CreateEntries[180];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 181:
- createEntry = CreateEntries[181];
- HARVEST = [10007, 10008];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 182:
- createEntry = CreateEntries[182];
- HARVEST = [10007, 10008];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 183:
- createEntry = CreateEntries[184];
- HARVEST = [10007, 10008];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 184:
- createEntry = CreateEntries[184];
- HARVEST = [10007, 10008];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 185:
- createEntry = CreateEntries[185];
- HARVEST = [10007, 10008];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- case 186:
- createEntry = CreateEntries[135];
- HARVEST = [10016];
- mainSkill = parseInt( pUser.skills.tailoring );
- break;
- // Start Fletching
- case 190:
- createEntry = CreateEntries[190];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 190: // Kindling
- createEntry = CreateEntries[190];
- HARVEST = [10014]; // Boards or Logs
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 191: // Bow
- createEntry = CreateEntries[191];
- HARVEST = [10014]; // Boards or Logs
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 192: // Crossbow
- createEntry = CreateEntries[192];
- HARVEST = [10014]; // Boards or Logs
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 193: // Heavy Crossbow
- createEntry = CreateEntries[193];
- HARVEST = [10014]; // Boards or Logs
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 194: // Shaft
- createEntry = CreateEntries[194];
- HARVEST = [10014]; // Boards or Logs
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 195: // Five Shafts
- createEntry = CreateEntries[195];
- HARVEST = [10014]; // Boards or Logs
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 196: // Twenty Shafts
- createEntry = CreateEntries[196];
- HARVEST = [10014]; // Boards or Logs
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 197: // Fifty Shafts
- createEntry = CreateEntries[197];
- HARVEST = [10014]; // Boards or Logs
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 198: // Arrow
- createEntry = CreateEntries[198];
- HARVEST = [10029, 10028]; // Shaft, Feather
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 199: // Five Arrows
- createEntry = CreateEntries[199];
- HARVEST = [10029, 10028]; // Shaft, Feather
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 200: // Twenty Arrows
- createEntry = CreateEntries[200];
- HARVEST = [10029, 10028]; // Shaft, Feather
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 201: // Fifty Arrows
- createEntry = CreateEntries[201];
- HARVEST = [10029, 10028]; // Shaft, Feather
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 202: // Bolt
- createEntry = CreateEntries[202];
- HARVEST = [10029, 10028]; // Shaft, Feather
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 203: // Five Bolts
- createEntry = CreateEntries[203];
- HARVEST = [10029, 10028]; // Shaft, Feather
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 204: // Twenty Bolts
- createEntry = CreateEntries[204];
- HARVEST = [10029, 10028]; // Shaft, Feather
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 205: // Fifty Bolts
- createEntry = CreateEntries[205];
- HARVEST = [10029, 10028]; // Shaft, Feather
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 206:
- createEntry = CreateEntries[206];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 207:
- createEntry = CreateEntries[207];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- case 208:
- createEntry = CreateEntries[208];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.bowcraft );
- break;
- // Start Tinkering
- case 274: // Axle
- createEntry = CreateEntries[274];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 273: // Clock Frame
- createEntry = CreateEntries[273];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 270: // Jointing Plane
- createEntry = CreateEntries[270];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 271: // Moulding Plane
- createEntry = CreateEntries[271];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 272: // Smoothing Plane
- createEntry = CreateEntries[272];
- HARVEST = [10014];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- // Page 2 - Tools
- case 218: // Dovetail Saw
- createEntry = CreateEntries[218];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 215: // Draw Knife
- createEntry = CreateEntries[215];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 252: // Froe
- createEntry = CreateEntries[252];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 255: // Hammer
- createEntry = CreateEntries[255];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 214: // Hatchet
- createEntry = CreateEntries[214];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 258: // Inshave
- createEntry = CreateEntries[258];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 260: // Lockpick
- createEntry = CreateEntries[260];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 211: // Mortar and Pestle
- createEntry = CreateEntries[211];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 259: // Pick Axe
- createEntry = CreateEntries[259];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 217: // Saw
- createEntry = CreateEntries[217];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 210: // Scissors
- createEntry = CreateEntries[210];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 212: // Scorp
- createEntry = CreateEntries[212];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 216: // Sewing Kit
- createEntry = CreateEntries[216];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 254: // Shovel
- createEntry = CreateEntries[254];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 257: // Sledge Hammer
- createEntry = CreateEntries[257];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 256: // Smith's Hammer
- createEntry = CreateEntries[256];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 253: // Tongs
- createEntry = CreateEntries[253];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 213: // Tool Kit (Tinker's tools)
- createEntry = CreateEntries[213];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 214: // Fletcher's Tools
- createEntry = CreateEntries[284];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- // Page 3 - Parts
- case 224: // Barrel Hoops
- createEntry = CreateEntries[224];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 221: // Barrel Tap
- createEntry = CreateEntries[221];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 220: // Clock parts
- createEntry = CreateEntries[220];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 219: // Gears
- createEntry = CreateEntries[219];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 225: // Hinge
- createEntry = CreateEntries[225];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 223: // Sextant parts
- createEntry = CreateEntries[223];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 222: // Springs
- createEntry = CreateEntries[222];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- // Page 4 - Utensils
- case 226: // Butcher Knife
- createEntry = CreateEntries[226];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 232: // Cleaver
- createEntry = CreateEntries[232];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 230: // Fork
- createEntry = CreateEntries[230];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 231: // Fork
- createEntry = CreateEntries[231];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 235: // Goblet
- createEntry = CreateEntries[235];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 233: // Knife
- createEntry = CreateEntries[233];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 234: // Knife
- createEntry = CreateEntries[234];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 236: // Pewter Mug
- createEntry = CreateEntries[236];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 229: // Plate
- createEntry = CreateEntries[229];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 237: // Skinning Knife
- createEntry = CreateEntries[237];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 227: // Spoon
- createEntry = CreateEntries[227];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 228: // Spoon
- createEntry = CreateEntries[228];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- // Page 5 - Jewelry
- case 243: // Bracelet
- createEntry = CreateEntries[243];
- HARVEST = [10015, 12005];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 241: // Earrings
- createEntry = CreateEntries[241];
- HARVEST = [10015, 12005];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 239: // Necklage (Golden beads)
- createEntry = CreateEntries[239];
- HARVEST = [10015, 12005];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 240: // Necklace (Silver beads)
- createEntry = CreateEntries[240];
- HARVEST = [10015, 12005];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 242: // Necklace (Round)
- createEntry = CreateEntries[242];
- HARVEST = [10015, 12005];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 238: // Weddingband (newbiefied)
- createEntry = CreateEntries[238];
- HARVEST = [10015, 12006];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- // Page 6 - Miscellaneous
- case 248: // Globe
- createEntry = CreateEntries[248];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 251: // Heating stand
- createEntry = CreateEntries[251];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 247: // Iron Key
- createEntry = CreateEntries[247];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 244: // Keyring
- createEntry = CreateEntries[244];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 250: // Lantern
- createEntry = CreateEntries[250];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 246: // Scales
- createEntry = CreateEntries[246];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 249: // Spy glass
- createEntry = CreateEntries[249];
- HARVEST = [10015];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- // Page 7 - Multi-Component Items
- case 275: // Axle and Gears
- createEntry = CreateEntries[275];
- HARVEST = [11801, 11863];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 276: // Clock
- createEntry = CreateEntries[276];
- HARVEST = [11802, 11862];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 277: // Clock
- createEntry = CreateEntries[277];
- HARVEST = [11802, 11862];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 278: // Clock Parts
- createEntry = CreateEntries[278];
- HARVEST = [11801, 11863];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 279: // Locked Box
- createEntry = CreateEntries[279];
- HARVEST = [10634];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 280: // Locked Chest
- createEntry = CreateEntries[280];
- HARVEST = [10638];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 281: // Potion Keg
- createEntry = CreateEntries[281];
- HARVEST = [10642, 11861, 10612, 10928];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 282: // Sextant
- createEntry = CreateEntries[282];
- HARVEST = [11948];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 283: // Sextant Parts
- createEntry = CreateEntries[283];
- HARVEST = [11801, 11863];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- // Page 8 - Candles
- case 245: // Candelabra
- createEntry = CreateEntries[245];
- HARVEST = [10015, 12000];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 310: // Standing Candelabra
- createEntry = CreateEntries[310];
- HARVEST = [10015, 12000];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 315: // Regular Candle
- createEntry = CreateEntries[315];
- HARVEST = [10015, 12000];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 312: // Round Candle
- createEntry = CreateEntries[312];
- HARVEST = [12000];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 316: // Skull with Candle
- createEntry = CreateEntries[316];
- HARVEST = [12000, 12004];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 314: // Small Candle
- createEntry = CreateEntries[314];
- HARVEST = [12000];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 311: // Tall Candle
- createEntry = CreateEntries[311];
- HARVEST = [10015, 12000];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 313: // Thick Candle
- createEntry = CreateEntries[313];
- HARVEST = [12000];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- // Page 9 - Traps
- case 261: // Dart Trap
- createEntry = CreateEntries[261];
- HARVEST = [10015, 12001];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 263: // Explosion Trap
- createEntry = CreateEntries[263];
- HARVEST = [10015, 12003];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- case 262: // Poison Trap
- createEntry = CreateEntries[262];
- HARVEST = [10015, 12002];
- mainSkill = parseInt( pUser.skills.tinkering );
- break;
- //Start Alchemy
- case 290:
- createEntry = CreateEntries[290];
- HARVEST = [10019];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 291:
- createEntry = CreateEntries[291];
- HARVEST = [10019];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 292:
- createEntry = CreateEntries[292];
- HARVEST = [10020];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 293:
- createEntry = CreateEntries[293];
- HARVEST = [10020];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 294:
- createEntry = CreateEntries[294];
- HARVEST = [10020];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 295:
- createEntry = CreateEntries[295];
- HARVEST = [10021];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 296:
- createEntry = CreateEntries[296];
- HARVEST = [10021];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 297:
- createEntry = CreateEntries[297];
- HARVEST = [10021];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 298:
- createEntry = CreateEntries[298];
- HARVEST = [10022];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 299:
- createEntry = CreateEntries[299];
- HARVEST = [10022];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 300:
- createEntry = CreateEntries[300];
- HARVEST = [10022];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 301:
- createEntry = CreateEntries[301];
- HARVEST = [10024];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 302:
- createEntry = CreateEntries[302];
- HARVEST = [10024];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 303:
- createEntry = CreateEntries[303];
- HARVEST = [10024];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 304:
- createEntry = CreateEntries[304];
- HARVEST = [10024];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 305:
- createEntry = CreateEntries[305];
- HARVEST = [10025];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 306:
- createEntry = CreateEntries[306];
- HARVEST = [10025];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 307:
- createEntry = CreateEntries[307];
- HARVEST = [10026];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 308:
- createEntry = CreateEntries[308];
- HARVEST = [10026];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- case 309:
- createEntry = CreateEntries[309];
- HARVEST = [10023];
- mainSkill = parseInt( pUser.skills.alchemy );
- break;
- // Start Cooking
- // Page 1 - Ingredients
- case 1500: // Sack of Flour
- createEntry = CreateEntries[1500];
- HARVEST = [11636];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1501: // Dough
- createEntry = CreateEntries[1501];
- HARVEST = [11637, 11638];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1502: // Sweet Dough
- createEntry = CreateEntries[1502];
- HARVEST = [11607, 11639];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1503: // Cake Mix
- createEntry = CreateEntries[1503];
- HARVEST = [11637, 11608];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1504: // Cookie Mix
- createEntry = CreateEntries[1504];
- HARVEST = [11639, 11608];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- // Page 2 - Preparation
- case 1550: // Unbaked Quiche
- createEntry = CreateEntries[1550];
- HARVEST = [11607, 11640];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1551: // Unbaked Meat Pie
- createEntry = CreateEntries[1551];
- HARVEST = [11607, 11641];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1552: // Uncooked Sausage Pizza
- createEntry = CreateEntries[1552];
- HARVEST = [11607, 11642];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1553: // Uncooked Cheese Pizza
- createEntry = CreateEntries[1553];
- HARVEST = [11607, 11643];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1554: // Unbaked Fruit Pie
- createEntry = CreateEntries[1554];
- HARVEST = [11607, 11644];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1555: // Unbaked Peach Cobbler
- createEntry = CreateEntries[1555];
- HARVEST = [11607, 11645];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1556: // Unbaked Apple Pie
- createEntry = CreateEntries[1556];
- HARVEST = [11607, 11646];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1557: // Unbaked Pumpkin Pie
- createEntry = CreateEntries[1557];
- HARVEST = [11607, 11647];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- // Page 3 - Baking
- case 1600: // Bread Loaf
- createEntry = CreateEntries[1600];
- HARVEST = [11607];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1601: // Pan of Cookies
- createEntry = CreateEntries[1601];
- HARVEST = [11610];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1602: // Cake
- createEntry = CreateEntries[1602];
- HARVEST = [11609];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1603: // Muffins
- createEntry = CreateEntries[1603];
- HARVEST = [11608];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1604: // Baked Quiche
- createEntry = CreateEntries[1604];
- HARVEST = [11611];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1605: // Baked Meat Pie
- createEntry = CreateEntries[1605];
- HARVEST = [11612];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1606: // Sausage Pizza
- createEntry = CreateEntries[1606];
- HARVEST = [11613];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1607: // Cheese Pizza
- createEntry = CreateEntries[1607];
- HARVEST = [11614];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1608: // Baked Fruit Pie
- createEntry = CreateEntries[1608];
- HARVEST = [11615];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1609: // Baked Peach Cobbler
- createEntry = CreateEntries[1609];
- HARVEST = [11616];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1610: // Baked Apple Pie
- createEntry = CreateEntries[1610];
- HARVEST = [11617];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1611: // Baked Pumpkin Pie
- createEntry = CreateEntries[1611];
- HARVEST = [11618];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- // Page 4 - Barbecue
- case 1650: // Cooked Bird
- createEntry = CreateEntries[1650];
- HARVEST = [11648];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1651: // Chicken Leg
- createEntry = CreateEntries[1651];
- HARVEST = [11649];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1652: // Fish Steak
- createEntry = CreateEntries[1652];
- HARVEST = [11650];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1653: // Fried Eggs
- createEntry = CreateEntries[1653];
- HARVEST = [11651];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1654: // Leg of Lamb
- createEntry = CreateEntries[1654];
- HARVEST = [11652];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- case 1655: // Cut of Ribs
- createEntry = CreateEntries[1655];
- HARVEST = [11653];
- mainSkill = parseInt( pUser.skills.cooking );
- break;
- // Cartography
- case 2000: // Local Map
- createEntry = CreateEntries[2000];
- HARVEST = [13004];
- mainSkill = parseInt( pUser.skills.cartography );
- break;
- case 2001: // City Map
- createEntry = CreateEntries[2001];
- HARVEST = [13004];
- mainSkill = parseInt( pUser.skills.cartography );
- break;
- case 2002: // Sea Chart
- createEntry = CreateEntries[2002];
- HARVEST = [13004];
- mainSkill = parseInt( pUser.skills.cartography );
- break;
- case 2003: // World Map
- createEntry = CreateEntries[2003];
- HARVEST = [13004];
- mainSkill = parseInt( pUser.skills.cartography );
- break;
- // Glassblowing
- case 3000: // empty bottle
- createEntry = CreateEntries[3000];
- HARVEST = [13504];
- mainSkill = parseInt(pUser.skills.alchemy);
- break;
- case 3001: // flask (small)
- createEntry = CreateEntries[3001];
- HARVEST = [13504];
- mainSkill = parseInt(pUser.skills.alchemy);
- break;
- case 3002: // flask (medium)
- createEntry = CreateEntries[3002];
- HARVEST = [13504];
- mainSkill = parseInt(pUser.skills.alchemy);
- break;
- case 3003: // flask (curved)
- createEntry = CreateEntries[3003];
- HARVEST = [13504];
- mainSkill = parseInt(pUser.skills.alchemy);
- break;
- case 3004: // flask (large #1)
- createEntry = CreateEntries[3004];
- HARVEST = [13504];
- mainSkill = parseInt(pUser.skills.alchemy);
- break;
- case 3005: // flask (large #2)
- createEntry = CreateEntries[3005];
- HARVEST = [13504];
- mainSkill = parseInt(pUser.skills.alchemy);
- break;
- case 3006: // flask (bubbling blue)
- createEntry = CreateEntries[3006];
- HARVEST = [13504];
- mainSkill = parseInt(pUser.skills.alchemy);
- break;
- case 3007: // flask (bubbling purple)
- createEntry = CreateEntries[3007];
- HARVEST = [13504];
- mainSkill = parseInt(pUser.skills.alchemy);
- break;
- case 3008: // flask (bubbling red)
- createEntry = CreateEntries[3008];
- HARVEST = [13504];
- mainSkill = parseInt(pUser.skills.alchemy);
- break;
- case 3009: // empty vials
- createEntry = CreateEntries[3009];
- HARVEST = [13504];
- mainSkill = parseInt(pUser.skills.alchemy);
- break;
- case 3010: // full vials
- createEntry = CreateEntries[3010];
- HARVEST = [13504];
- mainSkill = parseInt(pUser.skills.alchemy);
- break;
- case 3011: // spinning hourglass
- createEntry = CreateEntries[3011];
- HARVEST = [13504];
- mainSkill = parseInt(pUser.skills.alchemy);
- break;
- default:
- break;
+ createEntry = CreateEntries[ detailTag ];
+ }
+
+ if( skillTag >= 0 && skillTag < skillNames.length )
+ {
+ mainSkill = parseInt( pUser.skills[ skillNames[skillTag] ] );
+ }
+ else
+ {// if all fails fallback to alchemy
+ mainSkill = mainSkill = parseInt( pUser.skills.alchemy );
+ }
+
+ // Helper to build harvest entries with both id + optional custom name
+ function makeHarvestObj(idTag, nameTag)
+ {
+ // nothing set at all
+ var hasName = !!(nameTag && nameTag.length);
+ if (idTag === null && !hasName)
+ return null;
+
+ var idNum = 0;
+ if (idTag !== null)
+ {
+ var n = parseInt(idTag, 10);
+ if (!isNaN(n) && n > 0)
+ idNum = n;
+ }
+
+ // if no valid dict id and no name, skip
+ if (idNum === 0 && !hasName)
+ return null;
+
+ return {
+ id: idNum, // 0 = "no dict"
+ name: hasName ? nameTag : ""
+ };
+ }
+
+ // If harvest info is provided, rebuild harvestResource array
+ if( harvestTag !== null || harvest2Tag !== null || harvest3Tag !== null || harvest4Tag !== null
+ || harvestNameTag !== null || harvest2NameTag !== null || harvest3NameTag !== null || harvest4NameTag !== null )
+ {
+ var h1 = makeHarvestObj( harvestTag, harvestNameTag );
+ var h2 = makeHarvestObj( harvest2Tag, harvest2NameTag );
+ var h3 = makeHarvestObj( harvest3Tag, harvest3NameTag );
+ var h4 = makeHarvestObj( harvest4Tag, harvest4NameTag );
+
+ if( h1 ) harvestResource.push( h1 );
+ if( h2 ) harvestResource.push( h2 );
+ if( h3 ) harvestResource.push( h3 );
+ if( h4 ) harvestResource.push( h4 );
}
if( createEntry == null )
@@ -1774,6 +161,14 @@ function ItemDetailGump( pUser )
return;
}
+ // Recipe flags
+ var needsRecipe = ( recipeID > 0 );
+ var hasRecipe = false;
+ if( needsRecipe )
+ {
+ hasRecipe = HasLearnedRecipe( pUser, recipeID );
+ }
+
// Fetch properties of create entry
var createName = createEntry.name; // name of the create entry
var createID = createEntry.id; // section id of item to craft
@@ -1817,7 +212,7 @@ function ItemDetailGump( pUser )
var minSkill = skillReq[1];
var maxSkill = skillReq[2];
- itemGump.AddHTMLGump( 170, 132 + ( i * 20 ), 200, 18, 0, 0, " " + GetDictionaryEntry( 15000 + skills[i][0], socket.language ) + " " );
+ itemGump.AddHTMLGump( 170, 132 + ( i * 20 ), 200, 18, false, false, " " + GetDictionaryEntry( 15000 + skills[i][0], socket.language ) + " " );
itemGump.AddText( 430, 132 + ( i * 20 ), textHue, skills[i][1]/10 );
if( i == 0 )
@@ -1882,9 +277,47 @@ function ItemDetailGump( pUser )
chance += (( pUser.intelligence * ( primaryCraftSkill.intelligence / 10 )) / 10000 );
}
- for( var i = 0; i < HARVEST.length; i++ )
+ var maxHarvest = harvestResource.length;
+ if( resources.length < maxHarvest )
+ maxHarvest = resources.length;
+
+ // MATERIALS list, now supporting custom names
+ for( var i = 0; i < maxHarvest; i++ )
{
- itemGump.AddText( 170, 219 + ( i * 20 ), textHue, GetDictionaryEntry( HARVEST[i], socket.language ));
+ var hObj = harvestResource[i]; // { id, name }
+ var label = "";
+
+ if( hObj )
+ {
+ var dictText = "";
+ if( hObj.id && hObj.id > 0 )
+ dictText = GetDictionaryEntry( hObj.id, socket.language ) || "";
+
+ if( hObj.name && hObj.name.length > 0 )
+ {
+ // Both set: "Custom (Dict)"
+ if( dictText && dictText.length > 0 )
+ label = hObj.name + " (" + dictText + ")";
+ else
+ label = hObj.name;
+ }
+ else
+ {
+ // Only dict
+ label = dictText;
+ }
+
+ // Optional safety fallback if both are empty
+ if( !label || label.length === 0 )
+ {
+ if( hObj.id && hObj.id > 0 )
+ label = "Resource " + hObj.id;
+ else
+ label = "Resource";
+ }
+ }
+
+ itemGump.AddText( 170, 219 + ( i * 20 ), textHue, label );
itemGump.AddText( 430, 219 + ( i * 20 ), textHue, resources[i][0] );
}
@@ -1894,7 +327,9 @@ function ItemDetailGump( pUser )
exceptionalChance = 0;
}
else if( chance > 1.0 )
+ {
chance = 1.0; // Cap chance at 100%
+ }
itemGump.AddText( 430, 80, textHue, ( chance * 100 ).toFixed( 1 ) + "%" ); // Success Chance:
if( !exceptionalWearablesOnly || CheckTileFlag( createID, 22 )) // TF_WEARABLE
@@ -1912,7 +347,24 @@ function ItemDetailGump( pUser )
{
itemGump.AddText( 430, 100, textHue, "-" ); // No chance of exceptional, not a wearable item!
}
- itemGump.Send( pUser );
+
+ if( needsRecipe )
+ {
+ var recipeMsg;
+ if( hasRecipe )
+ {
+ recipeMsg = "You have learned this recipe.";
+ }
+ else
+ {
+ recipeMsg = "You have not learned this recipe.";
+ }
+
+ // OTHER box starts at y=302, you already use 302+20 for the color note (dict 10006),
+ // so 302+40 (342) is a safe line under that.
+ itemGump.AddHTMLGump( 170, 342, 310, 18, false, false, recipeMsg );
+ }
+ itemGump.Send( socket );
itemGump.Free();
}
@@ -1932,18 +384,18 @@ function ItemDetailsGump( itemGump, pUser )
itemGump.AddTiledGump( 165, 302, 355, 80, 2624 );
itemGump.AddTiledGump( 10, 387, 510, 22, 2624 );
itemGump.AddCheckerTrans( 10, 10, 510, 399 );
- itemGump.AddHTMLGump( 170, 40, 150, 20, 0, 0, " " + GetDictionaryEntry( 10000, socket.language ) + " " ); // ITEM
+ itemGump.AddHTMLGump( 170, 40, 150, 20, false, false, " " + GetDictionaryEntry( 10000, socket.language ) + " " ); // ITEM
- itemGump.AddHTMLGump( 10, 217, 150, 22, 0, 0, " " + GetDictionaryEntry( 10001, socket.language ) + " " ); //MATERIALS
- itemGump.AddHTMLGump( 10, 302, 150, 22, 0, 0, " " + GetDictionaryEntry( 10002, socket.language ) + " " ); // OTHER
- itemGump.AddHTMLGump( 170, 80, 250, 18, 0, 0, "" + GetDictionaryEntry( 10003, socket.language ) + "" ); // Success Chance:
+ itemGump.AddHTMLGump( 10, 217, 150, 22, false, false, " " + GetDictionaryEntry( 10001, socket.language ) + " " ); //MATERIALS
+ itemGump.AddHTMLGump( 10, 302, 150, 22, false, false, " " + GetDictionaryEntry( 10002, socket.language ) + " " ); // OTHER
+ itemGump.AddHTMLGump( 170, 80, 250, 18, false, false, "" + GetDictionaryEntry( 10003, socket.language ) + "" ); // Success Chance:
if( GetServerSetting( "RankSystem" ))
{
- itemGump.AddHTMLGump( 170, 100, 250, 18, 0, 0, "" + GetDictionaryEntry( 10004, socket.language ) + "" ); // Exceptional Chance:
+ itemGump.AddHTMLGump( 170, 100, 250, 18, false, false, "" + GetDictionaryEntry( 10004, socket.language ) + "" ); // Exceptional Chance:
}
itemGump.AddButton( 15, 387, 0xfa5, 1, 0, 1 );
- itemGump.AddHTMLGump( 50, 390, 150, 18, 0, 0, "" + GetDictionaryEntry( 10005, socket.language ) + "" ); // BACK
- itemGump.AddHTMLGump( 170, ( 302 + 20 ), 310, 18, 0, 0, "" + GetDictionaryEntry( 10006, socket.language ) + "" ); // * The item retains the color of this material
+ itemGump.AddHTMLGump( 50, 390, 150, 18, false, false, "" + GetDictionaryEntry( 10005, socket.language ) + "" ); // BACK
+ itemGump.AddHTMLGump( 170, ( 302 + 20 ), 310, 18, false, false, "" + GetDictionaryEntry( 10006, socket.language ) + "" ); // * The item retains the color of this material
itemGump.AddText( 500, 219, textHue, "*" );
}
@@ -1957,7 +409,7 @@ function onGumpPress( pSock, pButton, gumpData )
return;
var bItem = pSock.tempObj;
- var gumpID = scriptID + 0xffff;
+ var gumpID = itemDetailID + 0xffff;
switch( pButton )
{
case 0:
@@ -2090,7 +542,7 @@ function onGumpPress( pSock, pButton, gumpData )
break;
}
break;
- case 8: // Cartography
+ case 8: // Cartography
pUser.SetTempTag( "ITEMDETAILS", null );
pSock.CloseGump( gumpID, 0 );
switch( pUser.GetTempTag( "page" ))
@@ -2111,9 +563,43 @@ function onGumpPress( pSock, pButton, gumpData )
TriggerEvent( Glassblowing, "PageX", pSock, pUser, pUser.GetTempTag( "page" ));
break;
default: TriggerEvent( Glassblowing, "PageX", pSock, pUser, 1 );
- break;
- }
- break;
+ }
+ break;
+ case 10: // masonry
+ pUser.SetTempTag( "ITEMDETAILS", null )
+ pSock.CloseGump( gumpID, 0 );
+ switch( pUser.GetTempTag("page" ))
+ {
+ case 1: // Page 1
+ case 2: // Page 2
+ case 3: // Page 3
+ case 4: // Page 4
+ case 5: // Page 5
+ case 6: // Page 6
+ case 7: // Page 7
+ case 8: // Page 8
+ case 9: // Page 9
+ TriggerEvent( Masonry, "PageX", pSock, pUser, pUser.GetTempTag( "page" ));
+ break;
+ default: TriggerEvent( Masonry, "PageX", pSock, pUser, 1 );
+ break;
+ }
+ break;
}
}
}
+
+function HasLearnedRecipe( pUser, recipeID )
+{
+ var myData = TriggerEvent( 4022, "ReadRecipeID", pUser );
+ if( !myData || myData.length == 0 )
+ return false;
+
+ for( var i = 0; i < myData.length; i++ )
+ {
+ var data = myData[i].split( "," );
+ if( data[0] == recipeID )
+ return true;
+ }
+ return false;
+}
\ No newline at end of file
diff --git a/data/js/skill/craft/masonry.js b/data/js/skill/craft/masonry.js
new file mode 100644
index 000000000..66f774315
--- /dev/null
+++ b/data/js/skill/craft/masonry.js
@@ -0,0 +1,1526 @@
+///
+// @ts-check
+const textHue = 0x480; // Color of the text.
+const masonryID = 4023; // Script ID used to identify and close this gump
+const gumpDelay = 2000; // Timer for the gump to reappear after crafting.
+const ingotDelay = 200; // Timer for the gump to reappear after selecting an ingot.
+const repairDelay = 200; // Timer for the gump to reappear after repairing an item
+const craftGumpID = 4027;
+const itemDetailsScriptID = 4026;
+
+const itemsPerPage = 10; // Number of craftable items shown per gump subpage
+const displayUnlearnedRecipes = true; // Show recipes player has not learned (if we add any later)
+const coreShardEra = EraStringToNum( GetServerSetting( "CoreShardEra" ));
+
+// If enabled, players can craft coloured variants of weapons, though unless the craftItems array
+// is updated with specific create entries for the coloured weapon variants, they will just be
+// regular weapons with granite colour applied
+const allowColouredWeapons = GetServerSetting( "CraftColouredWeapons" );
+const allowColouredBuildings = false; // Set to true if you want coloured stone walls, stairs, floors
+
+// Optional: if you later decide to make some masonry items recipe-locked, we will use this map:
+// MasonryMap[buttonID] = { dictID, page, timerID, graniteMake: [makeIDByOre], recipeID?, minEra?, maxEra? }
+// o--------------------------------------------------------------------------o
+// | Script - masonry.js |
+// | System - Masonry Crafting Gump |
+// o--------------------------------------------------------------------------o
+// | Purpose - |
+// | Provides the blacksmith crafting menu using the same data-driven |
+// | system used by the tailoring script. |
+// | |
+// | All craftable items are defined in tables (myPage, craftItems) and |
+// | then mapped into a MasonryMap structure that controls: |
+// | - Which dictionary entry is shown per row |
+// | - Which "makeID" entry is used for each ore type |
+// | - Which page and timer ID to use when reopening the gump |
+// | - Optional per-item recipe and era requirements |
+// | - Optional per-item custom names for display |
+// | |
+// | The script also handles: |
+// | - Granite selection (iron / colored ores) with skill requirements |
+// | - Smelting stone items back into Granite |
+// | - Repairing stone armor and weapons at an anvil |
+// | - Tool wear and runic hammer handling |
+// | - A "Make Last" feature |
+// | - A "Last Ten Masonry" list (optional) |
+// o--------------------------------------------------------------------------o
+// | Data Tables |
+// o--------------------------------------------------------------------------o
+// | myPage |
+// | myPage[pageIndex] = [ dictID1, dictID2, ... ] |
+// | pageIndex 0 => Page 1: Metal Armor |
+// | pageIndex 1 => Page 2: Helmets |
+// | pageIndex 2 => Page 3: Shields |
+// | pageIndex 3 => Page 4: Bladed weapons |
+// | pageIndex 4 => Page 5: Axes |
+// | pageIndex 5 => Page 6: Polearms |
+// | pageIndex 6 => Page 7: Bashing weapons |
+// | |
+// | Each entry is a dictionary ID that will be used to look up the text |
+// | for that row, unless a customName is defined for that button in |
+// | MasonryMap. |
+// | |
+// | craftItems |
+// | craftItems[graniteIndex][pageIndex][itemIndex] = makeID |
+// | graniteIndex 0 = Iron |
+// | graniteIndex 1 = Dull Copper |
+// | graniteIndex 2 = Shadow Iron |
+// | graniteIndex 3 = Copper |
+// | graniteIndex 4 = Bronze |
+// | graniteIndex 5 = Gold |
+// | graniteIndex 6 = Agapite |
+// | graniteIndex 7 = Verite |
+// | graniteIndex 8 = Valorite |
+// | |
+// | For each granite type and page, this holds the createEntry ID used by |
+// | MakeItem when the player crafts that item. The same index positions |
+// | on each page line up with the matching entries in myPage. |
+// o--------------------------------------------------------------------------o
+// | MasonryMap |
+// o--------------------------------------------------------------------------o
+// | MasonryMap is built automatically from myPage and craftItems. |
+// | |
+// | MasonryMap[buttonID] = { |
+// | dictID : number, // Base dictionary entry for the row |
+// | page : number, // Main page (1..7, or 999 for Last Ten) |
+// | timerID : number, // Timer ID to reopen same page |
+// | graniteMake : number[], // graniteMake[graniteIndex] = makeID |
+// | customName: string?, // Optional override for display text |
+// | recipeID : number?, // Optional recipe requirement |
+// | minEra : string?, // Optional minimum shard era |
+// | maxEra : string? // Optional maximum shard era |
+// | }; |
+// | |
+// | Button ID mapping (same as original script): |
+// | Page 1 (Decorations) : 100..108 |
+// | Page 2 (Furniture) : 200..205 |
+// | Page 3 (Statues) : 300..305 |
+// | Page 4 (Misc Addons) : 400..407 |
+// | Page 5 (Stone Armor) : 500..506 |
+// | Page 6 (Stone Weapons) : 600..604 |
+// | Page 7 (Stone Walls): 700..704 |
+// | Page 8 (Stone Stairs): 800..804 |
+// | Page 9 (Stone Floors): 900..904 |
+// | |
+// | Custom Names |
+// | To override the display name for a specific row, set customName after |
+// | the MasonryMap has been initialized, for example: |
+// | |
+// | MasonryMap[100].customName = "Hump vase"; |
+// | |
+// | PageX() will use this order of preference for text: |
+// | 1. entry.customName (if set) |
+// | 2. GetDictionaryEntry(entry.dictID) |
+// | 3. A fallback "[Unnamed Item: buttonID]" |
+// | |
+// | Recipes |
+// | If recipeID is set on a MasonryMap entry, onGumpPress will call: |
+// | TriggerEvent(4022, "NeedRecipe", pUser, recipeID) |
+// | to check if the player has learned that recipe. If not, the craft |
+// | attempt is blocked and a message is shown. |
+// | |
+// | Era Gating |
+// | The script reads the shard era using: |
+// | const coreShardEra = EraStringToNum(GetServerSetting("CoreShardEra")); |
+// | |
+// | If an entry defines minEra or maxEra (strings like "lbr","aos","ml", |
+// | "sa","hs","tol"), eraOK(entry) will ensure the current server era is |
+// | within that range before allowing craft or display. |
+// o--------------------------------------------------------------------------o
+// | Notes |
+// o--------------------------------------------------------------------------o
+// | - To add new Masonry items, update myPage and craftItems, then |
+// | optionally decorate their MasonryMap entries with customName, |
+// | recipeID, minEra, and maxEra. |
+// o--------------------------------------------------------------------------o
+
+const myPage = [
+ // Page 1 - Decorations
+ [ 14050, 14051, 14052, 14053, 14054, 14055, 14056, 14057, 14058 ],
+ // Page 2 - Furniture
+ [ 14059, 14060, 14061, 14062, 14063, 14064 ],
+ // Page 3 - Statues
+ [ 14065, 14066, 14067, 14068, 14069, 14070 ],
+ // Page 4 - Misc Addons
+ [ 14071, 14072, 14073, 14074, 14075, 14076 ],
+ // Page 5 - Stone Armor
+ [ 14077, 14078, 14079, 14080, 14081, 14082, 14083, 14084, 14085, 14086 ],
+ // Page 6 - Stone Weapons
+ [ 14087 ],
+ // Page 7 - Stone Walls
+ [ 14088, 14089, 14090, 14091, 14092, 14093, 14094, 14095, 14096,14097, 14098, 14099 ],
+ // Page 8 - Stone Stairs
+ [ 14100, 14101, 14102, 14103, 14104, 14105 ],
+ // Page 9 - Stone Floors
+ [ 14106, 14107, 14108 ]
+];
+
+const craftItems = [
+ // Iron
+ [
+ // Decorations
+ [ 3500, 3501, 3502, 3503, 3504, 3505, 3506, 3507, 3508 ],
+ // Furniture
+ [ 3509, 3510, 3511, 3512, 3513, 3514 ],
+ // Statues
+ [ 3515, 3516, 3517, 3518, 3519, 3520 ],
+ // Misc Addons
+ [ 3521, 3522, 3523, 3524, 3525, 3526 ],
+ // Stone Armor
+ [ 3527, 3528, 3529, 3530, 3531, 3532, 3533, 3534, 3535, 3536 ],
+ // Stone Weapons
+ [ 3537 ],
+ // Stone Walls
+ [ 3538, 3539, 3540, 3541, 3542, 3543, 3544, 3545, 3546, 3547, 3548, 3549 ],
+ // Stone Stairs
+ [ 3550, 3551, 3552, 3553, 3554, 3555 ],
+ // Stone Floors
+ [ 3556, 3557, 3558]
+ ],
+
+ // Dull Copper
+ [
+ // Decorations
+ [ 3600, 3601, 3602, 3603, 3604, 3605, 3606, 3607, 3608 ],
+ // Furniture
+ [ 3609, 3610, 3611, 3612, 3613, 3614 ],
+ // Statues
+ [ 3615, 3616, 3617, 3618, 3619, 3620 ],
+ // Misc Addons
+ [ 3621, 3622, 3623, 3624, 3625, 3626 ],
+ // Stone Armor
+ [ 3627, 3628, 3629, 3630, 3631, 3632, 3633, 3634, 3635, 3636 ],
+ // Stone Weapons
+ [ 3637 ],
+ // Stone Walls
+ [ 3638, 3639, 3640, 3641, 3642, 3643, 3644, 3645, 3646, 3647, 3648, 3649 ],
+ // Stone Stairs
+ [ 3650, 3651, 3652, 3653, 3654, 3655 ],
+ // Stone Floors
+ [ 3656, 3657, 3658]
+ ],
+
+ // Shadow Iron
+ [
+ // Decorations
+ [ 3700, 3701, 3702, 3703, 3704, 3705, 3706, 3707, 3708 ],
+ // Furniture
+ [ 3709, 3710, 3711, 3712, 3713, 3714 ],
+ // Statues
+ [ 3715, 3716, 3717, 3718, 3719, 3720 ],
+ // Misc Addons
+ [ 3721, 3722, 3723, 3724, 3725, 3726 ],
+ // Stone Armor
+ [ 3727, 3728, 3729, 3730, 3731, 3732, 3733, 3734, 3735, 3736 ],
+ // Stone Weapons
+ [ 3737 ],
+ // Stone Walls
+ [ 3738, 3739, 3740, 3741, 3742, 3743, 3744, 3745, 3746, 3747, 3748, 3749 ],
+ // Stone Stairs
+ [ 3750, 3751, 3752, 3753, 3754, 3755 ],
+ // Stone Floors
+ [ 3756, 3757, 3758]
+ ],
+
+ // Copper
+ [
+ // Decorations
+ [ 3800, 3801, 3802, 3803, 3804, 3805, 3806, 3807, 3808 ],
+ // Furniture
+ [ 3809, 3810, 3811, 3812, 3813, 3814 ],
+ // Statues
+ [ 3815, 3816, 3817, 3818, 3819, 3820 ],
+ // Misc Addons
+ [ 3821, 3822, 3823, 3824, 3825, 3826 ],
+ // Stone Armor
+ [ 3827, 3828, 3829, 3830, 3831, 3832, 3833, 3834, 3835, 3836 ],
+ // Stone Weapons
+ [ 3837 ],
+ // Stone Walls
+ [ 3838, 3839, 3840, 3841, 3842, 3843, 3844, 3845, 3846, 3847, 3848, 3849 ],
+ // Stone Stairs
+ [ 3850, 3851, 3852, 3853, 3854, 3855 ],
+ // Stone Floors
+ [ 3856, 3857, 3858]
+ ],
+
+ // Bronze
+ [
+ // Decorations
+ [ 3900, 3901, 3902, 3903, 3904, 3905, 3906, 3907, 3908 ],
+ // Furniture
+ [ 3909, 3910, 3911, 3912, 3913, 3914 ],
+ // Statues
+ [ 3915, 3916, 3917, 3918, 3919, 3920 ],
+ // Misc Addons
+ [ 3921, 3922, 3923, 3924, 3925, 3926 ],
+ // Stone Armor
+ [ 3927, 3928, 3929, 3930, 3931, 3932, 3933, 3934, 3935, 3936 ],
+ // Stone Weapons
+ [ 3937 ],
+ // Stone Walls
+ [ 3938, 3939, 3940, 3941, 3942, 3943, 3944, 3945, 3946, 3947, 3948, 3949 ],
+ // Stone Stairs
+ [ 3950, 3951, 3952, 3953, 3954, 3955 ],
+ // Stone Floors
+ [ 3956, 3957, 3958]
+ ],
+
+ // Gold
+ [
+ // Decorations
+ [ 4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007, 4008 ],
+ // Furniture
+ [ 4009, 4010, 4011, 4012, 4013, 4014 ],
+ // Statues
+ [ 4015, 4016, 4017, 4018, 4019, 4020 ],
+ // Misc Addons
+ [ 4021, 4022, 4023, 4024, 4025, 4026 ],
+ // Stone Armor
+ [ 4027, 4028, 4029, 4030, 4031, 4032, 4033, 4034, 4035, 4036 ],
+ // Stone Weapons
+ [ 4037 ],
+ // Stone Walls
+ [ 4038, 4039, 4040, 4041, 4042, 4043, 4044, 4045, 4046, 4047, 4048, 4049 ],
+ // Stone Stairs
+ [ 4050, 4051, 4052, 4053, 4054, 4055 ],
+ // Stone Floors
+ [ 4056, 4057, 4058]
+ ],
+
+ // Agapite
+ [
+ // Decorations
+ [ 4100, 4101, 4102, 4103, 4104, 4105, 4106, 4107, 4108 ],
+ // Furniture
+ [ 4109, 4110, 4111, 4112, 4113, 4114 ],
+ // Statues
+ [ 4115, 4116, 4117, 4118, 4119, 4120 ],
+ // Misc Addons
+ [ 4121, 4122, 4123, 4124, 4125, 4126 ],
+ // Stone Armor
+ [ 4127, 4128, 4129, 4130, 4131, 4132, 4133, 4134, 4135, 4136 ],
+ // Stone Weapons
+ [ 4137 ],
+ // Stone Walls
+ [ 4138, 4139, 4140, 4141, 4142, 4143, 4144, 4145, 4146, 4147, 4148, 4149 ],
+ // Stone Stairs
+ [ 4150, 4151, 4152, 4153, 4154, 4155 ],
+ // Stone Floors
+ [ 4156, 4157, 4158]
+ ],
+
+ // Verite
+ [
+ // Decorations
+ [ 4200, 4201, 4202, 4203, 4204, 4205, 4206, 4207, 4208 ],
+ // Furniture
+ [ 4209, 4210, 4211, 4212, 4213, 4214 ],
+ // Statues
+ [ 4215, 4216, 4217, 4218, 4219, 4220 ],
+ // Misc Addons
+ [ 4221, 4222, 4223, 4224, 4225, 4226 ],
+ // Stone Armor
+ [ 4227, 4228, 4229, 4230, 4231, 4232, 4233, 4234, 4235, 4236 ],
+ // Stone Weapons
+ [ 4237 ],
+ // Stone Walls
+ [ 4238, 4239, 4240, 4241, 4242, 4243, 4244, 4245, 4246, 4247, 4248, 4249 ],
+ // Stone Stairs
+ [ 4250, 4251, 4252, 4253, 4254, 4255 ],
+ // Stone Floors
+ [ 4256, 4257, 4258]
+ ],
+
+ // Valorite
+ [
+ // Decorations
+ [ 4300, 4301, 4302, 4303, 4304, 4305, 4306, 4307, 4308 ],
+ // Furniture
+ [ 4309, 4310, 4311, 4312, 4313, 4314 ],
+ // Statues
+ [ 4315, 4316, 4317, 4318, 4319, 4320 ],
+ // Misc Addons
+ [ 4321, 4322, 4323, 4324, 4325, 4326 ],
+ // Stone Armor
+ [ 4327, 4328, 4329, 4330, 4331, 4332, 4333, 4334, 4335, 4336 ],
+ // Stone Weapons
+ [ 4337 ],
+ // Stone Walls
+ [ 4338, 4339, 4340, 4341, 4342, 4343, 4344, 4345, 4346, 4347, 4348, 4349 ],
+ // Stone Stairs
+ [ 4350, 4351, 4352, 4353, 4354, 4355 ],
+ // Stone Floors
+ [ 4356, 4357, 4358]
+ ]
+];
+
+// MasonryMap[buttonID] = {
+// dictID: ,
+// page: ,
+// timerID: ,
+// graniteMake: [ makeIDForIron, makeIDForDullCopper, ... ], // index is graniteID (0..8)
+// // Optional later:
+// // recipeID: ,
+// // minEra: "lbr" / "aos" / "ml" / "sa" / "hs" / "tol",
+// // maxEra: ...
+// };
+
+const MasonryMap = {};
+
+(function initMasonryMap()
+{
+ // graniteIndex: 0 = iron, 1 = dull copper, ... 8 = valorite
+ for( var graniteIndex = 0; graniteIndex < craftItems.length; graniteIndex++ )
+ {
+ var graniteRows = craftItems[graniteIndex];
+
+ // pageIdx: 0..6 => pages 1..9
+ for( var pageIdx = 0; pageIdx < myPage.length; pageIdx++ )
+ {
+ var dictList = myPage[pageIdx];
+ var makeList = graniteRows[pageIdx];
+
+ for( var i = 0; i < dictList.length && i < makeList.length; i++ )
+ {
+ // Old script uses:
+ // page 1 => 100..112
+ // page 2 => 200..204
+ // page 3 => 300..305
+ // etc.
+ var buttonID = ( ( pageIdx + 1 ) * 100 ) + i;
+ var dictID = dictList[i];
+ var makeID = makeList[i];
+
+ if( !MasonryMap[buttonID] )
+ {
+ MasonryMap[buttonID] = {
+ dictID: dictID,
+ page: pageIdx + 1,
+ timerID: pageIdx + 1,
+ graniteMake: [],
+ // recipeID: undefined,
+ // minEra: undefined,
+ // maxEra: undefined
+ skill: 11, // carpentry / masonry skill ID
+ harvest: [14011], // granite dict
+ harvest2: [], // optional second resource
+ harvest3: [], // optional second resource
+ harvest4: [] // optional second resource
+ };
+ }
+
+ MasonryMap[buttonID].graniteMake[graniteIndex] = makeID;
+ }
+ }
+ }
+})();
+
+// 3) AFTER initMasonryMap, you can override entries:
+// Page 1 Starts buttonID 100 - 107 for map example
+//MasonryMap[100].customName = "Elven Broadsword";
+//MasonryMap[100].recipeID = 5101; // if you want it recipe-locked
+//MasonryMap[100].minEra = "ml"; // if you want it ML and later only
+
+//Page 1 starts at 100
+MasonryMap[102].minEra = "se"; // small urn
+MasonryMap[103].minEra = "se"; // Tower Sculpture
+MasonryMap[104].minEra = "sa"; // gargoyle painting
+MasonryMap[105].minEra = "sa"; // gargoyle sculpture
+MasonryMap[106].minEra = "sa"; // gargoyle vase
+MasonryMap[107].minEra = "tol"; // Tall 18th Anniversary Vase
+MasonryMap[107].recipeID = 3500;
+MasonryMap[108].minEra = "tol"; // Short 18th Anniversary Vase
+MasonryMap[108].recipeID = 3501;
+//Page 2 starts at 200
+MasonryMap[205].minEra = "sa"; // ritual table
+//Page 3 starts at 300
+MasonryMap[304].minEra = "sa"; // gargoyle statue
+MasonryMap[305].minEra = "sa"; // gryphon statue
+//Page 4 starts at 400
+MasonryMap[400].minEra = "ml"; // stone anvil (east)
+MasonryMap[400].recipeID = 3520;
+MasonryMap[401].minEra = "ml"; // stone anvil (south)
+MasonryMap[401].recipeID = 3521;
+MasonryMap[402].minEra = "sa"; // large gargish bed (east)
+MasonryMap[402].harvest2 = [10016]; // Cloth
+MasonryMap[403].minEra = "sa"; // large gargish bed (south)
+MasonryMap[403].harvest2 = [10016]; // Cloth
+MasonryMap[404].minEra = "sa"; // gargish cot (east)
+MasonryMap[404].harvest2 = [10016]; // Cloth
+MasonryMap[405].minEra = "sa"; // gargish cot (south)
+MasonryMap[405].harvest2 = [10016]; // Cloth
+//Page 5 starts at 500
+MasonryMap[500].minEra = "sa"; // gargish stone arms
+MasonryMap[501].minEra = "sa"; // gargish stone chest
+MasonryMap[502].minEra = "sa"; // gargish stone leggings
+MasonryMap[503].minEra = "sa"; // gargish stone kilt
+MasonryMap[504].minEra = "sa"; // gargish stone arms
+MasonryMap[505].minEra = "sa"; // gargish stone chest
+MasonryMap[506].minEra = "sa"; // gargish stone leggings
+MasonryMap[507].minEra = "sa"; // gargish stone kilt
+MasonryMap[508].minEra = "sa"; // large stone shield
+MasonryMap[509].minEra = "sa"; // gargish stone amulet
+//Page 6 starts at 600
+MasonryMap[600].minEra = "sa"; // stone war sword
+//Page 7 starts at 700
+MasonryMap[700].minEra = "tol"; // Rough Windowless
+MasonryMap[701].minEra = "tol"; // Rough Window
+MasonryMap[702].minEra = "tol"; // Rough Arch
+MasonryMap[703].minEra = "tol"; // Rough Pillar
+MasonryMap[704].minEra = "tol"; // Rough Rounded Arch
+MasonryMap[705].minEra = "tol"; // Rough Small Arch
+MasonryMap[706].minEra = "tol"; // Rough Angled Pillar
+MasonryMap[707].minEra = "tol"; // Short Rough
+MasonryMap[708].minEra = "tol"; // Stone Door (S In)
+MasonryMap[709].minEra = "tol"; // Stone Door (E Out)
+MasonryMap[710].minEra = "tol"; // Left Metal Door (S In)
+MasonryMap[711].minEra = "tol"; // Right Metal Door (S In)
+//Page 8 starts at 800
+MasonryMap[800].minEra = "tol"; // short rough
+MasonryMap[801].minEra = "tol"; // rough steps
+MasonryMap[802].minEra = "tol"; // rough corner steps
+MasonryMap[803].minEra = "tol"; // rough rounded corner step
+MasonryMap[804].minEra = "tol"; // rough inset steps
+MasonryMap[805].minEra = "tol"; // rough rounded inset steps
+//Page 9 starts at 900
+MasonryMap[900].minEra = "tol"; // light paver
+MasonryMap[901].minEra = "tol"; // medium paver
+MasonryMap[902].minEra = "tol"; // dark paver
+
+function PageX( socket, pUser, pageNum )
+{
+ if( !ValidateObject( pUser ))
+ return;
+
+ // Pages 1 - 9: normal crafting pages
+ // Page 999: optional "Last Ten Blacksmith" (if you decide to use it later)
+
+ var subPage = pUser.GetTempTag( "subPage" );
+ var pageItems = [];
+
+ if( pageNum == 999 )
+ {
+ var lastTenRaw = pUser.GetTempTag( "LastTenMasonry" ) || "";
+ var split = lastTenRaw.split( "," );
+ for( var i = 0; i < split.length; i++ )
+ {
+ var val = parseInt( split[i] );
+ if( !isNaN( val ) && MasonryMap[val] )
+ pageItems.push( val ); // here val is the buttonID itself
+ }
+ }
+ else
+ {
+ // Build list of buttonIDs for this page from MasonryMap
+ for( var buttonID in MasonryMap )
+ {
+ var data = MasonryMap[buttonID];
+ if( data.page == pageNum && eraOK( data ))
+ {
+ // If we later add recipes and want to hide unknown ones:
+ var needsRecipe = data.recipeID;
+ var showAll = displayUnlearnedRecipes;
+ if( !needsRecipe || showAll || HasLearnedRecipe( pUser, needsRecipe ))
+ {
+ pageItems.push( parseInt( buttonID ) );
+ }
+ }
+ }
+
+ // Sort by buttonID to keep consistent ordering
+ pageItems.sort( function( a, b ){ return a - b; } );
+ }
+
+ if( pageItems.length == 0 )
+ {
+ var emptyGump = new Gump;
+ TriggerEvent( craftGumpID, "CraftingGumpMenu", emptyGump, socket );
+ emptyGump.AddPage( 1 );
+ emptyGump.AddText( 220, 60, textHue, "No items available on this page." );
+ emptyGump.Send( socket );
+ emptyGump.Free();
+ return;
+ }
+
+ var totalSubPages = Math.ceil( pageItems.length / itemsPerPage );
+
+ if( subPage < 1 )
+ subPage = 1;
+ if( subPage > totalSubPages )
+ subPage = totalSubPages;
+
+ pUser.SetTempTag( "page", pageNum );
+ pUser.SetTempTag( "subPage", subPage );
+
+ var startIndex = ( subPage - 1 ) * itemsPerPage;
+ var endIndex = Math.min( startIndex + itemsPerPage, pageItems.length );
+
+ if( startIndex >= pageItems.length )
+ {
+ subPage = 1;
+ startIndex = 0;
+ endIndex = Math.min( itemsPerPage, pageItems.length );
+ pUser.SetTempTag( "subPage", subPage );
+ }
+
+ var resourceHue = pUser.GetTempTag( "resourceHue" );
+ var blacksmithMenu = new Gump;
+ TriggerEvent( craftGumpID, "CraftingGumpMenu", blacksmithMenu, socket );
+ blacksmithMenu.AddPage( 1 );
+
+ var drawIndex = 0;
+
+ for( var i = startIndex; i < endIndex; i++ )
+ {
+ var buttonID = pageItems[i];
+ var data = MasonryMap[buttonID];
+
+ // Do not show weapons when colored granited ingots selected and colored granited weapons are disabled
+ if( !allowColouredWeapons && resourceHue > 0 && data.page == 6 )
+ continue;
+
+ // Do not show walls when colored granited ingots selected and colored granited walls are disabled
+ if( !allowColouredBuildings && resourceHue > 0 && data.page > 6 )
+ continue;
+
+ var entryText = "";
+ if( data.customName )
+ {
+ entryText = data.customName;
+ }
+ else if( data.dictID )
+ {
+ entryText = GetDictionaryEntry( data.dictID, socket.language );
+ if( !entryText || entryText === "" )
+ entryText = "[Missing EntryID: " + data.dictID + "]";
+ }
+ else
+ {
+ entryText = "[Unnamed Item: " + buttonID + "]";
+ }
+
+ // Same layout as tailoring: button, text, details button
+ blacksmithMenu.AddButton( 220, 60 + ( drawIndex * 20 ), 4005, 4007, 1, 0, buttonID );
+ blacksmithMenu.AddText( 255, 60 + ( drawIndex * 20 ), textHue, entryText );
+ blacksmithMenu.AddButton( 480, 60 + ( drawIndex * 20 ), 4011, 4012, 1, 0, 2000 + buttonID );
+
+ drawIndex++;
+ }
+
+ // Prev subpage
+ if( subPage > 1 )
+ {
+ blacksmithMenu.AddButton( 220, 260, 4014, 4015, 1, 0, 8000 + ( subPage - 1 ));
+ blacksmithMenu.AddHTMLGump( 255, 263, 100, 18, 0, 0,
+ "" + GetDictionaryEntry( 10101, socket.language ) + "" );
+ }
+
+ // Next subpage
+ if( subPage < totalSubPages )
+ {
+ blacksmithMenu.AddButton( 370, 260, 4005, 4007, 1, 0, 9000 + ( subPage + 1 ));
+ blacksmithMenu.AddHTMLGump( 405, 263, 100, 18, 0, 0,
+ "" + GetDictionaryEntry( 10100, socket.language ) + "" );
+ }
+
+ blacksmithMenu.Send( socket );
+ blacksmithMenu.Free();
+}
+
+function Page20( socket, pUser )
+{
+ //granite Choices
+ var myGump = new Gump;
+ pUser.SetTempTag( "page", 20 );
+ TriggerEvent( craftGumpID, "CraftingGumpMenu", myGump, socket );
+ var iron = pUser.ResourceCount( 0x1779 );
+ var bronze = pUser.ResourceCount( 0x1779, 0x06d6 );
+ var copper = pUser.ResourceCount( 0x1779, 0x07dd );
+ var agapite = pUser.ResourceCount( 0x1779, 0x0979 );
+ var dullcopper = pUser.ResourceCount( 0x1779, 0x0973 );
+ var gold = pUser.ResourceCount( 0x1779, 0x08a5 );
+ var shadowiron = pUser.ResourceCount( 0x1779, 0x0966 );
+ var valorite = pUser.ResourceCount( 0x1779, 0x08ab );
+ var verite = pUser.ResourceCount( 0x1779, 0x089f );
+ var myPage20 = [
+ GetDictionaryEntry( 14011, socket.language ) + " (" + iron.toString() + ")",
+ GetDictionaryEntry( 14012, socket.language ) + " (" + dullcopper.toString() + ")",
+ GetDictionaryEntry( 14013, socket.language ) + " (" + shadowiron.toString() + ")",
+ GetDictionaryEntry( 14014, socket.language ) + " (" + copper.toString() + ")",
+ GetDictionaryEntry( 14015, socket.language ) + " (" + bronze.toString() + ")",
+ GetDictionaryEntry( 14016, socket.language ) + " (" + gold.toString() + ")",
+ GetDictionaryEntry( 14017, socket.language ) + " (" + agapite.toString() + ")",
+ GetDictionaryEntry( 14018, socket.language ) + " (" + verite.toString() + ")",
+ GetDictionaryEntry( 14019, socket.language ) + " (" + valorite.toString() + ")"
+ ];
+
+ for( var i = 0; i < myPage20.length; i++ )
+ {
+ var index = i % 10;
+ if( index == 0 )
+ {
+ if( i > 0 )
+ {
+ myGump.AddButton( 370, 260, 4005, 4007, 0, ( i / 10 ) + 1, 0 );
+ myGump.AddHTMLGump( 405, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10100, socket.language ) + "" );// NEXT PAGE
+ }
+
+ myGump.AddPage(( i / 10 ) + 1 );
+
+ if( i > 0 )
+ {
+ myGump.AddButton( 220, 260, 4014, 4015, 0, i / 10, 0 );
+ myGump.AddHTMLGump( 255, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10101, socket.language ) + "" );// PREV PAGE
+ }
+ }
+
+ myGump.AddButton( 220, 60 + ( index * 20), 4005, 4007, 1, 0, 1000 + i );
+ myGump.AddText( 255, 60 + ( index * 20), textHue, myPage20[i] );
+ }
+ myGump.Send( socket );
+ myGump.Free();
+}
+
+function FindNearbyAnvils( pUser, trgItem, pSock )
+{
+ if( !ValidateObject( trgItem ) || !trgItem.isItem )
+ return false;
+
+ return ( trgItem.id == 0x0faf || trgItem.id == 0x0fb0 || trgItem.id == 0x2dd5 || trgItem.id == 0x2dd6 );
+}
+
+function FindNearbyForges( pUser, trgItem, pSock )
+{
+ if( !ValidateObject( trgItem ) || !trgItem.isItem )
+ return false;
+
+ return (( trgItem.id >= 0x197a && trgItem.id <= 0x19a9 ) || trgItem.id == 0x0Fb1 || trgItem.id == 0x2db0 || trgItem.id == 0x2dd8 );
+}
+
+function SmeltTarget( pSock )
+{
+ pSock.CustomTarget( 1, GetDictionaryEntry( 440, pSock.language )); // What item would you like to smelt?
+}
+
+// Armor and weapons can be smelted back into ingots.
+/** @type { ( tSock: Socket, target: Character | Item | null ) => void } */
+function onCallback1( pSock, ourObj )
+{
+ // Smelt item, get ingots in return
+ var mChar = pSock.currentChar;
+
+ if( !ValidateObject( ourObj ) || !ourObj.isItem )
+ {
+ // Targeted object is not an item that can be smelted
+ mChar.SetTempTag( "prevActionResult", "CANTSMELT" );
+ mChar.StartTimer( ingotDelay, 1, true );
+ return;
+ }
+
+ var nearbyAnvil = AreaItemFunction( "FindNearbyAnvils", mChar, 3, pSock );
+ var nearbyForge = AreaItemFunction( "FindNearbyForges", mChar, 3, pSock );
+ if( nearbyForge == 0 || nearbyAnvil == 0)
+ {
+ // No forge nearby
+ mChar.SetTempTag( "prevActionResult", "NOFORGEORANVIL" );
+ mChar.StartTimer( ingotDelay, 1, true );
+ return;
+ }
+
+ var creatorSerial = ourObj.creator;
+ var entryMadeFrom = ourObj.entryMadeFrom;
+ var createEntry;
+ if( entryMadeFrom != null && entryMadeFrom != 0 )
+ {
+ createEntry = CreateEntries[entryMadeFrom];
+ }
+ if( createEntry != null && createEntry.id != ourObj.id )
+ {
+ createEntry = null;
+ }
+
+ var resourceName = "granite";
+ var resourceAmount = 0;
+ var maxResourceAmount = 1;
+ var resourceHue = ourObj.colour;
+
+ if( creatorSerial == -1 || entryMadeFrom == 0 || createEntry == null )
+ {
+ // Not a player-crafted item, return 1 ingot if item is made of metal
+ var materialType = TriggerEvent( 2506, "GetItemMaterialType", ourObj );
+ if( materialType == "metal" )
+ {
+ resourceAmount = 1;
+ }
+ }
+ else
+ {
+ if( createEntry.avgMinSkill > mChar.skills.mining )
+ {
+ pSock.CloseGump( gumpID, 0 );
+ mChar.SetTempTag( "prevActionResult", "NOSMELTSKILL" );
+ mChar.StartTimer( gumpDelay, 1, true );
+ return;
+ }
+
+ // Loop through resources used to craft item, see how many ingots were used
+ var resourcesUsed = createEntry.resources;
+ for( var i = 0; i < resourcesUsed.length; i++ )
+ {
+ var resource = resourcesUsed[i];
+ var amountNeeded = resource[0];
+ var colorNeeded = resource[1];
+ var resourceIDs = resource[2];
+
+ // Loop through list of resourceIDs that were valid for crafting this item, see if ANY
+ // were a match for the resource we're trying to return
+ for( var j = 0; j <= resourceIDs.length; j++ )
+ {
+ // If both resource needed matches up, and resource color matches up, go for it
+ if( resourceIDs[j] == 0x1bf2 && colorNeeded == resourceHue )
+ {
+ maxResourceAmount = amountNeeded;
+ break;
+ }
+ }
+ }
+
+ if( maxResourceAmount > 1 )
+ {
+ // Calculate amount of resources returned based on player's mining skill, item's wear and tear,
+ // and amount of resources that went into making the item in the first place
+ if ( ourObj.health >= 1 || ourObj.usesLeft >= 1 )
+ {
+ var healthPercentage = 0;
+ if( ourObj.health >= 1 )
+ {
+ healthPercentage = Math.floor( ( ourObj.health * 100) / ourObj.maxhp );
+ }
+
+ var usesPercentage = 0;
+ if( ourObj.usesLeft >= 1 )
+ {
+ usesPercentage = Math.floor( ( ourObj.usesLeft * 100 ) / ourObj.maxUses );
+ }
+
+ var itemPercentage = usesPercentage > 0 ? Math.min( healthPercentage, usesPercentage ) : healthPercentage;
+
+ resourceAmount = Math.floor( ( maxResourceAmount * itemPercentage ) / 100 );
+ }
+
+ // Halve the amount of resources returned
+ resourceAmount = Math.max( Math.floor( resourceAmount / 2 ), 1 );
+
+ // Fetch player's Mining skill
+ var playerSkill = mChar.skills.mining;
+
+ // Based on player's Mining skill, return between 1 to maxResourceAmount
+ resourceAmount = Math.min( Math.max( Math.floor( resourceAmount * ( playerSkill / 1000 )), 1 ), resourceAmount );
+ }
+ else
+ {
+ resourceAmount = 1;
+ }
+ }
+
+ if( resourceAmount == 0 )
+ {
+ // Targeted object is not an item that can be smelted
+ mChar.SetTempTag( "prevActionResult", "CANTSMELT" );
+ mChar.StartTimer( ingotDelay, 1, true );
+ return;
+ }
+
+ if( ourObj.isDyeable )
+ {
+ // Dyeable items should return regular iron ingots
+ resourceHue = 0;
+ }
+
+ switch( resourceHue )
+ {
+ case 0: // Iron granite
+ default:
+ break;
+ case 0x0973: // Dull Copper
+ resourceName = "dull copper granite";
+ break;
+ case 0x0966: // Shadow Iron
+ resourceName = "shadow iron granite";
+ break;
+ case 0x07dd: // Copper
+ resourceName = "copper granite";
+ break;
+ case 0x06d6: // Bronze
+ resourceName = "bronze granite";
+ break;
+ case 0x08a5: // Gold
+ resourceName = "gold granite";
+ break;
+ case 0x0979: // Agapite
+ resourceName = "agapite granite";
+ break;
+ case 0x089f: // Verite
+ resourceName = "verite granite";
+ break;
+ case 0x08ab: // Valorite
+ resourceName = "valorite granite";
+ break;
+ }
+
+ // Delete the melted item
+ ourObj.Delete();
+
+ // Run a generic skill check to give player a chance to increase their mining skill
+ mChar.CheckSkill( 45, 0, mChar.skillCaps.mining );
+
+ var newResource = CreateDFNItem( pSock, mChar, "0x1779", resourceAmount, "ITEM", true, resourceHue );
+ newResource.name = resourceName;
+
+ mChar.SetTempTag( "ingotsFromSmelting", resourceAmount );
+ mChar.SetTempTag( "prevActionResult", "SMELTITEMSUCCESS" );
+ mChar.StartTimer( gumpDelay, 1, true );
+}
+
+function RepairTarget( pSock )
+{
+ pSock.CustomTarget( 2, GetDictionaryEntry( 485, pSock.language )); // What item would you like to repair?
+}
+
+/** @type { ( tSock: Socket, target: Character | Item | null ) => void } */
+function onCallback2( pSock, ourObj )
+{
+ // Repair Item
+ var mChar = pSock.currentChar;
+
+ // Don't continue if character is invalid, or worse... dead!
+ if( !ValidateObject( mChar ) || mChar.dead )
+ return;
+
+ var bItem = pSock.tempObj;
+ var anvil = AreaItemFunction( "FindNearbyAnvils", mChar, 3, pSock );
+ var gumpID = masonryID + 0xffff;
+ pSock.tempObj = null;
+
+ if( ValidateObject( mChar ) && mChar.isChar && ValidateObject( bItem ) && bItem.isItem )
+ {
+ if( !ValidateObject( ourObj ) || !ourObj.isItem
+ || TriggerEvent( 2506, "GetItemMaterialType", ourObj ) != "metal"
+ || !CheckTileFlag( ourObj.id, 22 )) // TF_WEARABLE
+ {
+ // Targeted object is not an item that can be repaired
+ pSock.tempObj = bItem;
+ pSock.CloseGump( gumpID, 0 );
+ mChar.SetTempTag( "prevActionResult", "CANTREPAIR" );
+ mChar.StartTimer( repairDelay, 1, true );
+ return;
+ }
+
+ if( anvil == 0 )
+ {
+ // No anvil nearby
+ pSock.tempObj = bItem;
+ pSock.CloseGump( gumpID, 0 );
+ mChar.SetTempTag( "prevActionResult", "NOANVIL" );
+ mChar.StartTimer( repairDelay, 1, true );
+ return;
+ }
+
+ var itemDurabilityLossEnabled = GetServerSetting( "ItemRepairDurabilityLoss" );
+ var repairID = ourObj.id;
+ var ownerObj = GetPackOwner( ourObj, 0 );
+ if( ownerObj && mChar.serial == ownerObj.serial )
+ {
+ var maxHitpoints = ourObj.maxhp;
+ var currentHitpoints = ourObj.health;
+ if( currentHitpoints < maxHitpoints )
+ {
+ // Get base repair difficulty based on amount of HP missing and max hitpoints
+ var deltaHP = maxHitpoints - currentHitpoints;
+ var repairDifficulty = (( deltaHP / maxHitpoints ) * 1000 );
+ var minDifficulty = repairDifficulty - 250;
+ var skillBonus = 0;
+ var repairSkill = mChar.skills.blacksmithing;
+ if( minDifficulty < 0 )
+ {
+ // If minDifficulty is negative, add the negative value as a bonus to player's skill
+ skillBonus = minDifficulty * -1;
+ minDifficulty = 0;
+ }
+ else if( minDifficulty > repairSkill )
+ {
+ // Player skill below minimum repair difficulty, Too difficult to make the attempt!
+ pSock.tempObj = bItem;
+ pSock.CloseGump( gumpID, 0 );
+ mChar.SetTempTag( "prevActionResult", "CANTREPAIR" );
+ mChar.StartTimer( repairDelay, 1, true );
+ return;
+ }
+ var maxDifficulty = Math.min( repairDifficulty + 250, mChar.skillCaps.blacksmithing );
+
+ // Allow repair if random number between min and base difficulty is under player's skill
+ if( RandomNumber( minDifficulty, 1000 ) < ( Math.max( repairSkill + skillBonus, 999 )))
+ {
+ // Give player a chance every now and then to gain skill from repairing
+ if( RandomNumber( 1, 5 ) == 1 )
+ {
+ // Run a skill-check, which might trigger a skill-gain if player passes
+ mChar.CheckSkill( 8, minDifficulty, maxDifficulty ); // Skill 8 = blacksmithing
+ }
+
+ // Reduce object's max durability by 1
+ if( itemDurabilityLossEnabled )
+ {
+ ourObj.maxhp -= 1;
+ }
+
+ // Repair item here
+ ourObj.health = ourObj.maxhp;
+ pSock.SoundEffect( 0x002A, true );
+
+ // Reopen gump after a short delay
+ pSock.CloseGump( gumpID, 0 );
+ mChar.SetTempTag( "prevActionResult", "REPAIRSUCCESS" );
+ mChar.StartTimer( repairDelay, 1, true );
+
+ // GM skill (100.0 skillpoints)
+ // Item with 51 HP max
+ // item with 2 hp left - 99.65% chance to repair
+ // item with 25 hp left - 99.86% chance to repair
+ // item with 40 hp left - 99.9% chance to repair
+
+ // Expert Smith (71.5 skill points)
+ // Item with 51 HP max
+ // item with 2 hp left - 1.45% chance to repair
+ // item with 25 hp left - 61.49% chance to repair
+ // item with 40 hp left - 74.9% chance to repair
+ // item with 48 hp left - 90.6% chance to repair
+
+ // Apprentice Smith (51.5 skill points)
+ // Item with 51 HP max
+ // item with 2 hp left - 0% chance to repair
+ // item with 25 hp left - 34.5% chance to repair
+ // item with 40 hp left - 54.9% chance to repair
+ // item with 48 hp left - 70.6% chance to repair
+ }
+ else
+ {
+ // Failed to repair item - decrease item health!
+ if( repairSkill >= 1000 ) // GM Smith
+ {
+ ourObj.health -= 1;
+ }
+ else if( repairSkill >= 715 ) // Expert Smith
+ {
+ ourObj.health -= 2;
+ }
+ else // Below Expert Smith
+ {
+ ourObj.health -= 3;
+ }
+
+ if( ourObj.health <= 0 )
+ {
+ // Item has been destroyed!
+ pSock.SysMessage( GetDictionaryEntry( 311, pSock.language ).replace(/%s/gi, ourObj.name )); // Your %s has been destroyed.
+ ourObj.Delete();
+ }
+
+ pSock.tempObj = bItem;
+ pSock.CloseGump( gumpID, 0 );
+ mChar.SetTempTag( "prevActionResult", "FAILREPAIR" );
+ mChar.StartTimer( repairDelay, 1, true );
+ }
+ }
+ else
+ {
+ pSock.tempObj = bItem;
+ pSock.CloseGump( gumpID, 0 );
+ mChar.SetTempTag( "prevActionResult", "FULLREPAIR" );
+ mChar.StartTimer( repairDelay, 1, true );
+ }
+ }
+ else
+ {
+ pSock.tempObj = bItem;
+ pSock.CloseGump( gumpID, 0 );
+ mChar.SetTempTag( "prevActionResult", "CHECKPACK" );
+ mChar.StartTimer( repairDelay, 1, true );
+ }
+ }
+}
+
+/** @type { ( tObject: BaseObject, timerId: number ) => void } */
+function onTimer( pUser, timerID )
+{
+ if( !ValidateObject( pUser ))
+ return;
+
+ var socket = pUser.socket;
+
+ if( timerID >= 1 && timerID <= 9 )
+ {
+ PageX( socket, pUser, timerID ); // Pages 1 - 9
+ }
+ else if( timerID == 20 )
+ {
+ Page20( socket, pUser ); // Ingot selection
+ }
+ else if( timerID == 999 )
+ {
+ PageX( socket, pUser, 999 ); // Last Ten Blacksmith (if used)
+ }
+}
+
+
+/** @type { ( myObj: Socket, pressed: number, gump: GumpData ) => void } */
+function onGumpPress( pSock, pButton, gumpData )
+{
+ var pUser = pSock.currentChar;
+ var usedMakeLast = false;
+
+ if( !ValidateObject( pUser ) || pUser.dead )
+ return;
+
+ var bItem = pSock.tempObj;
+ if( !ValidateObject( bItem ) || !pUser.InRange( bItem, 3 ))
+ {
+ pSock.SysMessage( GetDictionaryEntry( 461, pSock.language )); // You are too far away.
+ return;
+ }
+
+ if( bItem.movable == 3 )
+ {
+ pSock.SysMessage( GetDictionaryEntry( 6031, pSock.language )); // Locked down resources cannot be used!
+ return;
+ }
+
+ var iPackOwner = GetPackOwner( bItem, 0 );
+ if( ValidateObject( iPackOwner ))
+ {
+ if( iPackOwner.serial != pUser.serial )
+ {
+ pSock.SysMessage( GetDictionaryEntry( 6032, pSock.language )); // That resource is in someone else's backpack!
+ return;
+ }
+ }
+ else
+ {
+ pSock.SysMessage( GetDictionaryEntry( 6022, pSock.language )); // This has to be in your backpack befgranite you can use it.
+ return;
+ }
+
+ var gumpID = masonryID + 0xffff;
+
+ // Close / Exit
+ if( pButton == 0 )
+ {
+ pUser.SetTempTag( "prevActionResult", null );
+ pUser.SetTempTag( "MAKELAST", null );
+ pSock.CloseGump( gumpID, 0 );
+ return;
+ }
+
+ // Repair Item
+ if( pButton == 49 )
+ {
+ RepairTarget( pSock );
+ return;
+ }
+
+ // Select Materials (ingots)
+ if( pButton == 50 )
+ {
+ pSock.CloseGump( gumpID, 0 );
+ Page20( pSock, pUser );
+ return;
+ }
+
+ // Smelt Item
+ if( pButton == 52 )
+ {
+ SmeltTarget( pSock );
+ return;
+ }
+
+ // Page buttons (1..9)
+ if( pButton >= 1 && pButton <= 9 )
+ {
+ pUser.SetTempTag( "page", pButton );
+ pUser.SetTempTag( "subPage", 1 );
+ pSock.CloseGump( gumpID, 0 );
+ PageX( pSock, pUser, pButton );
+ return;
+ }
+
+ // Last Ten page (if you wire it into the gump)
+ if( pButton == 11000 )
+ {
+ pUser.SetTempTag( "page", 999 );
+ pUser.SetTempTag( "subPage", 1 );
+ PageX( pSock, pUser, 999 );
+ return;
+ }
+
+ // Subpage navigation (8000 = prev, 9000 = next)
+ if( pButton >= 8000 && pButton < 9000 )
+ {
+ var prevSub = pButton - 8000;
+ var curPage = pUser.GetTempTag( "page" );
+ pUser.SetTempTag( "subPage", prevSub );
+ PageX( pSock, pUser, curPage );
+ return;
+ }
+
+ if( pButton >= 9000 && pButton < 10000 )
+ {
+ var nextSub = pButton - 9000;
+ var curPage2 = pUser.GetTempTag( "page" );
+ pUser.SetTempTag( "subPage", nextSub );
+ PageX( pSock, pUser, curPage2 );
+ return;
+ }
+
+ // Handle "Make Last"
+ if(( pButton >= 100 && pButton <= 998 ) || pButton == 5000 )
+ {
+ if( pButton == 5000 )
+ {
+ pButton = pUser.GetTempTag( "MAKELAST" );
+ usedMakeLast = true;
+ }
+ else
+ {
+ pUser.SetTempTag( "MAKELAST", pButton );
+ }
+ }
+
+ // Item detail buttons (2000 + buttonID)
+ if( pButton >= 2000 && pButton < 3000 )
+ {
+ var detailButtonID = pButton - 2000;
+ var entry = MasonryMap[detailButtonID];
+ if( entry )
+ {
+ // For details we just pass the granite version (granite index 0) to 4026
+ var graniteMakeID = entry.graniteMake[0];
+ if( graniteMakeID > 0 )
+ {
+ // Masonry uses Carpentry skill
+ pUser.SetTempTag( "Skill", entry.skill | 0 );
+
+ // Clear old harvests
+ pUser.SetTempTag( "Harvest", null );
+ pUser.SetTempTag( "Harvest2", null );
+ pUser.SetTempTag( "Harvest3", null );
+ pUser.SetTempTag( "Harvest4", null );
+
+ // Clear old harvest names
+ pUser.SetTempTag( "HarvestName", null );
+ pUser.SetTempTag( "Harvest2Name", null );
+ pUser.SetTempTag( "Harvest3Name", null );
+ pUser.SetTempTag( "Harvest4Name", null );
+
+ if( entry.harvest && entry.harvest.length > 0 )
+ {
+ if( entry.harvest.length >= 1 )
+ pUser.SetTempTag( "Harvest", entry.harvest[0] );
+ if( entry.harvest.length >= 2 )
+ pUser.SetTempTag( "Harvest2", entry.harvest[1] );
+ if( entry.harvest.length >= 3 )
+ pUser.SetTempTag( "Harvest3", entry.harvest[2] );
+ if( entry.harvest.length >= 4 )
+ pUser.SetTempTag( "Harvest4", entry.harvest[3] );
+ }
+
+ // OPTIONAL custom names – these override the dictionary string
+ if( entry.harvestNames && entry.harvestNames.length > 0 )
+ {
+ if( entry.harvestNames.length >= 1 )
+ pUser.SetTempTag( "HarvestName", entry.harvestNames[0] );
+ if( entry.harvestNames.length >= 2 )
+ pUser.SetTempTag( "Harvest2Name", entry.harvestNames[1] );
+ if( entry.harvestNames.length >= 3 )
+ pUser.SetTempTag( "Harvest3Name", entry.harvestNames[2] );
+ if( entry.harvestNames.length >= 4 )
+ pUser.SetTempTag( "Harvest4Name", entry.harvestNames[3] );
+ }
+
+ if( entry.recipeID && entry.recipeID > 0 )
+ pUser.SetTempTag( "needRecipeID", entry.recipeID );
+ else
+ pUser.SetTempTag( "needRecipeID", 0 );
+
+ pUser.SetTempTag( "ITEMDETAILS", graniteMakeID );
+ TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
+ }
+ }
+ return;
+ }
+
+ // If this is a craft button in our map:
+ if( MasonryMap[pButton] != undefined )
+ {
+ var entry2 = MasonryMap[pButton];
+ var graniteID = pUser.GetTempTag( "Granite" );
+ var resourceHue = pUser.GetTempTag( "resourceHue" );
+
+ // Ensure graniteID within range
+ if( graniteID < 0 || graniteID >= craftItems.length )
+ graniteID = 0;
+
+ // Era / recipe gating
+ if( !eraOK( entry2 ))
+ {
+ pSock.SysMessage( "That item is not available in this era." );
+ return;
+ }
+
+ if( entry2.recipeID && !TriggerEvent( 4022, "NeedRecipe", pUser, entry2.recipeID ))
+ {
+ pSock.SysMessage( "You must learn that recipe from a scroll." );
+ return;
+ }
+
+ // No colored granited weapons if disabled and using non-iron ingots
+ if( !allowColouredWeapons && resourceHue > 0 && entry2.page > 3 )
+ {
+ pSock.SysMessage( "You cannot use colored granited ingots for weapons on this shard." );
+ return;
+ }
+
+ var makeID = entry2.graniteMake[graniteID];
+ if( !makeID || makeID == 0 )
+ {
+ // Fallback to iron version if for some reason we did not get a specific granite entry
+ makeID = entry2.graniteMake[0];
+ }
+
+ if( !makeID || makeID == 0 )
+ {
+ pSock.SysMessage( "That item is not properly configured." );
+ return;
+ }
+
+ // Runic hammer bonus logic (unchanged from your original)
+ pUser.AddScriptTrigger( 4033 );
+
+ MakeItem( pSock, pUser, makeID, resourceHue );
+
+ // Tool wear
+ var toolUseLimit = GetServerSetting( "ToolUseLimit" );
+ var toolUseBreak = GetServerSetting( "ToolUseBreak" );
+
+ var runicHammer = pUser.FindItemLayer( 0x01 ); // Right Hand
+ if( ValidateObject( runicHammer ) && runicHammer.GetTag( "runicHammer" ) && runicHammer.usesLeft > 0 )
+ {
+ pUser.SetTempTag( "usedRunicHammer", true );
+ pUser.SetTempTag( "runicHammerType", runicHammer.color );
+
+ if( toolUseLimit && runicHammer != bItem )
+ {
+ runicHammer.usesLeft -= 1;
+ if( runicHammer.usesLeft == 0 && toolUseBreak )
+ {
+ runicHammer.Delete();
+ pSock.SysMessage( GetDictionaryEntry( 10202, pSock.language ));
+ }
+ }
+ }
+
+ if( toolUseLimit )
+ {
+ bItem.usesLeft -= 1;
+ if( bItem.usesLeft == 0 && toolUseBreak )
+ {
+ bItem.Delete();
+ pSock.SysMessage( GetDictionaryEntry( 10202, pSock.language ));
+ }
+ }
+
+ // Track in last ten list for blacksmith
+ AddToLastTenmMasonry( pUser, pButton );
+
+ // Reopen page after delay
+ pUser.StartTimer( gumpDelay, entry2.timerID, true );
+ return;
+ }
+
+ // Granite selection buttons (Page20)
+ if( pButton >= 1000 && pButton <= 1008 )
+ {
+ var index = pButton - 1000; // 0..8
+ var newGraniteID = index;
+ var newResourceHue = 0;
+
+ // Optional: use Mining skill gating like ingots
+ var miningSkill = pUser.skills.mining | 0;
+
+ switch( index )
+ {
+ case 0: // Iron
+ newResourceHue = 0;
+ break;
+
+ case 1: // Dull Copper
+ if( miningSkill < 650 )
+ {
+ pSock.CloseGump( gumpID, 0 );
+ pUser.StartTimer( ingotDelay, 20, true );
+ pUser.SetTempTag( "prevActionResult", "FAILED" );
+ return;
+ }
+ newResourceHue = 0x0973;
+ break;
+
+ case 2: // Shadow Iron
+ if( miningSkill < 700 )
+ {
+ pSock.CloseGump( gumpID, 0 );
+ pUser.StartTimer( ingotDelay, 20, true );
+ pUser.SetTempTag( "prevActionResult", "FAILED" );
+ return;
+ }
+ newResourceHue = 0x0966;
+ break;
+
+ case 3: // Copper
+ if( miningSkill < 750 )
+ {
+ pSock.CloseGump( gumpID, 0 );
+ pUser.StartTimer( ingotDelay, 20, true );
+ pUser.SetTempTag( "prevActionResult", "FAILED" );
+ return;
+ }
+ newResourceHue = 0x07dd;
+ break;
+
+ case 4: // Bronze
+ if( miningSkill < 800 )
+ {
+ pSock.CloseGump( gumpID, 0 );
+ pUser.StartTimer( ingotDelay, 20, true );
+ pUser.SetTempTag( "prevActionResult", "FAILED" );
+ return;
+ }
+ newResourceHue = 0x06d6;
+ break;
+
+ case 5: // Gold
+ if( miningSkill < 850 )
+ {
+ pSock.CloseGump( gumpID, 0 );
+ pUser.StartTimer( ingotDelay, 20, true );
+ pUser.SetTempTag( "prevActionResult", "FAILED" );
+ return;
+ }
+ newResourceHue = 0x08a5;
+ break;
+
+ case 6: // Agapite
+ if( miningSkill < 900 )
+ {
+ pSock.CloseGump( gumpID, 0 );
+ pUser.StartTimer( ingotDelay, 20, true );
+ pUser.SetTempTag( "prevActionResult", "FAILED" );
+ return;
+ }
+ newResourceHue = 0x0979;
+ break;
+
+ case 7: // Verite
+ if( miningSkill < 950 )
+ {
+ pSock.CloseGump( gumpID, 0 );
+ pUser.StartTimer( ingotDelay, 20, true );
+ pUser.SetTempTag( "prevActionResult", "FAILED" );
+ return;
+ }
+ newResourceHue = 0x089f;
+ break;
+
+ case 8: // Valorite
+ if( miningSkill < 990 )
+ {
+ pSock.CloseGump( gumpID, 0 );
+ pUser.StartTimer( ingotDelay, 20, true );
+ pUser.SetTempTag( "prevActionResult", "FAILED" );
+ return;
+ }
+ newResourceHue = 0x08ab;
+ break;
+ }
+
+ // Store selection
+ pUser.SetTempTag( "Granite", newGraniteID );
+ pUser.SetTempTag( "resourceHue", newResourceHue );
+ pUser.SetTempTag( "prevActionResult", null );
+ pUser.SetTempTag( "MAKELAST", null );
+
+ // Close the material select gump
+ pSock.CloseGump( gumpID, 0 );
+
+ // Go back to the last craft page, or default to page 1
+ var curPage = pUser.GetTempTag( "page" );
+ if( !curPage || curPage == 20 )
+ curPage = 1;
+
+ pUser.SetTempTag( "page", curPage );
+ pUser.SetTempTag( "subPage", 1 );
+ PageX( pSock, pUser, curPage );
+ return;
+ }
+}
+
+function AddToLastTenmMasonry( pUser, buttonID )
+{
+ var raw = pUser.GetTempTag( "LastTenMasonry" ) || "";
+ var list = raw.split( "," );
+
+ // Remove if already in list
+ for( var i = 0; i < list.length; i++ )
+ {
+ if( parseInt( list[i] ) == buttonID )
+ {
+ list.splice( i, 1 );
+ break;
+ }
+ }
+
+ var newList = [buttonID];
+ for( var j = 0; j < list.length && newList.length < 10; j++ )
+ {
+ var entry = parseInt( list[j] );
+ if( !isNaN( entry ) && entry > 0 )
+ newList.push( entry );
+ }
+
+ pUser.SetTempTag( "LastTenMasonry", newList.join( "," ) );
+}
+
+function HasLearnedRecipe( pUser, recipeID )
+{
+ var myData = TriggerEvent( 4022, "ReadRecipeID", pUser );
+ if( !myData || myData.length == 0 )
+ return false;
+
+ for( var i = 0; i < myData.length; i++ )
+ {
+ var data = myData[i].split( "," );
+ if( data[0] == recipeID )
+ return true;
+ }
+ return false;
+}
+
+function eraOK( entry )
+{
+ // Optional per-entry gates. Strings like "lbr","aos","ml","sa","hs","tol".
+ // If not present, the entry is valid for all eras.
+ if( entry.minEra && coreShardEra < EraStringToNum( entry.minEra ))
+ return false;
+ if( entry.maxEra && coreShardEra > EraStringToNum( entry.maxEra ))
+ return false;
+ return true;
+}
\ No newline at end of file
diff --git a/data/js/skill/craft/tailoring.js b/data/js/skill/craft/tailoring.js
index 66cf7874f..f07b50e95 100644
--- a/data/js/skill/craft/tailoring.js
+++ b/data/js/skill/craft/tailoring.js
@@ -1,194 +1,227 @@
///
// @ts-check
-const textHue = 0x480; // Color hue for all text in the crafting gump
-const scriptID = 4030; // Script ID used to identify and close this gump
-const gumpDelay = 2000; // Delay (ms) before gump reappears after crafting
-const repairDelay = 200; // Delay (ms) before gump reappears after selecting a resource
-const itemDetailsScriptID = 4026; // Script ID used to show item detail tooltips
-const craftGumpID = 4027; // TriggerEvent ID used to build the crafting gump UI
-const itemsPerPage = 10; // Number of craftable items shown per gump subpage
-const displayUnlearnedRecipes = true; // Show recipes player hasn't learned
-const coreShardEra = EraStringToNum( GetServerSetting( "CoreShardEra" ));
+const textHue = 0x480; // Color hue for all text in the crafting gump
+const tailoringID = 4030; // Script ID used to identify and close this gump
+const gumpDelay = 2000; // Delay (ms) before gump reappears after crafting
+const repairDelay = 200; // Delay (ms) before gump reappears after selecting a resource
+const itemDetailsScriptID = 4026; // Script ID used to show item detail tooltips
+const craftGumpID = 4027; // TriggerEvent ID used to build the crafting gump UI
+const itemsPerPage = 10; // Number of craftable items shown per gump subpage
+const displayUnlearnedRecipes = true; // Show recipes player hasn't learned
+const coreShardEra = EraStringToNum( GetServerSetting( "CoreShardEra" ));
+const tailoringSkillID = 34; // Tailoring skill index
+
//////////////////////////////////////////////////////////////////////////////////////////
-// UOX3 Tailoring CraftingMap Configuration
-//
-// Description:
-// This file defines the `CraftingMap` used to populate the tailoring crafting gump in UOX3.
-// Each entry links a craftable item (defined in `create/tailoring.dfn`) to its display name,
-// gump page, and crafting behavior. This setup powers the dynamic UI for tailoring.
-//
-// Data Sources:
-// - `makeID` refers to the unique item ID defined in `create/tailoring.dfn`.
-// - `dictID` refers to a localized string entry defined in `dictionaries/dictionary.eng`.
-// - `customName` is an optional hardcoded string that overrides dictionary text if present.
-// - `recipeID` is an optional ID that locks the item behind a learned recipe.
-//
-// Entry Format:
-// makeID: {
-// dictID: , // (Optional) Used for localized names via dictionary.eng
-// customName: "- ", // (Optional) Overrides dictionary name if present
-// page: , // Gump page to display item on
-// timerID: , // Timer used to re-show gump after crafting
-// recipeID: // (Optional) Requires the player to learn the recipe before crafting
-// }
-//
-// Display Logic:
-// - If `customName` is present, it is used directly in the crafting gump.
-// - If `dictID` is present (and `customName` is not), `GetDictionaryEntry()` is used.
-// - If both are missing, the gump will show a fallback like `[Unnamed Item: ####]`.
-// - The `page` field determines which category tab the item appears under.
-// - If `recipeID` is present and the player has not learned it, the item will either be:
-// - Hidden entirely (default behavior), or
-// - Displayed but uncraftable, depending on `displayUnlearnedRecipes` setting.
-//
-// Example Entries:
-//
-// // Standard item using dictionary ID for localization
-// 130: { dictID: 11415, page: 1, timerID: 1 },
-//
-// // Custom item with hardcoded display name, no dictionary entry required
-// 999: { customName: "harrys sword", page: 1, timerID: 1 },
-//
-// // Recipe-locked item (only craftable if recipe is learned)
-// 185: { dictID: 11469, page: 8, timerID: 8, recipeID: 185 },
-//
-// // Era Gating
-// 130: { dictID: 11415, page: 1, timerID: 1, minEra: "uo" }, // from UO and up
-// 185: { dictID: 11469, page: 8, timerID: 8, recipeID: 185, minEra: "aos" }, // AoS+
-// 169: { dictID: 11454, page: 6, timerID: 6, maxEra: "lbr" }, // up to LBR
-//
-// Organization:
-// - Items are grouped by `page` value (e.g., Hats, Armor, etc.).
-// - The crafting gump dynamically builds its layout from this CraftingMap.
-// - This system supports localized, custom, hybrid, and recipe-locked crafting menus.
-//
+// Tailoring CraftingMap
//////////////////////////////////////////////////////////////////////////////////////////
+// You can later add:
+// skill, minEra, maxEra, recipeID, harvest, harvestNames, customName
+// per entry, like in Alchemy / Tinkering.
+
const CraftingMap = {
// Hats (Page 1)
- 130: { dictID: 11415, page: 1, timerID: 1 },
- 131: { dictID: 11416, page: 1, timerID: 1 },
- 132: { dictID: 11417, page: 1, timerID: 1 },
- 134: { dictID: 11418, page: 1, timerID: 1 },
- 133: { dictID: 11419, page: 1, timerID: 1 },
- 136: { dictID: 11420, page: 1, timerID: 1 },
- 137: { dictID: 11421, page: 1, timerID: 1 },
- 138: { dictID: 11422, page: 1, timerID: 1 },
- 139: { dictID: 11423, page: 1, timerID: 1 },
- 140: { dictID: 11424, page: 1, timerID: 1 },
- 141: { dictID: 11425, page: 1, timerID: 1 },
- 135: { dictID: 11470, page: 1, timerID: 1 },
+ 130: { dictID: 11415, page: 1, timerID: 1, harvest: [ 10016 ] },
+ 131: { dictID: 11416, page: 1, timerID: 1, harvest: [ 10016 ] },
+ 132: { dictID: 11417, page: 1, timerID: 1, harvest: [ 10016 ] },
+ 134: { dictID: 11418, page: 1, timerID: 1, harvest: [ 10016 ] },
+ 133: { dictID: 11419, page: 1, timerID: 1, harvest: [ 10016 ] },
+ 136: { dictID: 11420, page: 1, timerID: 1, harvest: [ 10016 ] },
+ 137: { dictID: 11421, page: 1, timerID: 1, harvest: [ 10016 ] },
+ 138: { dictID: 11422, page: 1, timerID: 1, harvest: [ 10016 ] },
+ 139: { dictID: 11423, page: 1, timerID: 1, harvest: [ 10016 ] },
+ 140: { dictID: 11424, page: 1, timerID: 1, harvest: [ 10016 ] },
+ 141: { dictID: 11425, page: 1, timerID: 1, harvest: [ 10016 ] },
+ 135: { dictID: 11470, page: 1, timerID: 1, harvest: [ 10016 ] },
// Shirts & Pants (Page 2)
- 142: { dictID: 11426, page: 2, timerID: 2 },
- 143: { dictID: 11427, page: 2, timerID: 2 },
- 144: { dictID: 11428, page: 2, timerID: 2 },
- 145: { dictID: 11429, page: 2, timerID: 2 },
- 146: { dictID: 11430, page: 2, timerID: 2 },
- 147: { dictID: 11431, page: 2, timerID: 2 },
- 148: { dictID: 11432, page: 2, timerID: 2 },
- 149: { dictID: 11433, page: 2, timerID: 2 },
- 150: { dictID: 11434, page: 2, timerID: 2 },
- 151: { dictID: 11435, page: 2, timerID: 2 },
- 180: { dictID: 11436, page: 2, timerID: 2 },
- 152: { dictID: 11437, page: 2, timerID: 2 },
- 153: { dictID: 11438, page: 2, timerID: 2 },
- 154: { dictID: 11439, page: 2, timerID: 2 },
+ 142: { dictID: 11426, page: 2, timerID: 2, harvest: [ 10016 ] },
+ 143: { dictID: 11427, page: 2, timerID: 2, harvest: [ 10016 ] },
+ 144: { dictID: 11428, page: 2, timerID: 2, harvest: [ 10016 ] },
+ 145: { dictID: 11429, page: 2, timerID: 2, harvest: [ 10016 ] },
+ 146: { dictID: 11430, page: 2, timerID: 2, harvest: [ 10016 ] },
+ 147: { dictID: 11431, page: 2, timerID: 2, harvest: [ 10016 ] },
+ 148: { dictID: 11432, page: 2, timerID: 2, harvest: [ 10016 ] },
+ 149: { dictID: 11433, page: 2, timerID: 2, harvest: [ 10016 ] },
+ 150: { dictID: 11434, page: 2, timerID: 2, harvest: [ 10016 ] },
+ 151: { dictID: 11435, page: 2, timerID: 2, harvest: [ 10016 ] },
+ 180: { dictID: 11436, page: 2, timerID: 2, harvest: [ 10016 ] },
+ 152: { dictID: 11437, page: 2, timerID: 2, harvest: [ 10016 ] },
+ 153: { dictID: 11438, page: 2, timerID: 2, harvest: [ 10016 ] },
+ 154: { dictID: 11439, page: 2, timerID: 2, harvest: [ 10016 ] },
// Misc (Page 3)
- 155: { dictID: 11440, page: 3, timerID: 3 },
- 156: { dictID: 11441, page: 3, timerID: 3 },
- 157: { dictID: 11442, page: 3, timerID: 3 },
- 158: { dictID: 11443, page: 3, timerID: 3 },
+ 155: { dictID: 11440, page: 3, timerID: 3, harvest: [ 10016 ] },
+ 156: { dictID: 11441, page: 3, timerID: 3, harvest: [ 10016 ] },
+ 157: { dictID: 11442, page: 3, timerID: 3, harvest: [ 10016 ] },
+ 158: { dictID: 11443, page: 3, timerID: 3, harvest: [ 10016 ] },
// Footwear (Page 4)
- 159: { dictID: 11444, page: 4, timerID: 4 },
- 160: { dictID: 11445, page: 4, timerID: 4 },
- 161: { dictID: 11446, page: 4, timerID: 4 },
- 162: { dictID: 11447, page: 4, timerID: 4 },
+ 159: { dictID: 11444, page: 4, timerID: 4, harvest: [ 10007 ] },
+ 160: { dictID: 11445, page: 4, timerID: 4, harvest: [ 10007 ] },
+ 161: { dictID: 11446, page: 4, timerID: 4, harvest: [ 10007 ] },
+ 162: { dictID: 11447, page: 4, timerID: 4, harvest: [ 10007 ] },
// Leather Armor (Page 5)
- 163: { dictID: 11448, page: 5, timerID: 5 },
- 164: { dictID: 11449, page: 5, timerID: 5 },
- 165: { dictID: 11450, page: 5, timerID: 5 },
- 166: { dictID: 11451, page: 5, timerID: 5 },
- 167: { dictID: 11452, page: 5, timerID: 5 },
- 168: { dictID: 11453, page: 5, timerID: 5 },
+ 163: { dictID: 11448, page: 5, timerID: 5, harvest: [ 10007 ] },
+ 164: { dictID: 11449, page: 5, timerID: 5, harvest: [ 10007 ] },
+ 165: { dictID: 11450, page: 5, timerID: 5, harvest: [ 10007 ] },
+ 166: { dictID: 11451, page: 5, timerID: 5, harvest: [ 10007 ] },
+ 167: { dictID: 11452, page: 5, timerID: 5, harvest: [ 10007 ] },
+ 168: { dictID: 11453, page: 5, timerID: 5, harvest: [ 10007 ] },
// Studded Armor (Page 6)
- 169: { dictID: 11454, page: 6, timerID: 6 },
- 170: { dictID: 11455, page: 6, timerID: 6 },
- 171: { dictID: 11456, page: 6, timerID: 6 },
- 172: { dictID: 11457, page: 6, timerID: 6 },
- 173: { dictID: 11458, page: 6, timerID: 6 },
+ 169: { dictID: 11454, page: 6, timerID: 6, harvest: [ 10007 ] },
+ 170: { dictID: 11455, page: 6, timerID: 6, harvest: [ 10007 ] },
+ 171: { dictID: 11456, page: 6, timerID: 6, harvest: [ 10007 ] },
+ 172: { dictID: 11457, page: 6, timerID: 6, harvest: [ 10007 ] },
+ 173: { dictID: 11458, page: 6, timerID: 6, harvest: [ 10007 ] },
// Female Armor (Page 7)
- 174: { dictID: 11459, page: 7, timerID: 7 },
- 175: { dictID: 11460, page: 7, timerID: 7 },
- 176: { dictID: 11461, page: 7, timerID: 7 },
- 177: { dictID: 11462, page: 7 , timerID: 7},
- 178: { dictID: 11463, page: 7, timerID: 7 },
- 179: { dictID: 11464, page: 7, timerID: 7 },
+ 174: { dictID: 11459, page: 7, timerID: 7, harvest: [ 10007 ] },
+ 175: { dictID: 11460, page: 7, timerID: 7, harvest: [ 10007 ] },
+ 176: { dictID: 11461, page: 7, timerID: 7, harvest: [ 10007 ] },
+ 177: { dictID: 11462, page: 7, timerID: 7, harvest: [ 10007 ] },
+ 178: { dictID: 11463, page: 7, timerID: 7, harvest: [ 10007 ] },
+ 179: { dictID: 11464, page: 7, timerID: 7, harvest: [ 10007 ] },
// Bone Armor (Page 8)
- 181: { dictID: 11465, page: 8, timerID: 8 },
- 182: { dictID: 11466, page: 8, timerID: 8 },
- 183: { dictID: 11467, page: 8, timerID: 8 },
- 184: { dictID: 11468, page: 8, timerID: 8 },
- 185: { dictID: 11469, page: 8, timerID: 8 }
+ 181: { dictID: 11465, page: 8, timerID: 8, harvest: [ 10007, 10008 ] },
+ 182: { dictID: 11466, page: 8, timerID: 8, harvest: [ 10007, 10008 ] },
+ 183: { dictID: 11467, page: 8, timerID: 8, harvest: [ 10007, 10008 ] },
+ 184: { dictID: 11468, page: 8, timerID: 8, harvest: [ 10007, 10008 ] },
+ 185: { dictID: 11469, page: 8, timerID: 8, harvest: [ 10007, 10008 ] } // can add recipeID/minEra here later
};
-function PageX( socket, pUser, pageNum )
+// Fill defaults (skill, etc.)
+(function initTailoringMap()
{
- let subPage = pUser.GetTempTag( "subPage" ) || 1;
-
- // Build pages dynamically from CraftingMap
- let myPage = [];
- let dictToMakeID = {}; // local reverse map
-
- for( let makeID in CraftingMap )
+ for( var key in CraftingMap )
{
- let data = CraftingMap[makeID];
- let page = data.page;
- if( !myPage[page - 1] )
- myPage[page - 1] = [];
-
- let needsRecipe = data.recipeID;
- let showAll = displayUnlearnedRecipes;
+ if( !CraftingMap.hasOwnProperty( key ))
+ continue;
- if( eraOK( data ) && ( !needsRecipe || showAll || HasLearnedRecipe( pUser, needsRecipe )))
- {
- myPage[page - 1].push( data.dictID );
- }
-
- dictToMakeID[data.dictID] = parseInt( makeID );
+ var entry = CraftingMap[key];
+ if( entry.skill === undefined )
+ entry.skill = tailoringSkillID;
}
+})();
- let pageItems;
+/** @type { ( socket: Socket, pUser: Character, pageNum: number ) => void } */
+function PageX( socket, pUser, pageNum )
+{
+ if( !socket || !ValidateObject( pUser ))
+ return;
+
+ var subPage = pUser.GetTempTag( "subPage" ) || 1;
+ var pageItems;
+ // Last Ten page
if( pageNum == 999 )
{
- let lastTenRaw = pUser.GetTag( "LastTenTailoring" ) || "";
- let split = lastTenRaw.split( "," );
+ var lastTenRaw = pUser.GetTempTag( "LastTenTailoring" ) || "";
+ var split = lastTenRaw.split( "," );
pageItems = [];
for( var i = 0; i < split.length; i++ )
{
- let val = parseInt(split[i]);
+ var val = parseInt( split[i] );
if( !isNaN( val ))
- pageItems.push( val );
+ pageItems.push( val ); // makeID
}
}
else
{
- if( pageNum < 1 || pageNum > myPage.length )
+ // Collect all makeIDs for this page
+ var makeIDs = [];
+ for( var key in CraftingMap )
+ {
+ if( !CraftingMap.hasOwnProperty( key ))
+ continue;
+
+ var makeID = parseInt( key );
+ var data = CraftingMap[makeID];
+ if( !data || data.page != pageNum )
+ continue;
+
+ makeIDs.push( makeID );
+ }
+
+ // Sort by dictID so order matches dictionary sequence
+ makeIDs.sort( function( a, b )
+ {
+ var ea = CraftingMap[a];
+ var eb = CraftingMap[b];
+ if( ea && eb )
+ return ( ea.dictID || 0 ) - ( eb.dictID || 0 );
+ return a - b;
+ });
+
+ // Era / recipe filtering
+ pageItems = [];
+ for( var k = 0; k < makeIDs.length; k++ )
+ {
+ var id = makeIDs[k];
+ var data2 = CraftingMap[id];
+ if( !data2 )
+ continue;
+
+ var needsRecipe = data2.recipeID;
+ var showAll = displayUnlearnedRecipes;
+
+ if( eraOK( data2 ) && ( !needsRecipe || showAll || HasLearnedRecipe( pUser, needsRecipe )) )
+ pageItems.push( id );
+ }
+
+ // Fallback if page empty (and not page 1)
+ if( pageItems.length == 0 && pageNum != 1 )
+ {
pageNum = 1;
- pageItems = myPage[pageNum - 1];
- }
+ makeIDs = [];
+ for( var key2 in CraftingMap )
+ {
+ if( !CraftingMap.hasOwnProperty( key2 ))
+ continue;
+
+ var mid2 = parseInt( key2 );
+ var d3 = CraftingMap[mid2];
+ if( !d3 || d3.page != 1 )
+ continue;
- let totalSubPages = Math.ceil( pageItems.length / itemsPerPage );
+ makeIDs.push( mid2 );
+ }
+ makeIDs.sort( function( a, b )
+ {
+ var ea2 = CraftingMap[a];
+ var eb2 = CraftingMap[b];
+ if( ea2 && eb2 )
+ return ( ea2.dictID || 0 ) - ( eb2.dictID || 0 );
+ return a - b;
+ });
+
+ pageItems = [];
+ for( var m = 0; m < makeIDs.length; m++ )
+ {
+ var id2 = makeIDs[m];
+ var data4 = CraftingMap[id2];
+ if( !data4 )
+ continue;
+
+ var needsRecipe2 = data4.recipeID;
+ var showAll2 = displayUnlearnedRecipes;
+
+ if( eraOK( data4 ) && ( !needsRecipe2 || showAll2 || HasLearnedRecipe( pUser, needsRecipe2 )) )
+ pageItems.push( id2 );
+ }
+ }
+ }
+
+ // Subpage handling
+ var totalSubPages = Math.ceil( pageItems.length / itemsPerPage );
+ if( totalSubPages < 1 )
+ totalSubPages = 1;
if( subPage < 1 )
subPage = 1;
if( subPage > totalSubPages )
@@ -197,108 +230,104 @@ function PageX( socket, pUser, pageNum )
pUser.SetTempTag( "page", pageNum );
pUser.SetTempTag( "subPage", subPage );
- let startIndex = ( subPage - 1 ) * itemsPerPage;
- let endIndex = Math.min( startIndex + itemsPerPage, pageItems.length );
+ var startIndex = ( subPage - 1 ) * itemsPerPage;
+ var endIndex = Math.min( startIndex + itemsPerPage, pageItems.length );
- if( startIndex >= pageItems.length )
+ if( startIndex >= pageItems.length )
{
- subPage = 1;
+ subPage = 1;
startIndex = 0;
- endIndex = Math.min( itemsPerPage, pageItems.length );
+ endIndex = Math.min( itemsPerPage, pageItems.length );
pUser.SetTempTag( "subPage", subPage );
}
- let tailoringMenu = new Gump;
+ var tailoringMenu = new Gump;
TriggerEvent( craftGumpID, "CraftingGumpMenu", tailoringMenu, socket );
tailoringMenu.AddPage( 1 );
- for( let i = startIndex; i < endIndex; i++)
+ for( var j = startIndex; j < endIndex; j++ )
{
- let index = i - startIndex;
- let makeID, entryID, entryText, buttonID;
+ var index = j - startIndex;
+ var makeID = pageItems[j];
+ var entryText;
+ var buttonID = makeID; // craft button uses makeID directly
+ var data5 = CraftingMap[makeID];
- if( pageNum == 999 )
+ if( !data5 )
{
- makeID = pageItems[i];
+ entryText = "[Missing MakeID: " + makeID + "]";
}
else
{
- entryID = pageItems[i];
- makeID = dictToMakeID[entryID];
+ if( data5.customName )
+ {
+ entryText = data5.customName;
+ }
+ else if( data5.dictID )
+ {
+ entryText = GetDictionaryEntry( data5.dictID, socket.language );
+ if( !entryText || entryText === "" )
+ entryText = "[Missing EntryID: " + data5.dictID + "]";
+ }
+ else
+ {
+ entryText = "[Unnamed Item: " + makeID + "]";
+ }
}
- let data = CraftingMap[makeID];
-
- if( !data )
- {
- entryText = "[Missing MakeID: " + makeID + "]";
- buttonID = makeID;
+ tailoringMenu.AddButton( 220, 60 + ( index * 20 ), 4005, 4007, 1, 0, buttonID );
+ tailoringMenu.AddText( 255, 60 + ( index * 20 ), textHue, entryText );
+ // Detail button: 20000 + makeID (new system pattern)
+ tailoringMenu.AddButton( 480, 60 + ( index * 20 ), 4011, 4012, 1, 0, 20000 + buttonID );
}
- else
- {
- buttonID = makeID;
-
- if( data.customName )
- {
- entryText = data.customName;
- }
- else if( data.dictID )
- {
- entryText = GetDictionaryEntry( data.dictID, socket.language );
- if( !entryText || entryText === "" )
- entryText = "[Missing EntryID: " + data.dictID + "]";
- }
- else
- {
- entryText = "[Unnamed Item: " + makeID + "]";
- }
- }
-
- tailoringMenu.AddButton( 220, 60 + ( index * 20 ), 4005, 4007, 1, 0, buttonID );
- tailoringMenu.AddText( 255, 60 + ( index * 20 ), textHue, entryText );
- tailoringMenu.AddButton( 480, 60 + ( index * 20 ), 4011, 4012, 1, 0, 2000 + buttonID );
-}
+ // Prev subpage
if( subPage > 1 )
{
tailoringMenu.AddButton( 220, 260, 4014, 4015, 1, 0, 8000 + ( subPage - 1 ));
- tailoringMenu.AddHTMLGump( 255, 263, 100, 18, 0, 0, "" + GetDictionaryEntry(10101, socket.language ) + "" );
+ tailoringMenu.AddHTMLGump( 255, 263, 100, 18, false, false,
+ "" + GetDictionaryEntry( 10101, socket.language ) + "" );
}
+ // Next subpage
if( subPage < totalSubPages )
{
tailoringMenu.AddButton( 370, 260, 4005, 4007, 1, 0, 9000 + ( subPage + 1 ));
- tailoringMenu.AddHTMLGump( 405, 263, 100, 18, 0, 0, "" + GetDictionaryEntry(10100, socket.language ) + "" );
+ tailoringMenu.AddHTMLGump( 405, 263, 100, 18, false, false,
+ "" + GetDictionaryEntry( 10100, socket.language ) + "" );
}
tailoringMenu.Send( socket );
tailoringMenu.Free();
}
-/** @type { ( tObject: BaseObject, timerId: number ) => void } */
+/** @type { ( pUser: Character, timerID: number ) => void } */
function onTimer( pUser, timerID )
{
if( !ValidateObject( pUser ))
return;
- let socket = pUser.socket;
+ var socket = pUser.socket;
+ if( socket == null )
+ return;
if( timerID >= 1 && timerID <= 8 )
{
- PageX( socket, pUser, timerID ); // Pages 1 - 8
+ PageX( socket, pUser, timerID ); // Pages 1–8
}
- else if ( timerID == 999 )
+ else if( timerID == 999 )
{
- PageX(socket, pUser, 999); // Last Ten
+ PageX( socket, pUser, 999 ); // Last Ten
}
}
-/** @type { ( myObj: Socket, pressed: number, gump: GumpData ) => void } */
+/** @type { ( socket: Socket, pButton: number, gumpData: GumpData ) => void } */
function onGumpPress( socket, pButton, gumpData )
{
- var pUser = socket.currentChar;
- var usedMakeLast = false;
+ if( socket == null )
+ return;
+ var pUser = socket.currentChar;
if( !ValidateObject( pUser ) || pUser.dead )
return;
@@ -315,39 +344,43 @@ function onGumpPress( socket, pButton, gumpData )
return;
}
- var iPackOwner = GetPackOwner(bItem, 0);
+ var iPackOwner = GetPackOwner( bItem, 0 );
if( ValidateObject( iPackOwner ))
{
if( iPackOwner.serial != pUser.serial )
{
- socket.SysMessage(GetDictionaryEntry( 6032, socket.language ));
+ socket.SysMessage( GetDictionaryEntry( 6032, socket.language ));
return;
}
}
else
{
- socket.SysMessage(GetDictionaryEntry( 6022, socket.language ));
+ socket.SysMessage( GetDictionaryEntry( 6022, socket.language ));
return;
}
+ var gumpID = tailoringID + 0xffff;
+
+ // Subpage back/forward
if( pButton >= 8001 && pButton < 9000 )
{
- let subPage = pButton - 8000;
- let pageNum = pUser.GetTempTag( "page" ) || 1;
+ var subPage = pButton - 8000;
+ var pageNum = pUser.GetTempTag( "page" ) || 1;
pUser.SetTempTag( "subPage", subPage );
- PageX(socket, pUser, pageNum);
+ PageX( socket, pUser, pageNum );
return;
}
if( pButton >= 9001 && pButton < 10000 )
{
- let subPage = pButton - 9000;
- let pageNum = pUser.GetTempTag( "page" ) || 1;
- pUser.SetTempTag("subPage", subPage);
- PageX( socket, pUser, pageNum );
+ var subPage2 = pButton - 9000;
+ var pageNum2 = pUser.GetTempTag( "page" ) || 1;
+ pUser.SetTempTag( "subPage", subPage2 );
+ PageX( socket, pUser, pageNum2 );
return;
}
+ // Page tabs (1–8)
if( pButton >= 1 && pButton <= 8 )
{
pUser.SetTempTag( "page", pButton );
@@ -356,83 +389,95 @@ function onGumpPress( socket, pButton, gumpData )
return;
}
+ // Last Ten
if( pButton == 11000 )
{
pUser.SetTempTag( "page", 999 );
pUser.SetTempTag( "subPage", 1 );
- PageX(socket, pUser, 999);
+ PageX( socket, pUser, 999 );
+ return;
+ }
+
+ // Close gump
+ if( pButton == 0 )
+ {
+ pUser.SetTempTag( "MakeLast_Tailoring", null );
+ pUser.SetTempTag( "CRAFT", null );
+ socket.CloseGump( gumpID, 0 );
+ return;
+ }
+
+ // Unravel button
+ if( pButton == 52 )
+ {
+ UnravelTarget( socket );
return;
}
+ var usedMakeLast = false;
var makeID = 0;
var timerID = 0;
- // Handle "Make Last"
- if(( pButton >= 100 && pButton <= 804 ) || pButton == 5000 )
+ // Make Last
+ if( pButton == 5000 )
{
- if( pButton == 5000 )
+ var last = pUser.GetTempTag( "MakeLast_Tailoring" );
+ if( last )
{
- pButton = pUser.GetTempTag( "MAKELAST" );
+ pButton = last;
usedMakeLast = true;
}
else
{
- pUser.SetTempTag( "MAKELAST", pButton );
+ return;
}
}
- // If it's a craft button (found in map)
+ // Craft buttons use makeID directly (if in map)
if( CraftingMap[pButton] != undefined )
{
makeID = pButton;
- timerID = CraftingMap[makeID].timerID || 1;
+ var data = CraftingMap[makeID];
+ timerID = data.timerID || 1;
+
+ // Era / recipe checks
+ if( !eraOK( data ))
+ {
+ socket.SysMessage( "That item is not available in this era." );
+ return;
+ }
+ if( data.recipeID && !TriggerEvent( 4022, "NeedRecipe", pUser, data.recipeID ))
+ {
+ socket.SysMessage( "You must learn that recipe from a scroll." );
+ return;
+ }
- let materialHue = pUser.GetTempTag( "LastResourceColor" );
+ pUser.SetTempTag( "MakeLast_Tailoring", makeID );
+
+ var materialHue = pUser.GetTempTag( "LastResourceColor" );
+
+ // Cloth items: select cloth (colour) unless Make Last + cached hue
if(( makeID >= 100 && makeID <= 158 ) || makeID == 180 )
{
if( usedMakeLast && materialHue != null )
{
- // Check if recipe required and not known
- let data = CraftingMap[makeID];
- if( data && !eraOK( data ))
- {
- socket.SysMessage( "That item is not available in this era." );
- return;
- }
-
- if( data.recipeID && !TriggerEvent( 4022, "NeedRecipe", pUser, data.recipeID ))
- {
- socket.SysMessage("You must learn that recipe from a scroll.");
- return;
- }
MakeItem( socket, pUser, makeID, materialHue );
AddToLastTen( pUser, makeID );
- pUser.StartTimer( gumpDelay, timerID, 4030 );
+ pUser.StartTimer( gumpDelay, timerID, tailoringID );
}
else
{
pUser.SetTempTag( "makeID", makeID );
pUser.SetTempTag( "timerID", timerID );
- socket.CustomTarget(1, GetDictionaryEntry( 444, socket.language ));
+ socket.CustomTarget( 1, GetDictionaryEntry( 444, socket.language )); // Select material:
}
}
else
{
- // Check if recipe required and not known
- let data = CraftingMap[makeID];
- if( data && !eraOK( data ))
- {
- socket.SysMessage( "That item is not available in this era." );
- return;
- }
-
- if( data.recipeID && !TriggerEvent( 4022, "NeedRecipe", pUser, data.recipeID ))
- {
- socket.SysMessage("You must learn that recipe from a scroll.");
- return;
- }
+ // Non-colour-selected tailoring (leather/bone etc.)
MakeItem( socket, pUser, makeID );
AddToLastTen( pUser, makeID );
+
if( GetServerSetting( "ToolUseLimit" ))
{
bItem.usesLeft -= 1;
@@ -442,40 +487,74 @@ function onGumpPress( socket, pButton, gumpData )
socket.SysMessage( GetDictionaryEntry( 10202, socket.language ));
}
}
- pUser.StartTimer( gumpDelay, timerID, 4030 );
+ pUser.StartTimer( gumpDelay, timerID, tailoringID );
}
return;
}
- // If it's a detail button (2000+ button ID pattern)
- if( pButton >= 2000 && pButton <= 3000 )
+ // Detail buttons: 20000 + makeID (new system)
+ if( pButton >= 20000 && pButton < 30000 )
{
- let makeID = pButton - 2000;
- if( CraftingMap[makeID] )
+ var detailMakeID = pButton - 20000;
+ var entry = CraftingMap[detailMakeID];
+ if( entry )
{
- pUser.SetTempTag( "ITEMDETAILS", makeID );
- TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
- }
- return;
- }
+ // Which item to show
+ pUser.SetTempTag( "ITEMDETAILS", detailMakeID );
+
+ // Skill used
+ pUser.SetTempTag( "Skill", entry.skill || tailoringSkillID );
+
+ // Clear old harvest tags
+ pUser.SetTempTag( "Harvest", null );
+ pUser.SetTempTag( "Harvest2", null );
+ pUser.SetTempTag( "Harvest3", null );
+ pUser.SetTempTag( "Harvest4", null );
+
+ // Clear old custom harvest names
+ pUser.SetTempTag( "HarvestName", null );
+ pUser.SetTempTag( "Harvest2Name", null );
+ pUser.SetTempTag( "Harvest3Name", null );
+ pUser.SetTempTag( "Harvest4Name", null );
+
+ // Optional harvest dictIDs (cloth, leather, bone etc.) – add later per item
+ if( entry.harvest && entry.harvest.length > 0 )
+ {
+ if( entry.harvest.length >= 1 )
+ pUser.SetTempTag( "Harvest", entry.harvest[0] );
+ if( entry.harvest.length >= 2 )
+ pUser.SetTempTag( "Harvest2", entry.harvest[1] );
+ if( entry.harvest.length >= 3 )
+ pUser.SetTempTag( "Harvest3", entry.harvest[2] );
+ if( entry.harvest.length >= 4 )
+ pUser.SetTempTag( "Harvest4", entry.harvest[3] );
+ }
- if( pButton == 0 ) // Exit/Close
- {
- let gumpID = scriptID + 0xffff;
- pUser.SetTempTag( "MAKELAST", null );
- pUser.SetTempTag( "CRAFT", null );
- socket.CloseGump( gumpID, 0 );
- return;
- }
+ // Optional custom resource names (e.g. "Cloth", "Leather")
+ if( entry.harvestNames && entry.harvestNames.length > 0 )
+ {
+ if( entry.harvestNames.length >= 1 )
+ pUser.SetTempTag( "HarvestName", entry.harvestNames[0] );
+ if( entry.harvestNames.length >= 2 )
+ pUser.SetTempTag( "Harvest2Name", entry.harvestNames[1] );
+ if( entry.harvestNames.length >= 3 )
+ pUser.SetTempTag( "Harvest3Name", entry.harvestNames[2] );
+ if( entry.harvestNames.length >= 4 )
+ pUser.SetTempTag( "Harvest4Name", entry.harvestNames[3] );
+ }
- if( pButton == 52 ) // Unravel Item
- {
- UnravelTarget( socket );
+ if( entry.recipeID && entry.recipeID > 0 )
+ pUser.SetTempTag( "needRecipeID", entry.recipeID );
+ else
+ pUser.SetTempTag( "needRecipeID", 0 );
+
+ TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
+ }
return;
}
}
-/** @type { ( tSock: Socket, target: Character | Item | null ) => void } */
+/** @type { ( socket: Socket, ourObj: Character | Item | null ) => void } */
function onCallback1( socket, ourObj )
{
var pUser = socket.currentChar;
@@ -483,58 +562,57 @@ function onCallback1( socket, ourObj )
return;
// Fetch makeID and timerID from temp tag
- var makeID = pUser.GetTempTag( "makeID" );
+ var makeID = pUser.GetTempTag( "makeID" );
var timerID = pUser.GetTempTag( "timerID" );
pUser.SetTempTag( "makeID", null );
pUser.SetTempTag( "timerID", null );
var bItem = socket.tempObj;
- if( ValidateObject( bItem ))
+ if( !ValidateObject( bItem ))
+ return;
+
+ if( ValidateObject( ourObj ) && ourObj.isItem )
{
- if( ValidateObject( ourObj ) && ourObj.isItem )
+ // Make sure targeted item is in player's backpack
+ var iPackOwner = GetPackOwner( ourObj, 0 );
+ if( ValidateObject( iPackOwner ))
{
- // Make sure targeted item is in player's backpack
- var iPackOwner = GetPackOwner( ourObj, 0 );
- if( ValidateObject( iPackOwner )) // Is the item in a backpack?
+ if( iPackOwner.serial != pUser.serial )
{
- if( iPackOwner.serial != pUser.serial ) // And if so does the pack belong to the user?
- {
- socket.SysMessage( GetDictionaryEntry( 6032, socket.language )); // That resource is in someone else's backpack!
- return;
- }
- }
- else
- {
- socket.SysMessage( GetDictionaryEntry( 6022, socket.language )); // This has to be in your backpack before you can use it.
+ socket.SysMessage( GetDictionaryEntry( 6032, socket.language ));
return;
}
+ }
+ else
+ {
+ socket.SysMessage( GetDictionaryEntry( 6022, socket.language ));
+ return;
+ }
- //Cache cloth color for future "Make Last"
- pUser.SetTempTag( "LastResourceColor", ourObj.colour );
+ // Cache cloth color for Make Last
+ pUser.SetTempTag( "LastResourceColor", ourObj.colour );
- // Check if recipe required and not known
- let data = CraftingMap[makeID];
- if( data.recipeID && !TriggerEvent( 4022, "NeedRecipe", pUser, data.recipeID ))
- {
- socket.SysMessage("You must learn that recipe from a scroll.");
- return;
- }
+ // Recipe re-check (just in case)
+ var data = CraftingMap[makeID];
+ if( data && data.recipeID && !TriggerEvent( 4022, "NeedRecipe", pUser, data.recipeID ))
+ {
+ socket.SysMessage( "You must learn that recipe from a scroll." );
+ return;
+ }
- // Pass in the colour of the desired material to use for crafting
- MakeItem( socket, pUser, makeID, ourObj.colour );
- AddToLastTen( pUser, makeID );
- if( GetServerSetting( "ToolUseLimit" ))
+ MakeItem( socket, pUser, makeID, ourObj.colour );
+ AddToLastTen( pUser, makeID );
+
+ if( GetServerSetting( "ToolUseLimit" ))
+ {
+ bItem.usesLeft -= 1;
+ if( bItem.usesLeft == 0 && GetServerSetting( "ToolUseBreak" ))
{
- bItem.usesLeft -= 1;
- if( bItem.usesLeft == 0 && GetServerSetting( "ToolUseBreak" ))
- {
- bItem.Delete();
- socket.SysMessage( GetDictionaryEntry( 10202, socket.language )); // You have worn out your tool!
- // Play sound effect of tool breaking
- }
+ bItem.Delete();
+ socket.SysMessage( GetDictionaryEntry( 10202, socket.language )); // You have worn out your tool!
}
- pUser.StartTimer( gumpDelay, timerID, 4030 );
}
+ pUser.StartTimer( gumpDelay, timerID, tailoringID );
}
}
@@ -544,15 +622,13 @@ function UnravelTarget( socket )
}
// Clothes and leather armor can be unravelled back into cloth and leather
-/** @type { ( tSock: Socket, target: Character | Item | null ) => void } */
+/** @type { ( socket: Socket, ourObj: Character | Item | null ) => void } */
function onCallback2( socket, ourObj )
{
- // Unravel item, get cloth/leather in return
var mChar = socket.currentChar;
if( !ValidateObject( ourObj ) || !ourObj.isItem )
{
- // Targeted object is not an item that can be smelted
mChar.SetTempTag( "prevActionResult", "CANTUNRAVEL" );
mChar.StartTimer( repairDelay, 1, true );
return;
@@ -571,46 +647,40 @@ function onCallback2( socket, ourObj )
var resourceColor = ourObj.colour;
var materialType = TriggerEvent( 2506, "GetItemMaterialType", ourObj, 0 );
+ var resourceID = 0;
+
if( creatorSerial == -1 || entryMadeFrom == 0 || createEntry == null )
{
- // Not a player-crafted item, return 1 resource if item is made of cloth/leather
+ // Not player-crafted; 1 resource if cloth/leather
if( materialType == "cloth" || materialType == "leather" )
- {
resourceAmount = 1;
- }
}
else
{
if( createEntry.avgMinSkill > mChar.skills.tailoring )
{
+ var gumpID = tailoringID + 0xffff;
socket.CloseGump( gumpID, 0 );
mChar.SetTempTag( "prevActionResult", "NOUNRAVELSKILL" );
- mChar.StartTimer( gumpDelay, 1, 4030 );
+ mChar.StartTimer( gumpDelay, 1, tailoringID );
return;
}
- // Loop through resources used to craft item, see how many resources were used
- var resourceID = 0;
+ // Loop resources used to craft item
var resourcesUsed = createEntry.resources;
for( var i = 0; i < resourcesUsed.length; i++ )
{
var resource = resourcesUsed[i];
var amountNeeded = resource[0];
- var colorNeeded = resource[1];
- var resourceIDs = resource[2];
+ var colorNeeded = resource[1]; // unused here but kept for completeness
+ var resourceIDs = resource[2];
- // Loop through list of resourceIDs that were valid for crafting this item, see if ANY
- // were a match for the resource we're trying to return
for( var j = 0; j <= resourceIDs.length; j++ )
{
if( !isNaN( parseInt( resourceIDs[j] )))
{
- // If we found some resource that match up to cloth, or leather, go for it
- var resourceType = TriggerEvent( 2506, "GetResourceType", parseInt( resourceIDs[j] ));
- /*mChar.TextMessage( "MaterialType: " + materialType );
- mChar.TextMessage( "ResourceType: " + resourceType );
- mChar.TextMessage( "resourceID: " + resourceIDs[j] );*/
- if( materialType == resourceType )
+ var resType = TriggerEvent( 2506, "GetResourceType", parseInt( resourceIDs[j] ));
+ if( materialType == resType )
{
maxResourceAmount = amountNeeded;
resourceID = resourceIDs[j];
@@ -619,42 +689,26 @@ function onCallback2( socket, ourObj )
}
}
- // We only really care about the first and primary resource....
if( maxResourceAmount > 0 )
break;
}
if( maxResourceAmount > 1 )
{
- // Calculate amount of resources returned based on player's mining skill, item's wear and tear,
- // and amount of resources that went into making the item in the first place
- if( ourObj.health >= 1 || ourObj.usesLeft >= 1 )
- {
- var healthPercentage = 0;
- if( ourObj.health >= 1 )
- {
- healthPercentage = Math.floor( ( ourObj.health * 100 ) / ourObj.maxhp );
- }
+ var healthPercentage = 0;
+ if( ourObj.health >= 1 )
+ healthPercentage = Math.floor( ( ourObj.health * 100 ) / ourObj.maxhp );
- var usesPercentage = 0;
- if( ourObj.usesLeft >= 1 )
- {
- usesPercentage = Math.floor( ( ourObj.usesLeft * 100 ) / ourObj.maxUses );
- }
+ var usesPercentage = 0;
+ if( ourObj.usesLeft >= 1 )
+ usesPercentage = Math.floor( ( ourObj.usesLeft * 100 ) / ourObj.maxUses );
- var itemPercentage = usesPercentage > 0 ? Math.min( healthPercentage, usesPercentage ) : healthPercentage;
+ var itemPercentage = usesPercentage > 0 ? Math.min( healthPercentage, usesPercentage ) : healthPercentage;
- // Reduce amount of resources returned based on state of object's wear and tear
- resourceAmount = Math.floor( ( maxResourceAmount * itemPercentage ) / 100 );
- }
-
- // Halve the amount of resources returned
+ resourceAmount = Math.floor( ( maxResourceAmount * itemPercentage ) / 100 );
resourceAmount = Math.max( Math.floor( resourceAmount / 2 ), 1 );
- // Fetch player's Tailoring skill
var playerSkill = mChar.skills.tailoring;
-
- // Based on player's Tailoring skill, return between 1 to maxResourceAmount
resourceAmount = Math.min( Math.max( Math.floor( resourceAmount * ( playerSkill / 1000 )), 1 ), resourceAmount );
}
else
@@ -665,76 +719,67 @@ function onCallback2( socket, ourObj )
if( resourceAmount == 0 || resourceID == 0 )
{
- // Targeted object is not an item that can be unravelled
mChar.SetTempTag( "prevActionResult", "CANTUNRAVEL" );
- mChar.StartTimer( repairDelay, 1, 4030 );
+ mChar.StartTimer( repairDelay, 1, tailoringID );
return;
}
- // Delete the unravelled item
+ // Delete unravelled item
ourObj.Delete();
- // Run a generic skill check to give player a chance to increase their tailoring skill
- mChar.CheckSkill( 34, 0, mChar.skillCaps.tailoring );
+ // Generic skill check
+ mChar.CheckSkill( tailoringSkillID, 0, mChar.skillCaps.tailoring );
- // Determine the actual resource item to add to player's backpack
- // We'll default to one specific resource per material type
+ // Determine returned resource
var itemToAdd = "";
switch( materialType )
{
- case "cloth":
- itemToAdd = "0x1766"; // cut cloth
- break;
- case "leather":
- itemToAdd = "0x1068"; // cut leather
- break;
- default:
- break;
+ case "cloth": itemToAdd = "0x1766"; break; // cut cloth
+ case "leather": itemToAdd = "0x1068"; break; // cut leather
+ default: break;
}
+
var newResource = CreateDFNItem( socket, mChar, itemToAdd, resourceAmount, "ITEM", true, resourceColor );
mChar.SetTempTag( "resourceFromUnravelling", resourceAmount );
mChar.SetTempTag( "prevActionResult", "UNRAVELSUCCESS" );
- mChar.StartTimer( gumpDelay, 1, 4030 );
+ mChar.StartTimer( gumpDelay, 1, tailoringID );
}
function AddToLastTen( pUser, makeID )
{
- // Append makeID to last ten list
- var raw = pUser.GetTag( "LastTenTailoring" ) || "";
+ var raw = pUser.GetTempTag( "LastTenTailoring" ) || "";
var list = raw.split( "," );
- // Remove if already in list
for( var i = 0; i < list.length; i++ )
{
- if( parseInt( list[i]) == makeID )
+ if( parseInt( list[i] ) == makeID )
{
- list.splice(i, 1);
+ list.splice( i, 1 );
break;
}
}
- // Add to front (no unshift in SpiderMonkey 1.8)
- var newList = [makeID];
- for( var i = 0; i < list.length && newList.length < 10; i++ )
+ var newList = [ makeID ];
+ for( var j = 0; j < list.length && newList.length < 10; j++ )
{
- var entry = parseInt( list[i] );
+ var entry = parseInt( list[j] );
if( !isNaN( entry ) && entry > 0 )
newList.push( entry );
}
- pUser.SetTag( "LastTenTailoring", newList.join( "," ));
+ pUser.SetTempTag( "LastTenTailoring", newList.join( "," ));
}
function HasLearnedRecipe( pUser, recipeID )
{
var myData = TriggerEvent( 4022, "ReadRecipeID", pUser );
- if( !myData || myData.length == 0)
+ if( !myData || myData.length == 0 )
return false;
- for( let i = 0; i < myData.length; i++ )
+ for( var i = 0; i < myData.length; i++ )
{
- let data = myData[i].split( "," );
+ var data = myData[i].split( "," );
if( data[0] == recipeID )
return true;
}
@@ -743,11 +788,9 @@ function HasLearnedRecipe( pUser, recipeID )
function eraOK( entry )
{
- // Optional per-entry gates. Accept either/both. Strings like "lbr","aos","ml","tol".
- // If not present, the entry is valid for all eras.
if( entry.minEra && coreShardEra < EraStringToNum( entry.minEra ))
return false;
if( entry.maxEra && coreShardEra > EraStringToNum( entry.maxEra ))
return false;
return true;
-}
+}
\ No newline at end of file
diff --git a/data/js/skill/craft/tinkering.js b/data/js/skill/craft/tinkering.js
index dfb4b3623..c1ab673e5 100644
--- a/data/js/skill/craft/tinkering.js
+++ b/data/js/skill/craft/tinkering.js
@@ -1,538 +1,584 @@
///
// @ts-check
-const LabelHue = 0x480; // Color of the text.
-const LabelColor = 0x7FFF; // Second Color of text.
-const scriptID = 4032; // Use this to tell the gump what script to close.
-const gumpDelay = 2000; // Timer for the gump to reapear after crafting.
-const itemDetailsScriptID = 4026;
-const craftGumpID = 4027;
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// The section below is the tables for each page.
-// All you have to do is add the item to your dictionary
-// and then list the dictionary number in the right page and it will
-// add it to the crafting gump.
-///////////////////////////////////////////////////////////////////////////////////////////
-
-const myPage = [
+const textHue = 0x480; // Color of the text.
+const tinkeringID = 4032; // Script ID for this tinkering gump
+const gumpDelay = 2000; // Delay (ms) before gump reappears after crafting
+const itemDetailsScriptID = 4026; // Generic item details gump
+const craftGumpID = 4027; // Shared crafting menu frame
+const itemsPerPage = 10; // Items per subpage
+const displayUnlearnedRecipes = true; // For future recipe use
+const coreShardEra = EraStringToNum( GetServerSetting( "CoreShardEra" ));
+const tinkeringSkillID = 37; // Index of "tinkering" in ItemDetailGump.skillNames[]
+
+// o--------------------------------------------------------------------------o
+// | TinkeringMap |
+// o--------------------------------------------------------------------------o
+// | Keyed by makeID (create entry ID). |
+// | Each entry: |
+// | dictID - dictionary entry for row text (11801..11982) |
+// | page - main category page (1..9) |
+// | timerID - which page timer should reopen |
+// | skill - skill used (default: tinkeringSkillID) |
+// | recipeID? - optional recipe ID |
+// | minEra?, maxEra? - optional era gating |
+// | harvest?[] - optional dictIDs for MATERIALS list |
+// | harvestNames?[] - optional custom material names |
+// | requiresGemTarget? - jewelry requiring manual gem selection |
+// o--------------------------------------------------------------------------o
+
+const TinkeringMap = {
// Page 1 - Wooden Items
- [ 11801, 11802, 11803, 11804, 11805 ],
+ 274: { dictID: 11801, page: 1, timerID: 1, harvest: [ 10014 ] }, // Axle
+ 273: { dictID: 11802, page: 1, timerID: 1, harvest: [ 10014 ] }, // Clock Frame
+ 270: { dictID: 11803, page: 1, timerID: 1, harvest: [ 10014 ] }, // Jointing Plane
+ 271: { dictID: 11804, page: 1, timerID: 1, harvest: [ 10014 ] }, // Moulding Plane
+ 272: { dictID: 11805, page: 1, timerID: 1, harvest: [ 10014 ] }, // Smoothing Plane
// Page 2 - Tools
- [ 11820, 11821, 11822, 11823, 11824, 11825, 11826, 11827, 11828, 11829, 11830, 11831, 11832, 11833, 11834, 11835, 11836, 11837, 11838 ],
+ 218: { dictID: 11820, page: 2, timerID: 2, harvest: [ 10015 ] }, // Dovetail Saw
+ 215: { dictID: 11821, page: 2, timerID: 2, harvest: [ 10015 ] }, // Draw Knife
+ 252: { dictID: 11822, page: 2, timerID: 2, harvest: [ 10015 ] }, // Froe
+ 255: { dictID: 11823, page: 2, timerID: 2, harvest: [ 10015 ] }, // Hammer
+ 214: { dictID: 11824, page: 2, timerID: 2, harvest: [ 10015 ] }, // Hatchet
+ 258: { dictID: 11825, page: 2, timerID: 2, harvest: [ 10015 ] }, // Inshave
+ 260: { dictID: 11826, page: 2, timerID: 2, harvest: [ 10015 ] }, // Lockpick
+ 211: { dictID: 11827, page: 2, timerID: 2, harvest: [ 10015 ] }, // Mortar and Pestle
+ 259: { dictID: 11828, page: 2, timerID: 2, harvest: [ 10015 ] }, // Pick Axe
+ 217: { dictID: 11829, page: 2, timerID: 2, harvest: [ 10015 ] }, // Saw
+ 210: { dictID: 11830, page: 2, timerID: 2, harvest: [ 10015 ] }, // Scissors
+ 212: { dictID: 11831, page: 2, timerID: 2, harvest: [ 10015 ] }, // Scorp
+ 216: { dictID: 11832, page: 2, timerID: 2, harvest: [ 10015 ] }, // Sewing Kit
+ 254: { dictID: 11833, page: 2, timerID: 2, harvest: [ 10015 ] }, // Shovel
+ 257: { dictID: 11834, page: 2, timerID: 2, harvest: [ 10015 ] }, // Sledge Hammer
+ 256: { dictID: 11835, page: 2, timerID: 2, harvest: [ 10015 ] }, // Smith's Hammer
+ 253: { dictID: 11836, page: 2, timerID: 2, harvest: [ 10015 ] }, // Tongs
+ 213: { dictID: 11837, page: 2, timerID: 2, harvest: [ 10015 ] }, // Tool Kit (Tinker's tools)
+ 284: { dictID: 11838, page: 2, timerID: 2, harvest: [ 10015 ] }, // Fletcher's Tools
// Page 3 - Parts
- [ 11860, 11861, 11862, 11863, 11864, 11865, 11866 ],
+ 224: { dictID: 11860, page: 3, timerID: 3, harvest: [ 10015 ] }, // Barrel Hoops
+ 221: { dictID: 11861, page: 3, timerID: 3, harvest: [ 10015 ] }, // Barrel Tap
+ 220: { dictID: 11862, page: 3, timerID: 3, harvest: [ 10015 ] }, // Clock parts
+ 219: { dictID: 11863, page: 3, timerID: 3, harvest: [ 10015 ] }, // Gears
+ 225: { dictID: 11864, page: 3, timerID: 3, harvest: [ 10015 ] }, // Hinge
+ 223: { dictID: 11865, page: 3, timerID: 3, harvest: [ 10015 ] }, // Sextant parts
+ 222: { dictID: 11866, page: 3, timerID: 3, harvest: [ 10015 ] }, // Springs
// Page 4 - Utensils
- [ 11880, 11881, 11882, 11883, 11884, 11885, 11886, 11887, 11888, 11889, 11890, 11891 ],
-
- // Page 5 - Jewelry
- [ 11900, 11901, 11902, 11903, 11904, 11905 ],
-
- // Page 6 - Miscellaneuos
- [ 11920, 11921, 11922, 11923, 11924, 11925, 11926, 11927 ],
+ 226: { dictID: 11880, page: 4, timerID: 4, harvest: [ 10015 ] }, // Butcher Knife
+ 232: { dictID: 11881, page: 4, timerID: 4, harvest: [ 10015 ] }, // Cleaver
+ 230: { dictID: 11882, page: 4, timerID: 4, harvest: [ 10015 ] }, // Fork
+ 231: { dictID: 11883, page: 4, timerID: 4, harvest: [ 10015 ] }, // Fork
+ 235: { dictID: 11884, page: 4, timerID: 4, harvest: [ 10015 ] }, // Goblet
+ 233: { dictID: 11885, page: 4, timerID: 4, harvest: [ 10015 ] }, // Knife
+ 234: { dictID: 11886, page: 4, timerID: 4, harvest: [ 10015 ] }, // Knife
+ 236: { dictID: 11887, page: 4, timerID: 4, harvest: [ 10015 ] }, // Pewter Mug
+ 229: { dictID: 11888, page: 4, timerID: 4, harvest: [ 10015 ] }, // Plate
+ 237: { dictID: 11889, page: 4, timerID: 4, harvest: [ 10015 ] }, // Skinning Knife
+ 227: { dictID: 11890, page: 4, timerID: 4, harvest: [ 10015 ] }, // Spoon
+ 228: { dictID: 11891, page: 4, timerID: 4, harvest: [ 10015 ] }, // Spoon
+
+ // Page 5 - Jewelry (gem-targeted)
+ 243: { dictID: 11900, page: 5, timerID: 5, requiresGemTarget: true, harvest: [ 10015, 12005 ] }, // Bracelet
+ 241: { dictID: 11901, page: 5, timerID: 5, requiresGemTarget: true, harvest: [ 10015, 12005 ] }, // Earrings
+ 239: { dictID: 11902, page: 5, timerID: 5, requiresGemTarget: true, harvest: [ 10015, 12005 ] }, // Necklace (Golden beads)
+ 240: { dictID: 11903, page: 5, timerID: 5, requiresGemTarget: true, harvest: [ 10015, 12005 ] }, // Necklace (Silver beads)
+ 242: { dictID: 11904, page: 5, timerID: 5, requiresGemTarget: true, harvest: [ 10015, 12005 ] }, // Necklace (Round)
+ 238: { dictID: 11905, page: 5, timerID: 5, requiresGemTarget: true, harvest: [ 10015, 12006 ] }, // Weddingband (newbiefied)
+
+ // Page 6 - Miscellaneous
+ 245: { dictID: 11920, page: 6, timerID: 6, harvest: [ 10015, 12000 ] }, // Candelabra (also appears in Candles page in old script)
+ 248: { dictID: 11921, page: 6, timerID: 6, harvest: [ 10015 ] }, // Globe
+ 251: { dictID: 11922, page: 6, timerID: 6, harvest: [ 10015 ] }, // Heating stand
+ 247: { dictID: 11923, page: 6, timerID: 6, harvest: [ 10015 ] }, // Iron Key
+ 244: { dictID: 11924, page: 6, timerID: 6, harvest: [ 10015 ] }, // Keyring
+ 250: { dictID: 11925, page: 6, timerID: 6, harvest: [ 10015 ] }, // Lantern
+ 246: { dictID: 11926, page: 6, timerID: 6, harvest: [ 10015 ] }, // Scales
+ 249: { dictID: 11927, page: 6, timerID: 6, harvest: [ 10015 ] }, // Spy glass
// Page 7 - Multi-Component Items
- [ 11940, 11941, 11942, 11943, 11946, 11947, 11948 ],
+ 275: { dictID: 11940, page: 7, timerID: 7, harvest: [ 11801, 11863 ] }, // Axle and Gears
+ 276: { dictID: 11941, page: 7, timerID: 7, harvest: [ 11802, 11862 ] }, // Clock
+ 277: { dictID: 11942, page: 7, timerID: 7, harvest: [ 11802, 11862 ] }, // Clock
+ 278: { dictID: 11943, page: 7, timerID: 7, harvest: [ 11801, 11863 ] }, // Clock Parts
+ 279: { dictID: 11944, page: 7, timerID: 7, harvest: [ 10634 ] }, // Locked Box
+ 280: { dictID: 11945, page: 7, timerID: 7, harvest: [ 10638 ] }, // Locked Chest
+ 281: { dictID: 11946, page: 7, timerID: 7, harvest: [ 10642, 11861, 10612, 10928 ] }, // Potion Keg
+ 282: { dictID: 11947, page: 7, timerID: 7, harvest: [ 11948 ] }, // Sextant
+ 283: { dictID: 11948, page: 7, timerID: 7, harvest: [ 11801, 11863 ] }, // Sextant Parts
// Page 8 - Candles
- [ 11960, 11961, 11962, 11963, 11964, 11965, 11966, 11967 ],
+ // NOTE: in the original script, Candelabra (245) appears here too.
+ // To avoid conflicts (map is keyed by makeID), this page uses the
+ // actual candle-specific items only.
+ 310: { dictID: 11961, page: 8, timerID: 8, harvest: [ 10015, 12000 ] }, // Standing Candelabra
+ 315: { dictID: 11962, page: 8, timerID: 8, harvest: [ 10015, 12000 ] }, // Regular Candle
+ 312: { dictID: 11963, page: 8, timerID: 8, harvest: [ 12000 ] }, // Round Candle
+ 316: { dictID: 11964, page: 8, timerID: 8, harvest: [ 12000, 12004 ] }, // Skull with Candle
+ 314: { dictID: 11965, page: 8, timerID: 8, harvest: [ 12000 ] }, // Small Candle
+ 311: { dictID: 11966, page: 8, timerID: 8, harvest: [ 10015, 12000 ] }, // Tall Candle
+ 313: { dictID: 11967, page: 8, timerID: 8, harvest: [ 12000 ] }, // Thick Candle
// Page 9 - Traps
- [ 11980, 11981, 11982 ]
-];
+ 261: { dictID: 11980, page: 9, timerID: 9, harvest: [ 10015, 12001 ] }, // Dart Trap
+ 263: { dictID: 11981, page: 9, timerID: 9, harvest: [ 10015, 12003 ] }, // Explosion Trap
+ 262: { dictID: 11982, page: 9, timerID: 9, harvest: [ 10015, 12002 ] } // Poison Trap
+};
+
+// Fill in defaults (skill, etc)
+(function initTinkeringMap()
+{
+ for( var key in TinkeringMap )
+ {
+ if( !TinkeringMap.hasOwnProperty( key ))
+ continue;
+
+ var entry = TinkeringMap[key];
+ if( entry.skill === undefined )
+ entry.skill = tinkeringSkillID;
+
+ // In future you can do e.g.:
+ // entry.harvest = [ woodDictID, ingotDictID ];
+ // entry.harvestNames = [ "Wood", "Ingots" ];
+ }
+})();
+
+// o--------------------------------------------------------------------------o
+// | PageX() - build a page of tinkering items |
+// o--------------------------------------------------------------------------o
+/** @type { ( socket: Socket, pUser: Character, pageNum: number ) => void } */
function PageX( socket, pUser, pageNum )
{
- // Pages 1 - 9
- var myGump = new Gump;
- pUser.SetTempTag( "page", pageNum );
- TriggerEvent( craftGumpID, "CraftingGumpMenu", myGump, socket );
- for ( var i = 0; i < myPage[pageNum - 1].length; i++ )
+ if( !socket || !ValidateObject( pUser ))
+ return;
+
+ var pageItems;
+
+ // Last Ten page (if you wire a tab to 999 later)
+ if( pageNum == 999 )
{
- var index = i % 10;
- if ( index == 0 )
+ var lastTenRaw = pUser.GetTempTag( "LastTenTinkering" ) || "";
+ var split = lastTenRaw.split( "," );
+ pageItems = [];
+
+ for( var i = 0; i < split.length; i++ )
{
- if ( i > 0 )
+ var val = parseInt( split[i] );
+ if( !isNaN( val ))
+ pageItems.push( val );
+ }
+ }
+ else
+ {
+ // Collect all makeIDs for this page
+ var makeIDs = [];
+ for( var key in TinkeringMap )
+ {
+ if( !TinkeringMap.hasOwnProperty( key ))
+ continue;
+
+ var makeID = parseInt( key );
+ var data = TinkeringMap[makeID];
+ if( !data || data.page != pageNum )
+ continue;
+
+ makeIDs.push( makeID );
+ }
+
+ // Sort by dictID so order matches dictionary sequence
+ makeIDs.sort( function( a, b )
+ {
+ var ea = TinkeringMap[a];
+ var eb = TinkeringMap[b];
+ if( ea && eb )
+ return ( ea.dictID || 0 ) - ( eb.dictID || 0 );
+ return a - b;
+ });
+
+ // Era / recipe filtering
+ pageItems = [];
+ for( var k = 0; k < makeIDs.length; k++ )
+ {
+ var id = makeIDs[k];
+ var data2 = TinkeringMap[id];
+ if( !data2 )
+ continue;
+
+ var needsRecipe = data2.recipeID;
+ var showAll = displayUnlearnedRecipes;
+
+ if( eraOK( data2 ) && ( !needsRecipe || showAll || HasLearnedRecipe( pUser, needsRecipe )) )
+ pageItems.push( id );
+ }
+
+ // Fallback: if no items on this page and it's not page 1, go to page 1
+ if( pageItems.length == 0 && pageNum != 1 )
+ {
+ pageNum = 1;
+
+ makeIDs = [];
+ for( var key2 in TinkeringMap )
+ {
+ if( !TinkeringMap.hasOwnProperty( key2 ))
+ continue;
+
+ var mid2 = parseInt( key2 );
+ var d3 = TinkeringMap[mid2];
+ if( !d3 || d3.page != 1 )
+ continue;
+
+ makeIDs.push( mid2 );
+ }
+
+ makeIDs.sort( function( a, b )
+ {
+ var ea2 = TinkeringMap[a];
+ var eb2 = TinkeringMap[b];
+ if( ea2 && eb2 )
+ return ( ea2.dictID || 0 ) - ( eb2.dictID || 0 );
+ return a - b;
+ });
+
+ pageItems = [];
+ for( var m = 0; m < makeIDs.length; m++ )
{
- myGump.AddButton( 370, 260, 4005, 4007, 0, ( i / 10 ) + 1, 0 );
- myGump.AddHTMLGump( 405, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10100, socket.language ) + "" );// NEXT PAGE
+ var id2 = makeIDs[m];
+ var data4 = TinkeringMap[id2];
+ if( !data4 )
+ continue;
+
+ var needsRecipe2 = data4.recipeID;
+ var showAll2 = displayUnlearnedRecipes;
+
+ if( eraOK( data4 ) && ( !needsRecipe2 || showAll2 || HasLearnedRecipe( pUser, needsRecipe2 )) )
+ pageItems.push( id2 );
}
+ }
+ }
+
+ // Subpage handling (future-proof; currently you effectively have 1 or 2 subpages per category)
+ var subPage = pUser.GetTempTag( "subPage" );
+ var totalSubPages = Math.ceil( pageItems.length / itemsPerPage );
+
+ if( totalSubPages < 1 )
+ totalSubPages = 1;
+ if( subPage < 1 )
+ subPage = 1;
+ if( subPage > totalSubPages )
+ subPage = totalSubPages;
+
+ pUser.SetTempTag( "page", pageNum );
+ pUser.SetTempTag( "subPage", subPage );
- myGump.AddPage( ( i / 10 ) + 1 );
+ var startIndex = ( subPage - 1 ) * itemsPerPage;
+ var endIndex = Math.min( startIndex + itemsPerPage, pageItems.length );
- if ( i > 0 )
+ if( startIndex >= pageItems.length )
+ {
+ subPage = 1;
+ startIndex = 0;
+ endIndex = Math.min( itemsPerPage, pageItems.length );
+ pUser.SetTempTag( "subPage", subPage );
+ }
+
+ var tinkGump = new Gump;
+ TriggerEvent( craftGumpID, "CraftingGumpMenu", tinkGump, socket );
+ tinkGump.AddPage( 1 );
+
+ for( var j = startIndex; j < endIndex; j++ )
+ {
+ var index = j - startIndex;
+ var makeID = pageItems[j];
+ var entryText;
+ var buttonID = makeID; // use makeID directly as buttonID
+
+ var data5 = TinkeringMap[makeID];
+
+ if( !data5 )
+ {
+ entryText = "[Missing MakeID: " + makeID + "]";
+ }
+ else
+ {
+ if( data5.customName )
{
- myGump.AddButton( 220, 260, 4014, 4015, 0, i / 10, 0 );
- myGump.AddHTMLGump( 255, 263, 100, 18, 0, 0, "" + GetDictionaryEntry( 10101, socket.language ) + "" );// PREV PAGE
+ entryText = data5.customName;
+ }
+ else if( data5.dictID )
+ {
+ entryText = GetDictionaryEntry( data5.dictID, socket.language );
+ if( !entryText || entryText === "" )
+ entryText = "[Missing EntryID: " + data5.dictID + "]";
+ }
+ else
+ {
+ entryText = "[Unnamed Item: " + makeID + "]";
}
}
- myGump.AddButton( 220, 60 + ( index * 20 ), 4005, 4007, 1, 0, ( 100 * pageNum ) + i );
- myGump.AddText( 255, 60 + ( index * 20 ), LabelHue, GetDictionaryEntry( myPage[pageNum - 1][i], socket.language ) );
+ // Craft button
+ tinkGump.AddButton( 220, 60 + ( index * 20 ), 4005, 4007, 1, 0, buttonID );
+ tinkGump.AddText( 255, 60 + ( index * 20 ), textHue, entryText );
- myGump.AddButton( 480, 60 + ( index * 20 ), 4011, 4012, 1, 0, 2000 + ( 100 * pageNum ) + i );
+ // Detail button: 20000 + makeID
+ tinkGump.AddButton( 480, 60 + ( index * 20 ), 4011, 4012, 1, 0, 20000 + buttonID );
}
- myGump.Send( socket );
- myGump.Free();
+
+ // Prev subpage
+ if( subPage > 1 )
+ {
+ tinkGump.AddButton( 220, 260, 4014, 4015, 1, 0, 8000 + ( subPage - 1 ));
+ tinkGump.AddHTMLGump( 255, 263, 100, 18, false, false,
+ "" + GetDictionaryEntry( 10101, socket.language ) + "" ); // PREV PAGE
+ }
+
+ // Next subpage
+ if( subPage < totalSubPages )
+ {
+ tinkGump.AddButton( 370, 260, 4005, 4007, 1, 0, 9000 + ( subPage + 1 ));
+ tinkGump.AddHTMLGump( 405, 263, 100, 18, false, false,
+ "" + GetDictionaryEntry( 10100, socket.language ) + "" ); // NEXT PAGE
+ }
+
+ tinkGump.Send( socket );
+ tinkGump.Free();
}
-/** @type { ( tObject: BaseObject, timerId: number ) => void } */
+/** @type { ( pUser: Character, timerID: number ) => void } */
function onTimer( pUser, timerID )
{
if( !ValidateObject( pUser ))
return;
- var socket = pUser.socket;
+ var pSocket = pUser.socket;
+ if( pSocket == null )
+ return;
- switch ( timerID )
+ if( timerID >= 1 && timerID <= 9 )
{
- case 1: // Page 1 - Wooden Items
- case 2: // Page 2 - Tools
- case 3: // Page 3 - Parts
- case 4: // Page 4 - Utensils
- case 5: // Page 5 - Jewelry
- case 6: // Page 6 - Miscellaneous
- case 7: // Page 7 - Multi-Component Items
- case 8: // Page 8 - Candles
- case 9: // Page 9 - Traps
- PageX( socket, pUser, timerID );
- break;
+ PageX( pSocket, pUser, timerID );
+ }
+ else if( timerID == 999 )
+ {
+ PageX( pSocket, pUser, 999 );
}
}
-/** @type { ( myObj: Socket, pressed: number, gump: GumpData ) => void } */
-function onGumpPress( pSock, pButton, gumpData )
+/** @type { ( socket: Socket, pButton: number, gumpData: GumpData ) => void } */
+function onGumpPress( socket, pButton, gumpData )
{
- var pUser = pSock.currentChar;
+ if( socket == null )
+ return;
- // Don't continue if character is invalid, or worse... dead!
+ var pUser = socket.currentChar;
if( !ValidateObject( pUser ) || pUser.dead )
return;
// Don't continue if player no longer has access to the crafting tool
- var bItem = pSock.tempObj;
- if( !ValidateObject( bItem ) || !pUser.InRange( bItem, 3 ))
+ var tool = socket.tempObj;
+ if( !ValidateObject( tool ) || !pUser.InRange( tool, 3 ))
{
- pSock.SysMessage( GetDictionaryEntry( 461, pSock.language )); // You are too far away.
+ socket.SysMessage( GetDictionaryEntry( 461, socket.language )); // You are too far away.
return;
}
- if( bItem.movable == 3 )
+ if( tool.movable == 3 )
{
- pSock.SysMessage( GetDictionaryEntry( 6031, pSock.language )); // Locked down resources cannot be used!
+ socket.SysMessage( GetDictionaryEntry( 6031, socket.language )); // Locked down resources cannot be used!
return;
}
- var iPackOwner = GetPackOwner( bItem, 0 );
- if( ValidateObject( iPackOwner )) // Is the item in a backpack?
+ var packOwner = GetPackOwner( tool, 0 );
+ if( ValidateObject( packOwner ))
{
- if( iPackOwner.serial != pUser.serial ) // And if so does the pack belong to the user?
+ if( packOwner.serial != pUser.serial )
{
- pSock.SysMessage( GetDictionaryEntry( 6032, pSock.language )); // That resource is in someone else's backpack!
+ socket.SysMessage( GetDictionaryEntry( 6032, socket.language )); // That resource is in someone else's backpack!
return;
}
}
else
{
- pSock.SysMessage( GetDictionaryEntry( 6022, pSock.language )); // This has to be in your backpack before you can use it.
+ socket.SysMessage( GetDictionaryEntry( 6022, socket.language )); // This has to be in your backpack before you can use it.
return;
}
- var gumpID = scriptID + 0xffff;
- var makeID = 0;
- var itemDetailsID = 0;
- var timerID = 0;
+ var gumpID = tinkeringID + 0xffff;
- if(( pButton >= 100 && pButton <= 950 ) || pButton == 5000 )
+ // Subpage back / forward
+ if( pButton >= 8001 && pButton < 9000 )
{
- if( pButton == 5000 )
- {
- // Make Last button
- pButton = pUser.GetTempTag( "MAKELAST" );
- }
- else
- {
- pUser.SetTempTag( "MAKELAST", pButton );
- }
+ var subPage = pButton - 8000;
+ var pageNum = pUser.GetTempTag( "page" );
+ pUser.SetTempTag( "subPage", subPage );
+ PageX( socket, pUser, pageNum );
+ return;
}
- switch ( pButton )
+ if( pButton >= 9001 && pButton < 10000 )
{
- case 0: // Abort and do nothing
- pUser.SetTempTag( "MAKELAST", null );
- pUser.SetTempTag( "CRAFT", null )
- pSock.CloseGump( gumpID, 0 );
- break;
- case 1: // Page 1 - Wooden Items
- case 2: // Page 2 - Tools
- case 3: // Page 3 - Parts
- case 4: // Page 4 - Utensils
- case 5: // Page 5 - Jewelry
- case 6: // Page 6 - Miscellaneous
- case 7: // Page 7 - Multi-Component Items
- case 8: // Page 8 - Candles
- case 9: // Page 9 - Traps
- pSock.CloseGump( gumpID, 0 );
- PageX( pSock, pUser, pButton );
- break;
- // Make Items
- // Page 1 - Wooden Items
- case 100: // Axle
- makeID = 274; timerID = 1; break;
- case 101: // Clock Frame
- makeID = 273; timerID = 1; break;
- case 102: // Jointing Plane
- makeID = 270; timerID = 1; break;
- case 103: // Moulding Plane
- makeID = 271; timerID = 1; break;
- case 104: // Smoothing Plane
- makeID = 272; timerID = 1; break;
- // Page 2 - Tools
- case 200: // Dovetail Saw
- makeID = 218; timerID = 2; break;
- case 201: // Draw Knife
- makeID = 215; timerID = 2; break;
- case 202: // Froe
- makeID = 252; timerID = 2; break;
- case 203: // Hammer
- makeID = 255; timerID = 2; break;
- case 204: // Hatchet
- makeID = 214; timerID = 2; break;
- case 205: // Inshave
- makeID = 258; timerID = 2; break;
- case 206: // Lockpick
- makeID = 260; timerID = 2; break;
- case 207: // Mortar and Pestle
- makeID = 211; timerID = 2; break;
- case 208: // Pick Axe
- makeID = 259; timerID = 2; break;
- case 209: // Saw
- makeID = 217; timerID = 2; break;
- case 210: // Scissors
- makeID = 210; timerID = 2; break;
- case 211: // Scorp
- makeID = 212; timerID = 2; break;
- case 212: // Sewing Kit
- makeID = 216; timerID = 2; break;
- case 213: // Shovel
- makeID = 254; timerID = 2; break;
- case 214: // Sledge Hammer
- makeID = 257; timerID = 2; break;
- case 215: // Smith's Hammer
- makeID = 256; timerID = 2; break;
- case 216: // Tongs
- makeID = 253; timerID = 2; break;
- case 217: // Tool Kit (Tinker's tools)
- makeID = 213; timerID = 2; break;
- case 218: // Fletcher's Tools
- makeID = 284; timerID = 2; break;
- // Page 3 - Parts
- case 300: // Barrel Hoops
- makeID = 224; timerID = 3; break;
- case 301: // Barrel Tap
- makeID = 221; timerID = 3; break;
- case 302: // Clock parts
- makeID = 220; timerID = 3; break;
- case 303: // Gears
- makeID = 219; timerID = 3; break;
- case 304: // Hinge
- makeID = 225; timerID = 3; break;
- case 305: // Sextant parts
- makeID = 223; timerID = 3; break;
- case 306: // Springs
- makeID = 222; timerID = 3; break;
- // Page 4 - Utensils
- case 400: // Butcher Knife
- makeID = 226; timerID = 4; break;
- case 401: // Cleaver
- makeID = 232; timerID = 4; break;
- case 402: // Fork
- makeID = 230; timerID = 4; break;
- case 403: // Fork
- makeID = 231; timerID = 4; break;
- case 404: // Goblet
- makeID = 235; timerID = 4; break;
- case 405: // Knife
- makeID = 233; timerID = 4; break;
- case 406: // Knife
- makeID = 234; timerID = 4; break;
- case 407: // Pewter Mug
- makeID = 236; timerID = 4; break;
- case 408: // Plate
- makeID = 229; timerID = 4; break;
- case 409: // Skinning Knife
- makeID = 237; timerID = 4; break;
- case 410: // Spoon
- makeID = 227; timerID = 4; break;
- case 411: // Spoon
- makeID = 228; timerID = 4; break;
- // Page 5 - Jewelry
- case 500: // Bracelet
- makeID = 243; timerID = 5; break;
- case 501: // Earrings
- makeID = 241; timerID = 5; break;
- case 502: // Necklage (Golden beads)
- makeID = 239; timerID = 5; break;
- case 503: // Necklace (Silver beads)
- makeID = 240; timerID = 5; break;
- case 504: // Necklace (Round)
- makeID = 242; timerID = 5; break;
- case 505: // Weddingband (newbiefied)
- makeID = 238; timerID = 5; break;
- // Page 6 - Miscellaneous
- case 600: // Candelabra
- makeID = 245; timerID = 6; break;
- case 601: // Globe
- makeID = 248; timerID = 6; break;
- case 602: // Heating stand
- makeID = 251; timerID = 6; break;
- case 603: // Iron Key
- makeID = 247; timerID = 6; break;
- case 604: // Keyring
- makeID = 244; timerID = 6; break;
- case 605: // Lantern
- makeID = 250; timerID = 6; break;
- case 606: // Scales
- makeID = 246; timerID = 6; break;
- case 607: // Spy glass
- makeID = 249; timerID = 6; break;
- // Page 7 - Multi-Component Items
- case 700: // Axle and Gears
- makeID = 275; timerID = 7; break;
- case 701: // Clock
- makeID = 276; timerID = 7; break;
- case 702: // Clock
- makeID = 277; timerID = 7; break;
- case 703: // Clock Parts
- makeID = 278; timerID = 7; break;
- case 704: // Potion Keg
- makeID = 281; timerID = 7; break;
- case 705: // Sextant
- makeID = 282; timerID = 7; break;
- case 706: // Sextant Parts
- makeID = 283; timerID = 7; break;
- // Page 8 - Candles
- case 800: // Candelabra
- makeID = 245; timerID = 8; break;
- case 801: // Standing Candelabra
- makeID = 310; timerID = 8; break;
- case 802: // Regular Candle
- makeID = 315; timerID = 8; break;
- case 803: // Round Candle
- makeID = 312; timerID = 8; break;
- case 804: // Skull with Candle
- makeID = 316; timerID = 8; break;
- case 805: // Small Candle
- makeID = 314; timerID = 8; break;
- case 806: // Tall Candle
- makeID = 311; timerID = 8; break;
- case 807: // Thick Candle
- makeID = 313; timerID = 8; break;
- // Page 9 - Traps
- case 900: // Dart Trap
- makeID = 261; timerID = 9; break;
- case 901: // Explosion Trap
- makeID = 263; timerID = 9; break;
- case 902: // Poison Trap
- makeID = 262; timerID = 9; break;
- // Show Item Details
- case 2100: // Axle
- itemDetailsID = 274; break;
- case 2101: // Clock Frame
- itemDetailsID = 273; break;
- case 2102: // Jointing Plane
- itemDetailsID = 270; break;
- case 2103: // Moulding Plane
- itemDetailsID = 271; break;
- case 2104: // Smoothing Plane
- itemDetailsID = 272; break;
- // Page 2 - Tools
- case 2200: // Dovetail Saw
- itemDetailsID = 218; break;
- case 2201: // Draw Knife
- itemDetailsID = 215; break;
- case 2202: // Froe
- itemDetailsID = 252; break;
- case 2203: // Hammer
- itemDetailsID = 255; break;
- case 2204: // Hatchet
- itemDetailsID = 214; break;
- case 2205: // Inshave
- itemDetailsID = 258; break;
- case 2206: // Lockpick
- itemDetailsID = 260; break;
- case 2207: // Mortar and Pestle
- itemDetailsID = 211; break;
- case 2208: // Pick Axe
- itemDetailsID = 259; break;
- case 2209: // Saw
- itemDetailsID = 217; break;
- case 2210: // Scissors
- itemDetailsID = 210; break;
- case 2211: // Scorp
- itemDetailsID = 212; break;
- case 2212: // Sewing Kit
- itemDetailsID = 216; break;
- case 2213: // Shovel
- itemDetailsID = 254; break;
- case 2214: // Sledge Hammer
- itemDetailsID = 257; break;
- case 2215: // Smith's Hammer
- itemDetailsID = 256; break;
- case 2216: // Tongs
- itemDetailsID = 253; break;
- case 2217: // Tool Kit (Tinker's tools)
- itemDetailsID = 213; break;
- case 2218: // Fletcher's Tools
- itemDetailsID = 284; break;
- // Page 3 - Parts
- case 2300: // Barrel Hoops
- itemDetailsID = 224; break;
- case 2301: // Barrel Tap
- itemDetailsID = 221; break;
- case 2302: // Clock parts
- itemDetailsID = 220; break;
- case 2303: // Gears
- itemDetailsID = 219; break;
- case 2304: // Hinge
- itemDetailsID = 225; break;
- case 2305: // Sextant parts
- itemDetailsID = 223; break;
- case 2306: // Springs
- itemDetailsID = 222; break;
- // Page 4 - Utensils
- case 2400: // Butcher Knife
- itemDetailsID = 226; break;
- case 2401: // Cleaver
- itemDetailsID = 232; break;
- case 2402: // Fork
- itemDetailsID = 230; break;
- case 2403: // Fork
- itemDetailsID = 231; break;
- case 2404: // Goblet
- itemDetailsID = 235; break;
- case 2405: // Knife
- itemDetailsID = 233; break;
- case 2406: // Knife
- itemDetailsID = 234; break;
- case 2407: // Pewter Mug
- itemDetailsID = 236; break;
- case 2408: // Plate
- itemDetailsID = 229; break;
- case 2409: // Skinning Knife
- itemDetailsID = 237; break;
- case 2410: // Spoon
- itemDetailsID = 227; break;
- case 2411: // Spoon
- itemDetailsID = 228; break;
- // Page 5 - Jewelry
- case 2500: // Bracelet
- itemDetailsID = 243; break;
- case 2501: // Earrings
- itemDetailsID = 241; break;
- case 2502: // Necklage (Golden beads)
- itemDetailsID = 239; break;
- case 2503: // Necklace (Silver beads)
- itemDetailsID = 240; break;
- case 2504: // Necklace (Round)
- itemDetailsID = 242; break;
- case 2505: // Weddingband (newbiefied)
- itemDetailsID = 238; break;
- // Page 6 - Miscellaneous
- case 2600: // Candelabra
- itemDetailsID = 245; break;
- case 2601: // Globe
- itemDetailsID = 248; break;
- case 2602: // Heating stand
- itemDetailsID = 251; break;
- case 2603: // Iron Key
- itemDetailsID = 247; break;
- case 2604: // Keyring
- itemDetailsID = 244; break;
- case 2605: // Lantern
- itemDetailsID = 250; break;
- case 2606: // Scales
- itemDetailsID = 246; break;
- case 2607: // Spy glass
- itemDetailsID = 249; break;
- // Page 7 - Multi-Component Items
- case 2700: // Axle and Gears
- itemDetailsID = 275; break;
- case 2701: // Clock
- itemDetailsID = 276; break;
- case 2702: // Clock
- itemDetailsID = 277; break;
- case 2703: // Clock Parts
- itemDetailsID = 278; break;
- case 2704: // Potion Keg
- itemDetailsID = 281; break;
- case 2705: // Sextant
- itemDetailsID = 282; break;
- case 2706: // Sextant Parts
- itemDetailsID = 283; break;
- // Page 8 - Candles
- case 2800: // Candelabra
- itemDetailsID = 245; break;
- case 2801: // Standing Candelabra
- itemDetailsID = 310; break;
- case 2802: // Regular Candle
- itemDetailsID = 315; break;
- case 2803: // Round Candle
- itemDetailsID = 312; break;
- case 2804: // Skull with Candle
- itemDetailsID = 316; break;
- case 2805: // Small Candle
- itemDetailsID = 314; break;
- case 2806: // Tall Candle
- itemDetailsID = 311; break;
- case 2807: // Thick Candle
- itemDetailsID = 313; break;
- // Page 9 - Traps
- case 2900: // Dart Trap
- itemDetailsID = 261; break;
- case 2901: // Explosion Trap
- itemDetailsID = 263; break;
- case 2902: // Poison Trap
- itemDetailsID = 262; break;
- default:
- break;
+ var subPage2 = pButton - 9000;
+ var pageNum2 = pUser.GetTempTag( "page" );
+ pUser.SetTempTag( "subPage", subPage2 );
+ PageX( socket, pUser, pageNum2 );
+ return;
+ }
+
+ // Page tabs (1–9)
+ if( pButton >= 1 && pButton <= 9 )
+ {
+ pUser.SetTempTag( "page", pButton );
+ pUser.SetTempTag( "subPage", 1 );
+ PageX( socket, pUser, pButton );
+ return;
+ }
+
+ // Last Ten (if you add a tab that sends 11000)
+ if( pButton == 11000 )
+ {
+ pUser.SetTempTag( "page", 999 );
+ pUser.SetTempTag( "subPage", 1 );
+ PageX( socket, pUser, 999 );
+ return;
+ }
+
+ // Close gump
+ if( pButton == 0 )
+ {
+ pUser.SetTempTag( "MakeLast_Tinkering", null );
+ pUser.SetTempTag( "CRAFT", null );
+ socket.CloseGump( gumpID, 0 );
+ return;
}
- if( makeID != 0 )
+ // Make Last
+ if( pButton == 5000 )
{
- if(( pButton >= 500 && pButton <= 505 ))
+ var last = pUser.GetTempTag( "MakeLast_Tinkering" );
+ if( last )
+ pButton = last;
+ else
+ return;
+ }
+
+ var makeID = 0;
+ var timerID = 0;
+
+ // Craft buttons use makeID directly
+ if( TinkeringMap[pButton] != undefined )
+ {
+ makeID = pButton;
+ var data = TinkeringMap[makeID];
+ timerID = data.timerID || 1;
+
+ // Era / recipe checks
+ if( !eraOK( data ))
+ {
+ socket.SysMessage( "That item is not available in this era." );
+ return;
+ }
+
+ if( data.recipeID && !TriggerEvent( 4022, "NeedRecipe", pUser, data.recipeID ))
+ {
+ socket.SysMessage( "You must learn that recipe from a scroll." );
+ return;
+ }
+
+ // Jewelry that needs gem targeting
+ if( data.requiresGemTarget )
{
- // Ask crafter which material to use
pUser.SetTempTag( "makeID", makeID );
pUser.SetTempTag( "timerID", timerID );
- pUser.AddScriptTrigger( 4033 );
- pSock.CustomTarget( 2, GetDictionaryEntry( 12008, pSock.language )); // Select material to use:
+ pUser.AddScriptTrigger( 4033 ); // crafting_complete.js
+ socket.CustomTarget( 2, GetDictionaryEntry( 12008, socket.language )); // Select material to use:
return;
}
- MakeItem( pSock, pUser, makeID );
+ // Normal craft
+ pUser.SetTempTag( "MakeLast_Tinkering", makeID );
+
+ MakeItem( socket, pUser, makeID );
+ AddToLastTen( pUser, makeID );
+
if( GetServerSetting( "ToolUseLimit" ))
{
- bItem.usesLeft -= 1;
- if( bItem.usesLeft == 0 && GetServerSetting( "ToolUseBreak" ))
+ tool.usesLeft -= 1;
+ if( tool.usesLeft == 0 && GetServerSetting( "ToolUseBreak" ))
{
- bItem.Delete();
- pSock.SysMessage( GetDictionaryEntry( 10202, pSock.language )); // You have worn out your tool!
- // Play sound effect of tool breaking
+ tool.Delete();
+ socket.SysMessage( GetDictionaryEntry( 10202, socket.language )); // You have worn out your tool!
}
- }
- pUser.StartTimer( gumpDelay, timerID, true );
+ }
+
+ pUser.StartTimer( gumpDelay, timerID, tinkeringID );
+ return;
}
- else if( itemDetailsID != 0 )
+
+ // Detail buttons: 20000 + makeID
+ if( pButton >= 20000 && pButton < 30000 )
{
- pUser.SetTempTag( "ITEMDETAILS", itemDetailsID );
- TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
+ var detailMakeID = pButton - 20000;
+ var entry = TinkeringMap[detailMakeID];
+
+ if( entry )
+ {
+ // Which item details to show
+ pUser.SetTempTag( "ITEMDETAILS", detailMakeID );
+
+ // Skill used
+ pUser.SetTempTag( "Skill", entry.skill || tinkeringSkillID );
+
+ // Clear old harvest tags
+ pUser.SetTempTag( "Harvest", null );
+ pUser.SetTempTag( "Harvest2", null );
+ pUser.SetTempTag( "Harvest3", null );
+ pUser.SetTempTag( "Harvest4", null );
+
+ // Clear old custom harvest names
+ pUser.SetTempTag( "HarvestName", null );
+ pUser.SetTempTag( "Harvest2Name", null );
+ pUser.SetTempTag( "Harvest3Name", null );
+ pUser.SetTempTag( "Harvest4Name", null );
+
+ // Optional harvest dictIDs
+ if( entry.harvest && entry.harvest.length > 0 )
+ {
+ if( entry.harvest.length >= 1 )
+ pUser.SetTempTag( "Harvest", entry.harvest[0] );
+ if( entry.harvest.length >= 2 )
+ pUser.SetTempTag( "Harvest2", entry.harvest[1] );
+ if( entry.harvest.length >= 3 )
+ pUser.SetTempTag( "Harvest3", entry.harvest[2] );
+ if( entry.harvest.length >= 4 )
+ pUser.SetTempTag( "Harvest4", entry.harvest[3] );
+ }
+
+ // Optional custom names – plugs into your ItemDetail custom harvest name logic
+ if( entry.harvestNames && entry.harvestNames.length > 0 )
+ {
+ if( entry.harvestNames.length >= 1 )
+ pUser.SetTempTag( "HarvestName", entry.harvestNames[0] );
+ if( entry.harvestNames.length >= 2 )
+ pUser.SetTempTag( "Harvest2Name", entry.harvestNames[1] );
+ if( entry.harvestNames.length >= 3 )
+ pUser.SetTempTag( "Harvest3Name", entry.harvestNames[2] );
+ if( entry.harvestNames.length >= 4 )
+ pUser.SetTempTag( "Harvest4Name", entry.harvestNames[3] );
+ }
+
+ if( entry.recipeID && entry.recipeID > 0 )
+ pUser.SetTempTag( "needRecipeID", entry.recipeID );
+ else
+ pUser.SetTempTag( "needRecipeID", 0 );
+
+ TriggerEvent( itemDetailsScriptID, "ItemDetailGump", pUser );
+ }
+ return;
}
}
-/** @type { ( tSock: Socket, target: Character | Item | null ) => void } */
+/** @type { ( pSock: Socket, targObj: Character | Item | null ) => void } */
function onCallback2( pSock, targObj )
{
var pUser = pSock.currentChar;
@@ -540,61 +586,109 @@ function onCallback2( pSock, targObj )
return;
// Fetch makeID and timerID from temp tag
- var makeID = pUser.GetTempTag( "makeID" );
+ var makeID = pUser.GetTempTag( "makeID" );
var timerID = pUser.GetTempTag( "timerID" );
pUser.SetTempTag( "makeID", null );
pUser.SetTempTag( "timerID", null );
var bItem = pSock.tempObj; // tool
- if( ValidateObject( bItem ))
+ if( !ValidateObject( bItem ))
+ return;
+
+ if( ValidateObject( targObj ) && targObj.isItem )
{
- if( ValidateObject( targObj ) && targObj.isItem )
+ // Make sure targeted item is in player's backpack
+ var iPackOwner = GetPackOwner( targObj, 0 );
+ if( ValidateObject( iPackOwner ))
{
- // Make sure targeted item is in player's backpack
- var iPackOwner = GetPackOwner( targObj, 0 );
- if( ValidateObject( iPackOwner )) // Is the item in a backpack?
- {
- if( iPackOwner.serial != pUser.serial ) // And if so does the pack belong to the user?
- {
- pSock.SysMessage( GetDictionaryEntry( 6032, pSock.language )); // That resource is in someone else's backpack!
- return;
- }
- }
- else
+ if( iPackOwner.serial != pUser.serial )
{
- pSock.SysMessage( GetDictionaryEntry( 6022, pSock.language )); // This has to be in your backpack before you can use it.
+ pSock.SysMessage( GetDictionaryEntry( 6032, pSock.language )); // That resource is in someone else's backpack!
return;
}
+ }
+ else
+ {
+ pSock.SysMessage( GetDictionaryEntry( 6022, pSock.language )); // This has to be in your backpack before you can use it.
+ return;
+ }
- if( makeID >= 238 && makeID <= 243 )
+ // Jewelry: verify gem
+ if( makeID >= 238 && makeID <= 243 )
+ {
+ var resourceType = TriggerEvent( 2506, "GetResourceType", targObj.id );
+ if( resourceType != "gems" )
{
- // Jewlery
- var resourceType = TriggerEvent( 2506, "GetResourceType", targObj.id );
- if( resourceType != "gems" )
- {
- pSock.SysMessage( GetDictionaryEntry( 12007, pSock.language )); // That's not a gem resource!
- return;
- }
-
- // Set a temporary tag on character with ID of selected gem
- // We'll check for this ID in the crafting process, and remove it
- // in the onMakeItem event script (crafting_complete.js)
- pUser.SetTempTag( "targetedSubResourceId", targObj.id );
- pUser.SetTempTag( "targetedSubResourceName", targObj.name );
+ pSock.SysMessage( GetDictionaryEntry( 12007, pSock.language )); // That's not a gem resource!
+ return;
}
- MakeItem( pSock, pUser, makeID );
- if( GetServerSetting( "ToolUseLimit" ))
+ // Tags used by crafting_complete.js
+ pUser.SetTempTag( "targetedSubResourceId", targObj.id );
+ pUser.SetTempTag( "targetedSubResourceName", targObj.name );
+ }
+
+ MakeItem( pSock, pUser, makeID );
+ AddToLastTen( pUser, makeID );
+
+ if( GetServerSetting( "ToolUseLimit" ))
+ {
+ bItem.usesLeft -= 1;
+ if( bItem.usesLeft == 0 && GetServerSetting( "ToolUseBreak" ))
{
- bItem.usesLeft -= 1;
- if( bItem.usesLeft == 0 && GetServerSetting( "ToolUseBreak" ))
- {
- bItem.Delete();
- pSock.SysMessage( GetDictionaryEntry( 10202, pSock.language )); // You have worn out your tool!
- // Play sound effect of tool breaking
- }
+ bItem.Delete();
+ pSock.SysMessage( GetDictionaryEntry( 10202, pSock.language )); // You have worn out your tool!
}
- pUser.StartTimer( gumpDelay, timerID, true );
}
+ pUser.StartTimer( gumpDelay, timerID, tinkeringID );
}
}
+
+function AddToLastTen( pUser, makeID )
+{
+ var raw = pUser.GetTempTag( "LastTenTinkering" ) || "";
+ var list = raw.split( "," );
+
+ for( var i = 0; i < list.length; i++ )
+ {
+ if( parseInt( list[i] ) == makeID )
+ {
+ list.splice( i, 1 );
+ break;
+ }
+ }
+
+ var newList = [ makeID ];
+ for( var j = 0; j < list.length && newList.length < 10; j++ )
+ {
+ var entry = parseInt( list[j] );
+ if( !isNaN( entry ) && entry > 0 )
+ newList.push( entry );
+ }
+
+ pUser.SetTempTag( "LastTenTinkering", newList.join( "," ) );
+}
+
+function HasLearnedRecipe( pUser, recipeID )
+{
+ var myData = TriggerEvent( 4022, "ReadRecipeID", pUser );
+ if( !myData || myData.length == 0 )
+ return false;
+
+ for( var i = 0; i < myData.length; i++ )
+ {
+ var data = myData[i].split( "," );
+ if( data[0] == recipeID )
+ return true;
+ }
+ return false;
+}
+
+function eraOK( entry )
+{
+ if( entry.minEra && coreShardEra < EraStringToNum( entry.minEra ))
+ return false;
+ if( entry.maxEra && coreShardEra > EraStringToNum( entry.maxEra ))
+ return false;
+ return true;
+}