├── .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 | --------------------------------------------------------------------------------