├── .gitignore
├── README.md
├── config-sample.json
├── data
├── 740
│ ├── account-template.json
│ ├── achievements.json
│ ├── actions
│ │ ├── definitions.json
│ │ └── definitions
│ │ │ ├── blueberry.js
│ │ │ ├── dough.js
│ │ │ ├── enterTiles.js
│ │ │ ├── exitTiles.js
│ │ │ ├── expertise.js
│ │ │ ├── fish.js
│ │ │ ├── flour.js
│ │ │ ├── food.js
│ │ │ ├── ladder.js
│ │ │ ├── lamp.js
│ │ │ ├── lever.js
│ │ │ ├── machete.js
│ │ │ ├── music.js
│ │ │ ├── pick.js
│ │ │ ├── pitfall.js
│ │ │ ├── rope.js
│ │ │ ├── rune.js
│ │ │ ├── scythe.js
│ │ │ ├── sewer.js
│ │ │ ├── shovel.js
│ │ │ ├── toggle.js
│ │ │ └── wheat.js
│ ├── clock
│ │ ├── definitions.json
│ │ └── definitions
│ │ │ ├── cemetery-ghost.js
│ │ │ ├── statue.js
│ │ │ └── timetile.js
│ ├── conditions
│ │ ├── definitions.json
│ │ └── definitions
│ │ │ ├── arena.js
│ │ │ ├── burning.js
│ │ │ ├── drunk.js
│ │ │ ├── electrified.js
│ │ │ ├── haste.js
│ │ │ ├── healing.js
│ │ │ ├── invisible.js
│ │ │ ├── light.js
│ │ │ ├── magic-flame-condition.js
│ │ │ ├── magic-shield.js
│ │ │ ├── morph.js
│ │ │ ├── poisoned.js
│ │ │ ├── regeneration.js
│ │ │ ├── sated.js
│ │ │ └── suppress-drunk.js
│ ├── constants.json
│ ├── doors
│ │ ├── README.md
│ │ ├── definitions.json
│ │ └── definitions
│ │ │ └── unwanted.js
│ ├── houses
│ │ ├── definitions.json
│ │ └── definitions
│ │ │ └── 1.json
│ ├── items
│ │ └── definitions.json
│ ├── looktypes.txt
│ ├── monsters
│ │ ├── README.md
│ │ ├── definitions.json
│ │ ├── definitions
│ │ │ ├── bear.json
│ │ │ ├── black-sheep.json
│ │ │ ├── cave-rat.json
│ │ │ ├── rabbit.json
│ │ │ ├── rat.json
│ │ │ ├── rotworm.json
│ │ │ ├── sheep.json
│ │ │ ├── skeleton.json
│ │ │ ├── slime.json
│ │ │ ├── spider.json
│ │ │ └── troll.json
│ │ └── schema.json
│ ├── mounts
│ │ └── mounts.json
│ ├── npcs
│ │ ├── README.md
│ │ ├── definitions.json
│ │ ├── definitions
│ │ │ ├── albert.json
│ │ │ ├── billy.json
│ │ │ ├── ghost.json
│ │ │ ├── humphrey.json
│ │ │ ├── jack.json
│ │ │ ├── karst.json
│ │ │ ├── marrow.json
│ │ │ ├── miller.json
│ │ │ ├── sarah.json
│ │ │ ├── script
│ │ │ │ ├── albert.js
│ │ │ │ ├── billy.js
│ │ │ │ ├── humphrey.js
│ │ │ │ ├── jack.js
│ │ │ │ ├── karst.js
│ │ │ │ ├── marrow.js
│ │ │ │ ├── miller.js
│ │ │ │ ├── sarah.js
│ │ │ │ ├── scene
│ │ │ │ │ ├── jack-story.js
│ │ │ │ │ ├── karst.js
│ │ │ │ │ ├── marrow.js
│ │ │ │ │ └── miller.js
│ │ │ │ └── teller.js
│ │ │ └── teller.json
│ │ └── schema.json
│ ├── outfits
│ │ └── outfits.json
│ ├── runes
│ │ ├── definitions.json
│ │ └── definitions
│ │ │ ├── destroyfield.js
│ │ │ ├── energyfield.js
│ │ │ ├── fire-bomb.js
│ │ │ ├── fireball.js
│ │ │ ├── firefield.js
│ │ │ ├── hearthrune.js
│ │ │ ├── heavy-magic-missile.js
│ │ │ ├── magicwall.js
│ │ │ ├── poisonfield.js
│ │ │ ├── sudden-death.js
│ │ │ ├── teleport.js
│ │ │ └── ultimate-healing.js
│ ├── spawns
│ │ └── definitions.json
│ ├── spells
│ │ ├── definitions.json
│ │ └── definitions
│ │ │ ├── beam.js
│ │ │ ├── cure.js
│ │ │ ├── explosion.js
│ │ │ ├── exura.js
│ │ │ ├── haste.js
│ │ │ ├── hearthstone.js
│ │ │ ├── invisible.js
│ │ │ ├── levitate.js
│ │ │ ├── life-drain.js
│ │ │ ├── light.js
│ │ │ └── morph.js
│ ├── unique
│ │ ├── definitions.json
│ │ └── definitions
│ │ │ ├── clock-secret.js
│ │ │ ├── crypt-chest.js
│ │ │ ├── crypt-tile.js
│ │ │ ├── enter-trunk.js
│ │ │ └── touch-magic-flame.js
│ └── world
│ │ ├── definitions.json
│ │ └── definitions
│ │ ├── borne-windmill-top.js
│ │ ├── borne.js
│ │ └── otbm
│ │ ├── borne-house.xml
│ │ ├── borne-spawn.xml
│ │ ├── borne-windmill-top.otbm
│ │ └── borne.otbm
└── 1098
│ ├── account-template.json
│ ├── achievements.json
│ ├── actions
│ ├── definitions.json
│ └── definitions
│ │ ├── blueberry.js
│ │ ├── dough.js
│ │ ├── enter.js
│ │ ├── fish.js
│ │ ├── flour.js
│ │ ├── food.js
│ │ ├── ladder.js
│ │ ├── lever.js
│ │ ├── machete.js
│ │ ├── music.js
│ │ ├── pick.js
│ │ ├── pitfall.js
│ │ ├── rope.js
│ │ ├── rune.js
│ │ ├── scythe.js
│ │ ├── sewer.js
│ │ ├── shovel.js
│ │ ├── toggle.js
│ │ └── wheat.js
│ ├── clock
│ └── definitions.json
│ ├── conditions
│ ├── definitions.json
│ └── definitions
│ │ ├── burning.js
│ │ ├── drunk.js
│ │ ├── electrified.js
│ │ └── poisoned.js
│ ├── constants.json
│ ├── items
│ └── definitions.json
│ ├── looktypes.txt
│ ├── monsters
│ ├── README.md
│ ├── definitions.json
│ ├── definitions
│ │ ├── bear.json
│ │ ├── black-sheep.json
│ │ ├── cave-rat.json
│ │ ├── rabbit.json
│ │ ├── rat.json
│ │ ├── rotworm.json
│ │ ├── sheep.json
│ │ ├── spider.json
│ │ └── troll.json
│ └── schema.json
│ ├── mounts
│ └── mounts.json
│ ├── npcs
│ ├── README.md
│ ├── definitions.json
│ ├── definitions
│ │ ├── albert.json
│ │ ├── ghost.json
│ │ ├── humphrey.json
│ │ ├── jack.json
│ │ ├── karst.json
│ │ ├── marrow.json
│ │ ├── miller.json
│ │ ├── sarah.json
│ │ ├── script
│ │ │ ├── albert.js
│ │ │ ├── humphrey.js
│ │ │ ├── jack.js
│ │ │ ├── karst.js
│ │ │ ├── marrow.js
│ │ │ ├── miller.js
│ │ │ ├── sarah.js
│ │ │ ├── scene
│ │ │ │ ├── jack-story.js
│ │ │ │ ├── karst.js
│ │ │ │ ├── marrow.js
│ │ │ │ └── miller.js
│ │ │ └── teller.js
│ │ └── teller.json
│ └── schema.json
│ ├── outfits
│ └── outfits.json
│ ├── runes
│ ├── definitions.json
│ └── definitions
│ │ ├── destroyfield.js
│ │ ├── energyfield.js
│ │ ├── fire-bomb.js
│ │ ├── fireball.js
│ │ ├── firefield.js
│ │ ├── hearthrune.js
│ │ ├── heavy-magic-missile.js
│ │ ├── magicwall.js
│ │ ├── poisonfield.js
│ │ ├── sudden-death.js
│ │ ├── teleport.js
│ │ └── ultimate-healing.js
│ ├── spawns
│ └── definitions.json
│ ├── spells
│ ├── definitions.json
│ └── definitions
│ │ ├── cure.js
│ │ ├── explosion.js
│ │ └── exura.js
│ ├── unique
│ └── definitions.json
│ └── world
│ ├── definitions.json
│ └── definitions
│ ├── otbm
│ ├── rat-cave-house.xml
│ ├── rat-cave-spawn.xml
│ ├── rat-cave.otbm
│ ├── start-town-house.xml
│ ├── start-town-spawn.xml
│ ├── start-town.otbm
│ └── temple.otbm
│ ├── rat-cave.json
│ ├── start-town.json
│ └── temple.json
├── engine.js
├── ipcclient.js
├── lib
├── headers.js
├── otb2json.js
└── otbm2json.js
├── login.js
├── package-lock.json
├── package.json
├── src
├── __proto__.js
├── account-manager.js
├── achievement-manager.js
├── achievement-properties.js
├── achievement-property.js
├── action.js
├── base-container.js
├── behaviour.js
├── binary-heap.js
├── bitflag.js
├── channel-default.js
├── channel-global.js
├── channel-manager.js
├── channel.js
├── chunk.js
├── condition-manager.js
├── condition.js
├── container-manager.js
├── container.js
├── corpse.js
├── creature.js
├── damage-map.js
├── database.js
├── depot.js
├── door.js
├── enum.js
├── equipment.js
├── event.js
├── eventemitter.js
├── eventqueue.js
├── file-access-handler.js
├── fluidcontainer.js
├── friendlist.js
├── gameloop.js
├── gameserver.js
├── gamesocket.js
├── generic-lock.js
├── house.js
├── http-server.js
├── inbox.js
├── ipcclient.js
├── ipchttpapi.js
├── ipcpacket.js
├── ipcsocket.js
├── item-stack.js
├── item.js
├── key.js
├── keyring.js
├── lattice.js
├── logger.js
├── login-server.js
├── mailbox-handler.js
├── monster.js
├── network-manager.js
├── npc.js
├── opcodes.js
├── outfit.js
├── packet-buffer.js
├── packet-handler.js
├── packet-reader.js
├── packet-writer.js
├── packet.js
├── pathfind-node.js
├── pathfinder.js
├── player.js
├── position.js
├── readable.js
├── rme-parser.js
├── rune.js
├── scene.js
├── spellbook.js
├── stat-manager.js
├── teleporter.js
├── thing-emitter.js
├── thing-prototype.js
├── thing.js
├── tile.js
├── validator.js
├── websocket-server.js
├── world.js
└── worldclock.js
├── test.js
├── tests
├── test-container.js
└── test-tile.js
└── tools
├── definitions.json
├── items-otb
├── 1098-items.otb
└── 740-items.otb
├── items-xml
├── 1098-items.xml
└── 740-items.xml
├── mounts-new.json
├── mounts.json
├── rewrite-items-xml.js
├── rewrite-mounts.py
└── rewrite-outfits.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | .DS_Store
3 | client/data
4 | server.log
5 | ssl
6 | node_modules
7 | data/*/accounts
8 | config.json
9 | client-server.py
--------------------------------------------------------------------------------
/config-sample.json:
--------------------------------------------------------------------------------
1 | {
2 | "HMAC": {
3 | "SHARED_SECRET": "0000000000000000000000000000000000000000000000000000000000000000"
4 | },
5 | "LOGIN": {
6 | "PORT": 1337,
7 | "HOST": "127.0.0.1"
8 | },
9 | "IPC": {
10 | "RECONNECT_MS": 1000,
11 | "HOST": "127.0.0.1",
12 | "PORT": 2000,
13 | "SOCKET": "game.sock"
14 | },
15 | "SERVER": {
16 | "EXTERNAL_HOST": "ws://127.0.0.1:2222",
17 | "VERSION": "0.0.0",
18 | "CLIENT_VERSION": "740",
19 | "DATE": "2022-03-24",
20 | "PORT": 2222,
21 | "HOST": "127.0.0.1",
22 | "ALLOW_MAXIMUM_CONNECTIONS": 50,
23 | "MS_TICK_INTERVAL": 50,
24 | "MS_SHUTDOWN_SCHEDULE": 1000,
25 | "MAX_PACKET_SIZE": 1024
26 | },
27 | "WORLD": {
28 | "CHUNK": {
29 | "WIDTH": 9,
30 | "HEIGHT": 7,
31 | "DEPTH": 8
32 | },
33 | "CLOCK": {
34 | "SPEED": 6,
35 | "START": "08:00"
36 | },
37 | "SPAWNS": {
38 | "ENABLED": true
39 | },
40 | "NPCS": {
41 | "ENABLED": true
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/data/1098/account-template.json:
--------------------------------------------------------------------------------
1 | {
2 | "achievements": {
3 | "completed": [
4 | 0,
5 | 1,
6 | 2,
7 | 3
8 | ],
9 | "properties": {
10 | "NUMBER_TILES_WALKED": 0,
11 | "TEST_ADDITION": 0,
12 | "EXPLORE_TOWN": 0
13 | }
14 | },
15 | "creatureStatistics": {
16 | "attack": 4,
17 | "attackSlowness": 20,
18 | "defense": 2,
19 | "direction": 2,
20 | "health": 50,
21 | "maxHealth": 50,
22 | "name": null,
23 | "outfit": {
24 | "id": null,
25 | "details": {
26 | "head": 78,
27 | "body": 69,
28 | "legs": 58,
29 | "feet": 76
30 | },
31 | "mount": 0,
32 | "mounted": false,
33 | "addonOne": false,
34 | "addonTwo": false
35 | },
36 | "slowness": 10
37 | },
38 | "depot": [
39 | {
40 | "id": 2148,
41 | "count": 100,
42 | "duration": null
43 | },
44 | null,
45 | null,
46 | null,
47 | null,
48 | null,
49 | null,
50 | null,
51 | null,
52 | null,
53 | null,
54 | null,
55 | null,
56 | null,
57 | null,
58 | null,
59 | null,
60 | null,
61 | null,
62 | null,
63 | null,
64 | null,
65 | null,
66 | null,
67 | null
68 | ],
69 | "characterStatistics": {
70 | "position": {
71 | "x": 82,
72 | "y": 81,
73 | "z": 8
74 | },
75 | "admin": false,
76 | "sex": null,
77 | "templePosition": {
78 | "x": 82,
79 | "y": 81,
80 | "z": 8
81 | },
82 | "maxCapacity": 20000,
83 | "level": 1,
84 | "experience": 0,
85 | "availableMounts": [],
86 | "availableOutfits": []
87 | },
88 | "equipment": [
89 | null,
90 | null,
91 | null,
92 | null,
93 | null,
94 | null,
95 | {
96 | "id": 1988,
97 | "duration": null,
98 | "items": [
99 | null,
100 | null,
101 | null,
102 | null,
103 | null,
104 | null,
105 | null,
106 | null,
107 | null,
108 | null,
109 | null,
110 | null,
111 | null,
112 | null,
113 | null,
114 | null,
115 | null,
116 | null,
117 | null,
118 | null
119 | ]
120 | },
121 | null,
122 | null,
123 | null
124 | ],
125 | "friends": []
126 | }
127 |
--------------------------------------------------------------------------------
/data/1098/achievements.json:
--------------------------------------------------------------------------------
1 | [{
2 | "title": "Walking",
3 | "description": "You have walked a single tile.",
4 | "conditions": [{
5 | "property": "NUMBER_TILES_WALKED",
6 | "type": "greater",
7 | "value": 1
8 | }]
9 | }, {
10 | "title": "Walking Some More",
11 | "description": "You have walked five tiles.",
12 | "conditions": [{
13 | "property": "NUMBER_TILES_WALKED",
14 | "type": "greater",
15 | "value": 5
16 | }]
17 | }, {
18 | "title": "Walking Some More and More and More!",
19 | "description": "You have walked five tiles.",
20 | "conditions": [{
21 | "property": "NUMBER_TILES_WALKED",
22 | "type": "greater",
23 | "value": 10
24 | }]
25 | }, {
26 | "title": "Taking a trip downtown..",
27 | "description": "Walking Fast!",
28 | "conditions": [{
29 | "property": "EXPLORE_TOWN",
30 | "type": "greater",
31 | "value": 7
32 | }]
33 | }]
--------------------------------------------------------------------------------
/data/1098/actions/definitions.json:
--------------------------------------------------------------------------------
1 | [
2 | {"id": 2580, "on": "useWith", "callback": "fish.js"},
3 | {"id": 2785, "on": "use", "callback": "blueberry.js"},
4 | {"from": 2666, "to": 2691, "on": "use", "callback": "food.js"},
5 | {"from": 1945, "to": 1946, "on": "use", "callback": "lever.js"},
6 | {"id": 1386, "on": "use", "callback": "ladder.js"},
7 | {"id": 2120, "on": "useWith", "callback": "rope.js"},
8 | {"id": 2553, "on": "useWith", "callback": "pick.js"},
9 | {"id": 2554, "on": "useWith", "callback": "shovel.js"},
10 | {"id": 2420, "on": "useWith", "callback": "machete.js"},
11 | {"id": 2041, "on": "use", "callback": "toggle.js"},
12 | {"id": 2057, "on": "use", "callback": "toggle.js"},
13 | {"id": 426, "on": "enter", "callback": "enter.js"},
14 | {"id": 425, "on": "exit", "callback": "enter.js"},
15 | {"id": 430, "on": "use", "callback": "sewer.js"},
16 |
17 | {"id": 2071, "on": "use", "callback": "music.js"},
18 |
19 | {"id": 2694, "on": "useWith", "callback": "wheat.js"},
20 | {"id": 2693, "on": "useWith", "callback": "dough.js"},
21 | {"id": 2692, "on": "useWith", "callback": "flour.js"},
22 | {"id": 2550, "on": "useWith", "callback": "scythe.js"},
23 |
24 | {"from": 1479, "to": 1480, "on": "use", "callback": "toggle.js"},
25 | {"from": 2058, "to": 2061, "on": "use", "callback": "toggle.js"},
26 | {"from": 2037, "to": 2040, "on": "use", "callback": "toggle.js"},
27 | {"from": 2044, "to": 2045, "on": "use", "callback": "toggle.js"},
28 | {"from": 2050, "to": 2055, "on": "use", "callback": "toggle.js"},
29 | {"from": 2261, "to": 2316, "on": "useWith", "callback": "rune.js"},
30 | {"from": 1786, "to": 1793, "on": "use", "callback": "toggle.js"}
31 |
32 | ]
--------------------------------------------------------------------------------
/data/1098/actions/definitions/blueberry.js:
--------------------------------------------------------------------------------
1 | module.exports = function blueberryBush(player, tile, item) {
2 |
3 | /*
4 | * Function blueberryBush
5 | * Picks blueberries from a blueberry bush
6 | */
7 |
8 | let bush = process.gameServer.database.createThing(2786);
9 | bush.scheduleDecay();
10 |
11 | item.replace(bush);
12 |
13 | let amount = Number.prototype.randomExp(3, 10, 3);
14 |
15 | tile.addTopThing(process.gameServer.database.createThing(2677).setCount(amount));
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/data/1098/actions/definitions/dough.js:
--------------------------------------------------------------------------------
1 | module.exports = function useDough(player, item, tile) {
2 |
3 | /*
4 | * Function useDough
5 | * Script called when dough is used on an oven
6 | */
7 |
8 | // Only allowed when not moving
9 | if(player.isMoving()) {
10 | return true;
11 | }
12 |
13 | if(!player.besides(tile)) {
14 | return;
15 | }
16 |
17 | let thing = tile.getTopItem();
18 |
19 | if(thing === null) {
20 | return;
21 | }
22 |
23 | // Identifiers of the ovens
24 | if(![1786, 1788, 1790, 1792].includes(thing.id)) {
25 | return;
26 | }
27 |
28 | let bread = [2689, 2690, 2691].random();
29 |
30 | item.removeCount(1);
31 | tile.addTopThing(process.gameServer.database.createThing(bread).setCount(1));
32 |
33 | return true;
34 |
35 | }
--------------------------------------------------------------------------------
/data/1098/actions/definitions/enter.js:
--------------------------------------------------------------------------------
1 | module.exports = function(tile, creature) {
2 |
3 | /*
4 | * Function toggleItem
5 | * Script called when a shovel is used on a stone pile
6 | */
7 |
8 | lookup = new Object({
9 | // Torch
10 | "426": "425",
11 | "425": "426"
12 | });
13 |
14 | if(!lookup.hasOwnProperty(tile.id)) {
15 | return;
16 | }
17 |
18 | if(tile.id === 425 && tile.getNumberCharacters() > 1) {
19 | return;
20 | }
21 |
22 | tile.replace(Number(lookup[tile.id]));
23 | process.gameServer.world.sendMagicEffect(tile.position, CONST.EFFECT.MAGIC.PURPLEENERGY);
24 |
25 | return true;
26 |
27 | }
--------------------------------------------------------------------------------
/data/1098/actions/definitions/fish.js:
--------------------------------------------------------------------------------
1 | module.exports = function useFishingRod(player, item, tile) {
2 |
3 | /*
4 | * Function useFishingRod
5 | * Function called when a fishing rod is used with
6 | */
7 |
8 | process.gameServer.world.sendMagicEffect(tile.position, CONST.EFFECT.MAGIC.LOSEENERGY);
9 |
10 | player.equipment.pushItem(process.gameServer.database.createThing(2667).setCount(1));
11 |
12 | }
--------------------------------------------------------------------------------
/data/1098/actions/definitions/flour.js:
--------------------------------------------------------------------------------
1 | module.exports = function useFlour(player, item, tile) {
2 |
3 | /*
4 | * Function useFlour
5 | * Script called when flour is used on a container with water
6 | */
7 |
8 | // Only allowed when not moving
9 | if(player.isMoving()) {
10 | return true;
11 | }
12 |
13 | if(!player.besides(tile)) {
14 | return;
15 | }
16 |
17 | let thing = tile.getTopItem();
18 |
19 | if(thing === null) {
20 | return;
21 | }
22 |
23 | if(!thing.isFluidContainer() || !thing.containsWater()) {
24 | return;
25 | }
26 |
27 | // Empty the container, remove the flour, and add dough
28 | thing.empty();
29 | item.removeCount(1);
30 | tile.addTopThing(process.gameServer.database.createThing(2693).setCount(1));
31 |
32 | return true;
33 |
34 | }
--------------------------------------------------------------------------------
/data/1098/actions/definitions/food.js:
--------------------------------------------------------------------------------
1 | module.exports = function playerEatFood(player, thing, index, item) {
2 |
3 | /*
4 | * Function playerEatFood
5 | * Writes a little text message and removes one of the item
6 | */
7 |
8 | player.internalCreatureSay("Yum!", CONST.COLOR.ORANGE);
9 |
10 | // Refactor
11 | thing.removeIndex(index, 1);
12 |
13 | }
--------------------------------------------------------------------------------
/data/1098/actions/definitions/ladder.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = function useLadder(player, tile, index, item) {
4 |
5 | // Only allowed when not moving
6 | if(player.isMoving()) {
7 | return true;
8 | }
9 |
10 | // Teleport the player and
11 | process.gameServer.world.teleportCreature(player, tile.position.ladder());
12 | player.__moveLock.lock(player.getSlowness());
13 |
14 | return true;
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/data/1098/actions/definitions/lever.js:
--------------------------------------------------------------------------------
1 | module.exports = function() {}
2 |
--------------------------------------------------------------------------------
/data/1098/actions/definitions/machete.js:
--------------------------------------------------------------------------------
1 | module.exports = function useMachete(player, item, tile) {
2 |
3 | /*
4 | * Function useMachete
5 | * Script called when a machete is used
6 | */
7 |
8 | // Only allowed when not moving
9 | if(player.isMoving()) {
10 | return true;
11 | }
12 |
13 | if(!player.besides(tile)) {
14 | return;
15 | }
16 |
17 | let grass = tile.getItem();
18 |
19 | if(grass === null) {
20 | return;
21 | }
22 |
23 | // If the tile being used has a rope hole: teleport up!
24 | if(grass.id === 2782) {
25 | grass.transform(2781);
26 | process.gameServer.eventQueue.addEvent(grass.transform.bind(grass, 2782), 100);
27 | }
28 |
29 | return true;
30 |
31 | }
--------------------------------------------------------------------------------
/data/1098/actions/definitions/music.js:
--------------------------------------------------------------------------------
1 | module.exports = function playMusic(player, tile, item) {
2 |
3 | process.gameServer.world.sendMagicEffect(tile.position, CONST.EFFECT.MAGIC.SOUND_GREEN);
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/data/1098/actions/definitions/pick.js:
--------------------------------------------------------------------------------
1 | module.exports = function useRope(player, item, tile) {
2 |
3 | /*
4 | * Function useRope
5 | * Script called when a rope is used
6 | */
7 |
8 | // Only allowed when not moving
9 | if(player.isMoving()) {
10 | return;
11 | }
12 |
13 | if(!player.besides(tile)) {
14 | return;
15 | }
16 |
17 | // Must be mud!
18 | if(tile.id < 351 || tile.id > 355) {
19 | return;
20 | }
21 |
22 | // Mud tiles with action ID 101 are pick holes
23 | if(tile.actionId === 101) {
24 | tile.replace(392);
25 | }
26 |
27 | return true;
28 |
29 | }
--------------------------------------------------------------------------------
/data/1098/actions/definitions/pitfall.js:
--------------------------------------------------------------------------------
1 | module.exports = function(tile, player) {
2 |
3 | tile.replace(294);
4 |
5 | }
--------------------------------------------------------------------------------
/data/1098/actions/definitions/rope.js:
--------------------------------------------------------------------------------
1 | const PacketHandler = requireModule("packet-handler");
2 |
3 | function useOnHole(player, tile) {
4 |
5 | /*
6 | * Function useOnHole
7 | * Script called when a rope is used on a hole
8 | */
9 |
10 | // Get the tile below the current hole
11 | let down = process.gameServer.world.getTileFromWorldPosition(tile.position.down());
12 |
13 | if(down === null) {
14 | return true;
15 | }
16 |
17 | // Must be an item down there
18 | let item = down.getTopItem();
19 |
20 | if(item === null) {
21 | return true;
22 | }
23 |
24 | if(!item.isMoveable()) {
25 | return true;
26 | }
27 |
28 | if(item.getAttribute("floorchange") !== null) {
29 | return true;
30 | }
31 |
32 | // The tile south of the hole must be available
33 | let up = process.gameServer.world.getTileFromWorldPosition(tile.position.south());
34 |
35 | if(up === null) {
36 | return true;
37 | }
38 |
39 | // Delegate the move event
40 | PacketHandler.prototype.__moveItem(player, down, 0xFF, up, 0xFF, 0);
41 |
42 | return true;
43 |
44 | }
45 |
46 | module.exports = function useRope(player, item, tile) {
47 |
48 | /*
49 | * Function useRope
50 | * Script called when a rope is used
51 | */
52 |
53 | // Only allowed when not moving
54 | if(player.isMoving()) {
55 | return true;
56 | }
57 |
58 | // Not besides the hole
59 | if(!player.besides(tile)) {
60 | return;
61 | }
62 |
63 | // These are holes
64 | if(tile.getFloorChange() === "down") {
65 | return useOnHole(player, tile);
66 | }
67 |
68 | // If the tile being used has a rope hole: teleport the player up!
69 | if(tile.id === 384) {
70 | process.gameServer.world.teleportCreature(player, tile.position.ladder());
71 | player.__moveLock.lock(player.getSlowness());
72 | }
73 |
74 | return true;
75 |
76 | }
--------------------------------------------------------------------------------
/data/1098/actions/definitions/rune.js:
--------------------------------------------------------------------------------
1 | module.exports = function useRuneWith(player, item, tile) {
2 |
3 | /*
4 | * Function useRuneWith
5 | * Callback fired when a rune is used on a tile
6 | */
7 |
8 | let from = player.position;
9 | let to = tile.position;
10 |
11 | // Check possible
12 | if(!from.inLineOfSight(to)) {
13 | return player.sendCancelMessage("Target is not in line of sight.");
14 | }
15 |
16 | // Look up the function to execute
17 | let rune = process.gameServer.database.getRune(item.id);
18 |
19 | // The rune does not exist
20 | if(rune === null) {
21 | return player.sendCancelMessage("The rune does nothing.");
22 | }
23 |
24 | // Call the configured rune function: return of true means succesful cast and we reduce a charge
25 | if(rune.call(null, player, tile)) {
26 | item.charges--;
27 | }
28 |
29 | // No more charges: delete the item
30 | if(item.charges === 0) {
31 | item.remove(1);
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/data/1098/actions/definitions/scythe.js:
--------------------------------------------------------------------------------
1 | module.exports = function useScythe(player, item, tile) {
2 |
3 | /*
4 | * Function useScythe
5 | * Script called when a scythe is used to cut wheat
6 | */
7 |
8 | // Only allowed when not moving
9 | if(player.isMoving()) {
10 | return true;
11 | }
12 |
13 | if(!player.besides(tile)) {
14 | return;
15 | }
16 |
17 | let thing = tile.getTopItem();
18 |
19 | if(thing === null) {
20 | return;
21 | }
22 |
23 | // These are the identifiers of wheat in the field
24 | if(thing.id === 2739 || thing.id === 2738) {
25 | thing.replace(process.gameServer.database.createThing(2737));
26 | }
27 |
28 | // Full grown? Add cut wheat
29 | if(thing.id === 2739) {
30 | tile.addTopThing(process.gameServer.database.createThing(2694).setCount(1));
31 | }
32 |
33 | return true;
34 |
35 | }
--------------------------------------------------------------------------------
/data/1098/actions/definitions/sewer.js:
--------------------------------------------------------------------------------
1 | module.exports = function useSewerGrate(player, tile, index, item) {
2 |
3 | // Only allowed when not moving
4 | if(player.isMoving()) {
5 | return true;
6 | }
7 |
8 | // Teleport the player and
9 | process.gameServer.world.teleportCreature(player, tile.position.down());
10 | player.__moveLock.lock(player.getSlowness());
11 |
12 | return true;
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/data/1098/actions/definitions/shovel.js:
--------------------------------------------------------------------------------
1 | module.exports = function useShovel(player, item, tile) {
2 |
3 | /*
4 | * Function useShovel
5 | * Script called when a shovel is used on a stone pile
6 | */
7 |
8 | // Only allowed when not moving
9 | if(player.isMoving()) {
10 | return player.sendCancelMessage("You can not do this while moving.");
11 | }
12 |
13 | if(!player.besides(tile)) {
14 | return player.sendCancelMessage("You need to be closer.");
15 | }
16 |
17 | // If the tile being used is a pile of loose stones: open it
18 | if(tile.id === 468) {
19 | tile.replace(469);
20 | }
21 |
22 | return true;
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/data/1098/actions/definitions/toggle.js:
--------------------------------------------------------------------------------
1 | const lookup = new Object({
2 | // Torch
3 | "2050": "2051",
4 | "2051": "2050",
5 | "2052": "2053",
6 | "2053": "2052",
7 | "2054": "2055",
8 | "2055": "2054",
9 |
10 | // Wall lamps
11 | "2058": "2059",
12 | "2059": "2058",
13 | "2060": "2061",
14 | "2061": "2060",
15 |
16 | // Lanterns
17 | "1479": "1480",
18 | "1480": "1479",
19 | "2044": "2045",
20 | "2045": "2044",
21 |
22 | // Wall lamps
23 | "2037": "2038",
24 | "2038": "2037",
25 | "2039": "2040",
26 | "2040": "2039",
27 |
28 | // Candelabrum
29 | "2041": "2057",
30 | "2057": "2041",
31 |
32 | // Ovens
33 | "1786": "1787",
34 | "1787": "1786",
35 | "1788": "1789",
36 | "1789": "1788",
37 | "1790": "1791",
38 | "1791": "1790",
39 | "1792": "1793",
40 | "1793": "1792"
41 | });
42 |
43 | module.exports = function(player, tile, index, item) {
44 |
45 | /*
46 | * Function toggleItem
47 | * Script called when a shovel is used on a stone pile
48 | */
49 |
50 | // Does not exist in the table
51 | if(!lookup.hasOwnProperty(item.id)) {
52 | return;
53 | }
54 |
55 | // Replace the items
56 | item.replace(process.gameServer.database.createThing(Number(lookup[item.id])));
57 |
58 | return true;
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/data/1098/actions/definitions/wheat.js:
--------------------------------------------------------------------------------
1 | module.exports = function useMachete(player, item, tile) {
2 |
3 | /*
4 | * Function useMachete
5 | * Script called when a machete is used
6 | */
7 |
8 | // Only allowed when not moving
9 | if(player.isMoving()) {
10 | return true;
11 | }
12 |
13 | if(!player.besides(tile)) {
14 | return;
15 | }
16 |
17 | let thing = tile.getTopItem();
18 |
19 | if(thing === null) {
20 | return;
21 | }
22 |
23 | if(![1381, 1382, 1383, 1384].includes(thing.id)) {
24 | return;
25 | }
26 |
27 | item.removeCount(1);
28 | tile.addTopThing(process.gameServer.database.createThing(2692).setCount(1));
29 |
30 | return true;
31 |
32 | }
--------------------------------------------------------------------------------
/data/1098/clock/definitions.json:
--------------------------------------------------------------------------------
1 | []
--------------------------------------------------------------------------------
/data/1098/conditions/definitions.json:
--------------------------------------------------------------------------------
1 | {
2 | "0": "drunk.js",
3 | "1": "poisoned.js",
4 | "2": "burning.js",
5 | "3": "electrified.js"
6 | }
--------------------------------------------------------------------------------
/data/1098/conditions/definitions/burning.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function onStart(creature) {
4 |
5 | /*
6 | * Function onStart
7 | * Callback fired on condition start
8 | */
9 |
10 | creature.sendCancelMessage("You are burning!");
11 |
12 | }
13 |
14 | function onExpire(creature) {
15 |
16 | /*
17 | * Function onExpire
18 | * Callback fired on condition expire
19 | */
20 |
21 | creature.sendCancelMessage("You feel better again.");
22 |
23 | }
24 |
25 | function onTick(creature) {
26 |
27 | /*
28 | * Function onTick
29 | * Callback fired every condition tick
30 | */
31 |
32 | process.gameServer.world.applyEnvironmentalDamage(creature, 10, CONST.COLOR.ORANGE);
33 | process.gameServer.world.sendMagicEffect(creature.position, CONST.EFFECT.MAGIC.HITBYFIRE);
34 |
35 | }
36 |
37 | module.exports.onStart = onStart;
38 | module.exports.onExpire = onExpire;
39 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/1098/conditions/definitions/drunk.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function onStart(creature) {
4 |
5 | /*
6 | * Function onStart
7 | * Callback fired on condition start
8 | */
9 |
10 | creature.sendCancelMessage("You are feeling drunk!");
11 |
12 | }
13 |
14 | function onExpire(creature) {
15 |
16 | /*
17 | * Function onExpire
18 | * Callback fired on condition expire
19 | */
20 |
21 | creature.sendCancelMessage("You feel sober again!");
22 |
23 | }
24 |
25 | function onTick(creature) {
26 |
27 | /*
28 | * Function onTick
29 | * Callback fired every condition tick
30 | */
31 |
32 | }
33 |
34 | module.exports.onStart = onStart;
35 | module.exports.onExpire = onExpire;
36 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/1098/conditions/definitions/electrified.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function onStart(creature) {
4 |
5 | /*
6 | * Function onStart
7 | * Callback fired on condition start
8 | */
9 |
10 | creature.sendCancelMessage("You were electrified!");
11 |
12 | }
13 |
14 | function onExpire(creature) {
15 |
16 | /*
17 | * Function onExpire
18 | * Callback fired on condition expire
19 | */
20 |
21 | creature.sendCancelMessage("You feel better again.");
22 |
23 | }
24 |
25 | function onTick(creature) {
26 |
27 | /*
28 | * Function onTick
29 | * Callback fired every condition tick
30 | */
31 |
32 | process.gameServer.world.applyEnvironmentalDamage(creature, 5, CONST.COLOR.LIGHTBLUE);
33 | process.gameServer.world.sendMagicEffect(creature.position, CONST.EFFECT.MAGIC.ENERGYHIT);
34 |
35 | }
36 |
37 | module.exports.onStart = onStart;
38 | module.exports.onExpire = onExpire;
39 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/1098/conditions/definitions/poisoned.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function onStart(creature) {
4 |
5 | /*
6 | * Function onStart
7 | * Callback fired on condition start
8 | */
9 |
10 | creature.sendCancelMessage("You were poisoned!");
11 |
12 | }
13 |
14 | function onExpire(creature) {
15 |
16 | /*
17 | * Function onExpire
18 | * Callback fired on condition expire
19 | */
20 |
21 | creature.sendCancelMessage("You feel healthy again.");
22 |
23 | }
24 |
25 | function onTick(creature) {
26 |
27 | /*
28 | * Function onTick
29 | * Callback fired every condition tick
30 | */
31 |
32 | let damage = Math.max(1, 3 * (this.getFraction()));
33 |
34 | // Apply poison damage to the player
35 | process.gameServer.world.applyEnvironmentalDamage(creature, damage, CONST.COLOR.LIGHTGREEN);
36 | process.gameServer.world.sendMagicEffect(creature.position, CONST.EFFECT.MAGIC.GREEN_RINGS);
37 |
38 | }
39 |
40 | module.exports.onStart = onStart;
41 | module.exports.onExpire = onExpire;
42 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/1098/monsters/README.md:
--------------------------------------------------------------------------------
1 | # Definitions for monsters and their behaviour
2 |
3 | The monster definition files should always validate with the provided `schema.json`. See e.g., this [JSON Schema Validator](https://www.jsonschemavalidator.net/)
--------------------------------------------------------------------------------
/data/1098/monsters/definitions.json:
--------------------------------------------------------------------------------
1 | {
2 | "troll": "troll.json",
3 | "rabbit": "rabbit.json",
4 | "sheep": "sheep.json",
5 | "black-sheep": "black-sheep.json",
6 | "rat": "rat.json",
7 | "cave-rat": "cave-rat.json",
8 | "bear": "bear.json",
9 | "rotworm": "rotworm.json",
10 | "spider": "spider.json"
11 | }
--------------------------------------------------------------------------------
/data/1098/monsters/definitions/bear.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Bear",
4 | "health": 80,
5 | "maxHealth": 80,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 50,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "slowness": 20,
12 | "outfit": {
13 | "id": 16
14 | }
15 | },
16 | "experience": 0,
17 | "corpse": 2849,
18 | "behaviour": {
19 | "type": "HOSTILE",
20 | "fleeHealth": 60,
21 | "openDoors": false
22 | },
23 | "loot": [
24 | {"id": 2666, "probability": 1, "min": 1, "max": 3}
25 | ],
26 | "sayings": {
27 | "texts": ["Grrrr", "Groar"],
28 | "slowness": 300,
29 | "chance": 0.75
30 | }
31 | }
--------------------------------------------------------------------------------
/data/1098/monsters/definitions/black-sheep.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Sheep",
4 | "health": 5,
5 | "maxHealth": 5,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "slowness": 30,
12 | "outfit": {
13 | "id": 13
14 | }
15 | },
16 | "experience": 0,
17 | "corpse": 2914,
18 | "behaviour": {
19 | "type": "FRIENDLY",
20 | "fleeHealth": 4,
21 | "openDoors": false
22 | },
23 | "loot": [
24 | {"id": 2666, "probability": 1, "min": 1, "max": 3}
25 | ],
26 | "sayings": {
27 | "texts": ["Behhh"],
28 | "slowness": 300,
29 | "chance": 0.75
30 | }
31 | }
--------------------------------------------------------------------------------
/data/1098/monsters/definitions/cave-rat.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Cave Rat",
4 | "health": 30,
5 | "maxHealth": 30,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 8,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "slowness": 20,
12 | "outfit": {
13 | "id": 56
14 | }
15 | },
16 | "experience": 10,
17 | "behaviour": {
18 | "type": "HOSTILE",
19 | "fleeHealth": 0,
20 | "openDoors": true
21 | },
22 | "corpse": 2813,
23 | "loot": [
24 | {
25 | "id": 2148,
26 | "probability": 1,
27 | "min": 1,
28 | "max": 4
29 | },
30 | {
31 | "id": 2696,
32 | "probability": 0.1,
33 | "min": 1,
34 | "max": 1
35 | }
36 | ],
37 | "sayings": {
38 | "texts": [
39 | "Meep!",
40 | "Meeeeep!"
41 | ],
42 | "slowness": 300,
43 | "chance": 0.75
44 | }
45 | }
--------------------------------------------------------------------------------
/data/1098/monsters/definitions/rabbit.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Rabbit",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "slowness": 15,
12 | "outfit": {
13 | "id": 74
14 | }
15 | },
16 | "experience": 0,
17 | "behaviour": {
18 | "type": "FRIENDLY",
19 | "fleeHealth": 50,
20 | "openDoors": true
21 | },
22 | "corpse": 3119,
23 | "loot": [
24 | {"id": 2666, "probability": 1, "min": 1, "max": 3}
25 | ],
26 | "sayings": {
27 | "texts": ["Mip mip mip"],
28 | "slowness": 300,
29 | "chance": 0.75
30 | }
31 | }
--------------------------------------------------------------------------------
/data/1098/monsters/definitions/rat.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Rat",
4 | "health": 20,
5 | "mana": 0,
6 | "maxMana": 0,
7 | "maxHealth": 20,
8 | "attack": 1,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "slowness": 20,
12 | "outfit": {
13 | "id": 21
14 | }
15 | },
16 | "experience": 50,
17 | "corpse": 2813,
18 | "behaviour": {
19 | "type": "HOSTILE",
20 | "fleeHealth": 5,
21 | "openDoors": true
22 | },
23 | "loot": [
24 | {
25 | "id": 2148,
26 | "probability": 1,
27 | "min": 1,
28 | "max": 4
29 | },
30 | {
31 | "id": 2696,
32 | "probability": 0.1,
33 | "min": 1,
34 | "max": 1
35 | }
36 | ],
37 | "sayings": {
38 | "texts": [
39 | "Meep!"
40 | ],
41 | "slowness": 300,
42 | "chance": 0.75
43 | }
44 | }
--------------------------------------------------------------------------------
/data/1098/monsters/definitions/rotworm.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Rotworm",
4 | "health": 40,
5 | "maxHealth": 40,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 15,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "slowness": 50,
12 | "outfit": {
13 | "id": 26
14 | }
15 | },
16 | "experience": 0,
17 | "corpse": 2849,
18 | "behaviour": {
19 | "type": "HOSTILE",
20 | "fleeHealth": 0,
21 | "openDoors": false
22 | },
23 | "loot": [
24 | {"id": 2666, "probability": 1, "min": 1, "max": 3}
25 | ]
26 | }
--------------------------------------------------------------------------------
/data/1098/monsters/definitions/sheep.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Sheep",
4 | "health": 5,
5 | "maxHealth": 5,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "slowness": 30,
12 | "outfit": {
13 | "id": 14
14 | }
15 | },
16 | "experience": 0,
17 | "corpse": 2914,
18 | "behaviour": {
19 | "type": "FRIENDLY",
20 | "fleeHealth": 4,
21 | "openDoors": false
22 | },
23 | "loot": [
24 | {"id": 2666, "probability": 1, "min": 1, "max": 3}
25 | ],
26 | "sayings": {
27 | "texts": ["Behhh"],
28 | "slowness": 300,
29 | "chance": 0.75
30 | }
31 | }
--------------------------------------------------------------------------------
/data/1098/monsters/definitions/spider.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Spider",
4 | "health": 30,
5 | "maxHealth": 30,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 10,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "slowness": 20,
12 | "outfit": {
13 | "id": 30
14 | }
15 | },
16 | "experience": 0,
17 | "corpse": 2807,
18 | "behaviour": {
19 | "type": "HOSTILE",
20 | "fleeHealth": 10,
21 | "openDoors": false
22 | }
23 | }
--------------------------------------------------------------------------------
/data/1098/monsters/definitions/troll.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Troll",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "attack": 1,
7 | "mana": 0,
8 | "maxMana": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "slowness": 20,
12 | "outfit": {
13 | "id": 15
14 | }
15 | },
16 | "experience": 20,
17 | "corpse": 2806,
18 | "behaviour": {
19 | "type": "HOSTILE",
20 | "fleeHealth": 5,
21 | "openDoors": true
22 | },
23 | "loot": [
24 | {
25 | "id": 2148,
26 | "probability": 1,
27 | "min": 1,
28 | "max": 5
29 | },
30 | {
31 | "id": 2148,
32 | "probability": 1,
33 | "min": 1,
34 | "max": 5
35 | },
36 | {
37 | "id": 2461,
38 | "probability": 0.1
39 | },
40 | {
41 | "id": 2148,
42 | "probability": 1,
43 | "min": 1,
44 | "max": 5
45 | }
46 | ],
47 | "sayings": {
48 | "texts": [
49 | "Hmmm, bugs",
50 | "Hmmm, dogs",
51 | "Grrr",
52 | "Groar",
53 | "Gruntz!"
54 | ],
55 | "slowness": 300,
56 | "chance": 0.75
57 | }
58 | }
--------------------------------------------------------------------------------
/data/1098/npcs/README.md:
--------------------------------------------------------------------------------
1 | # Definitions for NPCs and their behaviour
2 |
3 | The NPC definition files should always validate with the provided `schema.json`. See e.g., this [JSON Schema Validator](https://www.jsonschemavalidator.net/)
--------------------------------------------------------------------------------
/data/1098/npcs/definitions.json:
--------------------------------------------------------------------------------
1 | {
2 | "humphrey": {
3 | "position": {
4 | "x": 73,
5 | "y": 63,
6 | "z": 8
7 | },
8 | "enabled": true,
9 | "definition": "humphrey.json"
10 | }
11 | }
--------------------------------------------------------------------------------
/data/1098/npcs/definitions/albert.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Albert",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 1,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "slowness": 30,
12 | "outfit": {
13 | "id": 128,
14 | "details": {
15 | "head": 0,
16 | "body": 76,
17 | "legs": 116,
18 | "feet": 131
19 | }
20 | }
21 | },
22 | "hearingRange": 5,
23 | "wanderRange": 2,
24 | "trade": [
25 | {
26 | "name": "Shovel",
27 | "price": 5,
28 | "id": 2554,
29 | "sell": true
30 | },
31 | {
32 | "name": "Backpack",
33 | "price": 2,
34 | "id": 1988,
35 | "sell": true
36 | },
37 | {
38 | "name": "Rope",
39 | "price": 5,
40 | "id": 2120,
41 | "sell": true
42 | },
43 | {
44 | "name": "Torch",
45 | "price": 1,
46 | "id": 2050,
47 | "sell": true
48 | },
49 | {
50 | "name": "Pickaxe",
51 | "price": 3,
52 | "id": 2553,
53 | "sell": true
54 | },
55 | {
56 | "name": "Arrows",
57 | "price": 1,
58 | "id": 2544,
59 | "sell": true
60 | }
61 | ],
62 | "farewells": [
63 | "bye"
64 | ],
65 | "greetings": [
66 | "hello",
67 | "hi"
68 | ],
69 | "sayings": {
70 | "texts": [
71 | "Make sure you do not go exploring without adequate preparation and equipment!"
72 | ],
73 | "slowness": 300,
74 | "chance": 0.75
75 | },
76 | "script": "albert.js"
77 | }
--------------------------------------------------------------------------------
/data/1098/npcs/definitions/ghost.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "A Strange Ghost",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 1,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "slowness": 100,
12 | "outfit": {
13 | "id": 48
14 | }
15 | },
16 | "hearingRange": 5,
17 | "wanderRange": 2,
18 | "farewells": [
19 | "bye"
20 | ],
21 | "greetings": [
22 | "hello",
23 | "hi"
24 | ],
25 | "sayings": {
26 | "texts": [
27 | "..."
28 | ],
29 | "slowness": 300,
30 | "chance": 0.75
31 | }
32 | }
--------------------------------------------------------------------------------
/data/1098/npcs/definitions/humphrey.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Humphrey",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "attack": 1,
7 | "mana": 0,
8 | "maxMana": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "slowness": 40,
12 | "outfit": {
13 | "id": 57
14 | }
15 | },
16 | "hearingRange": 5,
17 | "wanderRange": 2,
18 | "farewells": [
19 | "bye"
20 | ],
21 | "greetings": [
22 | "hello",
23 | "hi"
24 | ],
25 | "sayings": {
26 | "texts": [
27 | "..."
28 | ],
29 | "slowness": 300,
30 | "chance": 0.75
31 | },
32 | "script": "humphrey.js"
33 | }
--------------------------------------------------------------------------------
/data/1098/npcs/definitions/jack.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Jack",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "attack": 1,
7 | "mana": 0,
8 | "maxMana": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "slowness": 40,
12 | "outfit": {
13 | "id": 129,
14 | "details": {
15 | "head": 97,
16 | "body": 95,
17 | "legs": 116,
18 | "feet": 114
19 | }
20 | }
21 | },
22 | "hearingRange": 5,
23 | "wanderRange": 2,
24 | "farewells": [
25 | "bye"
26 | ],
27 | "greetings": [
28 | "hello",
29 | "hi"
30 | ],
31 | "sayings": {
32 | "texts": [
33 | "Yoho-Yoho, a law abiding sailors life for me!"
34 | ],
35 | "slowness": 300,
36 | "chance": 0.75
37 | },
38 | "script": "jack.js"
39 | }
--------------------------------------------------------------------------------
/data/1098/npcs/definitions/karst.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Karst",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 1,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "slowness": 30,
12 | "outfit": {
13 | "id": 128,
14 | "details": {
15 | "head": 38,
16 | "body": 63,
17 | "legs": 67,
18 | "feet": 39
19 | }
20 | }
21 | },
22 | "hearingRange": 5,
23 | "wanderRange": 3,
24 | "trade": [
25 | {
26 | "name": "Mug of Beer",
27 | "price": 1,
28 | "id": 2012,
29 | "count": 3,
30 | "sell": true
31 | },
32 | {
33 | "name": "Mug of Wine",
34 | "price": 2,
35 | "id": 2012,
36 | "count": 15,
37 | "sell": true
38 | }
39 | ],
40 | "farewells": [
41 | "bye"
42 | ],
43 | "greetings": [
44 | "hello",
45 | "hi"
46 | ],
47 | "sayings": {
48 | "texts": [
49 | "Beer!",
50 | "Booze!"
51 | ],
52 | "slowness": 300,
53 | "chance": 0.75
54 | },
55 | "script": "karst.js"
56 | }
--------------------------------------------------------------------------------
/data/1098/npcs/definitions/marrow.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Marrow",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "attack": 1,
7 | "mana": 0,
8 | "maxMana": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "slowness": 20,
12 | "outfit": {
13 | "id": 134,
14 | "details": {
15 | "head": 0,
16 | "body": 39,
17 | "legs": 25,
18 | "feet": 131
19 | }
20 | }
21 | },
22 | "hearingRange": 5,
23 | "wanderRange": 2,
24 | "trade": [
25 | {
26 | "name": "Leather Armor",
27 | "price": 10,
28 | "id": 2467,
29 | "sell": true
30 | },
31 | {
32 | "name": "Leather Legs",
33 | "price": 5,
34 | "id": 2649,
35 | "sell": true
36 | },
37 | {
38 | "name": "Leather Helmet",
39 | "price": 3,
40 | "id": 2461,
41 | "sell": true
42 | },
43 | {
44 | "name": "Bow",
45 | "price": 10,
46 | "id": 2456,
47 | "sell": true
48 | },
49 | {
50 | "name": "Mace",
51 | "price": 5,
52 | "id": 2398,
53 | "sell": true
54 | },
55 | {
56 | "name": "Leather Boots",
57 | "price": 3,
58 | "id": 2643,
59 | "sell": true
60 | }
61 | ],
62 | "farewells": [
63 | "bye"
64 | ],
65 | "greetings": [
66 | "hello",
67 | "hi"
68 | ],
69 | "sayings": {
70 | "texts": [
71 | "All sorts of weaponry and armors for sale! The finest in town!"
72 | ],
73 | "slowness": 300,
74 | "chance": 0.75
75 | },
76 | "script": "marrow.js"
77 | }
--------------------------------------------------------------------------------
/data/1098/npcs/definitions/miller.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Miller",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "attack": 1,
7 | "mana": 0,
8 | "maxMana": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "slowness": 20,
12 | "outfit": {
13 | "id": 128,
14 | "details": {
15 | "head": 132,
16 | "body": 77,
17 | "legs": 116,
18 | "feet": 98
19 | }
20 | }
21 | },
22 | "hearingRange": 5,
23 | "wanderRange": 2,
24 | "farewells": [
25 | "bye"
26 | ],
27 | "trade": [
28 | {
29 | "name": "Scythe",
30 | "price": 5,
31 | "id": 2550,
32 | "sell": true
33 | }
34 | ],
35 | "greetings": [
36 | "hello",
37 | "hi"
38 | ],
39 | "sayings": {
40 | "texts": [
41 | "Round and round she goes.."
42 | ],
43 | "slowness": 300,
44 | "chance": 0.75
45 | },
46 | "script": "miller.js"
47 | }
--------------------------------------------------------------------------------
/data/1098/npcs/definitions/sarah.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Sarah",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "attack": 1,
7 | "mana": 0,
8 | "maxMana": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "slowness": 20,
12 | "outfit": {
13 | "id": 140,
14 | "details": {
15 | "head": 39,
16 | "body": 105,
17 | "legs": 96,
18 | "feet": 59
19 | }
20 | }
21 | },
22 | "hearingRange": 5,
23 | "wanderRange": 2,
24 | "farewells": [
25 | "bye"
26 | ],
27 | "greetings": [
28 | "hello",
29 | "hi"
30 | ],
31 | "sayings": {
32 | "texts": [
33 | "♫♪♪",
34 | "♪♫♪♫",
35 | "What a cloudy day.."
36 | ],
37 | "slowness": 300,
38 | "chance": 0.75
39 | },
40 | "script": "sarah.js"
41 | }
--------------------------------------------------------------------------------
/data/1098/npcs/definitions/script/albert.js:
--------------------------------------------------------------------------------
1 | module.exports = function talkScriptAlbert() {
2 |
3 | /*
4 | * Function talkScriptAlbert
5 | * Definitions for NPC Albert
6 | */
7 |
8 | // Reference the base state (a base state is required)
9 | this.__baseTalkState = this.__talkState = baseTalkState;
10 |
11 | this.on("focus", function(player) {
12 |
13 | if(process.gameServer.world.clock.isMorning()) {
14 | this.internalCreatureSay("Good morning %s! Did you catch the surise?".format(player.name), CONST.COLOR.YELLOW);
15 | } else if(process.gameServer.world.clock.isNight()) {
16 | this.internalCreatureSay("Beautiful night eh, %s?".format(player.name), CONST.COLOR.YELLOW);
17 | } else {
18 | this.internalCreatureSay("Good day %s!".format(player.name), CONST.COLOR.YELLOW);
19 | }
20 |
21 | }.bind(this));
22 |
23 | this.on("enter", player => this.privateSay(player, "Oh, it's you again.. %s".format(player.name), CONST.COLOR.YELLOW));
24 | this.on("defocus", player => this.internalCreatureSay("See you around, %s.".format(player.name), CONST.COLOR.YELLOW));
25 | this.on("exit", player => this.internalCreatureSay("Youths these days..", CONST.COLOR.YELLOW));
26 | this.on("regreet", player => this.internalCreatureSay("Can I help you, %s?".format(player.name), CONST.COLOR.YELLOW));
27 | this.on("idle", player => this.internalCreatureSay("Not really a talker, huh.", CONST.COLOR.YELLOW));
28 | this.on("busy", player => this.internalCreatureSay("One moment please!", CONST.COLOR.YELLOW));
29 |
30 | }
31 |
32 | function baseTalkState(player, message) {
33 |
34 | /*
35 | * Function baseTalkState
36 | * The base state of the NPC. It will respond to the following keywords
37 | */
38 |
39 | switch(message) {
40 | case "trade":
41 | this.internalCreatureSay("Have a browse!", CONST.COLOR.YELLOW);
42 | this.openTradeWindow(player);
43 | break;
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/data/1098/npcs/definitions/script/karst.js:
--------------------------------------------------------------------------------
1 | const { drinkSceneBeer, drinkSceneWine } = require("./scene/karst");
2 |
3 | module.exports = function talkScriptAlbert() {
4 |
5 | /*
6 | * Function talkScriptAlbert
7 | * Definitions for NPC Albert
8 | */
9 |
10 | // Reference the base state (a base state is required)
11 | this.__baseTalkState = this.__talkState = baseTalkState;
12 |
13 | process.gameServer.world.clock.on("time", function(time) {
14 |
15 | if(time.endsWith("10")) {
16 | return this.setScene([drinkSceneBeer, drinkSceneWine].random());
17 | }
18 |
19 | }.bind(this));
20 |
21 | this.on("focus", function(player) {
22 | this.sayEmote("Hicks!", CONST.COLOR.YELLOW);
23 | }.bind(this));
24 |
25 | this.on("enter", player => this.privateSay(player, "Oh, it's you again.. %s".format(player.name), CONST.COLOR.YELLOW));
26 | this.on("defocus", player => this.internalCreatureSay("See you around, %s.".format(player.name), CONST.COLOR.YELLOW));
27 | this.on("exit", player => this.internalCreatureSay("Youths these days..", CONST.COLOR.YELLOW));
28 | this.on("regreet", player => this.internalCreatureSay("Can I help you, %s?".format(player.name), CONST.COLOR.YELLOW));
29 | this.on("idle", player => this.internalCreatureSay("Not really a talker, huh.", CONST.COLOR.YELLOW));
30 | this.on("busy", player => this.internalCreatureSay("One moment please!", CONST.COLOR.YELLOW));
31 |
32 | }
33 |
34 | function baseTalkState(player, message) {
35 |
36 | /*
37 | * Function baseTalkState
38 | * The base state of the NPC. It will respond to the following keywords
39 | */
40 |
41 | switch(message) {
42 | case "trade":
43 | this.internalCreatureSay("Have a browse!", CONST.COLOR.YELLOW);
44 | this.openTradeWindow(player);
45 | break;
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/data/1098/npcs/definitions/script/marrow.js:
--------------------------------------------------------------------------------
1 | const { keyScene } = require("./scene/marrow");
2 |
3 | module.exports = function talkScriptMarrow() {
4 |
5 | /*
6 | * Function talkScriptMarrow
7 | * Definitions for NPC Marrow
8 | */
9 |
10 | // Reference the base state (a base state is required)
11 | this.__baseTalkState = this.__talkState = baseTalkState;
12 |
13 | this.on("enter", player => this.privateSay(player, "Wipe your feet before coming in, %s!".format(player.name), CONST.COLOR.YELLOW));
14 | this.on("focus", player => this.internalCreatureSay("Oi, %s!".format(player.name), CONST.COLOR.YELLOW));
15 | this.on("defocus", player => this.internalCreatureSay("Be safe out there, %s".format(player.name), CONST.COLOR.YELLOW));
16 | this.on("exit", player => this.internalCreatureSay("Selfish.", CONST.COLOR.YELLOW));
17 | this.on("regreet", player => this.internalCreatureSay("Hmm?", CONST.COLOR.YELLOW));
18 | this.on("idle", player => this.internalCreatureSay("You are wasting my time.", CONST.COLOR.YELLOW));
19 | this.on("busy", player => this.internalCreatureSay("Hold your horses there, %s. I'm busy here.".format(player.name), CONST.COLOR.YELLOW));
20 |
21 | }
22 |
23 | function baseTalkState(player, message) {
24 |
25 | /*
26 | * Function baseTalkState
27 | * The base state of the NPC. It will respond to the following keywords
28 | */
29 |
30 | switch(message) {
31 | case "trade":
32 | this.internalCreatureSay("Here are my wares.", CONST.COLOR.YELLOW);
33 | this.openTradeWindow(player);
34 | break;
35 | case "cellar infestation":
36 | case "infestation":
37 | this.internalCreatureSay("We have a little rat problem down there in the cellar.", CONST.COLOR.YELLOW);
38 | break;
39 | case "closed":
40 | case "door":
41 | case "cellar":
42 | this.internalCreatureSay("The cellar door is locked and you'll need a key to open it.", CONST.COLOR.YELLOW);
43 | break;
44 | case "key":
45 | this.internalCreatureSay("Are you looking for the key to the cellar?", CONST.COLOR.YELLOW);
46 | this.setTalkState(keyTalkState);
47 | break;
48 | }
49 |
50 | }
51 |
52 | function keyTalkState(player, message) {
53 |
54 | switch(message) {
55 | case "no":
56 | this.internalCreatureSay("Ok let me know if I can do something for you.", CONST.COLOR.YELLOW);
57 | this.setTalkState(baseTalkState);
58 | break;
59 | case "yes":
60 | this.setScene(keyScene);
61 | break;
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/data/1098/npcs/definitions/script/sarah.js:
--------------------------------------------------------------------------------
1 | module.exports = function sarah() {
2 |
3 | /*
4 | * Definitions for NPC Sarah
5 | * All events are emitters when the character engages with the NPC
6 | */
7 |
8 | // Reference the base state (a base state is required)
9 | this.__baseTalkState = this.__talkState = baseTalkState;
10 |
11 | // Defaults
12 | this.on("focus", player => this.internalCreatureSay("Good to see you here, %s.".format(player.name), CONST.COLOR.YELLOW));
13 | this.on("defocus", player => this.internalCreatureSay("Safe climb down!", CONST.COLOR.YELLOW));
14 | this.on("exit", player => this.internalCreatureSay("Pff.", CONST.COLOR.YELLOW));
15 | this.on("regreet", player => this.internalCreatureSay("Yes, sweety?", CONST.COLOR.YELLOW));
16 | this.on("idle", player => this.internalCreatureSay("Excuse me..", CONST.COLOR.YELLOW));
17 | this.on("busy", player => this.internalCreatureSay("One moment darling.", CONST.COLOR.YELLOW));
18 |
19 | }
20 |
21 | function baseTalkState(player, message) {
22 |
23 | /*
24 | * Function baseTalkState
25 | * The base state of the NPC. It will respond to the following keywords
26 | */
27 |
28 | switch(message) {
29 | case "name":
30 | this.internalCreatureSay("My name is Sarah.", CONST.COLOR.YELLOW);
31 | break;
32 | case "sarah":
33 | this.internalCreatureSay("That's me!", CONST.COLOR.YELLOW);
34 | this.sayEmote("❤", CONST.COLOR.RED);
35 | break;
36 | case "miller":
37 | this.internalCreatureSay("Miller is my dad. Look down there and you may see him!", CONST.COLOR.YELLOW);
38 | break;
39 | }
40 |
41 | }
--------------------------------------------------------------------------------
/data/1098/npcs/definitions/script/scene/jack-story.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | "mode": "move",
4 | "timeout": 10,
5 | "position": {
6 | "x": 85,
7 | "y": 127,
8 | "z": 8
9 | }
10 | },
11 | {
12 | "mode": "face",
13 | "direction": 2
14 | },
15 | {
16 | "mode": "talk",
17 | "duration": 100,
18 | "message": "The sea was angry that day.."
19 | },
20 | {
21 | "mode": "idle",
22 | "duration": 20
23 | },
24 | {
25 | "mode": "talk",
26 | "duration": 100,
27 | "message": "We went fishing and then.."
28 | },
29 | {
30 | "mode": "face",
31 | "direction": 0,
32 | },
33 | {
34 | "mode": "spell",
35 | "effect": 2,
36 | "position": {
37 | "x": 85,
38 | "y": 127,
39 | "z": 8
40 | }
41 | },
42 | {
43 | "mode": "talk",
44 | "duration": 20,
45 | "message": "Bam! Water in my face.. crazy time."
46 | },
47 | {
48 | "mode": "idle",
49 | "duration": 20
50 | }
51 | ]
--------------------------------------------------------------------------------
/data/1098/npcs/definitions/script/scene/karst.js:
--------------------------------------------------------------------------------
1 | module.exports.drinkSceneBeer = [{
2 | "mode": "talk",
3 | "duration": 20,
4 | "message": "Wait one second. I have got to try a little bit of this right here.."
5 | }, {
6 | "mode": "move",
7 | "timeout": 50,
8 | "position": {
9 | "x": 54,
10 | "y": 90,
11 | "z": 9
12 | }
13 | }, {
14 | "mode": "face",
15 | "direction": 0,
16 | }, {
17 | "mode": "spell",
18 | "effect": 2,
19 | "position": {
20 | "x": 54,
21 | "y": 89,
22 | "z": 9
23 | }
24 | }, {
25 | "mode": "talk",
26 | "duration": 5,
27 | "message": "Ahh, refressshing!"
28 | }, {
29 | "mode": "emote",
30 | "message": "Hicks!"
31 | }]
32 |
33 | module.exports.drinkSceneWine = [{
34 | "mode": "talk",
35 | "duration": 20,
36 | "message": "Maybe I will try a little bit of this one now.."
37 | }, {
38 | "mode": "move",
39 | "timeout": 50,
40 | "position": {
41 | "x": 53,
42 | "y": 90,
43 | "z": 9
44 | }
45 | }, {
46 | "mode": "face",
47 | "direction": 0,
48 | }, {
49 | "mode": "spell",
50 | "effect": 2,
51 | "position": {
52 | "x": 53,
53 | "y": 89,
54 | "z": 9
55 | }
56 | }, {
57 | "mode": "talk",
58 | "duration": 5,
59 | "message": "Delicious!"
60 | }, {
61 | "mode": "emote",
62 | "message": "Hicks!"
63 | }]
--------------------------------------------------------------------------------
/data/1098/npcs/definitions/script/scene/marrow.js:
--------------------------------------------------------------------------------
1 | module.exports.keyScene = [{
2 | "mode": "talk",
3 | "duration": 20,
4 | "message": "Let me fetch a spare key from storage for you."
5 | }, {
6 | "mode": "move",
7 | "timeout": 50,
8 | "position": {
9 | "x": 100,
10 | "y": 86,
11 | "z": 8
12 | }
13 | }, {
14 | "mode": "face",
15 | "direction": 3
16 | }, {
17 | "mode": "spell",
18 | "effect": 4,
19 | "position": {
20 | "x": 99,
21 | "y": 86,
22 | "z": 8
23 | }
24 | }, {
25 | "mode": "move",
26 | "timeout": 50,
27 | "position": {
28 | "x": 100,
29 | "y": 90,
30 | "z": 8
31 | }
32 | }, {
33 | "mode": "face",
34 | "direction": 1
35 | }, {
36 | "mode": "add",
37 | "item": 2088,
38 | "actionId": 2000,
39 | "count": 1,
40 | "position": {
41 | "x": 101,
42 | "y": 90,
43 | "z": 8
44 | }
45 | }, {
46 | "mode": "talk",
47 | "duration": 20,
48 | "message": "Alright. Be careful down there, buddy."
49 | }];
--------------------------------------------------------------------------------
/data/1098/npcs/definitions/script/scene/miller.js:
--------------------------------------------------------------------------------
1 | module.exports.millScene = [{
2 | "mode": "talk",
3 | "duration": 20,
4 | "message": "Alright, time to turn in for the night."
5 | }, {
6 | "mode": "move",
7 | "timeout": 50,
8 | "position": {
9 | "x": 84,
10 | "y": 116,
11 | "z": 8
12 | }
13 | }, {
14 | "mode": "anchor",
15 | "position": {
16 | "x": 84,
17 | "y": 116,
18 | "z": 8
19 | }
20 | }];
21 |
22 | module.exports.fieldScene = [{
23 | "mode": "talk",
24 | "duration": 20,
25 | "message": "It is a beautiful day out there!"
26 | }, {
27 | "mode": "move",
28 | "timeout": 50,
29 | "position": {
30 | "x": 96,
31 | "y": 119,
32 | "z": 8
33 | }
34 | }, {
35 | "mode": "anchor",
36 | "position": {
37 | "x": 96,
38 | "y": 119,
39 | "z": 8
40 | }
41 | }];
42 |
--------------------------------------------------------------------------------
/data/1098/npcs/definitions/script/teller.js:
--------------------------------------------------------------------------------
1 | module.exports = function sarah() {
2 |
3 | /*
4 | * Definitions for NPC Sarah
5 | * All events are emitters when the character engages with the NPC
6 | */
7 |
8 | // Reference the base state (a base state is required)
9 | this.__baseTalkState = this.__talkState = baseTalkState;
10 |
11 | // Defaults
12 | this.on("focus", player => this.internalCreatureSay("Welcome, %s.".format(player.name), CONST.COLOR.YELLOW));
13 | this.on("defocus", player => this.internalCreatureSay("Goodbye.", CONST.COLOR.YELLOW));
14 | this.on("exit", player => this.sayEmote("!?", CONST.COLOR.YELLOW));
15 | this.on("regreet", player => this.internalCreatureSay("Tell me what you need, %s".format(player.name), CONST.COLOR.YELLOW));
16 | this.on("idle", player => this.internalCreatureSay("Hello?", CONST.COLOR.YELLOW));
17 | this.on("busy", player => this.internalCreatureSay("I'll help you next, %s!".format(player.name), CONST.COLOR.YELLOW));
18 |
19 | }
20 |
21 | function baseTalkState(player, message) {
22 |
23 | /*
24 | * Function baseTalkState
25 | * The base state of the NPC. It will respond to the following keywords
26 | */
27 |
28 | switch(message) {
29 | case "name":
30 | this.internalCreatureSay("Name's Teller. Bet you can guess why!", CONST.COLOR.YELLOW);
31 | break;
32 | case "teller":
33 | this.internalCreatureSay("Yep! I'm Borne's banker! Head up stairs if you need to access your locker.", CONST.COLOR.YELLOW);
34 | this.sayEmote("💰", CONST.COLOR.RED);
35 | break;
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/data/1098/npcs/definitions/teller.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Teller",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "attack": 1,
7 | "mana": 0,
8 | "maxMana": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "slowness": 20,
12 | "outfit": {
13 | "id": 141,
14 | "details": {
15 | "head": 12,
16 | "body": 2,
17 | "legs": 12,
18 | "feet": 10
19 | }
20 | }
21 | },
22 | "hearingRange": 5,
23 | "wanderRange": 2,
24 | "farewells": [
25 | "bye"
26 | ],
27 | "greetings": [
28 | "hello",
29 | "hi"
30 | ],
31 | "sayings": {
32 | "texts": [
33 | "A safe place to store your items!"
34 | ],
35 | "slowness": 300,
36 | "chance": 0.75
37 | },
38 | "script": "teller.js"
39 | }
--------------------------------------------------------------------------------
/data/1098/runes/definitions.json:
--------------------------------------------------------------------------------
1 | {
2 | "2268": "sudden-death.js",
3 | "2311": "heavy-magic-missile.js",
4 | "2304": "fireball.js",
5 | "2301": "firefield.js",
6 | "2277": "energyfield.js",
7 | "2285": "poisonfield.js",
8 | "2293": "magicwall.js",
9 | "2261": "destroyfield.js",
10 | "2273": "ultimate-healing.js",
11 | "2305": "fire-bomb.js",
12 | "2312": "teleport.js",
13 | "2300": "hearthrune.js"
14 | }
--------------------------------------------------------------------------------
/data/1098/runes/definitions/destroyfield.js:
--------------------------------------------------------------------------------
1 | const Position = requireModule("position");
2 |
3 | module.exports = function destroyField(source, target) {
4 |
5 | /*
6 | * function suddenDeath
7 | * Code that handles the sudden death rune
8 | */
9 |
10 | let item = target.peekItem(0xFF);
11 |
12 | if(item === null) {
13 | return process.gameServer.world.sendMagicEffect(source.position, CONST.EFFECT.MAGIC.POFF);
14 | }
15 |
16 | if(!item.getPrototype().isMagicField()) {
17 | return process.gameServer.world.sendMagicEffect(source.position, CONST.EFFECT.MAGIC.POFF);
18 | }
19 |
20 | item.remove();
21 |
22 | process.gameServer.world.sendMagicEffect(target.position, CONST.EFFECT.MAGIC.POFF);
23 |
24 | return true;
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/data/1098/runes/definitions/energyfield.js:
--------------------------------------------------------------------------------
1 | const Position = requireModule("position");
2 |
3 | module.exports = function greatFireball(source, target) {
4 |
5 | /*
6 | * function suddenDeath
7 | * Code that handles the sudden death rune
8 | */
9 |
10 | // Get circle position for the GFB
11 | process.gameServer.world.sendDistanceEffect(source.position, target.position, CONST.EFFECT.PROJECTILE.ENERGY);
12 | target.addItem(process.gameServer.database.createThing(1495));
13 |
14 | return true;
15 |
16 | }
--------------------------------------------------------------------------------
/data/1098/runes/definitions/fire-bomb.js:
--------------------------------------------------------------------------------
1 | const Position = requireModule("position");
2 |
3 | module.exports = function greatFireball(source, target) {
4 |
5 | /*
6 | * function suddenDeath
7 | * Code that handles the sudden death rune
8 | */
9 |
10 | let square = Position.prototype.getSquare(1);
11 |
12 | process.gameServer.world.sendDistanceEffect(source.position, target.position, CONST.EFFECT.PROJECTILE.FIRE);
13 |
14 | square.forEach(function(position) {
15 |
16 | let relPosition = target.position.add(position);
17 | let tile = process.gameServer.world.getTileFromWorldPosition(relPosition);
18 |
19 | if(tile === null) {
20 | return;
21 | }
22 |
23 | if(tile.isBlockSolid()) {
24 | return;
25 | }
26 |
27 | // Get circle position for the GFB
28 | tile.addItem(process.gameServer.database.createThing(1487));
29 |
30 | });
31 |
32 | return true;
33 |
34 | }
--------------------------------------------------------------------------------
/data/1098/runes/definitions/fireball.js:
--------------------------------------------------------------------------------
1 | const Position = requireModule("position");
2 |
3 | module.exports = function greatFireball(source, target) {
4 |
5 | /*
6 | * function suddenDeath
7 | * Code that handles the sudden death rune
8 | */
9 |
10 | // Get circle position for the GFB
11 | let circle = Position.prototype.getRadius(2);
12 |
13 | process.gameServer.world.sendDistanceEffect(source.position, target.position, CONST.EFFECT.PROJECTILE.FIRE);
14 |
15 | circle.forEach(function(position) {
16 |
17 | let relPosition = target.position.add(position);
18 | let tile = process.gameServer.world.getTileFromWorldPosition(relPosition);
19 |
20 | if(tile === null) {
21 | return;
22 | }
23 |
24 | // Tile is blocked
25 | if(tile.isBlockSolid()) {
26 | return;
27 | }
28 |
29 | // Apply to the tile
30 | process.gameServer.world.sendMagicEffect(relPosition, CONST.EFFECT.MAGIC.FIREAREA);
31 |
32 | tile.monsters.forEach(function(monster) {
33 | process.gameServer.world.__damageEntity(source, monster, 1000, CONST.COLOR.RED);
34 | });
35 |
36 | });
37 |
38 | return true;
39 |
40 | }
--------------------------------------------------------------------------------
/data/1098/runes/definitions/firefield.js:
--------------------------------------------------------------------------------
1 | const Position = requireModule("position");
2 |
3 | module.exports = function greatFireball(source, target) {
4 |
5 | /*
6 | * function suddenDeath
7 | * Code that handles the sudden death rune
8 | */
9 |
10 | // Get circle position for the GFB
11 | process.gameServer.world.sendDistanceEffect(source.position, target.position, CONST.EFFECT.PROJECTILE.FIRE);
12 | target.addItem(process.gameServer.database.createThing(1487));
13 |
14 | return true;
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/data/1098/runes/definitions/hearthrune.js:
--------------------------------------------------------------------------------
1 | module.exports = function hearthrune(source, target) {
2 |
3 | /*
4 | * Function teleport
5 | * Code that handles the teleport rune to another (reachable) position
6 | */
7 |
8 | // Teleport and effects
9 | process.gameServer.world.teleportCreature(source, source.characterStatistics.templePosition);
10 | process.gameServer.world.sendMagicEffect(source.position, CONST.EFFECT.MAGIC.TELEPORT);
11 |
12 | // Never consume
13 | return false;
14 |
15 | }
--------------------------------------------------------------------------------
/data/1098/runes/definitions/heavy-magic-missile.js:
--------------------------------------------------------------------------------
1 | module.exports = function heavyMagicMissile(source, target) {
2 |
3 | /*
4 | * function heavyMagicMissile
5 | * Code that handles the sudden death rune
6 | */
7 |
8 | // If no monsters on the tile
9 | if(target.monsters.size === 0) {
10 | return false;
11 | }
12 |
13 | // Send magic effects
14 | process.gameServer.world.sendDistanceEffect(source.position, target.position, CONST.EFFECT.PROJECTILE.ENERGY);
15 | process.gameServer.world.sendMagicEffect(target.position, CONST.EFFECT.MAGIC.ENERGYHIT);
16 |
17 | // Apply the damage to all creatures
18 | target.monsters.forEach(function(monster) {
19 | process.gameServer.world.__damageEntity(source, monster, 1);
20 | });
21 |
22 | return true;
23 |
24 | }
--------------------------------------------------------------------------------
/data/1098/runes/definitions/magicwall.js:
--------------------------------------------------------------------------------
1 | const Position = requireModule("position");
2 |
3 | module.exports = function greatFireball(source, target) {
4 |
5 | /*
6 | * function suddenDeath
7 | * Code that handles the sudden death rune
8 | */
9 |
10 | // Get circle position for the GFB
11 | process.gameServer.world.sendDistanceEffect(source.position, target.position, CONST.EFFECT.PROJECTILE.ENERGY);
12 | target.addItem(process.gameServer.database.createThing(1498));
13 |
14 | return true;
15 |
16 | }
--------------------------------------------------------------------------------
/data/1098/runes/definitions/poisonfield.js:
--------------------------------------------------------------------------------
1 | const Position = requireModule("position");
2 |
3 | module.exports = function greatFireball(source, target) {
4 |
5 | /*
6 | * function suddenDeath
7 | * Code that handles the sudden death rune
8 | */
9 |
10 | // Get circle position for the GFB
11 | process.gameServer.world.sendDistanceEffect(source.position, target.position, CONST.EFFECT.PROJECTILE.POISON);
12 | target.addItem(process.gameServer.database.createThing(1496));
13 |
14 | return true;
15 |
16 | }
--------------------------------------------------------------------------------
/data/1098/runes/definitions/sudden-death.js:
--------------------------------------------------------------------------------
1 | module.exports = function suddenDeath(source, target) {
2 |
3 | /*
4 | * function suddenDeath
5 | * Code that handles the sudden death rune
6 | */
7 |
8 | // If no monsters on the tile
9 | if(target.monsters.size === 0) {
10 | return false;
11 | }
12 |
13 | process.gameServer.world.sendDistanceEffect(source.position, target.position, CONST.EFFECT.PROJECTILE.DEATH);
14 | process.gameServer.world.sendMagicEffect(target.position, CONST.EFFECT.MAGIC.MORTAREA);
15 |
16 | // Do damage
17 | target.monsters.forEach(function(monster) {
18 | process.gameServer.world.__damageEntity(source, monster, 10);
19 | });
20 |
21 | return true;
22 |
23 | }
--------------------------------------------------------------------------------
/data/1098/runes/definitions/teleport.js:
--------------------------------------------------------------------------------
1 | module.exports = function teleport(source, target) {
2 |
3 | /*
4 | * Function teleport
5 | * Code that handles the teleport rune to another (reachable) position
6 | */
7 |
8 | // Attempt to find a path from creature to the destination: if possible: allow teleport
9 | let path = process.gameServer.world.findPath(
10 | source,
11 | source.position,
12 | target.position,
13 | process.gameServer.world.pathfinder.opcodes.EXACT
14 | );
15 |
16 | // Pathfinding is not possible
17 | if(path.length === 0) {
18 | process.gameServer.world.sendMagicEffect(source.position, CONST.EFFECT.MAGIC.POFF);
19 | source.sendCancelMessage("You cannot teleport there.");
20 | return false;
21 | }
22 |
23 | // Teleport and effects
24 | process.gameServer.world.sendMagicEffect(source.position, CONST.EFFECT.MAGIC.MAGIC_BLUE);
25 | process.gameServer.world.teleportCreature(source, target.position);
26 | process.gameServer.world.sendMagicEffect(source.position, CONST.EFFECT.MAGIC.TELEPORT);
27 |
28 | // Consume
29 | return true;
30 |
31 | }
--------------------------------------------------------------------------------
/data/1098/runes/definitions/ultimate-healing.js:
--------------------------------------------------------------------------------
1 | module.exports = function suddenDeath(source, target) {
2 |
3 | /*
4 | * function suddenDeath
5 | * Code that handles the sudden death rune
6 | */
7 |
8 | // If no monsters on the tile
9 | if(target.players.size === 0) {
10 | return false;
11 | }
12 |
13 | process.gameServer.world.sendMagicEffect(target.position, CONST.EFFECT.MAGIC.MAGIC_BLUE);
14 |
15 | // Do damage
16 | target.players.forEach(function(player) {
17 | player.increaseHealth(10);
18 | });
19 |
20 | return true;
21 |
22 | }
--------------------------------------------------------------------------------
/data/1098/spawns/definitions.json:
--------------------------------------------------------------------------------
1 | [{
2 | "respawnTime": 40,
3 | "mid": "0000",
4 | "position": {
5 | "x": 80,
6 | "y": 43,
7 | "z": 8
8 | }
9 | }]
10 |
--------------------------------------------------------------------------------
/data/1098/spells/definitions.json:
--------------------------------------------------------------------------------
1 | {
2 | "0000": "cure.js",
3 | "0001": "explosion.js",
4 | "0002": "exura.js"
5 | }
--------------------------------------------------------------------------------
/data/1098/spells/definitions/cure.js:
--------------------------------------------------------------------------------
1 | const Condition = requireModule("condition");
2 |
3 | module.exports = function cureBurning() {
4 |
5 | /*
6 | * Function cureBurning
7 | * Spell to cure the burning condition from the player
8 | */
9 |
10 | // Not burning
11 | if(!this.conditions.has(Condition.prototype.BURNING)) {
12 | return 0;
13 | }
14 |
15 | this.conditions.removeCondition(Condition.prototype.BURNING);
16 | process.gameServer.world.sendMagicEffect(this.position, CONST.EFFECT.MAGIC.MAGIC_BLUE);
17 |
18 | // Return cooldown
19 | return 1000;
20 |
21 | }
--------------------------------------------------------------------------------
/data/1098/spells/definitions/explosion.js:
--------------------------------------------------------------------------------
1 | module.exports = function beamEnergy() {
2 |
3 | /*
4 | * Function beamEnergy
5 | * Creature energy beam function
6 | */
7 |
8 | let source = this;
9 |
10 | for(let i = 0; i < 5; i++) {
11 |
12 | let position = this.__getSpellPosition(0, -i);
13 |
14 | process.gameServer.world.sendMagicEffect(
15 | position,
16 | CONST.EFFECT.MAGIC.ENERGYHIT
17 | );
18 |
19 | let tile = process.gameServer.world.getTileFromWorldPosition(position);
20 |
21 | if(tile === null) {
22 | continue;
23 | }
24 |
25 | tile.players.forEach(function(player) {
26 |
27 | let damage = Number.prototype.random(0, 3);
28 |
29 | process.gameServer.world.__damageEntity(source, player, damage, CONST.COLOR.LIGHTBLUE);
30 |
31 | });
32 |
33 | }
34 |
35 | return 50;
36 |
37 | }
--------------------------------------------------------------------------------
/data/1098/spells/definitions/exura.js:
--------------------------------------------------------------------------------
1 | module.exports = function exura(properties) {
2 |
3 | process.gameServer.world.sendMagicEffect(
4 | this.position,
5 | CONST.EFFECT.MAGIC.SOUND_WHITE
6 | );
7 |
8 | return 10;
9 |
10 | }
--------------------------------------------------------------------------------
/data/1098/unique/definitions.json:
--------------------------------------------------------------------------------
1 | []
--------------------------------------------------------------------------------
/data/1098/world/definitions.json:
--------------------------------------------------------------------------------
1 | {
2 | "0000": "start-town.json",
3 | "0001": "rat-cave.json",
4 | "0002": "temple.json"
5 | }
--------------------------------------------------------------------------------
/data/1098/world/definitions/otbm/rat-cave-house.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/data/1098/world/definitions/otbm/rat-cave-spawn.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/data/1098/world/definitions/otbm/rat-cave.otbm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Inconcessus/html5-tibia-engine/d59136ecb516efad0b956145abd80c10c4ec7c0f/data/1098/world/definitions/otbm/rat-cave.otbm
--------------------------------------------------------------------------------
/data/1098/world/definitions/otbm/start-town-house.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/data/1098/world/definitions/otbm/start-town-spawn.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/data/1098/world/definitions/otbm/start-town.otbm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Inconcessus/html5-tibia-engine/d59136ecb516efad0b956145abd80c10c4ec7c0f/data/1098/world/definitions/otbm/start-town.otbm
--------------------------------------------------------------------------------
/data/1098/world/definitions/otbm/temple.otbm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Inconcessus/html5-tibia-engine/d59136ecb516efad0b956145abd80c10c4ec7c0f/data/1098/world/definitions/otbm/temple.otbm
--------------------------------------------------------------------------------
/data/1098/world/definitions/rat-cave.json:
--------------------------------------------------------------------------------
1 | {
2 | "file": "rat-cave.otbm",
3 | "name": "The Underwell",
4 | "title": "A Moist Cave",
5 | "music": "field",
6 | "weather": 128,
7 | "ambient": {
8 | "r": 0,
9 | "g": 0,
10 | "b": 0,
11 | "a": 255
12 | }
13 | }
--------------------------------------------------------------------------------
/data/1098/world/definitions/start-town.json:
--------------------------------------------------------------------------------
1 | {
2 | "file": "start-town.otbm",
3 | "name": "Gloria",
4 | "music": "field",
5 | "title": "The City of Faith",
6 | "weather": 0,
7 | "ambient": {
8 | "r": 155,
9 | "g": 155,
10 | "b": 155,
11 | "a": 255
12 | }
13 | }
--------------------------------------------------------------------------------
/data/1098/world/definitions/temple.json:
--------------------------------------------------------------------------------
1 | {
2 | "file": "temple.otbm",
3 | "name": "The Temple",
4 | "title": "The Alter of the Gods",
5 | "music": "field",
6 | "weather": 64,
7 | "ambient": {
8 | "r": 160,
9 | "g": 0,
10 | "b": 0,
11 | "a": 255
12 | }
13 | }
--------------------------------------------------------------------------------
/data/740/account-template.json:
--------------------------------------------------------------------------------
1 | {
2 | "achievements": {
3 | "completed": [
4 | 0,
5 | 1,
6 | 2,
7 | 3
8 | ],
9 | "properties": {
10 | "NUMBER_TILES_WALKED": 0,
11 | "TEST_ADDITION": 0,
12 | "EXPLORE_TOWN": 0
13 | }
14 | },
15 | "creatureStatistics": {
16 | "attack": 4,
17 | "attackSlowness": 20,
18 | "defense": 2,
19 | "direction": 2,
20 | "health": 50,
21 | "maxHealth": 50,
22 | "name": null,
23 | "outfit": {
24 | "id": null,
25 | "details": {
26 | "head": 78,
27 | "body": 69,
28 | "legs": 58,
29 | "feet": 76
30 | },
31 | "mount": 0,
32 | "mounted": false,
33 | "addonOne": false,
34 | "addonTwo": false
35 | },
36 | "speed": 120
37 | },
38 | "inbox": [],
39 | "depot": [
40 | {
41 | "id": 2594
42 | },
43 | null,
44 | null,
45 | null
46 | ],
47 | "characterStatistics": {
48 | "position": {
49 | "x": 82,
50 | "y": 81,
51 | "z": 8
52 | },
53 | "admin": false,
54 | "sex": null,
55 | "templePosition": {
56 | "x": 82,
57 | "y": 81,
58 | "z": 8
59 | },
60 | "maxCapacity": 20000,
61 | "level": 1,
62 | "experience": 0,
63 | "availableMounts": [],
64 | "availableOutfits": []
65 | },
66 | "spellbook": {
67 | "availableSpells": [
68 | 0,
69 | 1,
70 | 2,
71 | 3,
72 | 4,
73 | 5,
74 | 7,
75 | 8,
76 | 9
77 | ],
78 | "cooldowns": []
79 | },
80 | "keyring": [],
81 | "equipment": [
82 | null,
83 | null,
84 | null,
85 | null,
86 | null,
87 | null,
88 | {
89 | "id": 1988,
90 | "duration": null,
91 | "items": [
92 | null,
93 | null,
94 | null,
95 | null,
96 | null,
97 | null,
98 | null,
99 | null,
100 | null,
101 | null,
102 | null,
103 | null,
104 | null,
105 | null,
106 | null,
107 | null,
108 | null,
109 | null,
110 | null,
111 | null
112 | ]
113 | },
114 | null,
115 | null,
116 | null
117 | ],
118 | "friends": []
119 | }
120 |
--------------------------------------------------------------------------------
/data/740/achievements.json:
--------------------------------------------------------------------------------
1 | [{
2 | "title": "Walking",
3 | "description": "You have walked a single tile.",
4 | "conditions": [{
5 | "property": "NUMBER_TILES_WALKED",
6 | "type": "greater",
7 | "value": 1
8 | }]
9 | }, {
10 | "title": "Walking Some More",
11 | "description": "You have walked five tiles.",
12 | "conditions": [{
13 | "property": "NUMBER_TILES_WALKED",
14 | "type": "greater",
15 | "value": 5
16 | }]
17 | }, {
18 | "title": "Walking Some More and More and More!",
19 | "description": "You have walked five tiles.",
20 | "conditions": [{
21 | "property": "NUMBER_TILES_WALKED",
22 | "type": "greater",
23 | "value": 10
24 | }]
25 | }, {
26 | "title": "Taking a trip downtown..",
27 | "description": "Walking Fast!",
28 | "conditions": [{
29 | "property": "EXPLORE_TOWN",
30 | "type": "greater",
31 | "value": 7
32 | }]
33 | }]
--------------------------------------------------------------------------------
/data/740/actions/definitions.json:
--------------------------------------------------------------------------------
1 | [
2 | {"id": 2580, "on": "useWith", "callback": "fish.js"},
3 | {"id": 2785, "on": "use", "callback": "blueberry.js"},
4 |
5 | {"id": 2362, "on": "use", "callback": "food.js"},
6 | {"from": 2666, "to": 2691, "on": "use", "callback": "food.js"},
7 | {"from": 2695, "to": 2696, "on": "use", "callback": "food.js"},
8 | {"id": 2696, "on": "use", "callback": "food.js"},
9 | {"from": 2787, "to": 2796, "on": "use", "callback": "food.js"},
10 |
11 | {"from": 1945, "to": 1946, "on": "use", "callback": "lever.js"},
12 | {"id": 1386, "on": "use", "callback": "ladder.js"},
13 | {"id": 2120, "on": "useWith", "callback": "rope.js"},
14 | {"id": 2553, "on": "useWith", "callback": "pick.js"},
15 | {"id": 2554, "on": "useWith", "callback": "shovel.js"},
16 | {"id": 2420, "on": "useWith", "callback": "machete.js"},
17 | {"id": 2041, "on": "use", "callback": "toggle.js"},
18 | {"id": 2057, "on": "use", "callback": "toggle.js"},
19 | {"id": 430, "on": "use", "callback": "sewer.js"},
20 |
21 | {"id": 2071, "on": "use", "callback": "music.js"},
22 |
23 | {"id": 2694, "on": "useWith", "callback": "wheat.js"},
24 | {"id": 2693, "on": "useWith", "callback": "dough.js"},
25 | {"id": 2692, "on": "useWith", "callback": "flour.js"},
26 | {"id": 2550, "on": "useWith", "callback": "scythe.js"},
27 |
28 | {"from": 1479, "to": 1480, "on": "use", "callback": "toggle.js"},
29 | {"from": 2058, "to": 2061, "on": "use", "callback": "toggle.js"},
30 | {"from": 2066, "to": 2069, "on": "use", "callback": "toggle.js"},
31 | {"from": 2037, "to": 2040, "on": "use", "callback": "toggle.js"},
32 | {"from": 2044, "to": 2045, "on": "use", "callback": "toggle.js"},
33 | {"from": 2050, "to": 2055, "on": "use", "callback": "toggle.js"},
34 | {"from": 2261, "to": 2316, "on": "useWith", "callback": "rune.js"},
35 | {"from": 1786, "to": 1793, "on": "use", "callback": "toggle.js"},
36 | {"from": 2162, "to": 2163, "on": "use", "callback": "toggle.js"},
37 |
38 | {"ids": [416, 426, 446], "on": "enter", "callback": "enterTiles.js"},
39 | {"ids": [417, 425, 447], "on": "exit", "callback": "exitTiles.js"},
40 | {"id": 2046, "on": "useWith", "callback": "lamp.js"}
41 | ]
--------------------------------------------------------------------------------
/data/740/actions/definitions/blueberry.js:
--------------------------------------------------------------------------------
1 | module.exports = function blueberryBush(player, tile, index, item) {
2 |
3 | /*
4 | * Function blueberryBush
5 | * Picks blueberries from a blueberry bush
6 | */
7 |
8 | let bush = process.gameServer.database.createThing(2786);
9 | bush.scheduleDecay();
10 |
11 | item.replace(bush);
12 |
13 | let amount = Number.prototype.randomExp(3, 10, 3);
14 |
15 | tile.addTopThing(process.gameServer.database.createThing(2677).setCount(amount));
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/data/740/actions/definitions/dough.js:
--------------------------------------------------------------------------------
1 | module.exports = function useDough(player, item, tile) {
2 |
3 | /*
4 | * Function useDough
5 | * Script called when dough is used on an oven
6 | */
7 |
8 | // Only allowed when not moving
9 | if(player.isMoving()) {
10 | return true;
11 | }
12 |
13 | if(!player.isBesidesThing(tile)) {
14 | return;
15 | }
16 |
17 | let thing = tile.getTopItem();
18 |
19 | if(thing === null) {
20 | return;
21 | }
22 |
23 | // Identifiers of the ovens
24 | if(![1786, 1788, 1790, 1792].includes(thing.id)) {
25 | return;
26 | }
27 |
28 | let bread = [2689, 2690, 2691].random();
29 |
30 | item.removeCount(1);
31 | tile.addTopThing(process.gameServer.database.createThing(bread).setCount(1));
32 |
33 | return true;
34 |
35 | }
--------------------------------------------------------------------------------
/data/740/actions/definitions/enterTiles.js:
--------------------------------------------------------------------------------
1 | let lookup = new Object({
2 | "446": 447,
3 | "416": 417,
4 | "426": 425
5 | });
6 |
7 | module.exports = function onEnterTile(tile) {
8 |
9 | if(!lookup.hasOwnProperty(tile.id)) {
10 | return true;
11 | }
12 |
13 | tile.replace(lookup[tile.id]);
14 |
15 | return true;
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/data/740/actions/definitions/exitTiles.js:
--------------------------------------------------------------------------------
1 | let lookup = new Object({
2 | "447": 446,
3 | "417": 416,
4 | "425": 426
5 | });
6 |
7 | module.exports = function onExitTile(tile) {
8 |
9 | if(!lookup.hasOwnProperty(tile.id)) {
10 | return true;
11 | }
12 |
13 | tile.replace(lookup[tile.id]);
14 |
15 | return true;
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/data/740/actions/definitions/expertise.js:
--------------------------------------------------------------------------------
1 | module.exports = function expertiseDoor(player, tile, index, item) {
2 |
3 | /*
4 | * Function expertise
5 | * Picks blueberries from a blueberry bush
6 | */
7 |
8 | process.gameServer.world.teleportCreature(player, tile.position);
9 |
10 | player.on("move", function() {
11 | item.toggle(player);
12 | });
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/data/740/actions/definitions/fish.js:
--------------------------------------------------------------------------------
1 | module.exports = function useFishingRod(player, item, tile) {
2 |
3 | /*
4 | * Function useFishingRod
5 | * Function called when a fishing rod is used with
6 | */
7 |
8 | process.gameServer.world.sendMagicEffect(tile.position, CONST.EFFECT.MAGIC.LOSEENERGY);
9 |
10 | player.equipment.pushItem(process.gameServer.database.createThing(2667).setCount(1));
11 |
12 | }
--------------------------------------------------------------------------------
/data/740/actions/definitions/flour.js:
--------------------------------------------------------------------------------
1 | module.exports = function useFlour(player, item, tile) {
2 |
3 | /*
4 | * Function useFlour
5 | * Script called when flour is used on a container with water
6 | */
7 |
8 | // Only allowed when not moving
9 | if(player.isMoving()) {
10 | return true;
11 | }
12 |
13 | if(!player.isBesidesThing(tile)) {
14 | return;
15 | }
16 |
17 | let thing = tile.getTopItem();
18 |
19 | if(thing === null) {
20 | return;
21 | }
22 |
23 | if(!thing.isFluidContainer() || !thing.containsWater()) {
24 | return;
25 | }
26 |
27 | // Empty the container, remove the flour, and add dough
28 | thing.empty();
29 | item.removeCount(1);
30 | tile.addTopThing(process.gameServer.database.createThing(2693).setCount(1));
31 |
32 | return true;
33 |
34 | }
--------------------------------------------------------------------------------
/data/740/actions/definitions/ladder.js:
--------------------------------------------------------------------------------
1 | module.exports = function useLadder(player, tile, index, item) {
2 |
3 | // Only allowed when not moving
4 | if(player.isMoving()) {
5 | return true;
6 | }
7 |
8 | let attempts = new Array(tile.position.ladder(), tile.position.ladderNorth());
9 |
10 | for(let attempt of attempts) {
11 |
12 | let attemptTile = process.gameServer.world.getTileFromWorldPosition(attempt);
13 |
14 | if(!player.isTileOccupied(attemptTile)) {
15 | process.gameServer.world.teleportCreature(player, attempt);
16 | player.__moveLock.lock(10);
17 | return true;
18 | }
19 |
20 | }
21 |
22 | return true;
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/data/740/actions/definitions/lamp.js:
--------------------------------------------------------------------------------
1 | module.exports = function useScythe(player, item, tile) {
2 |
3 | /*
4 | * Function useScythe
5 | * Script called when a scythe is used to cut wheat
6 | */
7 |
8 | // Only allowed when not moving
9 | if(player.isMoving()) {
10 | return true;
11 | }
12 |
13 | if(!player.isBesidesThing(tile)) {
14 | return;
15 | }
16 |
17 | let thing = tile.getTopItem();
18 |
19 | if(thing.constructor.name !== "FluidContainer") {
20 | return;
21 | }
22 |
23 | if(thing.isEmpty() || !thing.isOil()) {
24 | return;
25 | }
26 |
27 | item.replace(process.gameServer.database.createThing(2044));
28 | thing.__empty();
29 |
30 | return true;
31 |
32 | }
--------------------------------------------------------------------------------
/data/740/actions/definitions/lever.js:
--------------------------------------------------------------------------------
1 | module.exports = function() {}
2 |
--------------------------------------------------------------------------------
/data/740/actions/definitions/machete.js:
--------------------------------------------------------------------------------
1 | module.exports = function useMachete(player, item, tile) {
2 |
3 | /*
4 | * Function useMachete
5 | * Script called when a machete is used
6 | */
7 |
8 | // Only allowed when not moving
9 | if(player.isMoving()) {
10 | return true;
11 | }
12 |
13 | if(!player.besides(tile)) {
14 | return;
15 | }
16 |
17 | let grass = tile.getItem();
18 |
19 | if(grass === null) {
20 | return;
21 | }
22 |
23 | // If the tile being used has a rope hole: teleport up!
24 | if(grass.id === 2782) {
25 | grass.transform(2781);
26 | process.gameServer.eventQueue.addEvent(grass.transform.bind(grass, 2782), 100);
27 | }
28 |
29 | return true;
30 |
31 | }
--------------------------------------------------------------------------------
/data/740/actions/definitions/music.js:
--------------------------------------------------------------------------------
1 | module.exports = function playMusic(player, tile, item) {
2 |
3 | process.gameServer.world.sendMagicEffect(tile.position, CONST.EFFECT.MAGIC.SOUND_GREEN);
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/data/740/actions/definitions/pick.js:
--------------------------------------------------------------------------------
1 | module.exports = function useRope(player, item, tile) {
2 |
3 | /*
4 | * Function useRope
5 | * Script called when a rope is used
6 | */
7 |
8 | // Only allowed when not moving
9 | if(player.isMoving()) {
10 | return;
11 | }
12 |
13 | if(!player.isBesidesThing(tile)) {
14 | return;
15 | }
16 |
17 | // Must be mud!
18 | if(tile.id < 351 || tile.id > 355) {
19 | return;
20 | }
21 |
22 | // Mud tiles with action ID 101 are pick holes
23 | if(tile.actionId === 101) {
24 | tile.replace(392);
25 | }
26 |
27 | return true;
28 |
29 | }
--------------------------------------------------------------------------------
/data/740/actions/definitions/pitfall.js:
--------------------------------------------------------------------------------
1 | module.exports = function(tile, player) {
2 |
3 | tile.replace(294);
4 |
5 | }
--------------------------------------------------------------------------------
/data/740/actions/definitions/rope.js:
--------------------------------------------------------------------------------
1 | const PacketHandler = requireModule("packet-handler");
2 |
3 | function useOnHole(player, tile) {
4 |
5 | /*
6 | * Function useOnHole
7 | * Script called when a rope is used on a hole
8 | */
9 |
10 | // Get the tile below the current hole
11 | let down = process.gameServer.world.getTileFromWorldPosition(tile.position.down());
12 |
13 | if(down === null) {
14 | return true;
15 | }
16 |
17 | // Must be an item down there
18 | let item = down.getTopItem();
19 |
20 | if(item === null) {
21 | return true;
22 | }
23 |
24 | if(!item.isMoveable()) {
25 | return true;
26 | }
27 |
28 | if(item.getAttribute("floorchange") !== null) {
29 | return true;
30 | }
31 |
32 | // The tile south of the hole must be available
33 | let up = process.gameServer.world.getTileFromWorldPosition(tile.position.south());
34 |
35 | if(up === null) {
36 | return true;
37 | }
38 |
39 | // Delegate the move event
40 | PacketHandler.prototype.__moveItem(player, down, 0xFF, up, 0xFF, 0);
41 |
42 | return true;
43 |
44 | }
45 |
46 | module.exports = function useRope(player, item, tile) {
47 |
48 | /*
49 | * Function useRope
50 | * Script called when a rope is used
51 | */
52 |
53 | // Only allowed when not moving
54 | if(player.isMoving()) {
55 | return true;
56 | }
57 |
58 | // Not besides the hole
59 | if(!player.isBesidesThing(tile)) {
60 | return;
61 | }
62 |
63 | // These are holes
64 | if(tile.getFloorChange() === "down") {
65 | return useOnHole(player, tile);
66 | }
67 |
68 | // If the tile being used has a rope hole: teleport the player up!
69 | if(tile.id === 384) {
70 | process.gameServer.world.teleportCreature(player, tile.position.ladder());
71 | player.__moveLock.lock(10);
72 | }
73 |
74 | return true;
75 |
76 | }
--------------------------------------------------------------------------------
/data/740/actions/definitions/rune.js:
--------------------------------------------------------------------------------
1 | module.exports = function useRuneWith(player, item, tile) {
2 |
3 | /*
4 | * Function useRuneWith
5 | * Callback fired when a rune is used on a tile
6 | */
7 |
8 | let from = player.position;
9 | let to = tile.position;
10 |
11 | // Check possible
12 | if(!from.inLineOfSight(to)) {
13 | return player.sendCancelMessage("Target is not in line of sight.");
14 | }
15 |
16 | // Look up the function to execute
17 | let rune = process.gameServer.database.getRune(item.id);
18 |
19 | // The rune does not exist
20 | if(rune === null) {
21 | return player.sendCancelMessage("The rune does nothing.");
22 | }
23 |
24 | // Call the configured rune function: return of true means succesful cast and we reduce a charge
25 | if(rune.call(null, player, tile)) {
26 | item.charges--;
27 | }
28 |
29 | // No more charges: delete the item
30 | if(item.charges === 0) {
31 | item.remove(1);
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/data/740/actions/definitions/scythe.js:
--------------------------------------------------------------------------------
1 | module.exports = function useScythe(player, item, tile) {
2 |
3 | /*
4 | * Function useScythe
5 | * Script called when a scythe is used to cut wheat
6 | */
7 |
8 | // Only allowed when not moving
9 | if(player.isMoving()) {
10 | return true;
11 | }
12 |
13 | if(!player.isBesidesThing(tile)) {
14 | return;
15 | }
16 |
17 | let thing = tile.getTopItem();
18 |
19 | if(thing === null) {
20 | return;
21 | }
22 |
23 | // These are the identifiers of wheat in the field
24 | if(thing.id === 2739 || thing.id === 2738) {
25 | thing.replace(process.gameServer.database.createThing(2737));
26 | }
27 |
28 | // Full grown? Add cut wheat
29 | if(thing.id === 2739) {
30 | tile.addTopThing(process.gameServer.database.createThing(2694).setCount(1));
31 | }
32 |
33 | return true;
34 |
35 | }
--------------------------------------------------------------------------------
/data/740/actions/definitions/sewer.js:
--------------------------------------------------------------------------------
1 | module.exports = function useSewerGrate(player, tile, index, item) {
2 |
3 | // Only allowed when not moving
4 | if(player.isMoving()) {
5 | return true;
6 | }
7 |
8 | // Teleport the player and
9 | process.gameServer.world.teleportCreature(player, tile.position.down());
10 | player.__moveLock.lock(10);
11 |
12 | return true;
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/data/740/actions/definitions/shovel.js:
--------------------------------------------------------------------------------
1 | module.exports = function useShovel(player, item, tile) {
2 |
3 | /*
4 | * Function useShovel
5 | * Script called when a shovel is used on a stone pile
6 | */
7 |
8 | // Only allowed when not moving
9 | if(player.isMoving()) {
10 | return player.sendCancelMessage("You can not do this while moving.");
11 | }
12 |
13 | if(!player.isBesidesThing(tile)) {
14 | return player.sendCancelMessage("You need to be closer.");
15 | }
16 |
17 | // If the tile being used is a pile of loose stones: open it
18 | if(tile.id === 468) {
19 | tile.replace(469);
20 | } else if(tile.id === 481) {
21 | tile.replace(482);
22 | } else if(tile.id === 483) {
23 | tile.replace(484);
24 | }
25 |
26 | return true;
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/data/740/actions/definitions/toggle.js:
--------------------------------------------------------------------------------
1 | const lookup = new Object({
2 | // Torch
3 | "2050": "2051",
4 | "2051": "2050",
5 | "2052": "2053",
6 | "2053": "2052",
7 | "2054": "2055",
8 | "2055": "2054",
9 |
10 | // Wall lamps
11 | "2058": "2059",
12 | "2059": "2058",
13 | "2060": "2061",
14 | "2061": "2060",
15 | "2066": "2067",
16 | "2067": "2066",
17 | "2068": "2069",
18 | "2069": "2068",
19 |
20 | // Lanterns
21 | "1479": "1480",
22 | "1480": "1479",
23 | "2044": "2045",
24 | "2045": "2044",
25 |
26 | // Wall lamps
27 | "2037": "2038",
28 | "2038": "2037",
29 | "2039": "2040",
30 | "2040": "2039",
31 |
32 | // Candelabrum
33 | "2041": "2057",
34 | "2057": "2041",
35 |
36 | // Ovens
37 | "1786": "1787",
38 | "1787": "1786",
39 | "1788": "1789",
40 | "1789": "1788",
41 | "1790": "1791",
42 | "1791": "1790",
43 | "1792": "1793",
44 | "1793": "1792",
45 |
46 | // MLW
47 | "2162": "2163",
48 | "2163": "2162"
49 |
50 | });
51 |
52 | module.exports = function(player, tile, index, item) {
53 |
54 | /*
55 | * Function toggleItem
56 | * Script called when a shovel is used on a stone pile
57 | */
58 |
59 | // Does not exist in the table
60 | if(!lookup.hasOwnProperty(item.id)) {
61 | return;
62 | }
63 |
64 | if(item.isHangable() && !player.canUseHangable(item)) {
65 | return player.sendCancelMessage("You have to move to the other side.");
66 | }
67 |
68 | // Replace the items
69 | item.replace(process.gameServer.database.createThing(Number(lookup[item.id])));
70 |
71 | return true;
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/data/740/actions/definitions/wheat.js:
--------------------------------------------------------------------------------
1 | module.exports = function useMachete(player, item, tile) {
2 |
3 | /*
4 | * Function useMachete
5 | * Script called when a machete is used
6 | */
7 |
8 | // Only allowed when not moving
9 | if(player.isMoving()) {
10 | return true;
11 | }
12 |
13 | if(!player.isBesidesThing(tile)) {
14 | return;
15 | }
16 |
17 | let thing = tile.getTopItem();
18 |
19 | if(thing === null) {
20 | return;
21 | }
22 |
23 | if(![1381, 1382, 1383, 1384].includes(thing.id)) {
24 | return;
25 | }
26 |
27 | item.removeCount(1);
28 | tile.addTopThing(process.gameServer.database.createThing(2692).setCount(1));
29 |
30 | return true;
31 |
32 | }
--------------------------------------------------------------------------------
/data/740/clock/definitions.json:
--------------------------------------------------------------------------------
1 | [
2 | {"callback": "statue.js"},
3 | {"callback": "timetile.js"},
4 | {"callback": "cemetery-ghost.js"}
5 | ]
--------------------------------------------------------------------------------
/data/740/clock/definitions/cemetery-ghost.js:
--------------------------------------------------------------------------------
1 | const Position = requireModule("position");
2 | //let ghost = process.gameServer.database.npcs["ghost"];
3 |
4 | module.exports = function cemeteryGhost(time) {
5 | return;
6 | /*
7 | * Function cemeteryGhost
8 | * Example callback implement when time changes
9 | */
10 |
11 | if(time === "04:00") {
12 | process.gameServer.world.sendMagicEffect(new Position(70, 81, 8), CONST.EFFECT.MAGIC.TELEPORT);
13 | return process.gameServer.world.addCreatureSpawn(ghost, new Position(70, 81, 8));
14 | }
15 |
16 | if(time === "04:10") {
17 | process.gameServer.world.sendMagicEffect(ghost.position, CONST.EFFECT.MAGIC.TELEPORT);
18 | return process.gameServer.world.removeCreature(ghost);
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/data/740/clock/definitions/statue.js:
--------------------------------------------------------------------------------
1 | const Position = requireModule("position");
2 |
3 | module.exports = function statue(time) {
4 |
5 | /*
6 | * Function statue
7 | * Example callback implement when time changes
8 | */
9 |
10 | if(time !== "08:30") {
11 | return;
12 | }
13 |
14 | let position = new Position(87, 108, 8);
15 |
16 | let effects = new Array(
17 | CONST.EFFECT.MAGIC.SOUND_GREEN,
18 | CONST.EFFECT.MAGIC.SOUND_RED,
19 | CONST.EFFECT.MAGIC.SOUND_YELLOW,
20 | CONST.EFFECT.MAGIC.SOUND_PURPLE,
21 | CONST.EFFECT.MAGIC.SOUND_BLUE,
22 | CONST.EFFECT.MAGIC.SOUND_WHITE
23 | );
24 |
25 | let thing = process.gameServer.database.createThing(2747);
26 |
27 | process.gameServer.world.sendMagicEffect(position, effects.random());
28 | process.gameServer.world.getTileFromWorldPosition(position).addTopThing(thing);
29 |
30 |
31 | }
--------------------------------------------------------------------------------
/data/740/clock/definitions/timetile.js:
--------------------------------------------------------------------------------
1 | const Position = requireModule("position");
2 |
3 | module.exports = function statue(time) {
4 |
5 | /*
6 | * Function statue
7 | * Example callback implement when time changes
8 | */
9 |
10 | //if(time !== "06:05") {
11 | // return;
12 | //}
13 |
14 | let position = new Position(134, 116, 5);
15 |
16 | let effects = new Array(
17 | CONST.EFFECT.MAGIC.SOUND_GREEN,
18 | CONST.EFFECT.MAGIC.SOUND_RED,
19 | CONST.EFFECT.MAGIC.SOUND_YELLOW,
20 | CONST.EFFECT.MAGIC.SOUND_PURPLE,
21 | CONST.EFFECT.MAGIC.SOUND_BLUE,
22 | CONST.EFFECT.MAGIC.SOUND_WHITE
23 | );
24 |
25 | process.gameServer.world.sendMagicEffect(position, effects.random());
26 |
27 | }
--------------------------------------------------------------------------------
/data/740/conditions/definitions.json:
--------------------------------------------------------------------------------
1 | {
2 | "0": "drunk.js",
3 | "1": "poisoned.js",
4 | "2": "burning.js",
5 | "3": "electrified.js",
6 | "4": "invisible.js",
7 | "7": "suppress-drunk.js",
8 | "8": "light.js",
9 | "9": "healing.js",
10 | "10": "regeneration.js",
11 | "11": "morph.js",
12 | "12": "magic-shield.js",
13 | "13": "magic-flame-condition.js",
14 | "14": "sated.js",
15 | "15": "haste.js",
16 | "17": "arena.js"
17 | }
--------------------------------------------------------------------------------
/data/740/conditions/definitions/arena.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const Condition = requireModule("condition");
4 | const Position = requireModule("position");
5 | const Monster = requireModule("monster");
6 |
7 | let monster;
8 |
9 | function handleOnBeforeDeath() {
10 |
11 | /*
12 | * Function handleOnBeforeDeath
13 | * Callback function to handle the on death event
14 | */
15 |
16 | // Remove the arena condition
17 | this.removeCondition(Condition.prototype.ARENA);
18 |
19 | }
20 |
21 | function onStart(creature) {
22 |
23 | /*
24 | * Function onStart
25 | * Callback fired on condition start
26 | */
27 |
28 | // Teleport the player to the arena
29 | process.gameServer.world.teleportCreature(creature, new Position(74, 74, 5));
30 |
31 | monster = new Monster({"mid": "troll", "position": new Position(77, 77, 5)});
32 | process.gameServer.world.addCreature(monster, new Position(77, 77, 5));
33 | process.gameServer.world.sendMagicEffect(new Position(77, 77, 5), CONST.EFFECT.MAGIC.TELEPORT);
34 | monster.on("death", handleOnBeforeDeath);
35 |
36 | // Subscribe to the before death event and remove the arena condition
37 | creature.on("beforeDeath", handleOnBeforeDeath);
38 |
39 | }
40 |
41 | function onExpire(creature) {
42 |
43 | /*
44 | * Function onExpire
45 | * Callback fired on condition expire
46 | */
47 |
48 | // Remove listener to death event
49 | creature.off("beforeDeath", handleOnBeforeDeath);
50 |
51 | // Teleport the player out of the arena
52 | process.gameServer.world.teleportCreature(creature, new Position(77, 94, 8));
53 |
54 | // Remove the creature
55 | process.gameServer.world.removeCreature(monster);
56 |
57 | creature.sendCancelMessage("You have been rescued.");
58 |
59 | // Return the player to full health
60 | creature.setFullHealth();
61 |
62 | }
63 |
64 | function onTick(creature) {
65 |
66 | /*
67 | * Function onTick
68 | * Callback fired every condition tick
69 | */
70 |
71 | creature.sendCancelMessage("You have %s seconds left.".format(this.numberTicks));
72 |
73 | }
74 |
75 | module.exports.onStart = onStart;
76 | module.exports.onExpire = onExpire;
77 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/740/conditions/definitions/burning.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function onStart(creature) {
4 |
5 | /*
6 | * Function onStart
7 | * Callback fired on condition start
8 | */
9 |
10 | creature.sendCancelMessage("You are burning!");
11 |
12 | }
13 |
14 | function onExpire(creature) {
15 |
16 | /*
17 | * Function onExpire
18 | * Callback fired on condition expire
19 | */
20 |
21 | creature.sendCancelMessage("You feel better again.");
22 |
23 | }
24 |
25 | function onTick(creature) {
26 |
27 | /*
28 | * Function onTick
29 | * Callback fired every condition tick
30 | */
31 |
32 | // Damage depends on the first tick
33 | let damage = this.isFirstTick() ? 2 : 1;
34 |
35 | process.gameServer.world.applyEnvironmentalDamage(creature, damage, CONST.COLOR.ORANGE);
36 | process.gameServer.world.sendMagicEffect(creature.position, CONST.EFFECT.MAGIC.HITBYFIRE);
37 |
38 | }
39 |
40 | module.exports.onStart = onStart;
41 | module.exports.onExpire = onExpire;
42 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/740/conditions/definitions/drunk.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function onStart(creature) {
4 |
5 | /*
6 | * Function onStart
7 | * Callback fired on condition start
8 | */
9 |
10 | creature.sendCancelMessage("You are feeling drunk!");
11 |
12 | }
13 |
14 | function onExpire(creature) {
15 |
16 | /*
17 | * Function onExpire
18 | * Callback fired on condition expire
19 | */
20 |
21 | creature.sendCancelMessage("You feel sober again!");
22 |
23 | }
24 |
25 | function onTick(creature) {
26 |
27 | /*
28 | * Function onTick
29 | * Callback fired every condition tick
30 | */
31 |
32 | }
33 |
34 | module.exports.onStart = onStart;
35 | module.exports.onExpire = onExpire;
36 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/740/conditions/definitions/electrified.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function onStart(creature) {
4 |
5 | /*
6 | * Function onStart
7 | * Callback fired on condition start
8 | */
9 |
10 | creature.sendCancelMessage("You were electrified!");
11 |
12 | }
13 |
14 | function onExpire(creature) {
15 |
16 | /*
17 | * Function onExpire
18 | * Callback fired on condition expire
19 | */
20 |
21 | creature.sendCancelMessage("You feel better again.");
22 |
23 | }
24 |
25 | function onTick(creature) {
26 |
27 | /*
28 | * Function onTick
29 | * Callback fired every condition tick
30 | */
31 |
32 | process.gameServer.world.applyEnvironmentalDamage(creature, 5, CONST.COLOR.LIGHTBLUE);
33 | process.gameServer.world.sendMagicEffect(creature.position, CONST.EFFECT.MAGIC.ENERGYHIT);
34 |
35 | }
36 |
37 | module.exports.onStart = onStart;
38 | module.exports.onExpire = onExpire;
39 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/740/conditions/definitions/haste.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function onStart(creature) {
4 |
5 | /*
6 | * Function onStart
7 | * Callback fired on condition start
8 | */
9 |
10 | creature.sendCancelMessage("You feel fast.");
11 |
12 | }
13 |
14 | function onExpire(creature) {
15 |
16 | /*
17 | * Function onExpire
18 | * Callback fired on condition expire
19 | */
20 |
21 | creature.sendCancelMessage("Your speed returns to normal.");
22 |
23 | }
24 |
25 | function onTick(creature) {
26 |
27 | /*
28 | * Function onTick
29 | * Callback fired every condition tick
30 | */
31 |
32 | }
33 |
34 | module.exports.onStart = onStart;
35 | module.exports.onExpire = onExpire;
36 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/740/conditions/definitions/healing.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function onStart(creature) {
4 |
5 | /*
6 | * Function onStart
7 | * Callback fired on condition start
8 | */
9 |
10 | creature.sendCancelMessage("You are healing!");
11 |
12 | }
13 |
14 | function onExpire(creature) {
15 |
16 | /*
17 | * Function onExpire
18 | * Callback fired on condition expire
19 | */
20 |
21 | creature.sendCancelMessage("You are done healing.");
22 |
23 | }
24 |
25 | function onTick(creature) {
26 |
27 | /*
28 | * Function onTick
29 | * Callback fired every condition tick
30 | */
31 |
32 | if(creature.isFullHealth()) {
33 | return;
34 | }
35 |
36 | let healing = 10;
37 |
38 | // Apply poison damage to the player
39 | creature.increaseHealth(healing);
40 | process.gameServer.world.sendMagicEffect(creature.position, CONST.EFFECT.MAGIC.MAGIC_BLUE);
41 |
42 | }
43 |
44 | module.exports.onStart = onStart;
45 | module.exports.onExpire = onExpire;
46 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/740/conditions/definitions/invisible.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function onStart(creature) {
4 |
5 | /*
6 | * Function onStart
7 | * Callback fired on condition start
8 | */
9 |
10 | creature.sendCancelMessage("You have turned invisible.");
11 |
12 | }
13 |
14 | function onExpire(creature) {
15 |
16 | /*
17 | * Function onExpire
18 | * Callback fired on condition expire
19 | */
20 |
21 | creature.sendCancelMessage("You are visible again.");
22 |
23 | }
24 |
25 | function onTick(creature) {
26 |
27 | }
28 |
29 | module.exports.onStart = onStart;
30 | module.exports.onExpire = onExpire;
31 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/740/conditions/definitions/light.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function onStart(creature) {
4 |
5 | /*
6 | * Function onStart
7 | * Callback fired on condition start
8 | */
9 |
10 | creature.sendCancelMessage("Light surrounds you.");
11 |
12 | }
13 |
14 | function onExpire(creature) {
15 |
16 | /*
17 | * Function onExpire
18 | * Callback fired on condition expire
19 | */
20 |
21 | creature.sendCancelMessage("The light has dimmed.");
22 |
23 | }
24 |
25 | function onTick(creature) {
26 |
27 | }
28 |
29 | module.exports.onStart = onStart;
30 | module.exports.onExpire = onExpire;
31 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/740/conditions/definitions/magic-flame-condition.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function onStart(creature) {
4 |
5 | /*
6 | * Function onStart
7 | * Callback fired on condition start
8 | */
9 |
10 | creature.sendCancelMessage("You feel warm.");
11 |
12 | }
13 |
14 | function onExpire(creature) {
15 |
16 | /*
17 | * Function onExpire
18 | * Callback fired on condition expire
19 | */
20 |
21 | creature.sendCancelMessage("The warmth fades.");
22 |
23 | }
24 |
25 | function onTick(creature) {
26 |
27 | /*
28 | * Function onTick
29 | * Callback fired every condition tick
30 | */
31 |
32 | process.gameServer.world.sendMagicEffect(creature.position, CONST.EFFECT.MAGIC.HITBYFIRE);
33 |
34 | }
35 |
36 | module.exports.onStart = onStart;
37 | module.exports.onExpire = onExpire;
38 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/740/conditions/definitions/magic-shield.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function onStart(creature) {
4 |
5 | /*
6 | * Function onStart
7 | * Callback fired on condition start
8 | */
9 |
10 | creature.sendCancelMessage("You are being protected by a magic shield.");
11 |
12 | }
13 |
14 | function onExpire(creature) {
15 |
16 | /*
17 | * Function onExpire
18 | * Callback fired on condition expire
19 | */
20 |
21 | creature.sendCancelMessage("Your magic shield wears off.");
22 |
23 | }
24 |
25 | function onTick(creature) {
26 |
27 | /*
28 | * Function onTick
29 | * Callback fired every condition tick
30 | */
31 |
32 | }
33 |
34 | module.exports.onStart = onStart;
35 | module.exports.onExpire = onExpire;
36 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/740/conditions/definitions/morph.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const Outfit = requireModule("outfit");
4 |
5 | function onStart(creature, properties) {
6 |
7 | /*
8 | * Function onStart
9 | * Callback fired on condition start
10 | */
11 |
12 | // Save a reference to the default outfit
13 | this.__defaultOutfit = creature.outfit;
14 |
15 | process.gameServer.world.sendMagicEffect(creature.position, CONST.EFFECT.MAGIC.TELEPORT);
16 | creature.changeOutfit(new Outfit({"id": properties.id}));
17 |
18 | }
19 |
20 | function onExpire(creature) {
21 |
22 | /*
23 | * Function onExpire
24 | * Callback fired on condition expire
25 | */
26 |
27 | process.gameServer.world.sendMagicEffect(creature.position, CONST.EFFECT.MAGIC.TELEPORT);
28 | creature.changeOutfit(this.__defaultOutfit);
29 |
30 | }
31 |
32 | function onTick(creature) {
33 |
34 | /*
35 | * Function onTick
36 | * Callback fired every condition tick
37 | */
38 |
39 | }
40 |
41 | module.exports.onStart = onStart;
42 | module.exports.onExpire = onExpire;
43 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/740/conditions/definitions/poisoned.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function onStart(creature) {
4 |
5 | /*
6 | * Function onStart
7 | * Callback fired on condition start
8 | */
9 |
10 | creature.sendCancelMessage("You were poisoned!");
11 |
12 | }
13 |
14 | function onExpire(creature) {
15 |
16 | /*
17 | * Function onExpire
18 | * Callback fired on condition expire
19 | */
20 |
21 | creature.sendCancelMessage("You feel healthy again.");
22 |
23 | }
24 |
25 | function onTick(creature) {
26 |
27 | /*
28 | * Function onTick
29 | * Callback fired every condition tick
30 | */
31 |
32 | let damage = Math.max(1, 3 * (this.getFraction()));
33 |
34 | // Apply poison damage to the player
35 | process.gameServer.world.applyEnvironmentalDamage(creature, damage, CONST.COLOR.LIGHTGREEN);
36 | process.gameServer.world.sendMagicEffect(creature.position, CONST.EFFECT.MAGIC.GREEN_RINGS);
37 |
38 | }
39 |
40 | module.exports.onStart = onStart;
41 | module.exports.onExpire = onExpire;
42 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/740/conditions/definitions/regeneration.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function onStart(creature) {
4 |
5 | /*
6 | * Function onStart
7 | * Callback fired on condition start
8 | */
9 |
10 | }
11 |
12 | function onExpire(creature) {
13 |
14 | /*
15 | * Function onExpire
16 | * Callback fired on condition expire
17 | */
18 |
19 | }
20 |
21 | function onTick(creature) {
22 |
23 | /*
24 | * Function onTick
25 | * Callback fired every condition tick
26 | */
27 |
28 | if(creature.isFullHealth() || creature.isInCombat()) {
29 | return;
30 | }
31 |
32 | creature.increaseHealth(1);
33 |
34 | }
35 |
36 | module.exports.onStart = onStart;
37 | module.exports.onExpire = onExpire;
38 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/740/conditions/definitions/sated.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function onStart(creature) {
4 |
5 | /*
6 | * Function onStart
7 | * Callback fired on condition start
8 | */
9 |
10 | creature.sendCancelMessage("Yum!");
11 |
12 | }
13 |
14 | function onExpire(creature) {
15 |
16 | /*
17 | * Function onExpire
18 | * Callback fired on condition expire
19 | */
20 |
21 | creature.sendCancelMessage("You are feeling hungry!");
22 |
23 | }
24 |
25 | function onTick(creature) {
26 |
27 | /*
28 | * Function onTick
29 | * Callback fired every condition tick
30 | */
31 |
32 | }
33 |
34 | module.exports.onStart = onStart;
35 | module.exports.onExpire = onExpire;
36 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/740/conditions/definitions/suppress-drunk.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function onStart(creature) {
4 |
5 | /*
6 | * Function onStart
7 | * Callback fired on condition start
8 | */
9 |
10 | }
11 |
12 | function onExpire(creature) {
13 |
14 | /*
15 | * Function onExpire
16 | * Callback fired on condition expire
17 | */
18 |
19 | }
20 |
21 | function onTick(creature) {
22 |
23 | /*
24 | * Function onTick
25 | * Callback fired every condition tick
26 | */
27 |
28 | }
29 |
30 | module.exports.onStart = onStart;
31 | module.exports.onExpire = onExpire;
32 | module.exports.onTick = onTick;
--------------------------------------------------------------------------------
/data/740/constants.json:
--------------------------------------------------------------------------------
1 | {
2 | "COLOR": {
3 | "BLUE": 5,
4 | "LIGHTGREEN": 30,
5 | "LIGHTBLUE": 35,
6 | "MAYABLUE": 95,
7 | "DARKRED": 108,
8 | "LIGHTGREY": 129,
9 | "SKYBLUE": 143,
10 | "PURPLE": 155,
11 | "RED": 180,
12 | "ORANGE": 198,
13 | "YELLOW": 210,
14 | "WHITE": 215
15 | },
16 | "BLOODTYPE": {
17 | "BLOOD": 0,
18 | "POISON": 1,
19 | "NONE": 2
20 | },
21 | "EFFECT": {
22 | "MAGIC": {
23 | "DRAWBLOOD": 1,
24 | "LOSEENERGY": 2,
25 | "POFF": 3,
26 | "BLOCKHIT": 4,
27 | "EXPLOSIONAREA": 5,
28 | "EXPLOSIONHIT": 6,
29 | "FIREAREA": 7,
30 | "YELLOW_RINGS": 8,
31 | "GREEN_RINGS": 9,
32 | "HITAREA": 10,
33 | "TELEPORT": 11,
34 | "ENERGYHIT": 12,
35 | "MAGIC_BLUE": 13,
36 | "MAGIC_RED": 14,
37 | "MAGIC_GREEN": 15,
38 | "HITBYFIRE": 16,
39 | "HITBYPOISON": 17,
40 | "MORTAREA": 18,
41 | "SOUND_GREEN": 19,
42 | "SOUND_RED": 20,
43 | "POISONAREA": 21,
44 | "SOUND_YELLOW": 22,
45 | "SOUND_PURPLE": 23,
46 | "SOUND_BLUE": 24,
47 | "SOUND_WHITE": 25
48 | },
49 | "PROJECTILE": {
50 | "SPEAR": 1,
51 | "BOLT": 2,
52 | "ARROW": 3,
53 | "FIRE": 4,
54 | "ENERGY": 5,
55 | "POISONARROW": 6,
56 | "BURSTARROW": 7,
57 | "THROWINGSTAR": 8,
58 | "THROWINGKNIFE": 9,
59 | "SMALLSTONE": 10,
60 | "DEATH": 11,
61 | "LARGEROCK": 12,
62 | "SNOWBALL": 13,
63 | "POWERBOLT": 14,
64 | "POISON": 15
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/data/740/doors/README.md:
--------------------------------------------------------------------------------
1 | # Definitions for special doors
2 |
3 | Script that handle "unwanted intruder" doors (by aid). Add an `aid` to the door and reference the script in the definitions. Returning true means the door opens for the player.
4 |
5 | Doors of experise (level doors) are handled through their action identifier that represents level.
--------------------------------------------------------------------------------
/data/740/doors/definitions.json:
--------------------------------------------------------------------------------
1 | {"1000": "unwanted.js"}
--------------------------------------------------------------------------------
/data/740/doors/definitions/unwanted.js:
--------------------------------------------------------------------------------
1 | const Condition = requireModule("condition");
2 |
3 | module.exports = function useTrunk(player) {
4 |
5 | return player.hasCondition(Condition.prototype.MAGIC_FLAME);
6 |
7 | }
--------------------------------------------------------------------------------
/data/740/houses/definitions.json:
--------------------------------------------------------------------------------
1 | {
2 | "1": {
3 | "owner": "Forbidden",
4 | "rent": 100,
5 | "exit": {
6 | "x": 87,
7 | "y": 99,
8 | "z": 9
9 | },
10 | "invited": [],
11 | "name": "Bakers Balcony"
12 | }
13 | }
--------------------------------------------------------------------------------
/data/740/houses/definitions/1.json:
--------------------------------------------------------------------------------
1 | [{"position":{"x":83,"y":97,"z":9},"item":{"id":1775,"count":0,"duration":null}}]
--------------------------------------------------------------------------------
/data/740/monsters/README.md:
--------------------------------------------------------------------------------
1 | # Definitions for monsters and their behaviour
2 |
3 | The monster definition files should always validate with the provided `schema.json`. See e.g., this [JSON Schema Validator](https://www.jsonschemavalidator.net/)
--------------------------------------------------------------------------------
/data/740/monsters/definitions.json:
--------------------------------------------------------------------------------
1 | {
2 | "troll": "troll.json",
3 | "rabbit": "rabbit.json",
4 | "sheep": "sheep.json",
5 | "black-sheep": "black-sheep.json",
6 | "rat": "rat.json",
7 | "cave-rat": "cave-rat.json",
8 | "bear": "bear.json",
9 | "rotworm": "rotworm.json",
10 | "spider": "spider.json",
11 | "skeleton": "skeleton.json",
12 | "slime": "slime.json"
13 | }
--------------------------------------------------------------------------------
/data/740/monsters/definitions/bear.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Bear",
4 | "health": 80,
5 | "maxHealth": 80,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 50,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 30,
12 | "outfit": {
13 | "id": 16
14 | }
15 | },
16 | "fluidType": 0,
17 | "experience": 0,
18 | "corpse": 2849,
19 | "behaviour": {
20 | "type": "HOSTILE",
21 | "fleeHealth": 60,
22 | "openDoors": false,
23 | "senseInvisible": false
24 | },
25 | "loot": [
26 | {"id": 2666, "probability": 1, "min": 1, "max": 3}
27 | ],
28 | "sayings": {
29 | "texts": ["Grrrr", "Groar"],
30 | "slowness": 300,
31 | "chance": 0.75
32 | }
33 | }
--------------------------------------------------------------------------------
/data/740/monsters/definitions/black-sheep.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Sheep",
4 | "health": 5,
5 | "maxHealth": 5,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 30,
12 | "outfit": {
13 | "id": 13
14 | }
15 | },
16 | "fluidType": 0,
17 | "experience": 0,
18 | "corpse": 2914,
19 | "behaviour": {
20 | "type": "FRIENDLY",
21 | "fleeHealth": 4,
22 | "openDoors": false,
23 | "senseInvisible": false
24 | },
25 | "loot": [
26 | {"id": 2666, "probability": 1, "min": 1, "max": 3}
27 | ],
28 | "sayings": {
29 | "texts": ["Behhh"],
30 | "slowness": 300,
31 | "chance": 0.75
32 | }
33 | }
--------------------------------------------------------------------------------
/data/740/monsters/definitions/cave-rat.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Cave Rat",
4 | "health": 30,
5 | "maxHealth": 30,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 8,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 30,
12 | "outfit": {
13 | "id": 56
14 | }
15 | },
16 | "fluidType": 0,
17 | "experience": 10,
18 | "behaviour": {
19 | "type": "HOSTILE",
20 | "fleeHealth": 0,
21 | "openDoors": true,
22 | "senseInvisible": false
23 | },
24 | "corpse": 2813,
25 | "loot": [
26 | {
27 | "id": 2148,
28 | "probability": 1,
29 | "min": 1,
30 | "max": 4
31 | },
32 | {
33 | "id": 2696,
34 | "probability": 0.1,
35 | "min": 1,
36 | "max": 1
37 | }
38 | ],
39 | "sayings": {
40 | "texts": [
41 | "Meep!",
42 | "Meeeeep!"
43 | ],
44 | "slowness": 300,
45 | "chance": 0.75
46 | }
47 | }
--------------------------------------------------------------------------------
/data/740/monsters/definitions/rabbit.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Rabbit",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 100,
12 | "outfit": {
13 | "id": 74
14 | }
15 | },
16 | "fluidType": 0,
17 | "experience": 0,
18 | "behaviour": {
19 | "type": "FRIENDLY",
20 | "fleeHealth": 50,
21 | "openDoors": true,
22 | "senseInvisible": false
23 | },
24 | "corpse": 3119,
25 | "loot": [
26 | {"id": 2666, "probability": 1, "min": 1, "max": 3}
27 | ],
28 | "sayings": {
29 | "texts": ["Mip mip mip"],
30 | "slowness": 300,
31 | "chance": 0.75
32 | }
33 | }
--------------------------------------------------------------------------------
/data/740/monsters/definitions/rat.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Rat",
4 | "health": 20,
5 | "mana": 0,
6 | "maxMana": 0,
7 | "maxHealth": 20,
8 | "attack": 1,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 30,
12 | "outfit": {
13 | "id": 21
14 | }
15 | },
16 | "fluidType": 0,
17 | "experience": 50,
18 | "corpse": 2813,
19 | "behaviour": {
20 | "type": "HOSTILE",
21 | "fleeHealth": 5,
22 | "openDoors": true,
23 | "senseInvisible": false
24 | },
25 | "loot": [
26 | {
27 | "id": 2148,
28 | "probability": 1,
29 | "min": 1,
30 | "max": 4
31 | },
32 | {
33 | "id": 2696,
34 | "probability": 0.1,
35 | "min": 1,
36 | "max": 1
37 | }
38 | ],
39 | "sayings": {
40 | "texts": [
41 | "Meep!"
42 | ],
43 | "slowness": 300,
44 | "chance": 0.75
45 | }
46 | }
--------------------------------------------------------------------------------
/data/740/monsters/definitions/rotworm.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Rotworm",
4 | "health": 40,
5 | "maxHealth": 40,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 15,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 30,
12 | "outfit": {
13 | "id": 26
14 | }
15 | },
16 | "fluidType": 0,
17 | "experience": 0,
18 | "corpse": 2849,
19 | "behaviour": {
20 | "type": "HOSTILE",
21 | "fleeHealth": 0,
22 | "openDoors": false,
23 | "senseInvisible": false
24 | },
25 | "loot": [
26 | {"id": 2666, "probability": 1, "min": 1, "max": 3}
27 | ]
28 | }
--------------------------------------------------------------------------------
/data/740/monsters/definitions/sheep.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Sheep",
4 | "health": 5,
5 | "maxHealth": 5,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 30,
12 | "outfit": {
13 | "id": 14
14 | }
15 | },
16 | "fluidType": 0,
17 | "experience": 0,
18 | "corpse": 2914,
19 | "behaviour": {
20 | "type": "FRIENDLY",
21 | "fleeHealth": 4,
22 | "openDoors": false,
23 | "senseInvisible": false
24 | },
25 | "loot": [
26 | {"id": 2666, "probability": 1, "min": 1, "max": 3}
27 | ],
28 | "sayings": {
29 | "texts": ["Behhh"],
30 | "slowness": 300,
31 | "chance": 0.75
32 | }
33 | }
--------------------------------------------------------------------------------
/data/740/monsters/definitions/skeleton.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Skeleton",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "attack": 10,
7 | "mana": 0,
8 | "maxMana": 0,
9 | "attackSlowness": 40,
10 | "defense": 10,
11 | "speed": 50,
12 | "outfit": {
13 | "id": 33
14 | }
15 | },
16 | "fluidType": 2,
17 | "experience": 35,
18 | "corpse": 2843,
19 | "behaviour": {
20 | "type": "HOSTILE",
21 | "fleeHealth": 0,
22 | "openDoors": true,
23 | "senseInvisible": false
24 | },
25 | "loot": [
26 | {
27 | "id": 2229,
28 | "probability": 1,
29 | "min": 1,
30 | "max": 1
31 | },
32 | {
33 | "id": 2230,
34 | "probability": 1
35 | },
36 | {
37 | "id": 2231,
38 | "probability": 1
39 | },
40 | {
41 | "id": 2148,
42 | "probability": 1,
43 | "min": 1,
44 | "max": 7
45 | },
46 | {
47 | "id": 2473,
48 | "probability": 0.5
49 | }
50 | ],
51 | "spells": [{
52 | "id": 6,
53 | "chance": 1,
54 | "cooldown": 50
55 | }],
56 | "sayings": {
57 | "texts": [
58 | "Rattle!"
59 | ],
60 | "slowness": 300,
61 | "chance": 0.75
62 | }
63 | }
--------------------------------------------------------------------------------
/data/740/monsters/definitions/slime.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Slime",
4 | "health": 150,
5 | "maxHealth": 150,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 60,
12 | "outfit": {
13 | "id": 19
14 | }
15 | },
16 | "fluidType": 1,
17 | "experience": 160,
18 | "corpse": 1496,
19 | "behaviour": {
20 | "type": "HOSTILE",
21 | "fleeHealth": 0,
22 | "openDoors": false,
23 | "senseInvisible": false
24 | },
25 | "loot": [],
26 | "sayings": {
27 | "texts": ["Blubb"],
28 | "slowness": 300,
29 | "chance": 0.75
30 | }
31 | }
--------------------------------------------------------------------------------
/data/740/monsters/definitions/spider.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Spider",
4 | "health": 30,
5 | "maxHealth": 30,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 10,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 30,
12 | "outfit": {
13 | "id": 30
14 | }
15 | },
16 | "fluidType": 1,
17 | "experience": 0,
18 | "corpse": 2807,
19 | "behaviour": {
20 | "type": "HOSTILE",
21 | "fleeHealth": 10,
22 | "openDoors": false,
23 | "senseInvisible": false
24 | }
25 | }
--------------------------------------------------------------------------------
/data/740/monsters/definitions/troll.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Troll",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "attack": 1,
7 | "mana": 0,
8 | "maxMana": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 30,
12 | "outfit": {
13 | "id": 15
14 | }
15 | },
16 | "fluidType": 0,
17 | "experience": 20,
18 | "corpse": 2806,
19 | "behaviour": {
20 | "type": "HOSTILE",
21 | "fleeHealth": 5,
22 | "openDoors": true,
23 | "senseInvisible": false
24 | },
25 | "loot": [
26 | {
27 | "id": 2148,
28 | "probability": 1,
29 | "min": 1,
30 | "max": 5
31 | },
32 | {
33 | "id": 2148,
34 | "probability": 1,
35 | "min": 1,
36 | "max": 5
37 | },
38 | {
39 | "id": 2461,
40 | "probability": 0.1
41 | },
42 | {
43 | "id": 2148,
44 | "probability": 1,
45 | "min": 1,
46 | "max": 5
47 | }
48 | ],
49 | "sayings": {
50 | "texts": [
51 | "Hmmm, bugs",
52 | "Hmmm, dogs",
53 | "Grrr",
54 | "Groar",
55 | "Gruntz!"
56 | ],
57 | "slowness": 300,
58 | "chance": 0.75
59 | }
60 | }
--------------------------------------------------------------------------------
/data/740/mounts/mounts.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/data/740/npcs/README.md:
--------------------------------------------------------------------------------
1 | # Definitions for NPCs and their behaviour
2 |
3 | The NPC definition files should always validate with the provided `schema.json`. See e.g., this [JSON Schema Validator](https://www.jsonschemavalidator.net/)
--------------------------------------------------------------------------------
/data/740/npcs/definitions.json:
--------------------------------------------------------------------------------
1 | {
2 | "humphrey": {
3 | "position": {
4 | "x": 81,
5 | "y": 80,
6 | "z": 8
7 | },
8 | "enabled": true,
9 | "definition": "humphrey.json"
10 | },
11 | "jack": {
12 | "position": {
13 | "x": 84,
14 | "y": 126,
15 | "z": 8
16 | },
17 | "enabled": true,
18 | "definition": "jack.json"
19 | },
20 | "albert": {
21 | "position": {
22 | "x": 73,
23 | "y": 94,
24 | "z": 8
25 | },
26 | "enabled": true,
27 | "definition": "albert.json"
28 | },
29 | "marrow": {
30 | "position": {
31 | "x": 100,
32 | "y": 90,
33 | "z": 8
34 | },
35 | "enabled": true,
36 | "definition": "marrow.json"
37 | },
38 | "miller": {
39 | "position": {
40 | "x": 96,
41 | "y": 119,
42 | "z": 8
43 | },
44 | "enabled": true,
45 | "definition": "miller.json"
46 | },
47 | "sarah": {
48 | "position": {
49 | "x": 85,
50 | "y": 117,
51 | "z": 13
52 | },
53 | "enabled": true,
54 | "definition": "sarah.json"
55 | },
56 | "ghost": {
57 | "position": null,
58 | "enabled": false,
59 | "definition": "ghost.json"
60 | },
61 | "teller": {
62 | "position": {
63 | "x": 93,
64 | "y": 84,
65 | "z": 8
66 | },
67 | "enabled": true,
68 | "definition": "teller.json"
69 | },
70 | "karst": {
71 | "position": {
72 | "x": 55,
73 | "y": 91,
74 | "z": 9
75 | },
76 | "enabled": true,
77 | "definition": "karst.json"
78 | },
79 | "billy": {
80 | "position": {
81 | "x": 91,
82 | "y": 99,
83 | "z": 9
84 | },
85 | "enabled": true,
86 | "definition": "billy.json"
87 | }
88 | }
--------------------------------------------------------------------------------
/data/740/npcs/definitions/albert.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Albert",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 1,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 30,
12 | "outfit": {
13 | "id": 128,
14 | "details": {
15 | "head": 0,
16 | "body": 76,
17 | "legs": 116,
18 | "feet": 131
19 | }
20 | }
21 | },
22 | "hearingRange": 5,
23 | "wanderRange": 2,
24 | "trade": [
25 | {
26 | "name": "Shovel",
27 | "price": 5,
28 | "id": 2554,
29 | "type": "sell"
30 | },
31 | {
32 | "name": "Backpack",
33 | "price": 2,
34 | "id": 1988,
35 | "type": "sell"
36 | },
37 | {
38 | "name": "Rope",
39 | "price": 5,
40 | "id": 2120,
41 | "type": "sell"
42 | },
43 | {
44 | "name": "Torch",
45 | "price": 1,
46 | "id": 2050,
47 | "type": "sell"
48 | },
49 | {
50 | "name": "Pickaxe",
51 | "price": 3,
52 | "id": 2553,
53 | "type": "sell"
54 | },
55 | {
56 | "name": "Arrows",
57 | "price": 1,
58 | "id": 2544,
59 | "type": "sell"
60 | }
61 | ],
62 | "farewells": [
63 | "bye"
64 | ],
65 | "greetings": [
66 | "hello",
67 | "hi"
68 | ],
69 | "sayings": {
70 | "texts": [
71 | "Make sure you do not go exploring without adequate preparation and equipment!"
72 | ],
73 | "slowness": 300,
74 | "chance": 0.75
75 | },
76 | "script": "albert.js"
77 | }
--------------------------------------------------------------------------------
/data/740/npcs/definitions/billy.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Billy",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 1,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 30,
12 | "outfit": {
13 | "id": 128,
14 | "details": {
15 | "head": 38,
16 | "body": 63,
17 | "legs": 67,
18 | "feet": 39
19 | }
20 | }
21 | },
22 | "hearingRange": 5,
23 | "wanderRange": 3,
24 | "trade": [],
25 | "farewells": [
26 | "bye"
27 | ],
28 | "greetings": [
29 | "hello",
30 | "hi"
31 | ],
32 | "sayings": {
33 | "texts": [
34 | "Bread!"
35 | ],
36 | "slowness": 300,
37 | "chance": 0.75
38 | },
39 | "script": "billy.js"
40 | }
--------------------------------------------------------------------------------
/data/740/npcs/definitions/ghost.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "A Strange Ghost",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 1,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 20,
12 | "outfit": {
13 | "id": 48
14 | }
15 | },
16 | "hearingRange": 5,
17 | "wanderRange": 2,
18 | "farewells": [
19 | "bye"
20 | ],
21 | "greetings": [
22 | "hello",
23 | "hi"
24 | ],
25 | "sayings": {
26 | "texts": [
27 | "..."
28 | ],
29 | "slowness": 300,
30 | "chance": 0.75
31 | }
32 | }
--------------------------------------------------------------------------------
/data/740/npcs/definitions/humphrey.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Humphrey",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "attack": 1,
7 | "mana": 0,
8 | "maxMana": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 30,
12 | "outfit": {
13 | "id": 57
14 | }
15 | },
16 | "hearingRange": 5,
17 | "wanderRange": 2,
18 | "farewells": [
19 | "bye"
20 | ],
21 | "greetings": [
22 | "hello",
23 | "hi"
24 | ],
25 | "sayings": {
26 | "texts": [
27 | "..."
28 | ],
29 | "slowness": 300,
30 | "chance": 0.75
31 | },
32 | "script": "humphrey.js"
33 | }
--------------------------------------------------------------------------------
/data/740/npcs/definitions/jack.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Jack",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "attack": 1,
7 | "mana": 0,
8 | "maxMana": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 30,
12 | "outfit": {
13 | "id": 129,
14 | "details": {
15 | "head": 97,
16 | "body": 95,
17 | "legs": 116,
18 | "feet": 114
19 | }
20 | }
21 | },
22 | "hearingRange": 5,
23 | "wanderRange": 2,
24 | "farewells": [
25 | "bye"
26 | ],
27 | "greetings": [
28 | "hello",
29 | "hi"
30 | ],
31 | "sayings": {
32 | "texts": [
33 | "Yoho-Yoho, a law abiding sailors life for me!"
34 | ],
35 | "slowness": 300,
36 | "chance": 0.75
37 | },
38 | "script": "jack.js"
39 | }
--------------------------------------------------------------------------------
/data/740/npcs/definitions/karst.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Karst",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "mana": 0,
7 | "maxMana": 0,
8 | "attack": 1,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 30,
12 | "outfit": {
13 | "id": 128,
14 | "details": {
15 | "head": 38,
16 | "body": 63,
17 | "legs": 67,
18 | "feet": 39
19 | }
20 | }
21 | },
22 | "hearingRange": 5,
23 | "wanderRange": 3,
24 | "trade": [
25 | {
26 | "name": "Mug of Beer",
27 | "price": 1,
28 | "id": 2012,
29 | "count": 3,
30 | "type": "sell"
31 | },
32 | {
33 | "name": "Mug of Wine",
34 | "price": 2,
35 | "id": 2012,
36 | "count": 15,
37 | "type": "sell"
38 | }
39 | ],
40 | "farewells": [
41 | "bye"
42 | ],
43 | "greetings": [
44 | "hello",
45 | "hi"
46 | ],
47 | "sayings": {
48 | "texts": [
49 | "Beer!",
50 | "Booze!"
51 | ],
52 | "slowness": 300,
53 | "chance": 0.75
54 | },
55 | "script": "karst.js"
56 | }
--------------------------------------------------------------------------------
/data/740/npcs/definitions/marrow.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Marrow",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "attack": 1,
7 | "mana": 0,
8 | "maxMana": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 30,
12 | "outfit": {
13 | "id": 134,
14 | "details": {
15 | "head": 0,
16 | "body": 39,
17 | "legs": 25,
18 | "feet": 131
19 | }
20 | }
21 | },
22 | "hearingRange": 5,
23 | "wanderRange": 2,
24 | "trade": [
25 | {
26 | "name": "Leather Armor",
27 | "price": 10,
28 | "id": 2467,
29 | "type": "sell"
30 | },
31 | {
32 | "name": "Leather Legs",
33 | "price": 5,
34 | "id": 2649,
35 | "type": "sell"
36 | },
37 | {
38 | "name": "Leather Helmet",
39 | "price": 3,
40 | "id": 2461,
41 | "type": "sell"
42 | },
43 | {
44 | "name": "Bow",
45 | "price": 10,
46 | "id": 2456,
47 | "type": "sell"
48 | },
49 | {
50 | "name": "Mace",
51 | "price": 5,
52 | "id": 2398,
53 | "type": "sell"
54 | },
55 | {
56 | "name": "Leather Boots",
57 | "price": 3,
58 | "id": 2643,
59 | "type": "sell"
60 | }
61 | ],
62 | "farewells": [
63 | "bye"
64 | ],
65 | "greetings": [
66 | "hello",
67 | "hi"
68 | ],
69 | "sayings": {
70 | "texts": [
71 | "All sorts of weaponry and armors for sale! The finest in town!"
72 | ],
73 | "slowness": 300,
74 | "chance": 0.75
75 | },
76 | "script": "marrow.js"
77 | }
--------------------------------------------------------------------------------
/data/740/npcs/definitions/miller.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Miller",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "attack": 1,
7 | "mana": 0,
8 | "maxMana": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 30,
12 | "outfit": {
13 | "id": 128,
14 | "details": {
15 | "head": 132,
16 | "body": 77,
17 | "legs": 116,
18 | "feet": 98
19 | }
20 | }
21 | },
22 | "hearingRange": 5,
23 | "wanderRange": 2,
24 | "farewells": [
25 | "bye"
26 | ],
27 | "trade": [
28 | {
29 | "name": "Scythe",
30 | "price": 5,
31 | "id": 2550,
32 | "type": "sell"
33 | }
34 | ],
35 | "greetings": [
36 | "hello",
37 | "hi"
38 | ],
39 | "sayings": {
40 | "texts": [
41 | "Round and round she goes.."
42 | ],
43 | "slowness": 300,
44 | "chance": 0.75
45 | },
46 | "script": "miller.js"
47 | }
--------------------------------------------------------------------------------
/data/740/npcs/definitions/sarah.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Sarah",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "attack": 1,
7 | "mana": 0,
8 | "maxMana": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 30,
12 | "outfit": {
13 | "id": 140,
14 | "details": {
15 | "head": 39,
16 | "body": 105,
17 | "legs": 96,
18 | "feet": 59
19 | }
20 | }
21 | },
22 | "hearingRange": 5,
23 | "wanderRange": 2,
24 | "farewells": [
25 | "bye"
26 | ],
27 | "greetings": [
28 | "hello",
29 | "hi"
30 | ],
31 | "sayings": {
32 | "texts": [
33 | "♫♪♪",
34 | "♪♫♪♫",
35 | "What a cloudy day.."
36 | ],
37 | "slowness": 300,
38 | "chance": 0.75
39 | },
40 | "script": "sarah.js"
41 | }
--------------------------------------------------------------------------------
/data/740/npcs/definitions/script/albert.js:
--------------------------------------------------------------------------------
1 | const Condition = requireModule("condition");
2 |
3 | module.exports = function talkScriptAlbert() {
4 |
5 | /*
6 | * Function talkScriptAlbert
7 | * Definitions for NPC Albert
8 | */
9 |
10 | // Reference the base state (a base state is required)
11 | this.__baseTalkState = this.__talkState = baseTalkState;
12 |
13 | this.on("focus", function(player) {
14 |
15 | if(process.gameServer.world.clock.isMorning()) {
16 | this.internalCreatureSay("Good morning %s! Did you catch the surise?".format(player.name), CONST.COLOR.YELLOW);
17 | } else if(process.gameServer.world.clock.isNight()) {
18 | this.internalCreatureSay("Beautiful night eh, %s?".format(player.name), CONST.COLOR.YELLOW);
19 | } else {
20 | this.internalCreatureSay("Good day %s!".format(player.name), CONST.COLOR.YELLOW);
21 | }
22 |
23 | }.bind(this));
24 |
25 | this.on("enter", player => this.privateSay(player, "Oh, it's you again.. %s".format(player.name), CONST.COLOR.YELLOW));
26 | this.on("defocus", player => this.internalCreatureSay("See you around, %s.".format(player.name), CONST.COLOR.YELLOW));
27 | this.on("exit", player => this.internalCreatureSay("Youths these days..", CONST.COLOR.YELLOW));
28 | this.on("regreet", player => this.internalCreatureSay("Can I help you, %s?".format(player.name), CONST.COLOR.YELLOW));
29 | this.on("idle", player => this.internalCreatureSay("Not really a talker, huh.", CONST.COLOR.YELLOW));
30 | this.on("busy", player => this.internalCreatureSay("One moment please!", CONST.COLOR.YELLOW));
31 |
32 | }
33 |
34 | function baseTalkState(player, message) {
35 |
36 | /*
37 | * Function baseTalkState
38 | * The base state of the NPC. It will respond to the following keywords
39 | */
40 |
41 | switch(message) {
42 | case "arena":
43 | player.addCondition(Condition.prototype.ARENA, 10, 20, null);
44 | break;
45 | case "trade":
46 | this.internalCreatureSay("Have a browse!", CONST.COLOR.YELLOW);
47 | this.openTradeWindow(player);
48 | break;
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/data/740/npcs/definitions/script/billy.js:
--------------------------------------------------------------------------------
1 | module.exports = function talkScriptBilly() {
2 |
3 | /*
4 | * Function talkScriptBilly
5 | * Definitions for NPC Billy
6 | */
7 |
8 | // Reference the base state (a base state is required)
9 | this.__baseTalkState = this.__talkState = baseTalkState;
10 |
11 | this.on("focus", player => this.privateSay(player, "Hello, %s!".format(player.name), CONST.COLOR.YELLOW));
12 | this.on("enter", player => this.privateSay(player, "Oh, it's you again.. %s".format(player.name), CONST.COLOR.YELLOW));
13 | this.on("defocus", player => this.internalCreatureSay("See you around, %s.".format(player.name), CONST.COLOR.YELLOW));
14 | this.on("exit", player => this.internalCreatureSay("Youths these days..", CONST.COLOR.YELLOW));
15 | this.on("regreet", player => this.internalCreatureSay("Can I help you, %s?".format(player.name), CONST.COLOR.YELLOW));
16 | this.on("idle", player => this.internalCreatureSay("Not really a talker, huh.", CONST.COLOR.YELLOW));
17 | this.on("busy", player => this.internalCreatureSay("One moment please!", CONST.COLOR.YELLOW));
18 |
19 | }
20 |
21 | function baseTalkState(player, message) {
22 |
23 | /*
24 | * Function baseTalkState
25 | * The base state of the NPC. It will respond to the following keywords
26 | */
27 |
28 | switch(message) {
29 | case "house":
30 | this.internalCreatureSay("Do you want to own my house?", CONST.COLOR.YELLOW);
31 | this.setTalkState(confirmTalkState);
32 | break;
33 | }
34 |
35 | }
36 |
37 | function confirmTalkState(player, message) {
38 |
39 | switch(message) {
40 | case "yes":
41 | process.gameServer.database.getHouse(1).setOwner(player);
42 | this.internalCreatureSay("Alrighty, you can have it!", CONST.COLOR.YELLOW);
43 | this.setTalkState(baseTalkState);
44 | break;
45 | case "no":
46 | this.internalCreatureSay("It is free you know..", CONST.COLOR.YELLOW);
47 | this.setTalkState(baseTalkState);
48 | break;
49 | }
50 |
51 | }
--------------------------------------------------------------------------------
/data/740/npcs/definitions/script/karst.js:
--------------------------------------------------------------------------------
1 | const { drinkSceneBeer, drinkSceneWine } = require("./scene/karst");
2 |
3 | module.exports = function talkScriptAlbert() {
4 |
5 | /*
6 | * Function talkScriptAlbert
7 | * Definitions for NPC Albert
8 | */
9 |
10 | // Reference the base state (a base state is required)
11 | this.__baseTalkState = this.__talkState = baseTalkState;
12 |
13 | process.gameServer.world.clock.on("time", function(time) {
14 |
15 | if(time.endsWith("10")) {
16 | return this.setScene([drinkSceneBeer, drinkSceneWine].random());
17 | }
18 |
19 | }.bind(this));
20 |
21 | this.on("focus", function(player) {
22 | this.sayEmote("Hicks!", CONST.COLOR.YELLOW);
23 | }.bind(this));
24 |
25 | this.on("enter", player => this.privateSay(player, "Oh, it's you again.. %s".format(player.name), CONST.COLOR.YELLOW));
26 | this.on("defocus", player => this.internalCreatureSay("See you around, %s.".format(player.name), CONST.COLOR.YELLOW));
27 | this.on("exit", player => this.internalCreatureSay("Youths these days..", CONST.COLOR.YELLOW));
28 | this.on("regreet", player => this.internalCreatureSay("Can I help you, %s?".format(player.name), CONST.COLOR.YELLOW));
29 | this.on("idle", player => this.internalCreatureSay("Not really a talker, huh.", CONST.COLOR.YELLOW));
30 | this.on("busy", player => this.internalCreatureSay("One moment please!", CONST.COLOR.YELLOW));
31 |
32 | }
33 |
34 | function baseTalkState(player, message) {
35 |
36 | /*
37 | * Function baseTalkState
38 | * The base state of the NPC. It will respond to the following keywords
39 | */
40 |
41 | switch(message) {
42 | case "trade":
43 | this.internalCreatureSay("Have a browse!", CONST.COLOR.YELLOW);
44 | this.openTradeWindow(player);
45 | break;
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/data/740/npcs/definitions/script/marrow.js:
--------------------------------------------------------------------------------
1 | const { keyScene } = require("./scene/marrow");
2 |
3 | module.exports = function talkScriptMarrow() {
4 |
5 | /*
6 | * Function talkScriptMarrow
7 | * Definitions for NPC Marrow
8 | */
9 |
10 | // Reference the base state (a base state is required)
11 | this.__baseTalkState = this.__talkState = baseTalkState;
12 |
13 | this.on("enter", player => this.privateSay(player, "Wipe your feet before coming in, %s!".format(player.name), CONST.COLOR.YELLOW));
14 | this.on("focus", player => this.internalCreatureSay("Oi, %s!".format(player.name), CONST.COLOR.YELLOW));
15 | this.on("defocus", player => this.internalCreatureSay("Be safe out there, %s".format(player.name), CONST.COLOR.YELLOW));
16 | this.on("exit", player => this.internalCreatureSay("Selfish.", CONST.COLOR.YELLOW));
17 | this.on("regreet", player => this.internalCreatureSay("Hmm?", CONST.COLOR.YELLOW));
18 | this.on("idle", player => this.internalCreatureSay("You are wasting my time.", CONST.COLOR.YELLOW));
19 | this.on("busy", player => this.internalCreatureSay("Hold your horses there, %s. I'm busy here.".format(player.name), CONST.COLOR.YELLOW));
20 |
21 | }
22 |
23 | function baseTalkState(player, message) {
24 |
25 | /*
26 | * Function baseTalkState
27 | * The base state of the NPC. It will respond to the following keywords
28 | */
29 |
30 | switch(message) {
31 | case "spell":
32 | this.internalCreatureSay("Learn this!", CONST.COLOR.YELLOW);
33 | return player.spellbook.addAvailableSpell(4);
34 | case "trade":
35 | this.internalCreatureSay("Here are my wares.", CONST.COLOR.YELLOW);
36 | this.openTradeWindow(player);
37 | break;
38 | case "cellar infestation":
39 | case "infestation":
40 | this.internalCreatureSay("We have a little rat problem down there in the cellar.", CONST.COLOR.YELLOW);
41 | break;
42 | case "closed":
43 | case "door":
44 | case "cellar":
45 | this.internalCreatureSay("The cellar door is locked and you'll need a key to open it.", CONST.COLOR.YELLOW);
46 | break;
47 | case "key":
48 | this.internalCreatureSay("Are you looking for the key to the cellar?", CONST.COLOR.YELLOW);
49 | this.setTalkState(keyTalkState);
50 | break;
51 | }
52 |
53 | }
54 |
55 | function keyTalkState(player, message) {
56 |
57 | switch(message) {
58 | case "no":
59 | this.internalCreatureSay("Ok let me know if I can do something for you.", CONST.COLOR.YELLOW);
60 | this.setTalkState(baseTalkState);
61 | break;
62 | case "yes":
63 | this.setScene(keyScene);
64 | break;
65 | }
66 |
67 | }
--------------------------------------------------------------------------------
/data/740/npcs/definitions/script/sarah.js:
--------------------------------------------------------------------------------
1 | module.exports = function sarah() {
2 |
3 | /*
4 | * Definitions for NPC Sarah
5 | * All events are emitters when the character engages with the NPC
6 | */
7 |
8 | // Reference the base state (a base state is required)
9 | this.__baseTalkState = this.__talkState = baseTalkState;
10 |
11 | // Defaults
12 | this.on("focus", player => this.internalCreatureSay("Good to see you here, %s.".format(player.name), CONST.COLOR.YELLOW));
13 | this.on("defocus", player => this.internalCreatureSay("Safe climb down!", CONST.COLOR.YELLOW));
14 | this.on("exit", player => this.internalCreatureSay("Pff.", CONST.COLOR.YELLOW));
15 | this.on("regreet", player => this.internalCreatureSay("Yes, sweety?", CONST.COLOR.YELLOW));
16 | this.on("idle", player => this.internalCreatureSay("Excuse me..", CONST.COLOR.YELLOW));
17 | this.on("busy", player => this.internalCreatureSay("One moment darling.", CONST.COLOR.YELLOW));
18 |
19 | }
20 |
21 | function baseTalkState(player, message) {
22 |
23 | /*
24 | * Function baseTalkState
25 | * The base state of the NPC. It will respond to the following keywords
26 | */
27 |
28 | switch(message) {
29 | case "name":
30 | this.internalCreatureSay("My name is Sarah.", CONST.COLOR.YELLOW);
31 | break;
32 | case "sarah":
33 | this.internalCreatureSay("That's me!", CONST.COLOR.YELLOW);
34 | this.sayEmote("❤", CONST.COLOR.RED);
35 | break;
36 | case "miller":
37 | this.internalCreatureSay("Miller is my dad. Look down there and you may see him!", CONST.COLOR.YELLOW);
38 | break;
39 | }
40 |
41 | }
--------------------------------------------------------------------------------
/data/740/npcs/definitions/script/scene/jack-story.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | "mode": "move",
4 | "timeout": 10,
5 | "position": {
6 | "x": 85,
7 | "y": 127,
8 | "z": 8
9 | }
10 | },
11 | {
12 | "mode": "face",
13 | "direction": 2
14 | },
15 | {
16 | "mode": "talk",
17 | "duration": 100,
18 | "message": "The sea was angry that day.."
19 | },
20 | {
21 | "mode": "idle",
22 | "duration": 20
23 | },
24 | {
25 | "mode": "talk",
26 | "duration": 100,
27 | "message": "We went fishing and then.."
28 | },
29 | {
30 | "mode": "face",
31 | "direction": 0,
32 | },
33 | {
34 | "mode": "spell",
35 | "effect": 2,
36 | "position": {
37 | "x": 85,
38 | "y": 127,
39 | "z": 8
40 | }
41 | },
42 | {
43 | "mode": "talk",
44 | "duration": 20,
45 | "message": "Bam! Water in my face.. crazy time."
46 | },
47 | {
48 | "mode": "idle",
49 | "duration": 20
50 | }
51 | ]
--------------------------------------------------------------------------------
/data/740/npcs/definitions/script/scene/karst.js:
--------------------------------------------------------------------------------
1 | module.exports.drinkSceneBeer = [{
2 | "mode": "talk",
3 | "duration": 20,
4 | "message": "Wait one second. I have got to try a little bit of this right here.."
5 | }, {
6 | "mode": "move",
7 | "timeout": 50,
8 | "position": {
9 | "x": 54,
10 | "y": 90,
11 | "z": 9
12 | }
13 | }, {
14 | "mode": "face",
15 | "direction": 0,
16 | }, {
17 | "mode": "spell",
18 | "effect": 2,
19 | "position": {
20 | "x": 54,
21 | "y": 89,
22 | "z": 9
23 | }
24 | }, {
25 | "mode": "talk",
26 | "duration": 5,
27 | "message": "Ahh, refressshing!"
28 | }, {
29 | "mode": "emote",
30 | "message": "Hicks!"
31 | }]
32 |
33 | module.exports.drinkSceneWine = [{
34 | "mode": "talk",
35 | "duration": 20,
36 | "message": "Maybe I will try a little bit of this one now.."
37 | }, {
38 | "mode": "move",
39 | "timeout": 50,
40 | "position": {
41 | "x": 53,
42 | "y": 90,
43 | "z": 9
44 | }
45 | }, {
46 | "mode": "face",
47 | "direction": 0,
48 | }, {
49 | "mode": "spell",
50 | "effect": 2,
51 | "position": {
52 | "x": 53,
53 | "y": 89,
54 | "z": 9
55 | }
56 | }, {
57 | "mode": "talk",
58 | "duration": 5,
59 | "message": "Delicious!"
60 | }, {
61 | "mode": "emote",
62 | "message": "Hicks!"
63 | }]
--------------------------------------------------------------------------------
/data/740/npcs/definitions/script/scene/marrow.js:
--------------------------------------------------------------------------------
1 | module.exports.keyScene = [{
2 | "mode": "talk",
3 | "duration": 20,
4 | "message": "Let me fetch a spare key from storage for you."
5 | }, {
6 | "mode": "move",
7 | "timeout": 50,
8 | "position": {
9 | "x": 100,
10 | "y": 86,
11 | "z": 8
12 | }
13 | }, {
14 | "mode": "face",
15 | "direction": 3
16 | }, {
17 | "mode": "spell",
18 | "effect": 4,
19 | "position": {
20 | "x": 99,
21 | "y": 86,
22 | "z": 8
23 | }
24 | }, {
25 | "mode": "move",
26 | "timeout": 50,
27 | "position": {
28 | "x": 100,
29 | "y": 90,
30 | "z": 8
31 | }
32 | }, {
33 | "mode": "face",
34 | "direction": 1
35 | }, {
36 | "mode": "add",
37 | "item": 2088,
38 | "actionId": 2000,
39 | "count": 1,
40 | "position": {
41 | "x": 101,
42 | "y": 90,
43 | "z": 8
44 | }
45 | }, {
46 | "mode": "talk",
47 | "duration": 20,
48 | "message": "Alright. Be careful down there, buddy."
49 | }];
--------------------------------------------------------------------------------
/data/740/npcs/definitions/script/scene/miller.js:
--------------------------------------------------------------------------------
1 | module.exports.millScene = [{
2 | "mode": "talk",
3 | "duration": 20,
4 | "message": "Alright, time to turn in for the night."
5 | }, {
6 | "mode": "move",
7 | "timeout": 50,
8 | "position": {
9 | "x": 84,
10 | "y": 116,
11 | "z": 8
12 | }
13 | }, {
14 | "mode": "anchor",
15 | "position": {
16 | "x": 84,
17 | "y": 116,
18 | "z": 8
19 | }
20 | }];
21 |
22 | module.exports.fieldScene = [{
23 | "mode": "talk",
24 | "duration": 20,
25 | "message": "It is a beautiful day out there!"
26 | }, {
27 | "mode": "move",
28 | "timeout": 50,
29 | "position": {
30 | "x": 96,
31 | "y": 119,
32 | "z": 8
33 | }
34 | }, {
35 | "mode": "anchor",
36 | "position": {
37 | "x": 96,
38 | "y": 119,
39 | "z": 8
40 | }
41 | }];
42 |
--------------------------------------------------------------------------------
/data/740/npcs/definitions/script/teller.js:
--------------------------------------------------------------------------------
1 | module.exports = function sarah() {
2 |
3 | /*
4 | * Definitions for NPC Sarah
5 | * All events are emitters when the character engages with the NPC
6 | */
7 |
8 | // Reference the base state (a base state is required)
9 | this.__baseTalkState = this.__talkState = baseTalkState;
10 |
11 | // Defaults
12 | this.on("focus", player => this.internalCreatureSay("Welcome, %s.".format(player.name), CONST.COLOR.YELLOW));
13 | this.on("defocus", player => this.internalCreatureSay("Goodbye.", CONST.COLOR.YELLOW));
14 | this.on("exit", player => this.sayEmote("!?", CONST.COLOR.YELLOW));
15 | this.on("regreet", player => this.internalCreatureSay("Tell me what you need, %s".format(player.name), CONST.COLOR.YELLOW));
16 | this.on("idle", player => this.internalCreatureSay("Hello?", CONST.COLOR.YELLOW));
17 | this.on("busy", player => this.internalCreatureSay("I'll help you next, %s!".format(player.name), CONST.COLOR.YELLOW));
18 |
19 | }
20 |
21 | function baseTalkState(player, message) {
22 |
23 | /*
24 | * Function baseTalkState
25 | * The base state of the NPC. It will respond to the following keywords
26 | */
27 |
28 | switch(message) {
29 | case "trade":
30 | this.openTradeWindow(player);
31 | break;
32 | case "name":
33 | this.internalCreatureSay("Name's Teller. Bet you can guess why!", CONST.COLOR.YELLOW);
34 | break;
35 | case "teller":
36 | this.internalCreatureSay("Yep! I'm Borne's banker! Head up stairs if you need to access your locker.", CONST.COLOR.YELLOW);
37 | this.sayEmote("💰", CONST.COLOR.RED);
38 | break;
39 | case "letter":
40 | this.internalCreatureSay("To who?", CONST.COLOR.YELLOW);
41 | this.setTalkState(letterTalkState);
42 | break;
43 | }
44 |
45 | }
46 |
47 | function letterTalkState(player, message) {
48 |
49 | /*
50 | * Function baseTalkState
51 | * The base state of the NPC. It will respond to the following keywords
52 | */
53 |
54 | process.gameServer.world.packetHandler.mailboxHandler.writeLetter(message, "%s sent you a letter.".format(player.name), function(error) {
55 |
56 | if(error) {
57 | return this.internalCreatureSay("Sorry I do not know this player.", CONST.COLOR.YELLOW);
58 | }
59 |
60 | return this.internalCreatureSay("Done!", CONST.COLOR.YELLOW);
61 |
62 | }.bind(this));
63 |
64 | this.setTalkState(baseTalkState);
65 |
66 | }
--------------------------------------------------------------------------------
/data/740/npcs/definitions/teller.json:
--------------------------------------------------------------------------------
1 | {
2 | "creatureStatistics": {
3 | "name": "Teller",
4 | "health": 50,
5 | "maxHealth": 50,
6 | "attack": 1,
7 | "mana": 0,
8 | "maxMana": 0,
9 | "attackSlowness": 40,
10 | "defense": 5,
11 | "speed": 30,
12 | "outfit": {
13 | "id": 141,
14 | "details": {
15 | "head": 12,
16 | "body": 2,
17 | "legs": 12,
18 | "feet": 10
19 | }
20 | }
21 | },
22 | "hearingRange": 5,
23 | "wanderRange": 2,
24 | "farewells": [
25 | "bye"
26 | ],
27 | "trade": [
28 | {
29 | "name": "Letter",
30 | "price": 0,
31 | "id": 2597,
32 | "type": "sell"
33 | },
34 | {
35 | "name": "Parcel",
36 | "price": 0,
37 | "id": 2595,
38 | "type": "sell"
39 | },
40 | {
41 | "name": "Label",
42 | "price": 0,
43 | "id": 2599,
44 | "type": "sell"
45 | }
46 | ],
47 | "greetings": [
48 | "hello",
49 | "hi"
50 | ],
51 | "sayings": {
52 | "texts": [
53 | "A safe place to store your items!"
54 | ],
55 | "slowness": 300,
56 | "chance": 0.75
57 | },
58 | "script": "teller.js"
59 | }
--------------------------------------------------------------------------------
/data/740/outfits/outfits.json:
--------------------------------------------------------------------------------
1 | {
2 | "128": {
3 | "type": "1",
4 | "name": "Citizen",
5 | "premium": false
6 | },
7 | "129": {
8 | "type": "1",
9 | "name": "Hunter",
10 | "premium": false
11 | },
12 | "130": {
13 | "type": "1",
14 | "name": "Mage",
15 | "premium": false
16 | },
17 | "131": {
18 | "type": "1",
19 | "name": "Knight",
20 | "premium": false
21 | },
22 | "132": {
23 | "type": "1",
24 | "name": "Nobleman",
25 | "premium": true
26 | },
27 | "133": {
28 | "type": "1",
29 | "name": "Summoner",
30 | "premium": true
31 | },
32 | "134": {
33 | "type": "1",
34 | "name": "Warrior",
35 | "premium": true
36 | },
37 | "136": {
38 | "type": "0",
39 | "name": "Citizen",
40 | "premium": false
41 | },
42 | "137": {
43 | "type": "0",
44 | "name": "Hunter",
45 | "premium": false
46 | },
47 | "138": {
48 | "type": "0",
49 | "name": "Mage",
50 | "premium": false
51 | },
52 | "139": {
53 | "type": "0",
54 | "name": "Knight",
55 | "premium": false
56 | },
57 | "140": {
58 | "type": "0",
59 | "name": "Noblewoman",
60 | "premium": true
61 | },
62 | "141": {
63 | "type": "0",
64 | "name": "Summoner",
65 | "premium": true
66 | },
67 | "142": {
68 | "type": "0",
69 | "name": "Warrior",
70 | "premium": true
71 | },
72 | "144": {
73 | "type": "1",
74 | "name": "Elf",
75 | "premium": true
76 | },
77 | "160": {
78 | "type": "1",
79 | "name": "Dwarf",
80 | "premium": true
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/data/740/runes/definitions.json:
--------------------------------------------------------------------------------
1 | {
2 | "2268": "sudden-death.js",
3 | "2311": "heavy-magic-missile.js",
4 | "2304": "fireball.js",
5 | "2301": "firefield.js",
6 | "2277": "energyfield.js",
7 | "2285": "poisonfield.js",
8 | "2293": "magicwall.js",
9 | "2261": "destroyfield.js",
10 | "2273": "ultimate-healing.js",
11 | "2305": "fire-bomb.js",
12 | "2312": "teleport.js",
13 | "2300": "hearthrune.js"
14 | }
--------------------------------------------------------------------------------
/data/740/runes/definitions/destroyfield.js:
--------------------------------------------------------------------------------
1 | const Position = requireModule("position");
2 |
3 | module.exports = function destroyField(source, target) {
4 |
5 | /*
6 | * function suddenDeath
7 | * Code that handles the sudden death rune
8 | */
9 |
10 | let item = target.peekItem(0xFF);
11 |
12 | if(item === null) {
13 | return process.gameServer.world.sendMagicEffect(source.position, CONST.EFFECT.MAGIC.POFF);
14 | }
15 |
16 | if(!item.getPrototype().isMagicField()) {
17 | return process.gameServer.world.sendMagicEffect(source.position, CONST.EFFECT.MAGIC.POFF);
18 | }
19 |
20 | item.remove();
21 |
22 | process.gameServer.world.sendMagicEffect(target.position, CONST.EFFECT.MAGIC.POFF);
23 |
24 | return true;
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/data/740/runes/definitions/energyfield.js:
--------------------------------------------------------------------------------
1 | const Position = requireModule("position");
2 |
3 | module.exports = function greatFireball(source, target) {
4 |
5 | /*
6 | * function suddenDeath
7 | * Code that handles the sudden death rune
8 | */
9 |
10 | // Get circle position for the GFB
11 | process.gameServer.world.sendDistanceEffect(source.position, target.position, CONST.EFFECT.PROJECTILE.ENERGY);
12 | target.addItem(process.gameServer.database.createThing(1495));
13 |
14 | return true;
15 |
16 | }
--------------------------------------------------------------------------------
/data/740/runes/definitions/fire-bomb.js:
--------------------------------------------------------------------------------
1 | const Position = requireModule("position");
2 |
3 | module.exports = function greatFireball(source, target) {
4 |
5 | /*
6 | * function suddenDeath
7 | * Code that handles the sudden death rune
8 | */
9 |
10 | let square = Position.prototype.getSquare(1);
11 |
12 | process.gameServer.world.sendDistanceEffect(source.position, target.position, CONST.EFFECT.PROJECTILE.FIRE);
13 |
14 | square.forEach(function(position) {
15 |
16 | let relPosition = target.position.add(position);
17 | let tile = process.gameServer.world.getTileFromWorldPosition(relPosition);
18 |
19 | if(tile === null) {
20 | return;
21 | }
22 |
23 | if(tile.isBlockSolid()) {
24 | return;
25 | }
26 |
27 | // Get circle position for the GFB
28 | tile.addItem(process.gameServer.database.createThing(1487));
29 |
30 | });
31 |
32 | return true;
33 |
34 | }
--------------------------------------------------------------------------------
/data/740/runes/definitions/fireball.js:
--------------------------------------------------------------------------------
1 | const Position = requireModule("position");
2 |
3 | module.exports = function greatFireball(source, target) {
4 |
5 | /*
6 | * function suddenDeath
7 | * Code that handles the sudden death rune
8 | */
9 |
10 | // Get circle position for the GFB
11 | let circle = Position.prototype.getRadius(2);
12 |
13 | process.gameServer.world.sendDistanceEffect(source.position, target.position, CONST.EFFECT.PROJECTILE.FIRE);
14 |
15 | circle.forEach(function(position) {
16 |
17 | let relPosition = target.position.add(position);
18 | let tile = process.gameServer.world.getTileFromWorldPosition(relPosition);
19 |
20 | if(tile === null) {
21 | return;
22 | }
23 |
24 | // Tile is blocked
25 | if(tile.isBlockSolid()) {
26 | return;
27 | }
28 |
29 | // Apply to the tile
30 | process.gameServer.world.sendMagicEffect(relPosition, CONST.EFFECT.MAGIC.FIREAREA);
31 |
32 | tile.monsters.forEach(function(monster) {
33 | process.gameServer.world.__damageEntity(source, monster, 1000, CONST.COLOR.RED);
34 | });
35 |
36 | });
37 |
38 | return true;
39 |
40 | }
--------------------------------------------------------------------------------
/data/740/runes/definitions/firefield.js:
--------------------------------------------------------------------------------
1 | const Position = requireModule("position");
2 |
3 | module.exports = function greatFireball(source, target) {
4 |
5 | /*
6 | * function suddenDeath
7 | * Code that handles the sudden death rune
8 | */
9 |
10 | // Get circle position for the GFB
11 | process.gameServer.world.sendDistanceEffect(source.position, target.position, CONST.EFFECT.PROJECTILE.FIRE);
12 | target.addItem(process.gameServer.database.createThing(1487));
13 |
14 | return true;
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/data/740/runes/definitions/hearthrune.js:
--------------------------------------------------------------------------------
1 | module.exports = function hearthrune(source, target) {
2 |
3 | /*
4 | * Function teleport
5 | * Code that handles the teleport rune to another (reachable) position
6 | */
7 |
8 | // Teleport and effects
9 | process.gameServer.world.teleportCreature(source, source.characterStatistics.templePosition);
10 | process.gameServer.world.sendMagicEffect(source.position, CONST.EFFECT.MAGIC.TELEPORT);
11 |
12 | // Never consume
13 | return false;
14 |
15 | }
--------------------------------------------------------------------------------
/data/740/runes/definitions/heavy-magic-missile.js:
--------------------------------------------------------------------------------
1 | module.exports = function heavyMagicMissile(source, target) {
2 |
3 | /*
4 | * function heavyMagicMissile
5 | * Code that handles the sudden death rune
6 | */
7 |
8 | // If no monsters on the tile
9 | if(target.monsters.size === 0) {
10 | return false;
11 | }
12 |
13 | // Send magic effects
14 | process.gameServer.world.sendDistanceEffect(source.position, target.position, CONST.EFFECT.PROJECTILE.ENERGY);
15 | process.gameServer.world.sendMagicEffect(target.position, CONST.EFFECT.MAGIC.ENERGYHIT);
16 |
17 | // Apply the damage to all creatures
18 | target.monsters.forEach(function(monster) {
19 | process.gameServer.world.__damageEntity(source, monster, 1);
20 | });
21 |
22 | return true;
23 |
24 | }
--------------------------------------------------------------------------------
/data/740/runes/definitions/magicwall.js:
--------------------------------------------------------------------------------
1 | const Position = requireModule("position");
2 |
3 | module.exports = function greatFireball(source, target) {
4 |
5 | /*
6 | * function suddenDeath
7 | * Code that handles the sudden death rune
8 | */
9 |
10 | // Get circle position for the GFB
11 | process.gameServer.world.sendDistanceEffect(source.position, target.position, CONST.EFFECT.PROJECTILE.ENERGY);
12 | target.addItem(process.gameServer.database.createThing(1498));
13 |
14 | return true;
15 |
16 | }
--------------------------------------------------------------------------------
/data/740/runes/definitions/poisonfield.js:
--------------------------------------------------------------------------------
1 | const Position = requireModule("position");
2 |
3 | module.exports = function greatFireball(source, target) {
4 |
5 | /*
6 | * function suddenDeath
7 | * Code that handles the sudden death rune
8 | */
9 |
10 | // Get circle position for the GFB
11 | process.gameServer.world.sendDistanceEffect(source.position, target.position, CONST.EFFECT.PROJECTILE.POISON);
12 | target.addItem(process.gameServer.database.createThing(1496));
13 |
14 | return true;
15 |
16 | }
--------------------------------------------------------------------------------
/data/740/runes/definitions/sudden-death.js:
--------------------------------------------------------------------------------
1 | module.exports = function suddenDeath(source, target) {
2 |
3 | /*
4 | * function suddenDeath
5 | * Code that handles the sudden death rune
6 | */
7 |
8 | // If no monsters on the tile
9 | if(target.monsters.size === 0) {
10 | return false;
11 | }
12 |
13 | process.gameServer.world.sendDistanceEffect(source.position, target.position, CONST.EFFECT.PROJECTILE.DEATH);
14 | process.gameServer.world.sendMagicEffect(target.position, CONST.EFFECT.MAGIC.MORTAREA);
15 |
16 | // Do damage
17 | target.monsters.forEach(function(monster) {
18 | process.gameServer.world.__damageEntity(source, monster, 10);
19 | });
20 |
21 | return true;
22 |
23 | }
--------------------------------------------------------------------------------
/data/740/runes/definitions/teleport.js:
--------------------------------------------------------------------------------
1 | module.exports = function teleport(source, target) {
2 |
3 | /*
4 | * Function teleport
5 | * Code that handles the teleport rune to another (reachable) position
6 | */
7 |
8 | // Attempt to find a path from creature to the destination: if possible: allow teleport
9 | let path = process.gameServer.world.findPath(
10 | source,
11 | source.position,
12 | target.position,
13 | process.gameServer.world.pathfinder.opcodes.EXACT
14 | );
15 |
16 | // Pathfinding is not possible
17 | if(path.length === 0) {
18 | process.gameServer.world.sendMagicEffect(source.position, CONST.EFFECT.MAGIC.POFF);
19 | source.sendCancelMessage("You cannot teleport there.");
20 | return false;
21 | }
22 |
23 | // Teleport and effects
24 | process.gameServer.world.sendMagicEffect(source.position, CONST.EFFECT.MAGIC.MAGIC_BLUE);
25 | process.gameServer.world.teleportCreature(source, target.position);
26 | process.gameServer.world.sendMagicEffect(source.position, CONST.EFFECT.MAGIC.TELEPORT);
27 |
28 | // Consume
29 | return true;
30 |
31 | }
--------------------------------------------------------------------------------
/data/740/runes/definitions/ultimate-healing.js:
--------------------------------------------------------------------------------
1 | module.exports = function suddenDeath(source, target) {
2 |
3 | /*
4 | * function suddenDeath
5 | * Code that handles the sudden death rune
6 | */
7 |
8 | // If no monsters on the tile
9 | if(target.players.size === 0) {
10 | return false;
11 | }
12 |
13 | process.gameServer.world.sendMagicEffect(target.position, CONST.EFFECT.MAGIC.MAGIC_BLUE);
14 |
15 | // Do damage
16 | target.players.forEach(function(player) {
17 | player.increaseHealth(10);
18 | });
19 |
20 | return true;
21 |
22 | }
--------------------------------------------------------------------------------
/data/740/spawns/definitions.json:
--------------------------------------------------------------------------------
1 | [{
2 | "respawnTime": 4,
3 | "mid": "slime",
4 | "position": {
5 | "x": 88,
6 | "y": 90,
7 | "z": 8
8 | }
9 | }, {
10 | "respawnTime": 4,
11 | "mid": "sheep",
12 | "position": {
13 | "x": 53,
14 | "y": 104,
15 | "z": 9
16 | }
17 | }, {
18 | "respawnTime": 4,
19 | "mid": "black-sheep",
20 | "position": {
21 | "x": 54,
22 | "y": 106,
23 | "z": 9
24 | }
25 | }, {
26 | "respawnTime": 4,
27 | "mid": "rat",
28 | "position": {
29 | "x": 109,
30 | "y": 92,
31 | "z": 7
32 | }
33 | }, {
34 | "respawnTime": 4,
35 | "mid": "cave-rat",
36 | "position": {
37 | "x": 107,
38 | "y": 93,
39 | "z": 7
40 | }
41 | }, {
42 | "respawnTime": 4,
43 | "mid": "rotworm",
44 | "position": {
45 | "x": 44,
46 | "y": 109,
47 | "z": 5
48 | }
49 | }, {
50 | "respawnTime": 4,
51 | "mid": "bear",
52 | "position": {
53 | "x": 124,
54 | "y": 98,
55 | "z": 8
56 | }
57 | }, {
58 | "respawnTime": 4,
59 | "mid": "spider",
60 | "position": {
61 | "x": 131,
62 | "y": 108,
63 | "z": 8
64 | }
65 | }, {
66 | "respawnTime": 10,
67 | "mid": "skeleton",
68 | "position": {
69 | "x": 85,
70 | "y": 36,
71 | "z": 6
72 | }
73 | }]
74 |
--------------------------------------------------------------------------------
/data/740/spells/definitions.json:
--------------------------------------------------------------------------------
1 | {
2 | "0": "cure.js",
3 | "1": "explosion.js",
4 | "2": "exura.js",
5 | "3": "invisible.js",
6 | "4": "morph.js",
7 | "5": "light.js",
8 | "6": "life-drain.js",
9 | "7": "hearthstone.js",
10 | "8": "haste.js",
11 | "9": "levitate.js"
12 | }
--------------------------------------------------------------------------------
/data/740/spells/definitions/beam.js:
--------------------------------------------------------------------------------
1 | module.exports = function beamEnergy() {
2 |
3 | /*
4 | * Function beamEnergy
5 | * Creature energy beam function
6 | */
7 |
8 | let source = this;
9 |
10 | for(let i = 0; i < 5; i++) {
11 |
12 | let position = this.__getSpellPosition(0, -i);
13 |
14 | process.gameServer.world.sendMagicEffect(
15 | position,
16 | CONST.EFFECT.MAGIC.ENERGYHIT
17 | );
18 |
19 | let tile = process.gameServer.world.getTileFromWorldPosition(position);
20 |
21 | if(tile === null) {
22 | continue;
23 | }
24 |
25 | tile.players.forEach(function(player) {
26 |
27 | let damage = Number.prototype.random(0, 3);
28 |
29 | process.gameServer.world.__damageEntity(source, player, damage, CONST.COLOR.LIGHTBLUE);
30 |
31 | });
32 |
33 | }
34 |
35 | return 50;
36 |
37 | }
--------------------------------------------------------------------------------
/data/740/spells/definitions/cure.js:
--------------------------------------------------------------------------------
1 | const Condition = requireModule("condition");
2 |
3 | module.exports = function cureBurning() {
4 |
5 | /*
6 | * Function cureBurning
7 | * Spell to cure the burning condition from the player
8 | */
9 |
10 | // Not burning
11 | if(!this.conditions.has(Condition.prototype.BURNING)) {
12 | this.sendCancelMessage("You are not burning.");
13 | return 0;
14 | }
15 |
16 | this.removeCondition(Condition.prototype.BURNING);
17 | process.gameServer.world.sendMagicEffect(this.position, CONST.EFFECT.MAGIC.MAGIC_BLUE);
18 |
19 | // Return cooldown
20 | return 1000;
21 |
22 | }
--------------------------------------------------------------------------------
/data/740/spells/definitions/explosion.js:
--------------------------------------------------------------------------------
1 | module.exports = function explosion() {
2 |
3 | /*
4 | * Function beamEnergy
5 | * Creature energy beam function
6 | */
7 |
8 | process.gameServer.world.applyEnvironmentalDamage(this, 40, CONST.COLOR.ORANGE);
9 | process.gameServer.world.sendMagicEffect(this.position, CONST.EFFECT.MAGIC.HITBYFIRE);
10 |
11 | return 50;
12 |
13 | }
--------------------------------------------------------------------------------
/data/740/spells/definitions/exura.js:
--------------------------------------------------------------------------------
1 | const Condition = requireModule("condition");
2 |
3 | module.exports = function exura(properties) {
4 |
5 | this.addCondition(Condition.prototype.HEALING, 5, 10);
6 |
7 | return 100;
8 |
9 | }
--------------------------------------------------------------------------------
/data/740/spells/definitions/haste.js:
--------------------------------------------------------------------------------
1 | const Condition = requireModule("condition");
2 |
3 | module.exports = function morph(properties) {
4 |
5 | process.gameServer.world.sendMagicEffect(this.position, CONST.EFFECT.MAGIC.MAGIC_GREEN);
6 | this.addCondition(Condition.prototype.HASTE, 1, 100);
7 |
8 | return 100;
9 |
10 | }
--------------------------------------------------------------------------------
/data/740/spells/definitions/hearthstone.js:
--------------------------------------------------------------------------------
1 | module.exports = function Hearthstone(properties) {
2 |
3 | /*
4 | * Function Hearthstone
5 | * Teleports the player to his/her temple position
6 | */
7 |
8 | process.gameServer.world.sendMagicEffect(this.position, CONST.EFFECT.MAGIC.POFF);
9 | process.gameServer.world.teleportCreature(this, this.characterStatistics.templePosition);
10 | process.gameServer.world.sendMagicEffect(this.position, CONST.EFFECT.MAGIC.TELEPORT);
11 |
12 | return 100;
13 |
14 | }
--------------------------------------------------------------------------------
/data/740/spells/definitions/invisible.js:
--------------------------------------------------------------------------------
1 | const Condition = requireModule("condition");
2 |
3 | module.exports = function exura(properties) {
4 |
5 | if(!this.addCondition(Condition.prototype.INVISIBLE, 100, 1)) {
6 | return 0;
7 | }
8 |
9 | return 50;
10 |
11 | }
--------------------------------------------------------------------------------
/data/740/spells/definitions/levitate.js:
--------------------------------------------------------------------------------
1 | module.exports = function levitate(properties) {
2 |
3 | let facePosition = this.getFacePosition();
4 | let faceTile = process.gameServer.world.getTileFromWorldPosition(facePosition);
5 |
6 | if(faceTile === null) {
7 |
8 | let downTile = process.gameServer.world.getTileFromWorldPosition(facePosition.down());
9 |
10 | if(downTile !== null && downTile.id !== 0 && !this.isTileOccupied(downTile)) {
11 | process.gameServer.world.sendMagicEffect(downTile.position, CONST.EFFECT.MAGIC.TELEPORT);
12 | process.gameServer.world.teleportCreature(this, downTile.position);
13 | return 100;
14 | }
15 |
16 | }
17 |
18 | let directUpTile = process.gameServer.world.getTileFromWorldPosition(this.position.up());
19 |
20 | if(directUpTile === null) {
21 |
22 | let upTile = process.gameServer.world.getTileFromWorldPosition(facePosition.up());
23 |
24 | if(upTile !== null && upTile.id !== 0 && !this.isTileOccupied(upTile)) {
25 | process.gameServer.world.sendMagicEffect(upTile.position, CONST.EFFECT.MAGIC.TELEPORT);
26 | process.gameServer.world.teleportCreature(this, upTile.position);
27 | return 100;
28 | }
29 | }
30 |
31 | return 0;
32 |
33 | }
--------------------------------------------------------------------------------
/data/740/spells/definitions/life-drain.js:
--------------------------------------------------------------------------------
1 | module.exports = function lifeDrain() {
2 |
3 | /*
4 | * Function lifeDrain
5 | * Creature energy beam function
6 | */
7 |
8 | let source = this;
9 |
10 | if(!this.hasTarget()) {
11 | return;
12 | }
13 |
14 | let amount = Number.prototype.random(5, 7);
15 |
16 | this.increaseHealth(amount);
17 | process.gameServer.world.sendMagicEffect(this.position, CONST.EFFECT.MAGIC.MAGIC_GREEN);
18 |
19 |
20 | this.getTarget().decreaseHealth(this, amount, CONST.COLOR.RED);
21 | process.gameServer.world.sendMagicEffect(this.getTarget().position, CONST.EFFECT.MAGIC.MAGIC_RED);
22 |
23 | return 50;
24 |
25 | }
--------------------------------------------------------------------------------
/data/740/spells/definitions/light.js:
--------------------------------------------------------------------------------
1 | const Condition = requireModule("condition");
2 |
3 | module.exports = function spellLight(properties) {
4 |
5 | process.gameServer.world.sendMagicEffect(this.position, CONST.EFFECT.MAGIC.MAGIC_BLUE);
6 | this.sayEmote("Parva Lux!", CONST.COLOR.SKYBLUE);
7 |
8 | if(!this.addCondition(Condition.prototype.LIGHT, 5000, 1)) {
9 | return 0;
10 | }
11 |
12 | return 50;
13 |
14 | }
--------------------------------------------------------------------------------
/data/740/spells/definitions/morph.js:
--------------------------------------------------------------------------------
1 | const Condition = requireModule("condition");
2 |
3 | module.exports = function morph(properties) {
4 |
5 | this.addCondition(Condition.prototype.MORPH, 1, 100, {"id": 74});
6 |
7 | return 100;
8 |
9 | }
--------------------------------------------------------------------------------
/data/740/unique/definitions.json:
--------------------------------------------------------------------------------
1 | [
2 | {"uid": 1000, "on": "use", "callback": "enter-trunk.js"},
3 | {"uid": 1001, "on": "use", "callback": "touch-magic-flame.js"},
4 | {"uid": 1002, "on": "use", "callback": "crypt-chest.js"},
5 | {"uid": 1003, "on": "use", "callback": "crypt-chest.js"},
6 | {"uid": 1004, "on": "enter", "callback": "crypt-tile.js"},
7 | {"uid": 1005, "on": "use", "callback": "clock-secret.js"}
8 | ]
--------------------------------------------------------------------------------
/data/740/unique/definitions/clock-secret.js:
--------------------------------------------------------------------------------
1 | let done = false;
2 |
3 | module.exports = function useTrunk(player, tile, index, item) {
4 |
5 | // Only allowed when not moving
6 | if(player.isMoving()) {
7 | return true;
8 | }
9 |
10 | let middle = process.gameServer.world.getTileFromWorldPosition({"x": 65, "y": 110, "z": 8});
11 | let right = process.gameServer.world.getTileFromWorldPosition({"x": 66, "y": 110, "z": 8});
12 |
13 | if(!done) {
14 | if(right.isOccupiedAny()) {
15 | player.sendCancelMessage("Somehow the light does not dim.");
16 | return false;
17 | }
18 | let thing = middle.removeIndex(0xFF);
19 | let newThing = thing.replace(process.gameServer.database.createThing(1718));
20 | right.addTopThing(newThing);
21 | } else {
22 | let thing = right.removeIndex(0xFF);
23 | let newThing = thing.replace(process.gameServer.database.createThing(1719));
24 | middle.addTopThing(newThing);
25 | }
26 |
27 | done = !done;
28 | process.gameServer.world.sendMagicEffect(middle.position, CONST.EFFECT.MAGIC.POFF);
29 |
30 | return true;
31 |
32 | }
--------------------------------------------------------------------------------
/data/740/unique/definitions/crypt-chest.js:
--------------------------------------------------------------------------------
1 | module.exports = function useTrunk(player, tile, index, item) {
2 |
3 | player.sendCancelMessage("You find treasure!");
4 |
5 | return true;
6 |
7 | }
--------------------------------------------------------------------------------
/data/740/unique/definitions/crypt-tile.js:
--------------------------------------------------------------------------------
1 | const Condition = requireModule("condition");
2 |
3 | module.exports = function enterCryptTile(tile, player) {
4 |
5 | if(player.hasCondition(Condition.prototype.INVISIBLE)) {
6 | process.gameServer.world.sendMagicEffect(tile.position, CONST.EFFECT.MAGIC.MAGIC_GREEN);
7 | return true;
8 | }
9 |
10 | player.sendCancelMessage("A magical barrier is holding you back.");
11 | process.gameServer.world.teleportCreature(player, tile.position.north());
12 | process.gameServer.world.sendMagicEffect(tile.position, CONST.EFFECT.MAGIC.ENERGYHIT);
13 |
14 | return false;
15 |
16 | }
--------------------------------------------------------------------------------
/data/740/unique/definitions/enter-trunk.js:
--------------------------------------------------------------------------------
1 | module.exports = function useTrunk(player, tile, index, item) {
2 |
3 | // Only allowed when not moving
4 | if(player.isMoving()) {
5 | return true;
6 | }
7 |
8 | // Teleport the player and
9 | process.gameServer.world.teleportCreature(player, tile.position.down());
10 | player.__moveLock.lock(player.getSlowness());
11 |
12 | process.gameServer.world.sendMagicEffect(tile.position.down(), CONST.EFFECT.MAGIC.YELLOW_RINGS);
13 |
14 | return true;
15 |
16 | }
--------------------------------------------------------------------------------
/data/740/unique/definitions/touch-magic-flame.js:
--------------------------------------------------------------------------------
1 | const Condition = requireModule("condition");
2 |
3 | module.exports = function useTrunk(player, tile, index, item) {
4 |
5 | // Only allowed when not moving
6 | if(player.isMoving()) {
7 | return true;
8 | }
9 |
10 | player.addCondition(Condition.prototype.MAGIC_FLAME, 1, 250, null);
11 |
12 | return true;
13 |
14 | }
--------------------------------------------------------------------------------
/data/740/world/definitions.json:
--------------------------------------------------------------------------------
1 | {
2 | "0000": "borne.js",
3 | "0001": "borne-windmill-top.js"
4 | }
5 |
--------------------------------------------------------------------------------
/data/740/world/definitions/borne-windmill-top.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "file": "borne-windmill-top.otbm",
3 | "name": "Borne",
4 | "music": "wind",
5 | "title": "Top of the Windmill",
6 | "weather": 255,
7 | "ambient": {
8 | "r": 255,
9 | "g": 255,
10 | "b": 255,
11 | "a": 0
12 | }
13 | }
--------------------------------------------------------------------------------
/data/740/world/definitions/borne.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "file": "borne.otbm",
3 | "name": "Borne",
4 | "music": "field",
5 | "title": "The Quaint Village",
6 | "weather": 0,
7 | "ambient": {
8 | "r": 255,
9 | "g": 255,
10 | "b": 255,
11 | "a": 0
12 | }
13 | }
--------------------------------------------------------------------------------
/data/740/world/definitions/otbm/borne-house.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/data/740/world/definitions/otbm/borne-spawn.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/data/740/world/definitions/otbm/borne-windmill-top.otbm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Inconcessus/html5-tibia-engine/d59136ecb516efad0b956145abd80c10c4ec7c0f/data/740/world/definitions/otbm/borne-windmill-top.otbm
--------------------------------------------------------------------------------
/data/740/world/definitions/otbm/borne.otbm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Inconcessus/html5-tibia-engine/d59136ecb516efad0b956145abd80c10c4ec7c0f/data/740/world/definitions/otbm/borne.otbm
--------------------------------------------------------------------------------
/engine.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const path = require("path");
4 |
5 | // Load the configuration
6 | global.CONFIG = require("./config");
7 |
8 | // Create some useful global functions
9 | global.getDataFile = function() {
10 |
11 | /*
12 | * Function global.getDataFile
13 | * Returns a file from the base data directory
14 | */
15 |
16 | return path.join(__dirname, "data", CONFIG.SERVER.CLIENT_VERSION, ...arguments);
17 |
18 | }
19 |
20 | global.requireModule = function() {
21 |
22 | /*
23 | * Function global.requireModule
24 | * Requires a module from the base source directory
25 | */
26 |
27 | return require(path.join(__dirname, "src", ...arguments));
28 |
29 | }
30 |
31 | // Load constants
32 | global.CONST = require(getDataFile("constants.json"));
33 |
34 | // Requires the prototype modifications
35 | requireModule("__proto__");
36 |
37 | if(require.main === module) {
38 |
39 | /*
40 | * Function __main__
41 | * Function called when the initialization script is executed
42 | */
43 |
44 | // Check the NodeJS version
45 | let [ major, minor, patch ] = process.versions.node.split(".");
46 |
47 | // Confirm major version
48 | if(major < 16) {
49 | console.log("Could not launch gameserver: required version: %s and current version: %s.".format("16.0.0", process.versions.node));
50 | return process.exit(1);
51 | }
52 |
53 | console.log("Starting NodeJS Forby Open Tibia Server.");
54 | console.log("Creating server with version [[ %s ]].".format(CONFIG.SERVER.CLIENT_VERSION));
55 | console.log("Setting data directory to [[ %s ]].".format(getDataFile("")));
56 |
57 | const GameServer = requireModule("gameserver");
58 |
59 | // Attach the gameserver to the process and initialize
60 | process.gameServer = new GameServer();
61 | process.gameServer.initialize();
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/ipcclient.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const path = require("path");
4 |
5 | // Load the configuration
6 | global.CONFIG = require("./config");
7 |
8 | // Create some useful global functions
9 | global.getDataFile = function() {
10 |
11 | /*
12 | * Function global.getDataFile
13 | * Returns a file from the base data directory
14 | */
15 |
16 | return path.join(__dirname, "data", CONFIG.SERVER.CLIENT_VERSION, ...arguments);
17 |
18 | }
19 |
20 | global.requireModule = function() {
21 |
22 | /*
23 | * Function global.requireModule
24 | * Requires a module from the base source directory
25 | */
26 |
27 | return require(path.join(__dirname, "src", ...arguments));
28 |
29 | }
30 |
31 | // Load constants
32 | global.CONST = require(getDataFile("constants.json"));
33 |
34 | // Requires the prototype modifications
35 | requireModule("__proto__");
36 |
37 | if(require.main === module) {
38 |
39 | /*
40 | * Function require.main
41 | * Returns a file from the base data directory
42 | */
43 |
44 | const IPCHTTPAPI = requireModule("ipchttpapi");
45 |
46 | new IPCHTTPAPI().listen(CONFIG.IPC.PORT, CONFIG.IPC.HOST);
47 |
48 | }
--------------------------------------------------------------------------------
/lib/headers.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "OTBM_MAP_HEADER": 0x00,
3 | "OTBM_MAP_DATA": 0x02,
4 | "OTBM_TILE_AREA": 0x04,
5 | "OTBM_TILE": 0x05,
6 | "OTBM_ITEM": 0x06,
7 | "OTBM_TOWNS": 0x0C,
8 | "OTBM_TOWN": 0x0D,
9 | "OTBM_HOUSETILE": 0x0E,
10 | "OTBM_WAYPOINTS": 0x0F,
11 | "OTBM_WAYPOINT": 0x10,
12 |
13 | "OTBM_ATTR_DESCRIPTION": 0x01,
14 | "OTBM_ATTR_EXT_FILE": 0x02,
15 | "OTBM_ATTR_TILE_FLAGS": 0x03,
16 | "OTBM_ATTR_ACTION_ID": 0x04,
17 | "OTBM_ATTR_UNIQUE_ID": 0x05,
18 | "OTBM_ATTR_TEXT": 0x06,
19 | "OTBM_ATTR_DESC": 0x07,
20 | "OTBM_ATTR_TELE_DEST": 0x08,
21 | "OTBM_ATTR_ITEM": 0x09,
22 | "OTBM_ATTR_DEPOT_ID": 0x0A,
23 | "OTBM_ATTR_EXT_SPAWN_FILE": 0x0B,
24 | "OTBM_ATTR_RUNE_CHARGES": 0x0C,
25 | "OTBM_ATTR_EXT_HOUSE_FILE": 0x0D,
26 | "OTBM_ATTR_HOUSEDOORID": 0x0E,
27 | "OTBM_ATTR_COUNT": 0x0F,
28 |
29 | "TILESTATE_NONE": 0x0000,
30 | "TILESTATE_PROTECTIONZONE": 0x0001,
31 | "TILESTATE_DEPRECATED": 0x0002,
32 | "TILESTATE_NOPVP": 0x0004,
33 | "TILESTATE_NOLOGOUT": 0x0008,
34 | "TILESTATE_PVPZONE": 0x0010,
35 | "TILESTATE_REFRESH": 0x0020
36 | }
37 |
--------------------------------------------------------------------------------
/login.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const path = require("path");
4 |
5 | // Load the configuration
6 | global.CONFIG = require("./config");
7 |
8 | // Create some useful global functions
9 | global.getDataFile = function() {
10 |
11 | /*
12 | * Function global.getDataFile
13 | * Returns a file from the base data directory
14 | */
15 |
16 | return path.join(__dirname, "data", CONFIG.SERVER.CLIENT_VERSION, ...arguments);
17 |
18 | }
19 |
20 | global.requireModule = function() {
21 |
22 | /*
23 | * Function global.requireModule
24 | * Requires a module from the base source directory
25 | */
26 |
27 | return require(path.join(__dirname, "src", ...arguments));
28 |
29 | }
30 |
31 | // Load constants
32 | global.CONST = require(getDataFile("constants.json"));
33 |
34 | // Requires the prototype modifications
35 | requireModule("__proto__");
36 |
37 | if(require.main === module) {
38 |
39 | /*
40 | * Function __main__
41 | * Function called when the initialization script is executed
42 | */
43 |
44 | // Create the login server
45 | const LoginServer = requireModule("login-server");
46 |
47 | // Start
48 | new LoginServer(function() {
49 | console.log("The login server is listening on %s:%s.".format(CONFIG.LOGIN.HOST, CONFIG.LOGIN.PORT));
50 | });
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "bcryptjs": "^2.4.3",
4 | "jsonschema": "^1.4.1",
5 | "ws": "^8.7.0",
6 | "xml2js": "^0.4.23"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/achievement-property.js:
--------------------------------------------------------------------------------
1 | const AchievementProperty = function(value) {
2 |
3 | /*
4 | * Class AchievementProperty
5 | * Container for a single property that can be incremented
6 | *
7 | * API:
8 | *
9 | * AchievementProperty.toJSON - Serializes the class to JSON
10 | * AchievementProperty.addTrigger - Adds an achievement identifier trigger
11 | * AchievementProperty.addValue - Adds a particular value to the property
12 | *
13 | */
14 |
15 | // Has a value and a set of triggers
16 | this.value = value;
17 | this.triggers = new Set();
18 |
19 | }
20 |
21 | AchievementProperty.prototype.toJSON = function() {
22 |
23 | /*
24 | * Function AchievementProperty.toJSON
25 | * Serializes an achievement property
26 | */
27 |
28 | return this.value;
29 |
30 | }
31 |
32 | AchievementProperty.prototype.addTrigger = function(id) {
33 |
34 | /*
35 | * Function AchievementProperty.addTrigger
36 | * Adds an achievement identifier to the list of triggers for this property
37 | */
38 |
39 | this.triggers.add(id);
40 |
41 | }
42 |
43 | AchievementProperty.prototype.setBitFlag = function(flag) {
44 |
45 | /*
46 | * Function AchievementProperty.setBitFlag
47 | * Sets a bit flag in the integer value
48 | */
49 |
50 | this.value = this.value |= flag;
51 |
52 | }
53 |
54 | AchievementProperty.prototype.addValue = function(value) {
55 |
56 | /*
57 | * Function AchievementProperty.addTrigger
58 | * Adds an achievement identifier to the list of triggers for this property
59 | */
60 |
61 | this.value = this.value + value;
62 |
63 | }
64 |
65 | module.exports = AchievementProperty;
--------------------------------------------------------------------------------
/src/channel-default.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const Channel = require("./channel");
4 |
5 | const DefaultChannel = function(id, name) {
6 |
7 | /*
8 | * Class DefaultChannel
9 | * Wrapper for the default channel that broadcasts to all characters inside a particular range
10 | */
11 |
12 | // Inherits from channel
13 | Channel.call(this, id, name);
14 |
15 | }
16 |
17 | DefaultChannel.prototype = Object.create(Channel.prototype);
18 | DefaultChannel.prototype.constructor = DefaultChannel;
19 |
20 | DefaultChannel.prototype.send = function(player, packet) {
21 |
22 | /*
23 | * Function DefaultChannel.send
24 | * Sends a message to all players near this player in the gameworld
25 | */
26 |
27 | // Message and loudness
28 | let message = packet.message;
29 | let loudness = packet.loudness;
30 |
31 | // Administrators have a red color; players yellow
32 | let color = player.characterStatistics.admin ? CONST.COLOR.RED : CONST.COLOR.YELLOW;
33 |
34 | // Whispers
35 | if(packet.loudness === 0) {
36 | return player.internalCreatureWhisper(message, color);
37 | }
38 |
39 | if(packet.loudness === 2) {
40 | return player.internalCreatureYell(message, color);
41 | }
42 |
43 | // Write to the default game screen and the default chat channel
44 | player.internalCreatureSay(message, color);
45 |
46 | // NPCs listen to all messages in the default channels
47 | this.__NPCListen(player, message.toLowerCase());
48 |
49 | }
50 |
51 | DefaultChannel.prototype.__NPCListen = function(player, message) {
52 |
53 | /*
54 | * Function PacketHandler.__NPCListen
55 | * Handler called when a player says a message and NPCs are nearby
56 | */
57 |
58 | // Go over all the NPCs in the nearby game world
59 | process.gameServer.world.forEachNearbyNPC(player.position, function(npc) {
60 |
61 | // Do not accept anything when in a scene
62 | if(npc.isScene()) {
63 | return;
64 | }
65 |
66 | // If in range
67 | if(npc.isWithinRangeOf(player, npc.hearingRange)) {
68 | return npc.handleResponse(player, message);
69 | }
70 |
71 | });
72 |
73 | }
74 |
75 | module.exports = DefaultChannel;
--------------------------------------------------------------------------------
/src/channel.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const Channel = function(id, name) {
4 |
5 | /*
6 | * Class Channel
7 | * Base for classes that implement channels (e.g., default or global channels)
8 | *
9 | */
10 |
11 | // Each channel has a readalbe name and identifier
12 | this.id = id;
13 | this.name = name;
14 |
15 | }
16 |
17 | Channel.prototype.equals = function(id) {
18 |
19 | /*
20 | * Function Channel.equals
21 | * Returns if this is the channel with identifier id
22 | */
23 |
24 | return this.id === id;
25 |
26 | }
27 |
28 | module.exports = Channel;
--------------------------------------------------------------------------------
/src/corpse.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const Container = require("./container.js");
4 | const FluidContainer = require("./fluidcontainer");
5 |
6 | const Corpse = function(id, size) {
7 |
8 | /*
9 | * Class Corpse
10 | * Wrapper for monster corpses that contain loot
11 | */
12 |
13 | // Inherits from container
14 | Container.call(this, id, size);
15 |
16 | }
17 |
18 | Corpse.prototype = Object.create(Container.prototype);
19 | Corpse.prototype.constructor = Corpse;
20 |
21 | Corpse.prototype.getFluidType = function() {
22 |
23 | /*
24 | * Function Corpse.getFluidType
25 | * Returns the fluid type of the corpse
26 | */
27 |
28 | // Add mappings here
29 | switch(this.getAttribute("corpseType")) {
30 | case "blood": return FluidContainer.prototype.FLUID_TYPES.BLOOD;
31 | case "venom": return FluidContainer.prototype.FLUID_TYPES.SLIME;
32 | default: return FluidContainer.prototype.FLUID_TYPES.BLOOD;
33 | }
34 |
35 | }
36 |
37 | Corpse.prototype.addLoot = function(loot) {
38 |
39 | /*
40 | * Function Corpse.addLoot
41 | * Adds loot to the corpse container
42 | */
43 |
44 | // Invalid: too much loot for the container size..
45 | if(loot.length > this.getSize()) {
46 | return;
47 | }
48 |
49 | // Add each entry in the loot table
50 | loot.forEach(function(thing) {
51 |
52 | // Check the probability
53 | if(Math.random() > thing.probability) {
54 | return;
55 | }
56 |
57 | let item = process.gameServer.database.createThing(thing.id);
58 |
59 | // Set the random between minimum and maximum
60 | if(thing.min && thing.max) {
61 | item.setCount(Number.prototype.random(thing.min, thing.max));
62 | }
63 |
64 | // Push the loot to the container
65 | this.addFirstEmpty(item);
66 |
67 | }, this);
68 |
69 | }
70 |
71 | module.exports = Corpse;
--------------------------------------------------------------------------------
/src/enum.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const Enum = function() {
4 |
5 | /*
6 | * Class Enum
7 | * Generates an enum by returning an object with symbols
8 | */
9 |
10 | // Go over each elements
11 | let enumerator = new Object();
12 |
13 | Array.from(arguments).forEach(function(x) {
14 | enumerator[x] = Symbol(x);
15 | });
16 |
17 | return Object.freeze(enumerator);
18 |
19 | }
20 |
21 | module.exports = Enum;
--------------------------------------------------------------------------------
/src/event.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const Event = function(callback, when) {
4 |
5 | /*
6 | * Class Event
7 | * Container for events that fire a callback at a given frame
8 | */
9 |
10 | this.callback = callback;
11 | this.__f = when;
12 |
13 | // If the event was cancelled and needs not to be executed
14 | this.cancelled = false;
15 |
16 | }
17 |
18 | Event.prototype.frame = function() {
19 |
20 | /*
21 | * Function Event.frame
22 | * Returns the frame that the event should be executed on
23 | */
24 |
25 | return this.__f;
26 |
27 | }
28 |
29 | Event.prototype.remove = function() {
30 |
31 | /*
32 | * Function Event.remove
33 | * Function to remove an event from the event queue
34 | */
35 |
36 | process.gameServer.world.eventQueue.remove(this);
37 |
38 | }
39 |
40 | Event.prototype.cancel = function() {
41 |
42 | /*
43 | * Function Event.cancel
44 | * Cancels a scheduled event so that it is no longer executed
45 | */
46 |
47 | this.cancelled = true;
48 |
49 | }
50 |
51 | Event.prototype.remainingFrames = function() {
52 |
53 | /*
54 | * Function Event.remainingFrames
55 | * Returns the number of frames remaining before the event is scheduled
56 | */
57 |
58 | return this.__f - process.gameServer.gameLoop.getCurrentFrame();
59 |
60 | }
61 |
62 | module.exports = Event;
--------------------------------------------------------------------------------
/src/friendlist.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const PacketWriter = require("./packet-writer");
4 |
5 | const Friendlist = function(player, friends) {
6 |
7 | /*
8 | * Class Friendlist
9 | * Wrapper for a characters friendlist
10 | */
11 |
12 | this.player = player;
13 | this.friends = friends;
14 |
15 | }
16 |
17 | Friendlist.prototype.remove = function(name) {
18 |
19 | /*
20 | * Function Friendlist.remove
21 | * Removes a character from the friendlist
22 | */
23 |
24 | let index = this.friends.indexOf(name);
25 |
26 | if(index === -1) {
27 | return;
28 | }
29 |
30 | this.friends.splice(index, 1);
31 | this.player.write(new PacketWriter(PacketWriter.prototype.opcodes.REMOVE_FRIEND).writeString(name));
32 |
33 | }
34 |
35 | Friendlist.prototype.add = function(name) {
36 |
37 | /*
38 | * Function Friendlist.add
39 | * Adds a character to the existing friendlist
40 | */
41 |
42 | let index = this.friends.indexOf(name);
43 |
44 | if(index !== -1) {
45 | return;
46 | }
47 |
48 | this.friends.push(name);
49 | this.player.write(new PacketWriter(PacketWriter.prototype.opcodes.FRIEND_STATUS).writeFriendStatus(name));
50 |
51 | }
52 |
53 | Friendlist.prototype.toJSON = function() {
54 |
55 | /*
56 | * Function Friendlist.toJSON
57 | * Serializes the friendlist to be saved to JSON
58 | */
59 |
60 | return this.friends;
61 |
62 | }
63 |
64 | Friendlist.prototype.writeFriendList = function() {
65 |
66 | /*
67 | * Function Friendlist.writeFriendList
68 | * Writes all the friends of a player to the player with the online/offline status
69 | */
70 |
71 | // Write the currently online characters to the player
72 | this.friends.forEach(function(friend) {
73 | this.player.write(new PacketWriter(PacketWriter.prototype.opcodes.FRIEND_STATUS).writeFriendStatus(friend));
74 | }, this);
75 |
76 | }
77 |
78 | module.exports = Friendlist;
--------------------------------------------------------------------------------
/src/key.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const Item = require("./item");
4 |
5 | const Key = function(id) {
6 |
7 | /*
8 | * Class Key
9 | * Wrapper for items that are keys and can open doors
10 | */
11 |
12 | // Inherit from container
13 | Item.call(this, id);
14 |
15 | }
16 |
17 | Key.prototype = Object.create(Item.prototype);
18 | Key.prototype.constructor = Key;
19 |
20 | Key.prototype.handleKeyUse = function(player, tile) {
21 |
22 | /*
23 | * Function Key.handleKeyUse
24 | * Wrapper for keys that can be used to open doors
25 | */
26 |
27 | // Get the top element
28 | let door = tile.getTopItem();
29 |
30 | // Nothing there
31 | if(door === null) {
32 | return;
33 | }
34 |
35 | // This it not a door..
36 | if(!door.isDoor()) {
37 | return;
38 | }
39 |
40 | if(door.getAttribute("expertise") || door.getAttribute("unwanted")) {
41 | return player.sendCancelMessage("Keys do not work on magic doors.");
42 | }
43 |
44 | // Already opened: close it
45 | if(door.isOpened()) {
46 | return door.toggle(player);
47 | }
48 |
49 | // Confirm the action identifiers matches with the key
50 | if(this.actionId !== door.actionId) {
51 | return player.sendCancelMessage("The key does not fit inside the keyhole.");
52 | }
53 |
54 | // Write message to the player
55 | player.sendCancelMessage("The door unlocks.");
56 |
57 | // Request the door to be opened by the key
58 | door.open();
59 |
60 | }
61 |
62 | module.exports = Key;
--------------------------------------------------------------------------------
/src/logger.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const fs = require("fs");
4 |
5 | const ServerLogger = function() {
6 |
7 | /*
8 | * Class ServerLogger
9 | * Logs internal server parameters to logfile
10 | */
11 |
12 | // State to keep average game loop exec time
13 | this.__gameLoopExecutionTime = 0;
14 |
15 | // Create writeable stream to disk
16 | this.logfile = fs.createWriteStream("server.log");
17 |
18 | // Write the header
19 | const header = new Array(
20 | "Timestamp",
21 | "Memory (MB)",
22 | "Clients",
23 | "Event Heap Size",
24 | "Bytes Recv",
25 | "Bytes Sent",
26 | "Loop Exec Time (ms)",
27 | "Drift",
28 | "Frame",
29 | "World Time"
30 | );
31 |
32 | this.writeLine(header);
33 |
34 | }
35 |
36 | ServerLogger.prototype.LOG_FRAMES = 60;
37 |
38 | ServerLogger.prototype.writeLine = function(line) {
39 |
40 | /*
41 | * Function ServerLogger.writeLine
42 | * Writes an array of parameters to a line delimited by tabs
43 | */
44 |
45 | this.logfile.write(line.join("\t"));
46 | this.logfile.write("\n");
47 |
48 | }
49 |
50 | ServerLogger.prototype.log = function() {
51 |
52 | /*
53 | * Function ServerLogger.log
54 | * Writes server diagnostics to logfile
55 | */
56 |
57 | let networkDetails = process.gameServer.server.websocketServer.networkManager.getDataDetails();
58 |
59 | // Generate a logging message
60 | let message = new Array(
61 | new Date().toISOString(),
62 | (process.memoryUsage().rss / (1024 * 1024)).toFixed(0),
63 | process.gameServer.server.websocketServer.connectedSockets().size,
64 | process.gameServer.world.eventQueue.heap.size(),
65 | networkDetails.received,
66 | networkDetails.sent,
67 | (this.__gameLoopExecutionTime / this.LOG_FRAMES).toFixed(0),
68 | process.gameServer.gameLoop.__drift,
69 | process.gameServer.gameLoop.getCurrentFrame(),
70 | process.gameServer.world.clock.getTimeString()
71 | );
72 |
73 | this.writeLine(message);
74 |
75 | // Reset the execution time
76 | this.__gameLoopExecutionTime = 0;
77 |
78 | }
79 |
80 | module.exports = ServerLogger;
81 |
--------------------------------------------------------------------------------
/src/packet-buffer.js:
--------------------------------------------------------------------------------
1 | const PacketBuffer = function() {
2 |
3 | /*
4 | * Class PacketBuffer
5 | * Buffer that collects packets and only flushes the buffer / concatenates the data to write a single message per frame
6 | *
7 | * API:
8 | *
9 | * @PacketBuffer.add(buffer) - adds a buffer to the main buffer
10 | * @PacketBuffer.flush() - flushes the outgoing buffer by emptying it and returning the concatenation of all buffers
11 | * @PacketBuffer.isEmpty() - Returns true if the buffer is empty
12 | *
13 | */
14 |
15 | // Collect the buffers
16 | this.__buffers = new Array();
17 |
18 | // State variable that is updated when a new packet is received with the new time
19 | this.__lastPacketReceived = Date.now();
20 |
21 | }
22 |
23 | PacketBuffer.prototype.add = function(buffer) {
24 |
25 | /*
26 | * Function PacketBuffer.add
27 | * Buffer that collects packets and only flushes the buffer / concatenates the data to write a single message per frame
28 | */
29 |
30 | this.__buffers.push(buffer);
31 | this.__lastPacketReceived = Date.now();
32 |
33 | }
34 |
35 | PacketBuffer.prototype.flush = function() {
36 |
37 | /*
38 | * Function PacketBuffer.flush
39 | * Flushes and resets the buffer
40 | */
41 |
42 | let buffer = Buffer.concat(this.__buffers);
43 | this.__buffers = new Array();
44 |
45 | return buffer;
46 |
47 | }
48 |
49 | PacketBuffer.prototype.isEmpty = function() {
50 |
51 | /*
52 | * Function PacketBuffer.isEmpty
53 | * Returns true if there are no messages in the buffer
54 | */
55 |
56 | return this.__buffers.length === 0;
57 |
58 | }
59 |
60 | module.exports = PacketBuffer;
61 |
--------------------------------------------------------------------------------
/src/packet.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const Packet = function() {
4 |
5 | /*
6 | * Class Packet
7 | * Parent class of a readable and writeable binary packets
8 | */
9 |
10 | this.index = 0;
11 |
12 | }
13 |
14 | module.exports = Packet;
15 |
--------------------------------------------------------------------------------
/src/pathfind-node.js:
--------------------------------------------------------------------------------
1 | const PathfindNode = function() {
2 |
3 | this.__parent = null;
4 | this.__closed = false;
5 | this.__visited = false;
6 | this.__f = 0;
7 | this.__g = 0;
8 | this.__h = 0;
9 |
10 | }
11 |
12 | PathfindNode.prototype.clean = function() {
13 |
14 | this.__parent = null;
15 | this.__closed = false;
16 | this.__visited = false;
17 | this.__f = 0;
18 | this.__g = 0;
19 | this.__h = 0;
20 |
21 | }
22 |
23 | module.exports = PathfindNode;
--------------------------------------------------------------------------------
/src/readable.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const Item = require("./item");
4 |
5 | const Readable = function(id) {
6 |
7 | /*
8 | * Class Readable
9 | * Container for weapons that inherit from an item
10 | */
11 |
12 | // Inherit
13 | Item.call(this, id, 1);
14 |
15 | // Save the properties
16 | this.content = null;
17 |
18 | }
19 |
20 | // Inherit from event emitter
21 | Readable.prototype = Object.create(Item.prototype);
22 | Readable.prototype.constructor = Readable;
23 |
24 | Readable.prototype.setContent = function(content) {
25 |
26 | this.content = content;
27 |
28 | }
29 |
30 | Readable.prototype.getContent = function() {
31 |
32 | /*
33 | * Function Readable.getContent
34 | * Returns the content of a readable
35 | */
36 |
37 | if(this.content === null) {
38 | return "Nothing is written on it";
39 | }
40 |
41 | return this.content;
42 |
43 | }
44 |
45 | module.exports = Readable;
--------------------------------------------------------------------------------
/src/rune.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const Item = require("./item");
4 |
5 | const Rune = function(id) {
6 |
7 | /*
8 | * Class Rune
9 | * Container for a corpse that inherits from a container
10 | */
11 |
12 | // Inherits from Item
13 | Item.call(this, id);
14 |
15 | // Set the initial charges from the otb metadata
16 | this.charges = this.getMaximumCharges();
17 |
18 | }
19 |
20 | Rune.prototype = Object.create(Item.prototype);
21 | Rune.prototype.constructor = Rune;
22 |
23 | Rune.prototype.getMaximumCharges = function() {
24 |
25 | /*
26 | * Function Rune.getMaximumCharges
27 | * Returns the number of initial charges from the prototype definitions
28 | */
29 |
30 | // Read from the prototype
31 | let proto = this.getPrototype();
32 |
33 | // Check if the number of charges exists
34 | if(proto.properties.hasOwnProperty("charges")) {
35 | return Number(proto.properties.charges);
36 | }
37 |
38 | return 1;
39 |
40 | }
41 |
42 | module.exports = Rune;
--------------------------------------------------------------------------------
/src/teleporter.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const Item = require("./item");
4 |
5 | const Teleporter = function(id) {
6 |
7 | /*
8 | * Class Teleporter
9 | * Wrapper for an item that teleports the player to another location
10 | */
11 |
12 | Item.call(this, id);
13 |
14 | this.destination = null;
15 |
16 | }
17 |
18 | Teleporter.prototype = Object.create(Item.prototype);
19 | Teleporter.prototype.constructor = Teleporter;
20 |
21 | Teleporter.prototype.setDestination = function(destination) {
22 |
23 | /*
24 | * Function Teleporter.setDestination
25 | * Wrapper for an item that teleports players and items to another location
26 | */
27 |
28 | this.destination = destination;
29 |
30 | }
31 |
32 | module.exports = Teleporter;
--------------------------------------------------------------------------------
/src/thing-emitter.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const EventEmitter = require("./eventemitter");
4 |
5 | const ThingEmitter = function() {
6 |
7 | /*
8 | * Class ThingEmitter
9 | * Subscribes to events and wait for emit
10 | */
11 |
12 | // Inherits from an event emitter
13 | EventEmitter.call(this);
14 |
15 | }
16 |
17 | ThingEmitter.prototype = Object.create(EventEmitter.prototype);
18 | ThingEmitter.prototype.constructor = ThingEmitter;
19 |
20 | ThingEmitter.prototype.emit = function(which, ...args) {
21 |
22 | /*
23 | * Function ThingEmitter.emit
24 | * Emits a call to the event emitter and executes callbacks
25 | * All user scripts are executed through here: wrap them in try|catch statements
26 | */
27 |
28 | // Delegate to internal eventemitter handler: if any callback returns false we halt the event propagation to the prototype
29 | // For example, all fishing rods have a prototype event listener: therefore, fishing works for all fishing rods. In the case
30 | // of a special fishing rod, we can attach an event handler to only that fishing rod and return false in its callback. This way
31 | // it will not execute the prototype.
32 | try {
33 | if(!this.__emit(which, ...args)) {
34 | return;
35 | }
36 | } catch(error) {
37 | console.debug(error);
38 | }
39 |
40 | // Wrap used defined scripts for safety and delegate to the prototype event listener too
41 | try {
42 | this.getPrototype().emit(which, ...args);
43 | } catch(error) {
44 | console.debug(error);
45 | }
46 |
47 | }
48 |
49 | module.exports = ThingEmitter;
--------------------------------------------------------------------------------
/src/validator.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const { Validator } = require("jsonschema");
4 | const fs = require("fs");
5 |
6 | const DataValidator = function() {
7 |
8 | /*
9 | * Class DataValidator
10 | * Validates NPC and monster data on server start
11 | */
12 |
13 | // JSON schema lib
14 | this.validator = new Validator();
15 |
16 | // We use JSON schemas to define proper definitions for monsters and NPCs
17 | this.npcSchema = JSON.parse(fs.readFileSync(getDataFile("npcs", "schema.json")));
18 | this.monsterSchema = JSON.parse(fs.readFileSync(getDataFile("monsters", "schema.json")));
19 |
20 | }
21 |
22 | DataValidator.prototype.validateMonster = function(name, monster) {
23 |
24 | /*
25 | * Function Validator.validateMonster
26 | * Validates Monster data on load
27 | */
28 |
29 | if(!CONFIG.SERVER.VALIDATE) {
30 | return;
31 | }
32 |
33 | let validated = this.validator.validate(monster, this.monsterSchema);
34 |
35 | if(!validated.valid) {
36 | throw new Error("Schema validation failed for: %s: %s".format(name, validated.errors.join("\n")));
37 | }
38 |
39 | }
40 |
41 | DataValidator.prototype.validateNPC = function(filename, npc) {
42 |
43 | /*
44 | * Function Validator.validateNPC
45 | * Validates NPC data on load
46 | */
47 |
48 | if(!CONFIG.SERVER.VALIDATE) {
49 | return;
50 | }
51 |
52 | let validated = this.validator.validate(npc, this.npcSchema);
53 |
54 | if(!validated.valid) {
55 | throw new Error("Schema validation failed for: %s: %s".format(filename, validated.errors.join("\n")));
56 | }
57 |
58 | }
59 |
60 | module.exports = DataValidator;
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const path = require("path");
4 | const fs = require("fs");
5 |
6 | // Load the configuration
7 | global.CONFIG = require("./config");
8 |
9 | // Create some useful global functions
10 | global.getDataFile = function() {
11 |
12 | /*
13 | * Function global.getDataFile
14 | * Returns a file from the base data directory
15 | */
16 |
17 | return path.join(__dirname, "data", CONFIG.SERVER.CLIENT_VERSION, ...arguments);
18 |
19 | }
20 |
21 | global.requireModule = function() {
22 |
23 | /*
24 | * Function global.requireModule
25 | * Requires a module from the base source directory
26 | */
27 |
28 | return require(path.join(__dirname, "src", ...arguments));
29 |
30 | }
31 |
32 | // Load constants
33 | global.CONST = require(getDataFile("constants.json"));
34 |
35 | // Requires the prototype modifications
36 | requireModule("__proto__");
37 |
38 | if(require.main === module) {
39 |
40 | /*
41 | * Function __main__
42 | * Function called when the initialization script is executed
43 | */
44 | const GameServer = requireModule("gameserver");
45 |
46 | // Attach the gameserver to the process and initialize
47 | process.gameServer = new GameServer();
48 | process.gameServer.initialize();
49 |
50 | fs.readdirSync("tests").forEach(function(file) {
51 |
52 | for(let fn of require(path.join(__dirname, "tests", file))) {
53 | console.log("Running test: %s".format(fn.name));
54 | fn.call();
55 | }
56 |
57 | process.gameServer.__scheduleShutdown();
58 |
59 | });
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/tests/test-tile.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 | const Chunk = requireModule("chunk");
3 | const Tile = requireModule("tile");
4 | const Position = requireModule("position");
5 |
6 | function testTileAddStackable() {
7 |
8 | let chunk = new Chunk(0, new Position(0, 0, 0));
9 | let tile = new Tile(chunk, 102, new Position(0, 0, 0));
10 |
11 | for(let i = 0; i < 100; i++) {
12 | tile.addTopThing(process.gameServer.database.createThing(2148).setCount(3))
13 | }
14 |
15 | assert(tile.itemStack.__items.length === 3);
16 |
17 | }
18 |
19 | function testTileAddOverflow() {
20 |
21 | let chunk = new Chunk(0, new Position(0, 0, 0));
22 | let tile = new Tile(chunk, 102, new Position(0, 0, 0));
23 |
24 | for(let i = 0; i < 17; i++) {
25 | tile.addTopThing(process.gameServer.database.createThing(2153));
26 | }
27 |
28 | assert(tile.itemStack.__items.length === 16);
29 |
30 | }
31 |
32 | module.exports = [
33 | testTileAddStackable,
34 | testTileAddOverflow
35 | ]
--------------------------------------------------------------------------------
/tools/items-otb/1098-items.otb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Inconcessus/html5-tibia-engine/d59136ecb516efad0b956145abd80c10c4ec7c0f/tools/items-otb/1098-items.otb
--------------------------------------------------------------------------------
/tools/items-otb/740-items.otb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Inconcessus/html5-tibia-engine/d59136ecb516efad0b956145abd80c10c4ec7c0f/tools/items-otb/740-items.otb
--------------------------------------------------------------------------------
/tools/rewrite-mounts.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | with open("mounts.json") as infile:
4 | data = json.load(infile)
5 |
6 | obj = {}
7 | for d in data:
8 | obj[d["id"]] = d
9 | d["premium"] = True if d["premium"] == "yes" else False
10 | d["speed"] = int(d["speed"])
11 | del d["id"]
12 |
13 | with open('mounts-new.json', 'w') as outfile:
14 | json.dump(obj, outfile)
15 |
--------------------------------------------------------------------------------
/tools/rewrite-outfits.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | with open("outfits.json") as infile:
4 | data = json.load(infile)
5 |
6 | obj = {}
7 | for d in data:
8 | obj[d["looktype"]] = d
9 | d["enabled"] = True if d["enabled"] == "yes" else False
10 | d["unlocked"] = True if d["unlocked"] == "yes" else False
11 | d["premium"] = True if d["premium"] == "yes" else False
12 | del d["looktype"]
13 |
14 | with open('outfits-new.json', 'w') as outfile:
15 | json.dump(obj, outfile)
16 |
--------------------------------------------------------------------------------