From df37ec697fa9f3cf08184d1503b10584a6058c6e Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Fri, 26 Mar 2021 15:26:39 +0100 Subject: [PATCH 01/30] initial romeo and juliet implementation --- .../quests/romeo-and-juliet-quest.plugin.ts | 207 ++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 src/plugins/quests/romeo-and-juliet-quest.plugin.ts diff --git a/src/plugins/quests/romeo-and-juliet-quest.plugin.ts b/src/plugins/quests/romeo-and-juliet-quest.plugin.ts new file mode 100644 index 000000000..7f799a9f6 --- /dev/null +++ b/src/plugins/quests/romeo-and-juliet-quest.plugin.ts @@ -0,0 +1,207 @@ +import { randomBetween } from '@engine/util/num'; +import { dialogue, Emote, execute, goto } from '@engine/world/actor/dialogue'; +import { Quest } from '@engine/world/actor/player/quest'; + +const journalHandler = { + 0: `I can start this quest by speaking to Romeo in + Varrock central square by the fountain.`, + 1: `I have agreed to find Juliet for Romeo and tell her how he feels.\nFor some reason he can't just do this by himself.\n + I should go and speak to Juliet. I can find her + west of Varrock.` +}; + +const startQuestAction = async details => { + const { player, npc } = details; + const participants = [player, { npc, key: 'romeo' }]; + + // Romeo starts with a random line + const randomDialog = randomBetween(0, 5); + switch (randomDialog) { + case 0: + await dialogue(participants, [ + romeo => [Emote.WORRIED, `Blub! Blub...where is my Juliet? Have you seen her?`] + ]); + break; + + case 1: + await dialogue(participants, [ + romeo => [Emote.WORRIED, `Looking for a blonde girl, goes by the name of Juliet..quite pretty...haven't seen her have you?`] + ]); + break; + + case 2: + await dialogue(participants, [ + romeo => [Emote.WORRIED, `Juliet, Juliet, wherefore art thou Juliet? Have you seen my Juliet?`] + ]); + break; + + case 3: + await dialogue(participants, [ + romeo => [Emote.WORRIED, `Oh woe is me that I cannot find my Juliet! You haven't seen Juliet have you?`] + ]); + break; + + case 4: + await dialogue(participants, [ + romeo => [Emote.WORRIED, `Sadness surrounds me now that Juliet's father forbids us to meet. Have you seen my Juliet?`] + ]); + break; + + case 5: + await dialogue(participants, [romeo => [Emote.WORRIED, `What is to become of me and my darling Juliet, I cannot find her anywhere, have you seen her?`]]); + break; + } + + await dialogue(participants, [ + options => [ + `Yes, I have seen her actually!`, [ + player => [Emote.GENERIC, `Yes, I have seen her actually!`], + player => [Emote.WONDERING, `At least, I think it was her... Blonde? A bit stressed?`], + romeo => [Emote.SHOCKED, `Golly...yes, yes...you make her sound very interesting!`], + romeo => [Emote.WONDERING, `And I'll bet she's a bit of a fox!`], + player => [Emote.WONDERING, `Well, I guess she could be considered attractive...`], + romeo => [Emote.HAPPY, `I'll bet she is! Wooooooooo!`], + romeo => [Emote.GENERIC, `Sorry, all that jubilation has made me forget what we were talking about.`], + player => [Emote.GENERIC, `You were asking me about Juliet? You seemed to know her?`], + romeo => [Emote.HAPPY, `Oh yes, Juliet!`], + romeo => [Emote.HAPPY, `The fox...could you tell her that she is the love of my long and that I life to be with her?`], + player => [Emote.WONDERING, `What? Surely you mean that she is the love of your life and that you long to be with her?`], + romeo => [Emote.HAPPY, `Oh yeah...what you said...tell her that, it sounds much better! Oh you're so good at this!`] + ], + `No sorry, I haven't seen her.`, [ + player => [Emote.GENERIC, `No sorry, I haven't seen her.`], + romeo => [Emote.SAD, `Oh...well, that's a shame...I was rather hoping you had.`], + player => [Emote.WONDERING, `Why? Is she a fugitive? Does she owe you some money or something?`], + romeo => [Emote.WONDERING, `Hmmm, she might do? Perhaps she does? How do you know?`], + player => [Emote.ANGRY, `I don't know? I was asking 'YOU' how 'YOU' know Juliet!`], + romeo => [Emote.HAPPY, `Ahh, yes Juliet, she's my one true love. Well, one of my one true loves! If you see her, could you tell her that she is the love of my long and that I life to be with her?`], + player => [Emote.WONDERING, `What? Surely you mean that she is the love of your life and that you long to be with her?`], + romeo => [Emote.HAPPY, `Oh yeah...what you said...tell her that, it sounds much better! Oh you're so good at this!`] + ], + `Perhaps I could help to find her for you? `, [ + player => [Emote.WONDERING, `Perhaps I can help find her for you? What does she look like?`], + romeo => [Emote.HAPPY, `Oh would you? That would be great! She has this sort of hair...`], + player => [Emote.WONDERING, `Hair...check..`], romeo => [Emote.HAPPY, `...and she these...great lips...`], + player => [Emote.WONDERING, `Lips...right.`], romeo => [Emote.HAPPY, `Oh and she has these lovely shoulders as well..`], + player => [Emote.GENERIC, `Shoulders...right, so she has hair, lips and shoulders...that should cut it down a bit.`], + romeo => [Emote.HAPPY, `Oh yes, Juliet is very different...please tell her that she is the love of my long and that I life to be with her?`], + player => [Emote.WONDERING, `What? Surely you mean that she is the love of your life and that you long to be with her?`], + romeo => [Emote.HAPPY, `Oh yeah...what you said...tell her that, it sounds much better! Oh you're so good at this!`] + ] + ] + ]); + + await dialogue(participants, [ + options => [ + `Yes, ok, I'll let her know.`, [ + execute(() => { + player.setQuestProgress('rs:romeo_and_juliet', 1); + }), + player => [Emote.GENERIC, `Yes, ok, I'll let her know.`], + romeo => [Emote.HAPPY, `Oh great! And tell her that I want to kiss her a give.`], + player => [Emote.ANGRY, `You mean you want to give her a kiss!`], + romeo => [Emote.HAPPY, `Oh you're good...you are good!`], + romeo => [Emote.HAPPY, `I see I've picked a true professional...!`], + moreInfo() + ], + `Sorry Romeo, I've got better things to do right now but maybe later?`, [ + player => [Emote.GENERIC, `Sorry Romeo, I've got better things to do right now but maybe later?`], + romeo => [Emote.SAD, `Oh, ok, well, I guess my Juliet and I can spend some time apart. And as the old saying goes, 'Absinthe makes the heart glow longer'.`], + player => [Emote.WONDERING, `Don't you mean that, 'Absence makes the...`], + player => [Emote.GENERIC, `Actually forget it...`], + romeo => [Emote.WONDERING, `Ok!`] + ] + ] + ]); +}; + +const romeoSecondTalk = async details => { + const { player, npc } = details; + const participants = [player, { npc, key: 'romeo' }]; + await dialogue(participants, [ + player => [Emote.GENERIC, `Hello again, remember me?`], + romeo => [Emote.WONDERING, `Of course, yes....how are.. you....ermmm...`], + player => [Emote.ANGRY, `You haven't got a clue who I am do you?`], + romeo => [Emote.GENERIC, `Not a clue my friend, but you seem to have a friendly face...a little blood stained, and perhaps in need of a wash, but friendly none the less.`], + player => [Emote.ANGRY, `You asked me to look for Juliet for you!`], + romeo => [Emote.HAPPY, `Ah yes, Juliet...my sweet darling...what news?`], + player => [Emote.WONDERING, `Nothing so far, but I need to ask a few questions? `], + moreInfo() + ]); +}; + +const moreInfo = () => { + return (options, tag_MORE_INFO) => [ + `Where can I find Juliet?`, [ + player => [Emote.WONDERING, `Where can I find Juliet?`], + romeo => [Emote.WONDERING, `Why do you ask?`], + player => [Emote.ANGRY, `So that I can try and find her for you!`], + romeo => [Emote.WONDERING, `Ah yes....quite right. Hmmm, let me think now.`], + romeo => [Emote.WONDERING, `She may still be locked away at her Father's house on the sest vide of Warrock.`], + romeo => [Emote.HAPPY, `Oh, I remember how she loved it when I would sing up to her balcony! She would reward me with her own personal items...`], + player => [Emote.WONDERING, `What, she just gave you her stuff?`], + romeo => [Emote.GENERIC, `Well, not exactly give...more like 'throw with considerable force'...she's always a kidder that Juliet!`], + goto('tag_MORE_INFO') + ], + `Is there anything else you can tell me about Juliet?`, [ + player => [Emote.WONDERING, `Is there anything else you can tell me about Juliet?`], + romeo => [Emote.HAPPY, `Oh, there is so much to tell...she is my true love, we intend to spend together forever...I can tell you so much about her..`], + player => [Emote.GENERIC, `Great!`], romeo => [Emote.WONDERING, `Ermmm.....`], + romeo => [Emote.WONDERING, `So much can I tell you...`], + player => [Emote.HAPPY, `Yes..`], + romeo => [Emote.WONDERING, `So much to tell...why, where do I start!`], + player => [Emote.GENERIC, `Yes..yes! Please go on...don't let me interrupt...`], + romeo => [Emote.WONDERING, `Ermmm.....`], + romeo => [Emote.WONDERING, `...`], + player => [Emote.WONDERING, `You can't remember can you?`], + romeo => [Emote.SAD, `Not a thing sorry....`], + goto('tag_MORE_INFO') + ], + `Ok, thanks.`, [ + player => [Emote.GENERIC, `Ok, thanks.`] + ] + ]; +}; + +export default { + pluginId: 'rs:romeo_and_juliet', + quests: [ + new Quest({ + id: 'rs:romeo_and_juliet', + questTabId: 37, + name: `Romeo & Juliet`, + points: 5, + journalHandler, + onComplete: { + questCompleteWidget: { + rewardText: ['5 Quest Points'], + itemId: 1891, + modelZoom: 240, + modelRotationX: 180, + modelRotationY: 180 + } + } + }) + ], + hooks: [{ + type: 'npc_interaction', + questRequirement: { + questId: 'rs:romeo_and_juliet', + stage: 0 + }, + npcs: 'rs:romeo', + options: 'talk-to', + walkTo: true, + handler: startQuestAction + }, { + type: 'npc_interaction', + questRequirement: { + questId: 'rs:romeo_and_juliet', + stage: 1 + }, + npcs: 'rs:romeo', + options: 'talk-to', + walkTo: true, + handler: romeoSecondTalk + }] +}; From 3284166ad2343f2a73957a3aa25fd37ad1325b32 Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Wed, 7 Apr 2021 22:38:29 +0200 Subject: [PATCH 02/30] add staircases plugin, add missing spawns --- .../npc-spawns/varrock/varrock-general.json | 26 +++++++ data/config/npcs/varrock.json | 9 +++ .../objects/ladders/staircase.plugin.ts | 71 +++++++++++++++++++ .../quests/romeo-and-juliet-quest.plugin.ts | 29 +++++++- 4 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 src/plugins/objects/ladders/staircase.plugin.ts diff --git a/data/config/npc-spawns/varrock/varrock-general.json b/data/config/npc-spawns/varrock/varrock-general.json index 3b53657a9..30e693208 100644 --- a/data/config/npc-spawns/varrock/varrock-general.json +++ b/data/config/npc-spawns/varrock/varrock-general.json @@ -35,6 +35,32 @@ "spawn_y": 3424, "movement_radius": 4 }, + { + "npc": "rs:draul_leptoc", + "spawn_x": 3162, + "spawn_y": 3435, + "movement_radius": 4 + }, + { + "npc": "rs:man", + "spawn_x": 3153, + "spawn_y": 3429, + "movement_radius": 4 + }, + { + "npc": "rs:phillipa", + "spawn_x": 3163, + "spawn_y": 3430, + "spawn_level": 1, + "movement_radius": 5 + }, + { + "npc": "rs:juliet", + "spawn_x": 3158, + "spawn_y": 3425, + "spawn_level": 1, + "movement_radius": 1 + }, { "npc": "rs:varrock_shop_keeper", "spawn_x": 3214, diff --git a/data/config/npcs/varrock.json b/data/config/npcs/varrock.json index d38839768..e4ac80daf 100644 --- a/data/config/npcs/varrock.json +++ b/data/config/npcs/varrock.json @@ -29,6 +29,15 @@ "rs:romeo": { "game_id": 639 }, + "rs:draul_leptoc": { + "game_id": 3324 + }, + "rs:phillipa": { + "game_id": 3325 + }, + "rs:juliet": { + "game_id": 637 + }, "rs:varrock_shop_keeper": { "game_id": 520 }, diff --git a/src/plugins/objects/ladders/staircase.plugin.ts b/src/plugins/objects/ladders/staircase.plugin.ts new file mode 100644 index 000000000..6d35bac9b --- /dev/null +++ b/src/plugins/objects/ladders/staircase.plugin.ts @@ -0,0 +1,71 @@ +import { objectInteractionActionHandler } from '@engine/world/action/object-interaction.action'; +import { dialogueAction } from '@engine/world/actor/player/dialogue-action'; +import { World } from '@engine/world'; +import { Position } from '@engine/world/position'; + +const planes = { min: 0, max: 3 }; +const validate: (level: number) => boolean = (level) => { + return planes.min <= level && level <= planes.max; +}; //TODO: prevent no-clipping. + +export const action: objectInteractionActionHandler = (details) => { + const { player, option, object } = details; + + const up = option === 'climb-up'; + const { position } = player; + const level = position.level + (up ? 1 : -1); + + const direction = object.orientation; + let newX = position.x; + let newY = position.y; + const tilesToMove = 4; + + switch (direction) { + case 0: + if (up) { + newY += tilesToMove; + } else { + newY -= tilesToMove; + } + break; + case 1: + if (up) { + newX += tilesToMove; + } else { + newX -= tilesToMove; + } + break; + case 2: + if (up) { + newY -= tilesToMove; + } else { + newY += tilesToMove; + } + break; + case 3: + if (up) { + newX -= tilesToMove; + } else { + newX += tilesToMove; + } + break; + } + + if (!validate(level)) return; + setTimeout(() => { + details.player.teleport(new Position(newX, newY, level)); + }, World.TICK_LENGTH); +}; + +export default { + pluginId: 'rs:staircases', + hooks: [ + { + type: 'object_interaction', + objectIds: [ 1722, 1723 ], + options: [ 'climb-up', 'climb-down' ], + walkTo: true, + handler: action + } + ] +}; diff --git a/src/plugins/quests/romeo-and-juliet-quest.plugin.ts b/src/plugins/quests/romeo-and-juliet-quest.plugin.ts index 7f799a9f6..9d7d163c3 100644 --- a/src/plugins/quests/romeo-and-juliet-quest.plugin.ts +++ b/src/plugins/quests/romeo-and-juliet-quest.plugin.ts @@ -48,7 +48,9 @@ const startQuestAction = async details => { break; case 5: - await dialogue(participants, [romeo => [Emote.WORRIED, `What is to become of me and my darling Juliet, I cannot find her anywhere, have you seen her?`]]); + await dialogue(participants, [ + romeo => [Emote.WORRIED, `What is to become of me and my darling Juliet, I cannot find her anywhere, have you seen her?`] + ]); break; } @@ -81,8 +83,10 @@ const startQuestAction = async details => { `Perhaps I could help to find her for you? `, [ player => [Emote.WONDERING, `Perhaps I can help find her for you? What does she look like?`], romeo => [Emote.HAPPY, `Oh would you? That would be great! She has this sort of hair...`], - player => [Emote.WONDERING, `Hair...check..`], romeo => [Emote.HAPPY, `...and she these...great lips...`], - player => [Emote.WONDERING, `Lips...right.`], romeo => [Emote.HAPPY, `Oh and she has these lovely shoulders as well..`], + player => [Emote.WONDERING, `Hair...check..`], + romeo => [Emote.HAPPY, `...and she these...great lips...`], + player => [Emote.WONDERING, `Lips...right.`], + romeo => [Emote.HAPPY, `Oh and she has these lovely shoulders as well..`], player => [Emote.GENERIC, `Shoulders...right, so she has hair, lips and shoulders...that should cut it down a bit.`], romeo => [Emote.HAPPY, `Oh yes, Juliet is very different...please tell her that she is the love of my long and that I life to be with her?`], player => [Emote.WONDERING, `What? Surely you mean that she is the love of your life and that you long to be with her?`], @@ -163,6 +167,15 @@ const moreInfo = () => { ]; }; +const julietFirstTalk = async details => { + const { player, npc } = details; + const participants = [player, { npc, key: 'juliet' }]; + await dialogue(participants, [ + player => [Emote.GENERIC, `test`], + juliet => [Emote.WONDERING, `test`] + ]); +}; + export default { pluginId: 'rs:romeo_and_juliet', quests: [ @@ -203,5 +216,15 @@ export default { options: 'talk-to', walkTo: true, handler: romeoSecondTalk + }, { + type: 'npc_interaction', + questRequirement: { + questId: 'rs:romeo_and_juliet', + stage: 1 + }, + npcs: 'rs:juliet', + options: 'talk-to', + walkTo: true, + handler: julietFirstTalk }] }; From ab1a40a5ff141e83a82c13c949fc0f71b114c3bb Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Thu, 8 Apr 2021 03:21:36 +0200 Subject: [PATCH 03/30] add stage 2 --- .../config/items/quests/romeo-and-juliet.json | 8 +++ src/game-engine/net/outbound-packets.ts | 6 +- src/game-engine/world/actor/dialogue.ts | 70 +++++++++++++++++-- .../dialogue/dialogue-option.plugin.ts | 16 +++-- .../quests/cooks-assistant-quest.plugin.ts | 4 +- .../goblin-diplomacy-quest.plugin.ts | 4 +- .../quests/romeo-and-juliet-quest.plugin.ts | 38 +++++++--- 7 files changed, 119 insertions(+), 27 deletions(-) create mode 100644 data/config/items/quests/romeo-and-juliet.json diff --git a/data/config/items/quests/romeo-and-juliet.json b/data/config/items/quests/romeo-and-juliet.json new file mode 100644 index 000000000..dbcf60f4d --- /dev/null +++ b/data/config/items/quests/romeo-and-juliet.json @@ -0,0 +1,8 @@ +{ + "rs:juliet_letter": { + "game_id": 755, + "tradable": false, + "examine": "A message from Juliet to Romeo.", + "weight": 0.02 + } +} diff --git a/src/game-engine/net/outbound-packets.ts b/src/game-engine/net/outbound-packets.ts index 117d5b9ff..9444e8505 100644 --- a/src/game-engine/net/outbound-packets.ts +++ b/src/game-engine/net/outbound-packets.ts @@ -265,11 +265,11 @@ export class OutboundPackets { this.queue(packet); } - public setWidgetModelRotationAndZoom(widgetId: number, childId: number, rotationX: number, rotationY: number, zoom: number): void { + public setWidgetModelRotationAndZoom(widgetId: number, childId: number, rotationY: number, rotationX: number, zoom: number): void { const packet = new Packet(142); - packet.put(rotationX, 'SHORT'); - packet.put(zoom, 'SHORT', 'LITTLE_ENDIAN'); packet.put(rotationY, 'SHORT'); + packet.put(zoom, 'SHORT', 'LITTLE_ENDIAN'); + packet.put(rotationX, 'SHORT'); packet.put(widgetId << 16 | childId, 'INT', 'LITTLE_ENDIAN'); this.queue(packet); diff --git a/src/game-engine/world/actor/dialogue.ts b/src/game-engine/world/actor/dialogue.ts index 982ba390d..7d4085959 100644 --- a/src/game-engine/world/actor/dialogue.ts +++ b/src/game-engine/world/actor/dialogue.ts @@ -4,7 +4,7 @@ import { filestore } from '@engine/game-server'; import { logger } from '@runejs/core'; import _ from 'lodash'; import { wrapText } from '@engine/util/strings'; -import { findNpc } from '@engine/config'; +import { findItem, findNpc } from '@engine/config'; export enum Emote { @@ -104,12 +104,13 @@ enum EmoteAnimation { } const nonLineEmotes = [ Emote.BLANK_STARE, Emote.SINGLE_WORD, Emote.EVIL_STARE, Emote.LAUGH_EVIL ]; -const playerWidgetIds = [ 64, 65, 66, 67 ]; -const npcWidgetIds = [ 241, 242, 243, 244 ]; -const optionWidgetIds = [ 228, 230, 232, 234, 235 ]; -const continuableTextWidgetIds = [ 210, 211, 212, 213, 214 ]; -const textWidgetIds = [ 215, 216, 217, 218, 219 ]; -const titledTextWidgetId = 372; +export const playerWidgetIds = [ 64, 65, 66, 67 ]; +export const npcWidgetIds = [ 241, 242, 243, 244 ]; +export const optionWidgetIds = [ 228, 230, 232, 234, 235 ]; +export const continuableTextWidgetIds = [ 210, 211, 212, 213, 214 ]; +export const textWidgetIds = [ 215, 216, 217, 218, 219 ]; +export const itemWidgetIds = [ 101, 102, 103, 104 ]; +export const titledTextWidgetId = 372; function wrapDialogueText(text: string, type: 'ACTOR' | 'TEXT'): string[] { return wrapText(text, type === 'ACTOR' ? 340 : 430); @@ -188,6 +189,11 @@ interface TextDialogueAction extends DialogueAction { canContinue: boolean; } +interface ItemDialogueAction extends DialogueAction { + lines: string[]; + itemId: number | string; +} + interface TitledTextDialogueAction extends DialogueAction { title: string; lines: string[]; @@ -285,6 +291,12 @@ function parseDialogueTree(player: Player, npcParticipants: NpcParticipant[], di const text: string = dialogueAction(); const lines = wrapDialogueText(text, 'TEXT'); parsedDialogueTree.push({ lines, tag, type: 'TEXT', canContinue: true } as TextDialogueAction); + } else if(dialogueType === 'item') { + // Dialogue with an item on the left + + const [ itemId, text ] = dialogueAction(); + const lines = wrapDialogueText(text, 'TEXT'); + parsedDialogueTree.push({ lines, tag, itemId, type: 'ITEM' } as ItemDialogueAction); } else if(dialogueType === 'overlay') { // Text-only dialogue (no option to continue). @@ -427,6 +439,50 @@ async function runDialogueAction(player: Player, dialogueAction: string | Dialog player.outgoingPackets.updateWidgetString(widgetId, i, lines[i]); } } + } else if(dialogueAction.type === 'ITEM') { + // Dialogue with an item on the left. + + if(tag === undefined || dialogueAction.tag === tag) { + tag = undefined; + + const itemDialogueAction = dialogueAction as ItemDialogueAction; + const lines = itemDialogueAction.lines; + + if(lines.length > 5) { + throw new Error(`Too many lines for item dialogue! Dialogue has ${lines.length} lines but ` + + `the maximum is 4: ${JSON.stringify(lines)}`); + } + + widgetId = itemWidgetIds[lines.length - 1]; + let itemId: number; + + if (typeof itemDialogueAction.itemId === 'number') { + itemId = itemDialogueAction.itemId; + } else if (typeof itemDialogueAction.itemId === 'string') { + const itemDetails = findItem(itemDialogueAction.itemId); + if (!itemDetails) { + throw new Error(`The item ${itemDialogueAction.itemId} is not configured in the server!`); + } + itemId = itemDetails.gameId; + } else { + throw new Error(`Invalid item ID provided: ${itemDialogueAction.itemId}`); + } + + const model = filestore.configStore.itemStore.getItem(itemId)?.model2d; + + if (!model) { + throw new Error(`The model for item ${itemDialogueAction.itemId} was not found in the filestore!`); + } + + player.outgoingPackets.updateWidgetModel1(widgetId, 0, model.widgetModel); + player.outgoingPackets.setWidgetModelRotationAndZoom(widgetId, 0, + model.rotationY || 0, + model.rotationX || 0, + model.zoom / 2 || 0); + for(let i = 0; i < lines.length; i++) { + player.outgoingPackets.updateWidgetString(widgetId, i + 1, lines[i]); + } + } } else if(dialogueAction.type === 'TITLED') { // Text-only dialogue. diff --git a/src/plugins/dialogue/dialogue-option.plugin.ts b/src/plugins/dialogue/dialogue-option.plugin.ts index 1db56b250..92005e448 100644 --- a/src/plugins/dialogue/dialogue-option.plugin.ts +++ b/src/plugins/dialogue/dialogue-option.plugin.ts @@ -1,10 +1,18 @@ import { widgetInteractionActionHandler } from '@engine/world/action/widget-interaction.action'; +import { + continuableTextWidgetIds, + itemWidgetIds, + npcWidgetIds, + optionWidgetIds, + playerWidgetIds +} from '@engine/world/actor/dialogue'; const dialogueIds = [ - 64, 65, 66, 67, 241, - 242, 243, 244, 228, 230, - 232, 234, - 210, 211, 212, 213, 214, + ...playerWidgetIds, + ...npcWidgetIds, + ...optionWidgetIds, + ...continuableTextWidgetIds, + ...itemWidgetIds ]; /** diff --git a/src/plugins/quests/cooks-assistant-quest.plugin.ts b/src/plugins/quests/cooks-assistant-quest.plugin.ts index ec01cef88..787603723 100644 --- a/src/plugins/quests/cooks-assistant-quest.plugin.ts +++ b/src/plugins/quests/cooks-assistant-quest.plugin.ts @@ -262,8 +262,8 @@ export default { rewardText: [ '300 Cooking XP' ], itemId: 1891, modelZoom: 240, - modelRotationX: 180, - modelRotationY: 180 + modelRotationY: 180, + modelRotationX: 180 }, giveRewards: (player: Player): void => player.skills.cooking.addExp(300) diff --git a/src/plugins/quests/goblin-diplomacy-tutorial/goblin-diplomacy-quest.plugin.ts b/src/plugins/quests/goblin-diplomacy-tutorial/goblin-diplomacy-quest.plugin.ts index b839815b4..89c6a53cd 100644 --- a/src/plugins/quests/goblin-diplomacy-tutorial/goblin-diplomacy-quest.plugin.ts +++ b/src/plugins/quests/goblin-diplomacy-tutorial/goblin-diplomacy-quest.plugin.ts @@ -225,8 +225,8 @@ export default { rewardText: [ 'A training sword & shield' ], itemId: 9703, modelZoom: 200, - modelRotationX: 0, - modelRotationY: 180 + modelRotationY: 0, + modelRotationX: 180 } } }) diff --git a/src/plugins/quests/romeo-and-juliet-quest.plugin.ts b/src/plugins/quests/romeo-and-juliet-quest.plugin.ts index 9d7d163c3..a6cc0d4f8 100644 --- a/src/plugins/quests/romeo-and-juliet-quest.plugin.ts +++ b/src/plugins/quests/romeo-and-juliet-quest.plugin.ts @@ -1,6 +1,8 @@ import { randomBetween } from '@engine/util/num'; import { dialogue, Emote, execute, goto } from '@engine/world/actor/dialogue'; import { Quest } from '@engine/world/actor/player/quest'; +import { ContentPlugin } from '@engine/plugins/content-plugin'; +import { npcInteractionActionHandler, NpcInteractionActionHook } from '@engine/world/action/npc-interaction.action'; const journalHandler = { 0: `I can start this quest by speaking to Romeo in @@ -10,7 +12,7 @@ const journalHandler = { west of Varrock.` }; -const startQuestAction = async details => { +const startQuestAction: npcInteractionActionHandler = async details => { const { player, npc } = details; const participants = [player, { npc, key: 'romeo' }]; @@ -119,7 +121,7 @@ const startQuestAction = async details => { ]); }; -const romeoSecondTalk = async details => { +const romeoSecondTalk: npcInteractionActionHandler = async details => { const { player, npc } = details; const participants = [player, { npc, key: 'romeo' }]; await dialogue(participants, [ @@ -167,16 +169,33 @@ const moreInfo = () => { ]; }; -const julietFirstTalk = async details => { +const julietFirstTalk: npcInteractionActionHandler = async details => { const { player, npc } = details; const participants = [player, { npc, key: 'juliet' }]; await dialogue(participants, [ - player => [Emote.GENERIC, `test`], - juliet => [Emote.WONDERING, `test`] + player => [Emote.GENERIC, `Juliet, I come from Romeo. He begs me to tell you that he cares still.`], + juliet => [Emote.HAPPY, `Oh how my heart soars to hear this news! Please take this message to him with great haste.`], + player => [Emote.GENERIC, `Well, I hope it's good news...he was quite upset when I left him.`], + juliet => [Emote.POMPOUS, `He's quite often upset...the poor sensitive soul. But I don't think he's going to take this news very well, however, all is not lost.`], + juliet => [Emote.HAPPY, `Everything is explained in the letter, would you be so kind and deliver it to him please?`], + player => [Emote.HAPPY, `Certainly, I'll do so straight away.`], + juliet => [Emote.HAPPY, `Many thanks! Oh, I'm so very grateful. You may be our only hope.`] ]); + + const giveLetterSuccess = player.giveItem('rs:juliet_letter'); + if (giveLetterSuccess) { + player.setQuestProgress('rs:romeo_and_juliet', 2); + await dialogue(participants, [ + item => ['rs:juliet_letter', `Juliet gives you a message.`] + ]); + } else { + await dialogue(participants, [ + text => `You don't have enough space in your inventory!` + ]); + } }; -export default { +export default { pluginId: 'rs:romeo_and_juliet', quests: [ new Quest({ @@ -190,13 +209,14 @@ export default { rewardText: ['5 Quest Points'], itemId: 1891, modelZoom: 240, - modelRotationX: 180, - modelRotationY: 180 + modelRotationY: 180, + modelRotationX: 180 } } }) ], - hooks: [{ + hooks: [{ + // TODO you can actually start the quest by talking to Juliet first type: 'npc_interaction', questRequirement: { questId: 'rs:romeo_and_juliet', From b1a1de77aebf411dc724a23fc8c7ec2f27086d2f Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Thu, 8 Apr 2021 16:22:14 +0200 Subject: [PATCH 04/30] fix quest requirement when providing stages variable --- .../world/action/hooks/hook-filters.ts | 18 +++++------ .../quests/romeo-and-juliet-quest.plugin.ts | 30 ++++++++++++++++--- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/game-engine/world/action/hooks/hook-filters.ts b/src/game-engine/world/action/hooks/hook-filters.ts index 0a8bee8f9..872842e9c 100644 --- a/src/game-engine/world/action/hooks/hook-filters.ts +++ b/src/game-engine/world/action/hooks/hook-filters.ts @@ -70,24 +70,20 @@ export function questHookFilter(player: Player, actionHook: ActionHook): boolean const questId = actionHook.questRequirement.questId; const playerQuest = player.quests.find(quest => quest.questId === questId); - if(!playerQuest) { + if (!playerQuest) { // @TODO quest requirements return actionHook.questRequirement.stage === 0; } - if(actionHook.questRequirement.stage === 'complete') { + if (actionHook.questRequirement.stage === 'complete') { return playerQuest.progress === 'complete'; } - if(typeof playerQuest.progress === 'number') { - if(actionHook.questRequirement.stage !== undefined) { - if(!numberHookFilter(actionHook.questRequirement.stage, playerQuest.progress)) { - return false; - } - } else if(actionHook.questRequirement.stages !== undefined) { - if(!numberHookFilter(actionHook.questRequirement.stages, playerQuest.progress)) { - return false; - } + if (typeof playerQuest.progress === 'number') { + if (actionHook.questRequirement.stage !== undefined) { + return numberHookFilter(actionHook.questRequirement.stage, playerQuest.progress); + } else if (actionHook.questRequirement.stages !== undefined) { + return numberHookFilter(actionHook.questRequirement.stages, playerQuest.progress); } } diff --git a/src/plugins/quests/romeo-and-juliet-quest.plugin.ts b/src/plugins/quests/romeo-and-juliet-quest.plugin.ts index a6cc0d4f8..72d9050b2 100644 --- a/src/plugins/quests/romeo-and-juliet-quest.plugin.ts +++ b/src/plugins/quests/romeo-and-juliet-quest.plugin.ts @@ -6,10 +6,12 @@ import { npcInteractionActionHandler, NpcInteractionActionHook } from '@engine/w const journalHandler = { 0: `I can start this quest by speaking to Romeo in - Varrock central square by the fountain.`, - 1: `I have agreed to find Juliet for Romeo and tell her how he feels.\nFor some reason he can't just do this by himself.\n - I should go and speak to Juliet. I can find her - west of Varrock.` + Varrock central square by the fountain.`, + 1: `I have agreed to find Juliet for Romeo and tell her how he feels.\nFor some reason he can't just do this by himself.\n + I should go and speak to Juliet. I can find her west of Varrock.`, + 2: `I have agreed to find Juliet for Romeo and tell her how he feels. For some reason he can't just do this himself.\n + I found Juliet on the Western edge of Varrock, and told her about Romeo. She gave me a message to take back.\n + I should take the message from Juliet to Romeo in Varrock central square.` }; const startQuestAction: npcInteractionActionHandler = async details => { @@ -195,6 +197,16 @@ const julietFirstTalk: npcInteractionActionHandler = async details => { } }; +const phillipaStartDialogue: npcInteractionActionHandler = async details => { + const { player, npc } = details; + const participants = [player, { npc, key: 'phillipa' }]; + await dialogue(participants, [ + player => [Emote.GENERIC, `Hello`], + phillipa => [Emote.HAPPY, `Hi, I'm Phillipa! Juliet's cousin? I like to keep an eye on her, make sure that dashing young Romeo doesn't just steal away from here under our plain old noses!`], + phillipa => [Emote.GENERIC, `He'd do it you know... he's ever so dashing, and cavalier, in a wet blanket sort of way.`] + ]); +}; + export default { pluginId: 'rs:romeo_and_juliet', quests: [ @@ -246,5 +258,15 @@ export default { options: 'talk-to', walkTo: true, handler: julietFirstTalk + }, { + type: 'npc_interaction', + questRequirement: { + questId: 'rs:romeo_and_juliet', + stages: [0, 1, 2] + }, + npcs: 'rs:phillipa', + options: 'talk-to', + walkTo: true, + handler: phillipaStartDialogue }] }; From e27b97466b1ae589bbbfcfdd909cdb71f22df6e9 Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Thu, 8 Apr 2021 23:23:33 +0200 Subject: [PATCH 05/30] fix wrapText function, fetch widths from filestore --- src/game-engine/util/strings.ts | 168 +++++++++++++----- src/game-engine/world/actor/dialogue.ts | 26 ++- src/plugins/quests/quest-journal.plugin.ts | 14 +- .../quests/romeo-and-juliet-quest.plugin.ts | 8 +- 4 files changed, 163 insertions(+), 53 deletions(-) diff --git a/src/game-engine/util/strings.ts b/src/game-engine/util/strings.ts index c00c17a10..5bb71792d 100644 --- a/src/game-engine/util/strings.ts +++ b/src/game-engine/util/strings.ts @@ -1,4 +1,6 @@ import { hexToHexString } from '@engine/util/colors'; +import { filestore } from '@engine/game-server'; +import { Font, FontName } from '@runejs/filestore'; export const startsWithVowel = (str: string): boolean => { str = str.trim().toLowerCase(); @@ -8,64 +10,140 @@ export const startsWithVowel = (str: string): boolean => { return (firstChar === 'a' || firstChar === 'e' || firstChar === 'i' || firstChar === 'o' || firstChar === 'u'); }; -// Thank you to the Apollo team for these values. :) -const charWidths = [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 7, 14, 9, 12, 12, 4, 5, - 5, 10, 8, 4, 8, 4, 7, 9, 7, 9, 8, 8, 8, 9, 7, 9, 9, 4, 5, 7, - 9, 7, 9, 14, 9, 8, 8, 8, 7, 7, 9, 8, 6, 8, 8, 7, 10, 9, 9, 8, - 9, 8, 8, 6, 9, 8, 10, 8, 8, 8, 6, 7, 6, 9, 10, 5, 8, 8, 7, 8, - 8, 7, 8, 8, 4, 7, 7, 4, 10, 8, 8, 8, 8, 6, 8, 6, 8, 8, 9, 8, - 8, 8, 6, 4, 6, 12, 3, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 8, 11, 8, 8, 4, 8, 7, 12, 6, 7, 9, 5, 12, 5, 6, 10, 6, 6, 6, - 8, 8, 4, 5, 5, 6, 7, 11, 11, 11, 9, 9, 9, 9, 9, 9, 9, 13, 8, 8, - 8, 8, 8, 4, 4, 5, 4, 8, 9, 9, 9, 9, 9, 9, 8, 10, 9, 9, 9, 9, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 13, 6, 8, 8, 8, 8, 4, 4, 5, 4, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8]; - -export function wrapText(text: string, maxWidth: number): string[] { - const lines = []; +export enum TextDecoration { + Color, + Decoration +} - let lineStartIdx = 0; - let width = 0; - let lastSpace = 0; - let widthAfterSpace = 0; - let lastSpaceChar = ''; +function getStylingType(tag: string) { + let _tag = tag; + if (_tag.charAt(0) === '/') { + _tag = _tag.substring(1); + } - for (let i = 0; i < text.length; i++) { - const char = text.charAt(i); + if (_tag.startsWith('col')) { + return TextDecoration.Color; + } else { + return TextDecoration.Decoration; + } +} - // Ignore and strings... - if (char === '<' && (text.charAt(i + 1) === '/' || text.charAt(i + 1) === 'c' && text.charAt(i + 2) === 'o' && text.charAt(i + 3) === 'l')) { - const tagCloseIndex = text.indexOf('>', i); - i = tagCloseIndex; - continue; +// TODO refactor a bit +export function wrapText(text: string, maxWidth: number, font?: number | string): string[] { + const lines = []; + const selectedFont = getFont(font); + const colorQueue: string[] = []; + const decorationQueue: string[] = []; + const remainingText = text.split('').reverse(); + let currentLine = ''; + let currentWidth = 0; + let currentTagIndex = -1; + + while (remainingText.length > 0) { + const char = remainingText.pop(); + + let hidden = false; + let rendered = true; + + switch (char) { + case '<': + hidden = true; + currentTagIndex = currentLine.length + 1; + break; + case '>': + hidden = true; + // eslint-disable-next-line no-case-declarations + const currentTag = currentLine.substring(currentTagIndex, currentLine.length); + currentTagIndex = -1; + // eslint-disable-next-line no-case-declarations + const isClosing = currentTag.charAt(0) === '/'; + // eslint-disable-next-line no-case-declarations + const type = getStylingType(currentTag); + if (type === TextDecoration.Decoration) { + if (!isClosing) { + decorationQueue.push(currentTag); + } else { + decorationQueue.pop(); + } + } else { + if (!isClosing) { + colorQueue.push(currentTag); + } else { + colorQueue.pop(); + } + } + break; + case '@': + break; + case '\n': + hidden = true; + currentWidth = maxWidth; + rendered = false; + break; + case ' ': + if(currentLine[currentLine.length-1] === ' ' || currentWidth === 0){ + hidden = true; + rendered = false; + } + break; + default: + break; } - - const charWidth = charWidths[text.charCodeAt(i)]; - width += charWidth; - widthAfterSpace += charWidth; - - if (char === ' ' || char === '\n' || char === '-') { - lastSpaceChar = char; - lastSpace = i; - widthAfterSpace = 0; + if(rendered) { + currentLine += char; + } + if (!hidden && currentTagIndex == -1) { + const charWidth = selectedFont.getCharWidth(char); + currentWidth += charWidth; } - if (width >= maxWidth || char === '\n') { - lines.push(text.substring(lineStartIdx, lastSpaceChar === '-' ? lastSpace + 1 : lastSpace)); - lineStartIdx = lastSpace + 1; - width = widthAfterSpace; + if (currentWidth >= maxWidth) { + let lastSpace = currentLine.lastIndexOf(' '); + const lastTag = currentLine.lastIndexOf('<'); + if(lastTag > lastSpace && char !== '\n') { + lastSpace = lastTag; + const type = getStylingType(currentLine.substring(lastTag+1)); + if (type === TextDecoration.Decoration) { + decorationQueue.pop(); + } else { + colorQueue.pop(); + } + } + let lineToPush = currentLine; + let remainder = ''; + if(lastSpace != -1 && char != '\n') { + lineToPush = lineToPush.substring(0, lastSpace); + remainder = currentLine.substring(lastSpace); + + } + + decorationQueue.slice(0).reverse().map(tag => lineToPush += ``); + colorQueue.slice(0).reverse().map(tag => lineToPush += ``); + lines.push(lineToPush.trim()); + currentLine = ''; + decorationQueue.slice(0).map(tag => currentLine += `<${tag}>`); + colorQueue.slice(0).map(tag => currentLine += `<${tag}>`); + remainingText.push(...remainder.split('').reverse()) + currentWidth = 0; } - } - if (lineStartIdx !== text.length - 1) { - lines.push(text.substring(lineStartIdx, text.length)); } + lines.push(currentLine); return lines; } +function getFont(font: number | string) { + if (font && typeof font === 'number') { + return filestore.fontStore.getFontById(font); + } else if (font && typeof font === 'string') { + return filestore.fontStore.getFontByName(FontName[font]); + } else { + // Default font, subject to change + return filestore.fontStore.getFontByName(FontName.p12_full); + } +} + const VALID_CHARS = ['_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', diff --git a/src/game-engine/world/actor/dialogue.ts b/src/game-engine/world/actor/dialogue.ts index 7d4085959..49dee5f07 100644 --- a/src/game-engine/world/actor/dialogue.ts +++ b/src/game-engine/world/actor/dialogue.ts @@ -5,6 +5,7 @@ import { logger } from '@runejs/core'; import _ from 'lodash'; import { wrapText } from '@engine/util/strings'; import { findItem, findNpc } from '@engine/config'; +import { FontName, ParentWidget, TextWidget } from '@runejs/filestore'; export enum Emote { @@ -112,8 +113,29 @@ export const textWidgetIds = [ 215, 216, 217, 218, 219 ]; export const itemWidgetIds = [ 101, 102, 103, 104 ]; export const titledTextWidgetId = 372; +/** + * Wraps dialogue text into multiple lines. + * @param text - The text to wrap. + * @param type - 'ACTOR' if the widget has a chat-head or an item sprite on the left, 'TEXT' if the dialogue is text only + */ function wrapDialogueText(text: string, type: 'ACTOR' | 'TEXT'): string[] { - return wrapText(text, type === 'ACTOR' ? 340 : 430); + let widget: TextWidget; + let width = 0; + + switch (type) { + case 'ACTOR': + widget = (filestore.widgetStore.decodeWidget(playerWidgetIds[0]) as ParentWidget).children[2] as TextWidget; + width = widget.width; + break; + case 'TEXT': + widget = filestore.widgetStore.decodeWidget(textWidgetIds[0]) as TextWidget; + width = widget.width; + break; + default: + throw new Error(`Unhandled widget type: ${type}`); + } + + return wrapText(text, width, widget.fontId); } function parseDialogueFunctionArgs(func: Function): string[] { @@ -295,7 +317,7 @@ function parseDialogueTree(player: Player, npcParticipants: NpcParticipant[], di // Dialogue with an item on the left const [ itemId, text ] = dialogueAction(); - const lines = wrapDialogueText(text, 'TEXT'); + const lines = wrapDialogueText(text, 'ACTOR'); parsedDialogueTree.push({ lines, tag, itemId, type: 'ITEM' } as ItemDialogueAction); } else if(dialogueType === 'overlay') { // Text-only dialogue (no option to continue). diff --git a/src/plugins/quests/quest-journal.plugin.ts b/src/plugins/quests/quest-journal.plugin.ts index 358be7443..5d52ebe6c 100644 --- a/src/plugins/quests/quest-journal.plugin.ts +++ b/src/plugins/quests/quest-journal.plugin.ts @@ -1,9 +1,10 @@ import { buttonActionHandler } from '@engine/world/action/button.action'; import { wrapText } from '@engine/util/strings'; -import { questMap } from '@engine/game-server'; +import { filestore, questMap } from '@engine/game-server'; import { widgets } from '@engine/config'; import { Quest } from '@engine/world/actor/player/quest'; import { QuestKey } from '@engine/config/quest-config'; +import { ParentWidget, TextWidget } from '@runejs/filestore'; export const handler: buttonActionHandler = async ({ player, buttonId }) => { @@ -40,7 +41,8 @@ export const handler: buttonActionHandler = async ({ player, buttonId }) => { } } - const color = 128; + // TODO check osrs for the right default color + const color = '#000000'; let text: string; if(typeof journalHandler === 'function') { @@ -49,9 +51,15 @@ export const handler: buttonActionHandler = async ({ player, buttonId }) => { text = journalHandler; } + // Fetch the quest diary widget + const widget = (filestore.widgetStore.decodeWidget(275) as ParentWidget).children[3] as TextWidget; + if (!widget) { + throw new Error('Error fetching the quest widget!'); + } + let lines; if(text) { - lines = wrapText(text as string, 395); + lines = wrapText(text as string, widget.width, widget.fontId); } else { lines = [ 'Invalid Quest Stage' ]; } diff --git a/src/plugins/quests/romeo-and-juliet-quest.plugin.ts b/src/plugins/quests/romeo-and-juliet-quest.plugin.ts index 72d9050b2..3c1362479 100644 --- a/src/plugins/quests/romeo-and-juliet-quest.plugin.ts +++ b/src/plugins/quests/romeo-and-juliet-quest.plugin.ts @@ -7,11 +7,13 @@ import { npcInteractionActionHandler, NpcInteractionActionHook } from '@engine/w const journalHandler = { 0: `I can start this quest by speaking to Romeo in Varrock central square by the fountain.`, + 1: `I have agreed to find Juliet for Romeo and tell her how he feels.\nFor some reason he can't just do this by himself.\n I should go and speak to Juliet. I can find her west of Varrock.`, - 2: `I have agreed to find Juliet for Romeo and tell her how he feels. For some reason he can't just do this himself.\n - I found Juliet on the Western edge of Varrock, and told her about Romeo. She gave me a message to take back.\n - I should take the message from Juliet to Romeo in Varrock central square.` + + 2: `I have agreed to find Juliet for Romeo and tell her how he feels. For some reason he can't just do this himself.\n +I found Juliet on the Western edge of Varrock, and told her about Romeo. She gave me a message to take back.\n +I should take the message from Juliet to Romeo in Varrock central square.` }; const startQuestAction: npcInteractionActionHandler = async details => { From 3376a08c238640484c716b1de55dacce5891d16d Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Thu, 8 Apr 2021 23:37:57 +0200 Subject: [PATCH 06/30] fix empty remaining lines, lint --- src/game-engine/util/strings.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/game-engine/util/strings.ts b/src/game-engine/util/strings.ts index 5bb71792d..291d8a36f 100644 --- a/src/game-engine/util/strings.ts +++ b/src/game-engine/util/strings.ts @@ -81,7 +81,7 @@ export function wrapText(text: string, maxWidth: number, font?: number | string) rendered = false; break; case ' ': - if(currentLine[currentLine.length-1] === ' ' || currentWidth === 0){ + if (currentLine[currentLine.length-1] === ' ' || currentWidth === 0){ hidden = true; rendered = false; } @@ -89,9 +89,11 @@ export function wrapText(text: string, maxWidth: number, font?: number | string) default: break; } - if(rendered) { + + if (rendered) { currentLine += char; } + if (!hidden && currentTagIndex == -1) { const charWidth = selectedFont.getCharWidth(char); currentWidth += charWidth; @@ -100,7 +102,7 @@ export function wrapText(text: string, maxWidth: number, font?: number | string) if (currentWidth >= maxWidth) { let lastSpace = currentLine.lastIndexOf(' '); const lastTag = currentLine.lastIndexOf('<'); - if(lastTag > lastSpace && char !== '\n') { + if (lastTag > lastSpace && char !== '\n') { lastSpace = lastTag; const type = getStylingType(currentLine.substring(lastTag+1)); if (type === TextDecoration.Decoration) { @@ -111,7 +113,7 @@ export function wrapText(text: string, maxWidth: number, font?: number | string) } let lineToPush = currentLine; let remainder = ''; - if(lastSpace != -1 && char != '\n') { + if (lastSpace != -1 && char != '\n') { lineToPush = lineToPush.substring(0, lastSpace); remainder = currentLine.substring(lastSpace); @@ -128,7 +130,10 @@ export function wrapText(text: string, maxWidth: number, font?: number | string) } } - lines.push(currentLine); + + if (currentLine !== '') { + lines.push(currentLine); + } return lines; } From d6156a1947cd230f4332b982cca775fb4642c529 Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Fri, 9 Apr 2021 03:28:42 +0200 Subject: [PATCH 07/30] fix standard colors --- src/game-engine/world/actor/player/player.ts | 8 +++++--- src/plugins/quests/quest-journal.plugin.ts | 3 +-- src/plugins/quests/romeo-and-juliet-quest.plugin.ts | 8 ++++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/game-engine/world/actor/player/player.ts b/src/game-engine/world/actor/player/player.ts index 173d1f94e..78359d961 100644 --- a/src/game-engine/world/actor/player/player.ts +++ b/src/game-engine/world/actor/player/player.ts @@ -459,15 +459,15 @@ export class Player extends Actor { } let playerQuest = this.quests.find(quest => quest.questId === questId); - if(!playerQuest) { + if (!playerQuest) { playerQuest = new PlayerQuest(questId); this.quests.push(playerQuest); } - if(playerQuest.progress === 0 && !playerQuest.complete) { + if (typeof progress === 'number' && progress > 0) { playerQuest.progress = progress; this.modifyWidget(widgets.questTab, { childId: questData.questTabId, textColor: colors.yellow }); - } else if(!playerQuest.complete && progress === 'complete') { + } else if (progress === 'complete') { playerQuest.complete = true; playerQuest.progress = 'complete'; this.outgoingPackets.updateClientConfig(widgetScripts.questPoints, questData.points + this.getQuestPoints()); @@ -510,6 +510,8 @@ export class Player extends Actor { if(questData.onComplete.giveRewards) { questData.onComplete.giveRewards(this); } + } else { + throw new Error('Unhandled progress value: ' + progress); } } diff --git a/src/plugins/quests/quest-journal.plugin.ts b/src/plugins/quests/quest-journal.plugin.ts index 5d52ebe6c..82e7da5f6 100644 --- a/src/plugins/quests/quest-journal.plugin.ts +++ b/src/plugins/quests/quest-journal.plugin.ts @@ -41,8 +41,7 @@ export const handler: buttonActionHandler = async ({ player, buttonId }) => { } } - // TODO check osrs for the right default color - const color = '#000000'; + const color = '000080'; let text: string; if(typeof journalHandler === 'function') { diff --git a/src/plugins/quests/romeo-and-juliet-quest.plugin.ts b/src/plugins/quests/romeo-and-juliet-quest.plugin.ts index 3c1362479..3376408fa 100644 --- a/src/plugins/quests/romeo-and-juliet-quest.plugin.ts +++ b/src/plugins/quests/romeo-and-juliet-quest.plugin.ts @@ -8,12 +8,12 @@ const journalHandler = { 0: `I can start this quest by speaking to Romeo in Varrock central square by the fountain.`, - 1: `I have agreed to find Juliet for Romeo and tell her how he feels.\nFor some reason he can't just do this by himself.\n + 1: `I have agreed to find Juliet for Romeo and tell her how he feels.\nFor some reason he can't just do this by himself.\n I should go and speak to Juliet. I can find her west of Varrock.`, - 2: `I have agreed to find Juliet for Romeo and tell her how he feels. For some reason he can't just do this himself.\n -I found Juliet on the Western edge of Varrock, and told her about Romeo. She gave me a message to take back.\n -I should take the message from Juliet to Romeo in Varrock central square.` + 2: `I have agreed to find Juliet for Romeo and tell her how he feels. For some reason he can't just do this himself. + I found Juliet on the Western edge of Varrock, and told her about Romeo. She gave me a message to take back. + I should take the message from Juliet to Romeo in Varrock central square.` }; const startQuestAction: npcInteractionActionHandler = async details => { From 0f6761dc97c93482d4bba6eb8a9de2f1242dcb46 Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Fri, 9 Apr 2021 04:05:33 +0200 Subject: [PATCH 08/30] add quest item object --- .../quests/romeo-and-juliet-quest.plugin.ts | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/plugins/quests/romeo-and-juliet-quest.plugin.ts b/src/plugins/quests/romeo-and-juliet-quest.plugin.ts index 3376408fa..ea8257bd4 100644 --- a/src/plugins/quests/romeo-and-juliet-quest.plugin.ts +++ b/src/plugins/quests/romeo-and-juliet-quest.plugin.ts @@ -3,6 +3,7 @@ import { dialogue, Emote, execute, goto } from '@engine/world/actor/dialogue'; import { Quest } from '@engine/world/actor/player/quest'; import { ContentPlugin } from '@engine/plugins/content-plugin'; import { npcInteractionActionHandler, NpcInteractionActionHook } from '@engine/world/action/npc-interaction.action'; +import { findItem } from '@engine/config'; const journalHandler = { 0: `I can start this quest by speaking to Romeo in @@ -16,6 +17,10 @@ const journalHandler = { I should take the message from Juliet to Romeo in Varrock central square.` }; +const questItems = { + julietLetter: findItem('rs:juliet_letter') +} + const startQuestAction: npcInteractionActionHandler = async details => { const { player, npc } = details; const participants = [player, { npc, key: 'romeo' }]; @@ -190,7 +195,7 @@ const julietFirstTalk: npcInteractionActionHandler = async details => { if (giveLetterSuccess) { player.setQuestProgress('rs:romeo_and_juliet', 2); await dialogue(participants, [ - item => ['rs:juliet_letter', `Juliet gives you a message.`] + item => [questItems.julietLetter.gameId, `Juliet gives you a message.`] ]); } else { await dialogue(participants, [ @@ -199,6 +204,40 @@ const julietFirstTalk: npcInteractionActionHandler = async details => { } }; +const julietSecondTalk: npcInteractionActionHandler = async details => { + const { player, npc } = details; + const participants = [player, { npc, key: 'juliet' }]; + + const hasLetter = player.hasItemInInventory(questItems.julietLetter.gameId) || player.hasItemInBank(questItems.julietLetter.gameId); + if (hasLetter) { + await dialogue(participants, [ + player => [Emote.HAPPY, `Hello Juliet!`], + juliet => [Emote.HAPPY, `Hello there...have you delivered the message to Romeo yet? What news do you have from my loved one?`], + player => [Emote.SKEPTICAL, `Oh, sorry, I've not had chance to deliver it yet!`], + juliet => [Emote.SAD, `Oh, that's a shame. I've been waiting so patiently to hear some word from him.`] + ]); + } else { + await dialogue(participants, [ + player => [Emote.HAPPY, `Hello Juliet!`], + juliet => [Emote.HAPPY, `Hello there...have you delivered the message to Romeo yet? What news do you have from my loved one?`], + player => [Emote.SKEPTICAL, `Hmmm, that's the thing about messages...they're so easy to misplace...`], + juliet => [Emote.SAD, `How could you lose that message? It was incredibly important...and it took me an age to write! I used joined up writing and everything!`], + juliet => [Emote.SAD, `Please, take this new message to him, and please don't lose it.`] + ]); + + const giveLetterSuccess = player.giveItem('rs:juliet_letter'); + if (giveLetterSuccess) { + await dialogue(participants, [ + item => [questItems.julietLetter.gameId, `Juliet gives you another message.`] + ]); + } else { + await dialogue(participants, [ + text => `You don't have enough space in your inventory!` + ]); + } + } +}; + const phillipaStartDialogue: npcInteractionActionHandler = async details => { const { player, npc } = details; const participants = [player, { npc, key: 'phillipa' }]; @@ -270,5 +309,15 @@ export default { options: 'talk-to', walkTo: true, handler: phillipaStartDialogue + }, { + type: 'npc_interaction', + questRequirement: { + questId: 'rs:romeo_and_juliet', + stage: 2 + }, + npcs: 'rs:juliet', + options: 'talk-to', + walkTo: true, + handler: julietSecondTalk }] }; From ae36e1f0bb956d9edbf34a3f7215ecd970fd8351 Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Tue, 13 Apr 2021 00:54:10 +0200 Subject: [PATCH 09/30] refactor quest file, add draul leptoc dialogue --- .../quests/romeo-and-juliet-quest.plugin.ts | 323 ------------------ .../romeo-and-juliet/draul-leptoc-dialogue.ts | 76 +++++ .../romeo-and-juliet/juliet-dialogue.ts | 64 ++++ .../romeo-and-juliet/phillipa-dialogue.ts | 24 ++ .../romeo-and-juliet-quest.plugin.ts | 140 ++++++++ .../quests/romeo-and-juliet/romeo-dialogue.ts | 161 +++++++++ 6 files changed, 465 insertions(+), 323 deletions(-) delete mode 100644 src/plugins/quests/romeo-and-juliet-quest.plugin.ts create mode 100644 src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts create mode 100644 src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts create mode 100644 src/plugins/quests/romeo-and-juliet/phillipa-dialogue.ts create mode 100644 src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts create mode 100644 src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts diff --git a/src/plugins/quests/romeo-and-juliet-quest.plugin.ts b/src/plugins/quests/romeo-and-juliet-quest.plugin.ts deleted file mode 100644 index ea8257bd4..000000000 --- a/src/plugins/quests/romeo-and-juliet-quest.plugin.ts +++ /dev/null @@ -1,323 +0,0 @@ -import { randomBetween } from '@engine/util/num'; -import { dialogue, Emote, execute, goto } from '@engine/world/actor/dialogue'; -import { Quest } from '@engine/world/actor/player/quest'; -import { ContentPlugin } from '@engine/plugins/content-plugin'; -import { npcInteractionActionHandler, NpcInteractionActionHook } from '@engine/world/action/npc-interaction.action'; -import { findItem } from '@engine/config'; - -const journalHandler = { - 0: `I can start this quest by speaking to Romeo in - Varrock central square by the fountain.`, - - 1: `I have agreed to find Juliet for Romeo and tell her how he feels.\nFor some reason he can't just do this by himself.\n - I should go and speak to Juliet. I can find her west of Varrock.`, - - 2: `I have agreed to find Juliet for Romeo and tell her how he feels. For some reason he can't just do this himself. - I found Juliet on the Western edge of Varrock, and told her about Romeo. She gave me a message to take back. - I should take the message from Juliet to Romeo in Varrock central square.` -}; - -const questItems = { - julietLetter: findItem('rs:juliet_letter') -} - -const startQuestAction: npcInteractionActionHandler = async details => { - const { player, npc } = details; - const participants = [player, { npc, key: 'romeo' }]; - - // Romeo starts with a random line - const randomDialog = randomBetween(0, 5); - switch (randomDialog) { - case 0: - await dialogue(participants, [ - romeo => [Emote.WORRIED, `Blub! Blub...where is my Juliet? Have you seen her?`] - ]); - break; - - case 1: - await dialogue(participants, [ - romeo => [Emote.WORRIED, `Looking for a blonde girl, goes by the name of Juliet..quite pretty...haven't seen her have you?`] - ]); - break; - - case 2: - await dialogue(participants, [ - romeo => [Emote.WORRIED, `Juliet, Juliet, wherefore art thou Juliet? Have you seen my Juliet?`] - ]); - break; - - case 3: - await dialogue(participants, [ - romeo => [Emote.WORRIED, `Oh woe is me that I cannot find my Juliet! You haven't seen Juliet have you?`] - ]); - break; - - case 4: - await dialogue(participants, [ - romeo => [Emote.WORRIED, `Sadness surrounds me now that Juliet's father forbids us to meet. Have you seen my Juliet?`] - ]); - break; - - case 5: - await dialogue(participants, [ - romeo => [Emote.WORRIED, `What is to become of me and my darling Juliet, I cannot find her anywhere, have you seen her?`] - ]); - break; - } - - await dialogue(participants, [ - options => [ - `Yes, I have seen her actually!`, [ - player => [Emote.GENERIC, `Yes, I have seen her actually!`], - player => [Emote.WONDERING, `At least, I think it was her... Blonde? A bit stressed?`], - romeo => [Emote.SHOCKED, `Golly...yes, yes...you make her sound very interesting!`], - romeo => [Emote.WONDERING, `And I'll bet she's a bit of a fox!`], - player => [Emote.WONDERING, `Well, I guess she could be considered attractive...`], - romeo => [Emote.HAPPY, `I'll bet she is! Wooooooooo!`], - romeo => [Emote.GENERIC, `Sorry, all that jubilation has made me forget what we were talking about.`], - player => [Emote.GENERIC, `You were asking me about Juliet? You seemed to know her?`], - romeo => [Emote.HAPPY, `Oh yes, Juliet!`], - romeo => [Emote.HAPPY, `The fox...could you tell her that she is the love of my long and that I life to be with her?`], - player => [Emote.WONDERING, `What? Surely you mean that she is the love of your life and that you long to be with her?`], - romeo => [Emote.HAPPY, `Oh yeah...what you said...tell her that, it sounds much better! Oh you're so good at this!`] - ], - `No sorry, I haven't seen her.`, [ - player => [Emote.GENERIC, `No sorry, I haven't seen her.`], - romeo => [Emote.SAD, `Oh...well, that's a shame...I was rather hoping you had.`], - player => [Emote.WONDERING, `Why? Is she a fugitive? Does she owe you some money or something?`], - romeo => [Emote.WONDERING, `Hmmm, she might do? Perhaps she does? How do you know?`], - player => [Emote.ANGRY, `I don't know? I was asking 'YOU' how 'YOU' know Juliet!`], - romeo => [Emote.HAPPY, `Ahh, yes Juliet, she's my one true love. Well, one of my one true loves! If you see her, could you tell her that she is the love of my long and that I life to be with her?`], - player => [Emote.WONDERING, `What? Surely you mean that she is the love of your life and that you long to be with her?`], - romeo => [Emote.HAPPY, `Oh yeah...what you said...tell her that, it sounds much better! Oh you're so good at this!`] - ], - `Perhaps I could help to find her for you? `, [ - player => [Emote.WONDERING, `Perhaps I can help find her for you? What does she look like?`], - romeo => [Emote.HAPPY, `Oh would you? That would be great! She has this sort of hair...`], - player => [Emote.WONDERING, `Hair...check..`], - romeo => [Emote.HAPPY, `...and she these...great lips...`], - player => [Emote.WONDERING, `Lips...right.`], - romeo => [Emote.HAPPY, `Oh and she has these lovely shoulders as well..`], - player => [Emote.GENERIC, `Shoulders...right, so she has hair, lips and shoulders...that should cut it down a bit.`], - romeo => [Emote.HAPPY, `Oh yes, Juliet is very different...please tell her that she is the love of my long and that I life to be with her?`], - player => [Emote.WONDERING, `What? Surely you mean that she is the love of your life and that you long to be with her?`], - romeo => [Emote.HAPPY, `Oh yeah...what you said...tell her that, it sounds much better! Oh you're so good at this!`] - ] - ] - ]); - - await dialogue(participants, [ - options => [ - `Yes, ok, I'll let her know.`, [ - execute(() => { - player.setQuestProgress('rs:romeo_and_juliet', 1); - }), - player => [Emote.GENERIC, `Yes, ok, I'll let her know.`], - romeo => [Emote.HAPPY, `Oh great! And tell her that I want to kiss her a give.`], - player => [Emote.ANGRY, `You mean you want to give her a kiss!`], - romeo => [Emote.HAPPY, `Oh you're good...you are good!`], - romeo => [Emote.HAPPY, `I see I've picked a true professional...!`], - moreInfo() - ], - `Sorry Romeo, I've got better things to do right now but maybe later?`, [ - player => [Emote.GENERIC, `Sorry Romeo, I've got better things to do right now but maybe later?`], - romeo => [Emote.SAD, `Oh, ok, well, I guess my Juliet and I can spend some time apart. And as the old saying goes, 'Absinthe makes the heart glow longer'.`], - player => [Emote.WONDERING, `Don't you mean that, 'Absence makes the...`], - player => [Emote.GENERIC, `Actually forget it...`], - romeo => [Emote.WONDERING, `Ok!`] - ] - ] - ]); -}; - -const romeoSecondTalk: npcInteractionActionHandler = async details => { - const { player, npc } = details; - const participants = [player, { npc, key: 'romeo' }]; - await dialogue(participants, [ - player => [Emote.GENERIC, `Hello again, remember me?`], - romeo => [Emote.WONDERING, `Of course, yes....how are.. you....ermmm...`], - player => [Emote.ANGRY, `You haven't got a clue who I am do you?`], - romeo => [Emote.GENERIC, `Not a clue my friend, but you seem to have a friendly face...a little blood stained, and perhaps in need of a wash, but friendly none the less.`], - player => [Emote.ANGRY, `You asked me to look for Juliet for you!`], - romeo => [Emote.HAPPY, `Ah yes, Juliet...my sweet darling...what news?`], - player => [Emote.WONDERING, `Nothing so far, but I need to ask a few questions? `], - moreInfo() - ]); -}; - -const moreInfo = () => { - return (options, tag_MORE_INFO) => [ - `Where can I find Juliet?`, [ - player => [Emote.WONDERING, `Where can I find Juliet?`], - romeo => [Emote.WONDERING, `Why do you ask?`], - player => [Emote.ANGRY, `So that I can try and find her for you!`], - romeo => [Emote.WONDERING, `Ah yes....quite right. Hmmm, let me think now.`], - romeo => [Emote.WONDERING, `She may still be locked away at her Father's house on the sest vide of Warrock.`], - romeo => [Emote.HAPPY, `Oh, I remember how she loved it when I would sing up to her balcony! She would reward me with her own personal items...`], - player => [Emote.WONDERING, `What, she just gave you her stuff?`], - romeo => [Emote.GENERIC, `Well, not exactly give...more like 'throw with considerable force'...she's always a kidder that Juliet!`], - goto('tag_MORE_INFO') - ], - `Is there anything else you can tell me about Juliet?`, [ - player => [Emote.WONDERING, `Is there anything else you can tell me about Juliet?`], - romeo => [Emote.HAPPY, `Oh, there is so much to tell...she is my true love, we intend to spend together forever...I can tell you so much about her..`], - player => [Emote.GENERIC, `Great!`], romeo => [Emote.WONDERING, `Ermmm.....`], - romeo => [Emote.WONDERING, `So much can I tell you...`], - player => [Emote.HAPPY, `Yes..`], - romeo => [Emote.WONDERING, `So much to tell...why, where do I start!`], - player => [Emote.GENERIC, `Yes..yes! Please go on...don't let me interrupt...`], - romeo => [Emote.WONDERING, `Ermmm.....`], - romeo => [Emote.WONDERING, `...`], - player => [Emote.WONDERING, `You can't remember can you?`], - romeo => [Emote.SAD, `Not a thing sorry....`], - goto('tag_MORE_INFO') - ], - `Ok, thanks.`, [ - player => [Emote.GENERIC, `Ok, thanks.`] - ] - ]; -}; - -const julietFirstTalk: npcInteractionActionHandler = async details => { - const { player, npc } = details; - const participants = [player, { npc, key: 'juliet' }]; - await dialogue(participants, [ - player => [Emote.GENERIC, `Juliet, I come from Romeo. He begs me to tell you that he cares still.`], - juliet => [Emote.HAPPY, `Oh how my heart soars to hear this news! Please take this message to him with great haste.`], - player => [Emote.GENERIC, `Well, I hope it's good news...he was quite upset when I left him.`], - juliet => [Emote.POMPOUS, `He's quite often upset...the poor sensitive soul. But I don't think he's going to take this news very well, however, all is not lost.`], - juliet => [Emote.HAPPY, `Everything is explained in the letter, would you be so kind and deliver it to him please?`], - player => [Emote.HAPPY, `Certainly, I'll do so straight away.`], - juliet => [Emote.HAPPY, `Many thanks! Oh, I'm so very grateful. You may be our only hope.`] - ]); - - const giveLetterSuccess = player.giveItem('rs:juliet_letter'); - if (giveLetterSuccess) { - player.setQuestProgress('rs:romeo_and_juliet', 2); - await dialogue(participants, [ - item => [questItems.julietLetter.gameId, `Juliet gives you a message.`] - ]); - } else { - await dialogue(participants, [ - text => `You don't have enough space in your inventory!` - ]); - } -}; - -const julietSecondTalk: npcInteractionActionHandler = async details => { - const { player, npc } = details; - const participants = [player, { npc, key: 'juliet' }]; - - const hasLetter = player.hasItemInInventory(questItems.julietLetter.gameId) || player.hasItemInBank(questItems.julietLetter.gameId); - if (hasLetter) { - await dialogue(participants, [ - player => [Emote.HAPPY, `Hello Juliet!`], - juliet => [Emote.HAPPY, `Hello there...have you delivered the message to Romeo yet? What news do you have from my loved one?`], - player => [Emote.SKEPTICAL, `Oh, sorry, I've not had chance to deliver it yet!`], - juliet => [Emote.SAD, `Oh, that's a shame. I've been waiting so patiently to hear some word from him.`] - ]); - } else { - await dialogue(participants, [ - player => [Emote.HAPPY, `Hello Juliet!`], - juliet => [Emote.HAPPY, `Hello there...have you delivered the message to Romeo yet? What news do you have from my loved one?`], - player => [Emote.SKEPTICAL, `Hmmm, that's the thing about messages...they're so easy to misplace...`], - juliet => [Emote.SAD, `How could you lose that message? It was incredibly important...and it took me an age to write! I used joined up writing and everything!`], - juliet => [Emote.SAD, `Please, take this new message to him, and please don't lose it.`] - ]); - - const giveLetterSuccess = player.giveItem('rs:juliet_letter'); - if (giveLetterSuccess) { - await dialogue(participants, [ - item => [questItems.julietLetter.gameId, `Juliet gives you another message.`] - ]); - } else { - await dialogue(participants, [ - text => `You don't have enough space in your inventory!` - ]); - } - } -}; - -const phillipaStartDialogue: npcInteractionActionHandler = async details => { - const { player, npc } = details; - const participants = [player, { npc, key: 'phillipa' }]; - await dialogue(participants, [ - player => [Emote.GENERIC, `Hello`], - phillipa => [Emote.HAPPY, `Hi, I'm Phillipa! Juliet's cousin? I like to keep an eye on her, make sure that dashing young Romeo doesn't just steal away from here under our plain old noses!`], - phillipa => [Emote.GENERIC, `He'd do it you know... he's ever so dashing, and cavalier, in a wet blanket sort of way.`] - ]); -}; - -export default { - pluginId: 'rs:romeo_and_juliet', - quests: [ - new Quest({ - id: 'rs:romeo_and_juliet', - questTabId: 37, - name: `Romeo & Juliet`, - points: 5, - journalHandler, - onComplete: { - questCompleteWidget: { - rewardText: ['5 Quest Points'], - itemId: 1891, - modelZoom: 240, - modelRotationY: 180, - modelRotationX: 180 - } - } - }) - ], - hooks: [{ - // TODO you can actually start the quest by talking to Juliet first - type: 'npc_interaction', - questRequirement: { - questId: 'rs:romeo_and_juliet', - stage: 0 - }, - npcs: 'rs:romeo', - options: 'talk-to', - walkTo: true, - handler: startQuestAction - }, { - type: 'npc_interaction', - questRequirement: { - questId: 'rs:romeo_and_juliet', - stage: 1 - }, - npcs: 'rs:romeo', - options: 'talk-to', - walkTo: true, - handler: romeoSecondTalk - }, { - type: 'npc_interaction', - questRequirement: { - questId: 'rs:romeo_and_juliet', - stage: 1 - }, - npcs: 'rs:juliet', - options: 'talk-to', - walkTo: true, - handler: julietFirstTalk - }, { - type: 'npc_interaction', - questRequirement: { - questId: 'rs:romeo_and_juliet', - stages: [0, 1, 2] - }, - npcs: 'rs:phillipa', - options: 'talk-to', - walkTo: true, - handler: phillipaStartDialogue - }, { - type: 'npc_interaction', - questRequirement: { - questId: 'rs:romeo_and_juliet', - stage: 2 - }, - npcs: 'rs:juliet', - options: 'talk-to', - walkTo: true, - handler: julietSecondTalk - }] -}; diff --git a/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts b/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts new file mode 100644 index 000000000..62611d97d --- /dev/null +++ b/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts @@ -0,0 +1,76 @@ +import { npcInteractionActionHandler } from '@engine/world/action/npc-interaction.action'; +import { dialogue, Emote } from '@engine/world/actor/dialogue'; +import { questItems } from './romeo-and-juliet-quest.plugin'; + +export const draulLeptocDialogue: npcInteractionActionHandler[] = [ + async (details) => { + const { player, npc } = details; + const participants = [player, { npc, key: 'draul_leptoc' }]; + await dialogue(participants, [ + draul_leptoc => [Emote.ANGRY, `What are you doing here? Snooping around...`], + player => [Emote.GENERIC, `Oh...just looking around...`], + draul_leptoc => [Emote.ANGRY, `Just looking around! This is MY house! You might have at least 'ASKED' to view my considerably well appointed abode...but no, you've just burst in with all the elegance of a Troll at a tea party.`], + player => [Emote.GENERIC, `I can see that you're busy ranting so I'll just nip off and investigate a bit.`] + ]); + }, + async (details) => { + const { player, npc } = details; + const participants = [player, { npc, key: 'draul_leptoc' }]; + await dialogue(participants, [ + draul_leptoc => [Emote.ANGRY, `What are you doing here? Snooping around... `], + options => [ + `I've come to see Juliet on Romeo's behalf.`, [ + player => [Emote.GENERIC, `I've come to see Juliet on Romeo's behalf.`], + draul_leptoc => [Emote.ANGRY, `What...what...Romeo! Why that good for nothing swine...he's always trying to get the affections of my daughter..that soppy, half brained nincompoop won't ever have the heart of my daughter.`], + draul_leptoc => [Emote.ANGRY, `She deserves someone of character, wit and repose.`], + player => [Emote.WONDERING, `What's so wrong about Romeo?`], + draul_leptoc => [Emote.ANGRY, `Wrong! What's wrong with him...have you actually talked to him? He's nothing but a dim witted upperclass twit, totally useless.`], + draul_leptoc => [Emote.ANGRY, `If he threw a stone at the ground, he'd probably miss! He's totally invisible when it's raining because he's so wet!`], + draul_leptoc => [Emote.ANGRY, `If you started with what's right with him, you'd have much less to consider!`], + player => [Emote.WONDERING, `Well, I admit, he's probably not the sharpest knife in the cutlery draw...`], + draul_leptoc => [Emote.ANGRY, `Sharp? I've seen keener wit in root vegetables. Anyway, stop changing the subject. Get out of here and don't think you can sneak up those stairs to see Juliet, because I'll catch you and then you'll be for it!`], + player => [Emote.WONDERING, `That seems a bit harsh....`], + draul_leptoc => [Emote.ANGRY, `Harsh but fair I think you'll find...now get OUT!`] + ], + `I've just come to have a chat with Juliet.`, [ + player => [Emote.GENERIC, `I've just come to have a chat with Juliet.`], + draul_leptoc => [Emote.ANGRY, `What on earth about? I hope you're not in cahoots with that good for nothing Romeo!`], + player => [Emote.WONDERING, `Err..no of course not....why would I be?`], + draul_leptoc => [Emote.SKEPTICAL, `He's been trying to wooo my daughter for an age. Up until now she's had the good sense to just ignore him. I just don't know what's gotten into her recently so that she would give him the time of day.`], + player => [Emote.SHOCKED, `Well, love is mysterious! Perhaps one day someone may even learn to love you!`], + draul_leptoc => [Emote.ANGRY, `What! Someone may fall in love with me...what are you trying to insinuate?`], + player => [Emote.WONDERING, `Err...Nothing....I guess I'd better be going now...`] + ], + `Oh...just looking around...`, [ + player => [Emote.GENERIC, `Oh...just looking around...`], + draul_leptoc => [Emote.ANGRY, `Just looking around! This is MY house! You might have at least 'ASKED' to view my considerably well appointed abode...but no, you've just burst in with all the elegance of a Troll at a tea party.`], + player => [Emote.GENERIC, `I can see that you're busy ranting so I'll just nip off and investigate a bit.`] + ] + ], + ]); + }, + async (details) => { + const { player, npc } = details; + const participants = [player, { npc, key: 'draul_leptoc' }]; + await dialogue(participants, [ + draul_leptoc => [Emote.ANGRY, `What are you doing in my house? Up to no good I shouldn't wonder!`], + player => [Emote.GENERIC, `Just a small chore for Juliet, you do have a lovely daughter in her sir.`], + draul_leptoc => [Emote.HAPPY, `Oh...why, thank you...I've always tried to my best...`], + draul_leptoc => [Emote.ANGRY, ` ...Hang on! Enough of that smiley talk. I have a daughter and I know what she's like. Don't even think of carrying on anything behind my back, I have the eyes of a hawk, nothing gets past me!`] + ]); + + if (!player.hasItemInInventory(questItems.julietLetter.gameId)) { + return; + } + + await dialogue(participants, [ + item => [questItems.julietLetter.gameId, `Sir Draul notices the message!`], + draul_leptoc => [Emote.ANGRY, `Hey! What's that in your hands...looks like a message to me...with Juliet's barely legible scrawl on it...`], + player => [Emote.SHOCKED, `Yes, yes, that's probably why I can't read it!`], + player => [Emote.SHOCKED, `Sorry, I mean, that's right sir. I'm just popping to the shops to get some groceries for Juliet.`], + player => [Emote.WONDERING, `Right, have to be off now...thanks...`], + draul_leptoc => [Emote.ANGRY, `Groceries!`], + draul_leptoc => [Emote.ANGRY, `Groceries!...at a time like this, does that girl know what she's putting me through!`] + ]); + }, +]; diff --git a/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts b/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts new file mode 100644 index 000000000..16d526131 --- /dev/null +++ b/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts @@ -0,0 +1,64 @@ +import { npcInteractionActionHandler } from '@engine/world/action/npc-interaction.action'; +import { dialogue, Emote } from '@engine/world/actor/dialogue'; +import { questItems } from './romeo-and-juliet-quest.plugin'; + +export const julietDialogue: npcInteractionActionHandler[] = [ + async details => { + const { player, npc } = details; + const participants = [player, { npc, key: 'juliet' }]; + await dialogue(participants, [ + player => [Emote.GENERIC, `Juliet, I come from Romeo. He begs me to tell you that he cares still.`], + juliet => [Emote.HAPPY, `Oh how my heart soars to hear this news! Please take this message to him with great haste.`], + player => [Emote.GENERIC, `Well, I hope it's good news...he was quite upset when I left him.`], + juliet => [Emote.POMPOUS, `He's quite often upset...the poor sensitive soul. But I don't think he's going to take this news very well, however, all is not lost.`], + juliet => [Emote.HAPPY, `Everything is explained in the letter, would you be so kind and deliver it to him please?`], + player => [Emote.HAPPY, `Certainly, I'll do so straight away.`], + juliet => [Emote.HAPPY, `Many thanks! Oh, I'm so very grateful. You may be our only hope.`] + ]); + + const giveLetterSuccess = player.giveItem('rs:juliet_letter'); + if (giveLetterSuccess) { + player.setQuestProgress('rs:romeo_and_juliet', 2); + await dialogue(participants, [ + item => [questItems.julietLetter.gameId, `Juliet gives you a message.`] + ]); + } else { + await dialogue(participants, [ + text => `You don't have enough space in your inventory!` + ]); + } + }, + async details => { + const { player, npc } = details; + const participants = [player, { npc, key: 'juliet' }]; + + const hasLetter = player.hasItemInInventory(questItems.julietLetter.gameId) || player.hasItemInBank(questItems.julietLetter.gameId); + if (hasLetter) { + await dialogue(participants, [ + player => [Emote.HAPPY, `Hello Juliet!`], + juliet => [Emote.HAPPY, `Hello there...have you delivered the message to Romeo yet? What news do you have from my loved one?`], + player => [Emote.SKEPTICAL, `Oh, sorry, I've not had chance to deliver it yet!`], + juliet => [Emote.SAD, `Oh, that's a shame. I've been waiting so patiently to hear some word from him.`] + ]); + } else { + await dialogue(participants, [ + player => [Emote.HAPPY, `Hello Juliet!`], + juliet => [Emote.HAPPY, `Hello there...have you delivered the message to Romeo yet? What news do you have from my loved one?`], + player => [Emote.SKEPTICAL, `Hmmm, that's the thing about messages...they're so easy to misplace...`], + juliet => [Emote.SAD, `How could you lose that message? It was incredibly important...and it took me an age to write! I used joined up writing and everything!`], + juliet => [Emote.SAD, `Please, take this new message to him, and please don't lose it.`] + ]); + + const giveLetterSuccess = player.giveItem('rs:juliet_letter'); + if (giveLetterSuccess) { + await dialogue(participants, [ + item => [questItems.julietLetter.gameId, `Juliet gives you another message.`] + ]); + } else { + await dialogue(participants, [ + text => `You don't have enough space in your inventory!` + ]); + } + } + } +]; diff --git a/src/plugins/quests/romeo-and-juliet/phillipa-dialogue.ts b/src/plugins/quests/romeo-and-juliet/phillipa-dialogue.ts new file mode 100644 index 000000000..fec17a6a3 --- /dev/null +++ b/src/plugins/quests/romeo-and-juliet/phillipa-dialogue.ts @@ -0,0 +1,24 @@ +import { npcInteractionActionHandler } from '@engine/world/action/npc-interaction.action'; +import { dialogue, Emote } from '@engine/world/actor/dialogue'; + +export const phillipaDialogue: npcInteractionActionHandler[] = [ + async (details) => { + const { player, npc } = details; + const participants = [player, { npc, key: 'phillipa' }]; + await dialogue(participants, [ + player => [Emote.GENERIC, `Hello`], + phillipa => [Emote.HAPPY, `Hi, I'm Phillipa! Juliet's cousin? I like to keep an eye on her, make sure that dashing young Romeo doesn't just steal away from here under our plain old noses!`], + phillipa => [Emote.GENERIC, `He'd do it you know... he's ever so dashing, and cavalier, in a wet blanket sort of way.`] + ]); + }, + async details => { + const { player, npc } = details; + const participants = [player, { npc, key: 'phillipa' }]; + await dialogue(participants, [ + phillipa => [Emote.HAPPY, `Oh, hello. Juliet has told me what you're doing for her and Romeo, and I have to say I'm very grateful to you. Juliet deserves a bit of happiness in her life.`], + phillipa => [Emote.HAPPY, `And I'm sure Romeo is just the sort of jester to make her laugh out loud - hysterically you might say.`], + phillipa => [Emote.HAPPY, `He always brings a tear to my eyes - tears of happiness at his foolish antics!`], + player => [Emote.GENERIC, `Oh, thanks. I like to do my cupid bit.`] + ]); + } +]; diff --git a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts new file mode 100644 index 000000000..dee3b7dfc --- /dev/null +++ b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts @@ -0,0 +1,140 @@ +import { Quest } from '@engine/world/actor/player/quest'; +import { ContentPlugin } from '@engine/plugins/content-plugin'; +import { NpcInteractionActionHook } from '@engine/world/action/npc-interaction.action'; +import { findItem } from '@engine/config'; + +// Dialogues +import { phillipaDialogue } from './phillipa-dialogue'; +import { julietDialogue } from './juliet-dialogue'; +import { romeoDialogue } from './romeo-dialogue'; +import { draulLeptocDialogue } from './draul-leptoc-dialogue'; + +const journalHandler = { + 0: `I can start this quest by speaking to Romeo in + Varrock central square by the fountain.`, + + 1: `I have agreed to find Juliet for Romeo and tell her how he feels.\nFor some reason he can't just do this by himself.\n + I should go and speak to Juliet. I can find her west of Varrock.`, + + 2: `I have agreed to find Juliet for Romeo and tell her how he feels. For some reason he can't just do this himself. + I found Juliet on the Western edge of Varrock, and told her about Romeo. She gave me a message to take back. + I should take the message from Juliet to Romeo in Varrock central square.` +}; + +export const questItems = { + julietLetter: findItem('rs:juliet_letter') +} + +export default { + pluginId: 'rs:romeo_and_juliet', + quests: [ + new Quest({ + id: 'rs:romeo_and_juliet', + questTabId: 37, + name: `Romeo & Juliet`, + points: 5, + journalHandler, + onComplete: { + questCompleteWidget: { + rewardText: ['5 Quest Points'], + itemId: 1891, + modelZoom: 240, + modelRotationY: 180, + modelRotationX: 180 + } + } + }) + ], + hooks: [{ + // TODO you can actually start the quest by talking to Juliet first + type: 'npc_interaction', + questRequirement: { + questId: 'rs:romeo_and_juliet', + stage: 0 + }, + npcs: 'rs:romeo', + options: 'talk-to', + walkTo: true, + handler: romeoDialogue[0] + }, { + type: 'npc_interaction', + questRequirement: { + questId: 'rs:romeo_and_juliet', + stage: 0 + }, + npcs: 'rs:draul_leptoc', + options: 'talk-to', + walkTo: true, + handler: draulLeptocDialogue[0] + }, { + type: 'npc_interaction', + questRequirement: { + questId: 'rs:romeo_and_juliet', + stage: 1 + }, + npcs: 'rs:draul_leptoc', + options: 'talk-to', + walkTo: true, + handler: draulLeptocDialogue[1] + }, { + type: 'npc_interaction', + questRequirement: { + questId: 'rs:romeo_and_juliet', + stage: 2 + }, + npcs: 'rs:draul_leptoc', + options: 'talk-to', + walkTo: true, + handler: draulLeptocDialogue[2] + }, { + type: 'npc_interaction', + questRequirement: { + questId: 'rs:romeo_and_juliet', + stage: 1 + }, + npcs: 'rs:romeo', + options: 'talk-to', + walkTo: true, + handler: romeoDialogue[1] + }, { + type: 'npc_interaction', + questRequirement: { + questId: 'rs:romeo_and_juliet', + stage: 1 + }, + npcs: 'rs:juliet', + options: 'talk-to', + walkTo: true, + handler: julietDialogue[0] + }, { + type: 'npc_interaction', + questRequirement: { + questId: 'rs:romeo_and_juliet', + stages: [0, 1] + }, + npcs: 'rs:phillipa', + options: 'talk-to', + walkTo: true, + handler: phillipaDialogue[0] + }, { + type: 'npc_interaction', + questRequirement: { + questId: 'rs:romeo_and_juliet', + stage: 2 + }, + npcs: 'rs:phillipa', + options: 'talk-to', + walkTo: true, + handler: phillipaDialogue[1] + }, { + type: 'npc_interaction', + questRequirement: { + questId: 'rs:romeo_and_juliet', + stage: 2 + }, + npcs: 'rs:juliet', + options: 'talk-to', + walkTo: true, + handler: julietDialogue[1] + }] +}; diff --git a/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts b/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts new file mode 100644 index 000000000..2ea5be16a --- /dev/null +++ b/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts @@ -0,0 +1,161 @@ +import { npcInteractionActionHandler } from '@engine/world/action/npc-interaction.action'; +import { randomBetween } from '@engine/util/num'; +import { dialogue, Emote, execute, goto } from '@engine/world/actor/dialogue'; + +export const romeoDialogue: npcInteractionActionHandler[] = [ + async details => { + const { player, npc } = details; + const participants = [player, { npc, key: 'romeo' }]; + + // Romeo starts with a random line + const randomDialog = randomBetween(0, 5); + switch (randomDialog) { + case 0: + await dialogue(participants, [ + romeo => [Emote.WORRIED, `Blub! Blub...where is my Juliet? Have you seen her?`] + ]); + break; + + case 1: + await dialogue(participants, [ + romeo => [Emote.WORRIED, `Looking for a blonde girl, goes by the name of Juliet..quite pretty...haven't seen her have you?`] + ]); + break; + + case 2: + await dialogue(participants, [ + romeo => [Emote.WORRIED, `Juliet, Juliet, wherefore art thou Juliet? Have you seen my Juliet?`] + ]); + break; + + case 3: + await dialogue(participants, [ + romeo => [Emote.WORRIED, `Oh woe is me that I cannot find my Juliet! You haven't seen Juliet have you?`] + ]); + break; + + case 4: + await dialogue(participants, [ + romeo => [Emote.WORRIED, `Sadness surrounds me now that Juliet's father forbids us to meet. Have you seen my Juliet?`] + ]); + break; + + case 5: + await dialogue(participants, [ + romeo => [Emote.WORRIED, `What is to become of me and my darling Juliet, I cannot find her anywhere, have you seen her?`] + ]); + break; + } + + await dialogue(participants, [ + options => [ + `Yes, I have seen her actually!`, [ + player => [Emote.GENERIC, `Yes, I have seen her actually!`], + player => [Emote.WONDERING, `At least, I think it was her... Blonde? A bit stressed?`], + romeo => [Emote.SHOCKED, `Golly...yes, yes...you make her sound very interesting!`], + romeo => [Emote.WONDERING, `And I'll bet she's a bit of a fox!`], + player => [Emote.WONDERING, `Well, I guess she could be considered attractive...`], + romeo => [Emote.HAPPY, `I'll bet she is! Wooooooooo!`], + romeo => [Emote.GENERIC, `Sorry, all that jubilation has made me forget what we were talking about.`], + player => [Emote.GENERIC, `You were asking me about Juliet? You seemed to know her?`], + romeo => [Emote.HAPPY, `Oh yes, Juliet!`], + romeo => [Emote.HAPPY, `The fox...could you tell her that she is the love of my long and that I life to be with her?`], + player => [Emote.WONDERING, `What? Surely you mean that she is the love of your life and that you long to be with her?`], + romeo => [Emote.HAPPY, `Oh yeah...what you said...tell her that, it sounds much better! Oh you're so good at this!`] + ], + `No sorry, I haven't seen her.`, [ + player => [Emote.GENERIC, `No sorry, I haven't seen her.`], + romeo => [Emote.SAD, `Oh...well, that's a shame...I was rather hoping you had.`], + player => [Emote.WONDERING, `Why? Is she a fugitive? Does she owe you some money or something?`], + romeo => [Emote.WONDERING, `Hmmm, she might do? Perhaps she does? How do you know?`], + player => [Emote.ANGRY, `I don't know? I was asking 'YOU' how 'YOU' know Juliet!`], + romeo => [Emote.HAPPY, `Ahh, yes Juliet, she's my one true love. Well, one of my one true loves! If you see her, could you tell her that she is the love of my long and that I life to be with her?`], + player => [Emote.WONDERING, `What? Surely you mean that she is the love of your life and that you long to be with her?`], + romeo => [Emote.HAPPY, `Oh yeah...what you said...tell her that, it sounds much better! Oh you're so good at this!`] + ], + `Perhaps I could help to find her for you? `, [ + player => [Emote.WONDERING, `Perhaps I can help find her for you? What does she look like?`], + romeo => [Emote.HAPPY, `Oh would you? That would be great! She has this sort of hair...`], + player => [Emote.WONDERING, `Hair...check..`], + romeo => [Emote.HAPPY, `...and she these...great lips...`], + player => [Emote.WONDERING, `Lips...right.`], + romeo => [Emote.HAPPY, `Oh and she has these lovely shoulders as well..`], + player => [Emote.GENERIC, `Shoulders...right, so she has hair, lips and shoulders...that should cut it down a bit.`], + romeo => [Emote.HAPPY, `Oh yes, Juliet is very different...please tell her that she is the love of my long and that I life to be with her?`], + player => [Emote.WONDERING, `What? Surely you mean that she is the love of your life and that you long to be with her?`], + romeo => [Emote.HAPPY, `Oh yeah...what you said...tell her that, it sounds much better! Oh you're so good at this!`] + ] + ] + ]); + + await dialogue(participants, [ + options => [ + `Yes, ok, I'll let her know.`, [ + execute(() => { + player.setQuestProgress('rs:romeo_and_juliet', 1); + }), + player => [Emote.GENERIC, `Yes, ok, I'll let her know.`], + romeo => [Emote.HAPPY, `Oh great! And tell her that I want to kiss her a give.`], + player => [Emote.ANGRY, `You mean you want to give her a kiss!`], + romeo => [Emote.HAPPY, `Oh you're good...you are good!`], + romeo => [Emote.HAPPY, `I see I've picked a true professional...!`], + moreInfo() + ], + `Sorry Romeo, I've got better things to do right now but maybe later?`, [ + player => [Emote.GENERIC, `Sorry Romeo, I've got better things to do right now but maybe later?`], + romeo => [Emote.SAD, `Oh, ok, well, I guess my Juliet and I can spend some time apart. And as the old saying goes, 'Absinthe makes the heart glow longer'.`], + player => [Emote.WONDERING, `Don't you mean that, 'Absence makes the...`], + player => [Emote.GENERIC, `Actually forget it...`], + romeo => [Emote.WONDERING, `Ok!`] + ] + ] + ]); + }, + async details => { + const { player, npc } = details; + const participants = [player, { npc, key: 'romeo' }]; + await dialogue(participants, [ + player => [Emote.GENERIC, `Hello again, remember me?`], + romeo => [Emote.WONDERING, `Of course, yes....how are.. you....ermmm...`], + player => [Emote.ANGRY, `You haven't got a clue who I am do you?`], + romeo => [Emote.GENERIC, `Not a clue my friend, but you seem to have a friendly face...a little blood stained, and perhaps in need of a wash, but friendly none the less.`], + player => [Emote.ANGRY, `You asked me to look for Juliet for you!`], + romeo => [Emote.HAPPY, `Ah yes, Juliet...my sweet darling...what news?`], + player => [Emote.WONDERING, `Nothing so far, but I need to ask a few questions? `], + moreInfo() + ]); + } +]; + +const moreInfo = () => { + return (options, tag_MORE_INFO) => [ + `Where can I find Juliet?`, [ + player => [Emote.WONDERING, `Where can I find Juliet?`], + romeo => [Emote.WONDERING, `Why do you ask?`], + player => [Emote.ANGRY, `So that I can try and find her for you!`], + romeo => [Emote.WONDERING, `Ah yes....quite right. Hmmm, let me think now.`], + romeo => [Emote.WONDERING, `She may still be locked away at her Father's house on the sest vide of Warrock.`], + romeo => [Emote.HAPPY, `Oh, I remember how she loved it when I would sing up to her balcony! She would reward me with her own personal items...`], + player => [Emote.WONDERING, `What, she just gave you her stuff?`], + romeo => [Emote.GENERIC, `Well, not exactly give...more like 'throw with considerable force'...she's always a kidder that Juliet!`], + goto('tag_MORE_INFO') + ], + `Is there anything else you can tell me about Juliet?`, [ + player => [Emote.WONDERING, `Is there anything else you can tell me about Juliet?`], + romeo => [Emote.HAPPY, `Oh, there is so much to tell...she is my true love, we intend to spend together forever...I can tell you so much about her..`], + player => [Emote.GENERIC, `Great!`], romeo => [Emote.WONDERING, `Ermmm.....`], + romeo => [Emote.WONDERING, `So much can I tell you...`], + player => [Emote.HAPPY, `Yes..`], + romeo => [Emote.WONDERING, `So much to tell...why, where do I start!`], + player => [Emote.GENERIC, `Yes..yes! Please go on...don't let me interrupt...`], + romeo => [Emote.WONDERING, `Ermmm.....`], + romeo => [Emote.WONDERING, `...`], + player => [Emote.WONDERING, `You can't remember can you?`], + romeo => [Emote.SAD, `Not a thing sorry....`], + goto('tag_MORE_INFO') + ], + `Ok, thanks.`, [ + player => [Emote.GENERIC, `Ok, thanks.`] + ] + ]; +}; From 63158b972e303d647143a14285f74b5db925fc4b Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Tue, 13 Apr 2021 01:36:22 +0200 Subject: [PATCH 10/30] add Father Lawrence spawn and first dialogue --- .../npc-spawns/varrock/varrock-general.json | 6 +++ data/config/npcs/varrock.json | 3 ++ .../father-lawrence-dialogue.ts | 47 +++++++++++++++++++ .../romeo-and-juliet-quest.plugin.ts | 11 +++++ 4 files changed, 67 insertions(+) create mode 100644 src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts diff --git a/data/config/npc-spawns/varrock/varrock-general.json b/data/config/npc-spawns/varrock/varrock-general.json index 30e693208..4c9d3e645 100644 --- a/data/config/npc-spawns/varrock/varrock-general.json +++ b/data/config/npc-spawns/varrock/varrock-general.json @@ -41,6 +41,12 @@ "spawn_y": 3435, "movement_radius": 4 }, + { + "npc": "rs:father_lawrence", + "spawn_x": 3254, + "spawn_y": 3485, + "movement_radius": 3 + }, { "npc": "rs:man", "spawn_x": 3153, diff --git a/data/config/npcs/varrock.json b/data/config/npcs/varrock.json index e4ac80daf..60a235bba 100644 --- a/data/config/npcs/varrock.json +++ b/data/config/npcs/varrock.json @@ -38,6 +38,9 @@ "rs:juliet": { "game_id": 637 }, + "rs:father_lawrence": { + "game_id": 640 + }, "rs:varrock_shop_keeper": { "game_id": 520 }, diff --git a/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts b/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts new file mode 100644 index 000000000..28cdf584b --- /dev/null +++ b/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts @@ -0,0 +1,47 @@ +import { npcInteractionActionHandler } from '@engine/world/action/npc-interaction.action'; +import { dialogue, Emote, goto } from '@engine/world/actor/dialogue'; + +export const lawrenceOptions = () => { + return (options, tag_OPTIONS) => [ + `I am always looking for a quest.`, [ + player => [Emote.HAPPY, `I am always looking for a quest.`], + father_lawrence => [Emote.GENERIC, `Well, I see poor Romeo wandering around the square. I think he may need help.`], + father_lawrence => [Emote.SAD, `I was helping him and Juliet to meet, but it became impossible.`], + father_lawrence => [Emote.HAPPY, `I am sure he can use some help.`], + goto('tag_OPTIONS') + ], + `No, I prefer just to kill things.`, [ + player => [Emote.HAPPY, `No, I prefer just to kill things.`], + father_lawrence => [Emote.HAPPY, `That's a fine career in these lands. There is more that needs killing every day.`], + goto('tag_OPTIONS') + ], + `Can you recommend a good bar?`, [ + player => [Emote.GENERIC, `Can you recommend a good bar?`], + father_lawrence => [Emote.ANGRY, `Drinking will be the death of you.`], + father_lawrence => [Emote.GENERIC, `But the Blue Moon in the city is cheap enough.`], + father_lawrence => [Emote.HAPPY, `And providing you buy one drink an hour they let you stay all night.`], + goto('tag_OPTIONS') + ], + `Ok, thanks`, [ + player => [Emote.GENERIC, `Ok, thanks`] + ] + ]; +} + +export const fatherLawrenceDialogue: npcInteractionActionHandler[] = [ + async (details) => { + const { player, npc } = details; + const participants = [player, { npc, key: 'father_lawrence' }]; + await dialogue(participants, [ + father_lawrence => [Emote.GENERIC, `Hello adventurer, do you seek a quest?`], + lawrenceOptions() + ]); + }, + async details => { + const { player, npc } = details; + const participants = [player, { npc, key: 'father_lawrence' }]; + await dialogue(participants, [ + + ]); + } +]; diff --git a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts index dee3b7dfc..da1bf04b5 100644 --- a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts +++ b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts @@ -8,6 +8,7 @@ import { phillipaDialogue } from './phillipa-dialogue'; import { julietDialogue } from './juliet-dialogue'; import { romeoDialogue } from './romeo-dialogue'; import { draulLeptocDialogue } from './draul-leptoc-dialogue'; +import { fatherLawrenceDialogue } from './father-lawrence-dialogue'; const journalHandler = { 0: `I can start this quest by speaking to Romeo in @@ -87,6 +88,16 @@ export default { walkTo: true, handler: draulLeptocDialogue[2] }, { + type: 'npc_interaction', + questRequirement: { + questId: 'rs:romeo_and_juliet', + stages: [0, 1, 2] + }, + npcs: 'rs:father_lawrence', + options: 'talk-to', + walkTo: true, + handler: fatherLawrenceDialogue[0] + },{ type: 'npc_interaction', questRequirement: { questId: 'rs:romeo_and_juliet', From 729c031f03d862fea64624066f96a1085d3f757d Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Tue, 13 Apr 2021 15:07:43 +0200 Subject: [PATCH 11/30] add index of for stages array --- src/game-engine/world/action/hooks/hook-filters.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game-engine/world/action/hooks/hook-filters.ts b/src/game-engine/world/action/hooks/hook-filters.ts index 872842e9c..28a6729a9 100644 --- a/src/game-engine/world/action/hooks/hook-filters.ts +++ b/src/game-engine/world/action/hooks/hook-filters.ts @@ -72,7 +72,7 @@ export function questHookFilter(player: Player, actionHook: ActionHook): boolean const playerQuest = player.quests.find(quest => quest.questId === questId); if (!playerQuest) { // @TODO quest requirements - return actionHook.questRequirement.stage === 0; + return actionHook.questRequirement.stage === 0 || actionHook.questRequirement.stages?.indexOf(0) !== -1; } if (actionHook.questRequirement.stage === 'complete') { From 28923dd5eeb27a465999d6ec3c144dcc87fe0d10 Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Tue, 13 Apr 2021 18:04:44 +0200 Subject: [PATCH 12/30] add packet to disable the minimap, disable minimap during cutscene --- src/game-engine/config/minimap-state.ts | 4 ++++ src/game-engine/net/outbound-packets.ts | 7 +++++++ src/game-engine/world/actor/player/cutscenes.ts | 5 +++++ 3 files changed, 16 insertions(+) create mode 100644 src/game-engine/config/minimap-state.ts diff --git a/src/game-engine/config/minimap-state.ts b/src/game-engine/config/minimap-state.ts new file mode 100644 index 000000000..fbfd385b7 --- /dev/null +++ b/src/game-engine/config/minimap-state.ts @@ -0,0 +1,4 @@ +export enum MinimapState { + NORMAL = 0, + BLACK = 2 +} diff --git a/src/game-engine/net/outbound-packets.ts b/src/game-engine/net/outbound-packets.ts index f05c9189a..a107bc01c 100644 --- a/src/game-engine/net/outbound-packets.ts +++ b/src/game-engine/net/outbound-packets.ts @@ -11,6 +11,7 @@ import { Npc } from '@engine/world/actor/npc/npc'; import { stringToLong } from '@engine/util/strings'; import { LandscapeObject } from '@runejs/filestore'; import { xteaRegions } from '@engine/config'; +import { MinimapState } from '@engine/config/minimap-state'; /** * A helper class for sending various network packets back to the game client. @@ -47,6 +48,12 @@ export class OutboundPackets { this.queue(packet); } + public setMinimapState(minimapState: MinimapState): void { + const packet = new Packet(235); + packet.put(minimapState); + this.queue(packet); + } + public updateSocialSettings(): void { const packet = new Packet(196); packet.put(this.player.settings.publicChatMode || 0); diff --git a/src/game-engine/world/actor/player/cutscenes.ts b/src/game-engine/world/actor/player/cutscenes.ts index 4646c888a..de008c783 100644 --- a/src/game-engine/world/actor/player/cutscenes.ts +++ b/src/game-engine/world/actor/player/cutscenes.ts @@ -1,5 +1,6 @@ import { Player } from '@engine/world/actor/player/player'; import { Position } from '@engine/world/position'; +import { MinimapState } from '@engine/config/minimap-state'; /** @@ -39,6 +40,9 @@ export class Cutscene { public constructor(player: Player, options?: CameraOptions) { this.player = player; + // Hide the minimap when creating a cutscene + this.player.outgoingPackets.setMinimapState(MinimapState.BLACK); + if(options) { this.setCamera(options); } @@ -103,6 +107,7 @@ export class Cutscene { */ public endCutscene(): void { this.player.outgoingPackets.resetCamera(); + this.player.outgoingPackets.setMinimapState(MinimapState.NORMAL); this.player.cutscene = null; } From ef09427c18daf745ce0cc8b75b78732500616e8e Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Wed, 14 Apr 2021 02:31:50 +0200 Subject: [PATCH 13/30] bonkers commit, i might revert this one. dunno what im doing --- data/config/widgets.json5 | 3 +- src/game-engine/config/index.ts | 2 +- src/game-engine/world/actor/dialogue.ts | 12 ++- .../world/actor/player/cutscenes.ts | 4 - .../world/actor/player/interface-state.ts | 4 + .../romeo-and-juliet-quest.plugin.ts | 12 ++- .../quests/romeo-and-juliet/romeo-dialogue.ts | 79 ++++++++++++++++++- 7 files changed, 105 insertions(+), 11 deletions(-) diff --git a/data/config/widgets.json5 b/data/config/widgets.json5 index 6f4691631..486242ac3 100644 --- a/data/config/widgets.json5 +++ b/data/config/widgets.json5 @@ -87,5 +87,6 @@ christmas: 23, killcount: 24 }, - whatWouldYouLikeToSpin: 459 + whatWouldYouLikeToSpin: 459, + fade: 115 } diff --git a/src/game-engine/config/index.ts b/src/game-engine/config/index.ts index 411d9a3ef..d37c1a7fa 100644 --- a/src/game-engine/config/index.ts +++ b/src/game-engine/config/index.ts @@ -39,7 +39,7 @@ export let skillGuides: SkillGuide[] = []; export let xteaRegions: { [key: number]: XteaRegion }; export const musicRegionMap = new Map(); -export const widgets: { [key: string]: any } = require('../../../data/config/widgets.json5'); +export const widgets = require('../../../data/config/widgets.json5'); export async function loadCoreConfigurations(): Promise { xteaRegions = await loadXteaRegionFiles('data/config/xteas'); diff --git a/src/game-engine/world/actor/dialogue.ts b/src/game-engine/world/actor/dialogue.ts index 49dee5f07..99b727ac7 100644 --- a/src/game-engine/world/actor/dialogue.ts +++ b/src/game-engine/world/actor/dialogue.ts @@ -164,6 +164,7 @@ export type DialogueTree = (Function | DialogueFunction | GoToAction)[]; export interface AdditionalOptions { closeOnWalk?: boolean; permanent?: boolean; + multi?: boolean; } interface NpcParticipant { @@ -598,13 +599,17 @@ async function runDialogueAction(player: Player, dialogueAction: string | Dialog if(tag === undefined && widgetId) { const permanent = additionalOptions?.permanent || false; + const multi = additionalOptions?.multi == null ? false : additionalOptions?.multi; + + // player.interfaceState.clearSlots(); + // player.interfaceState.closeOthers('chatbox'); if(permanent) { player.interfaceState.openChatOverlayWidget(widgetId); } else { player.interfaceState.openWidget(widgetId, { slot: 'chatbox', - multi: false + multi }); const widgetClosedEvent = await player.interfaceState.widgetClosed('chatbox'); @@ -665,11 +670,12 @@ export async function dialogue(participants: (Player | NpcParticipant)[], dialog try { await run(); - player.interfaceState.closeAllSlots(); + // TODO uncomment the next line + // player.interfaceState.closeAllSlots(); return true; } catch(error) { player.interfaceState.closeAllSlots(); - logger.warn(Object.keys(error.message)); + logger.warn(error); return false; } } diff --git a/src/game-engine/world/actor/player/cutscenes.ts b/src/game-engine/world/actor/player/cutscenes.ts index de008c783..03766334d 100644 --- a/src/game-engine/world/actor/player/cutscenes.ts +++ b/src/game-engine/world/actor/player/cutscenes.ts @@ -40,9 +40,6 @@ export class Cutscene { public constructor(player: Player, options?: CameraOptions) { this.player = player; - // Hide the minimap when creating a cutscene - this.player.outgoingPackets.setMinimapState(MinimapState.BLACK); - if(options) { this.setCamera(options); } @@ -107,7 +104,6 @@ export class Cutscene { */ public endCutscene(): void { this.player.outgoingPackets.resetCamera(); - this.player.outgoingPackets.setMinimapState(MinimapState.NORMAL); this.player.cutscene = null; } diff --git a/src/game-engine/world/actor/player/interface-state.ts b/src/game-engine/world/actor/player/interface-state.ts index 71f509df7..cd42ba873 100644 --- a/src/game-engine/world/actor/player/interface-state.ts +++ b/src/game-engine/world/actor/player/interface-state.ts @@ -145,6 +145,10 @@ export class InterfaceState { return; } + if (widget.slot === 'chatbox' && widget.multi) { + this.closeChatOverlayWidget(); + } + this.closed.next({ widget, data }); this.widgetSlots[widget.slot] = null; } diff --git a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts index da1bf04b5..b71a09afa 100644 --- a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts +++ b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts @@ -97,7 +97,7 @@ export default { options: 'talk-to', walkTo: true, handler: fatherLawrenceDialogue[0] - },{ + }, { type: 'npc_interaction', questRequirement: { questId: 'rs:romeo_and_juliet', @@ -107,6 +107,16 @@ export default { options: 'talk-to', walkTo: true, handler: romeoDialogue[1] + }, { + type: 'npc_interaction', + questRequirement: { + questId: 'rs:romeo_and_juliet', + stage: 2 + }, + npcs: 'rs:romeo', + options: 'talk-to', + walkTo: true, + handler: romeoDialogue[2] }, { type: 'npc_interaction', questRequirement: { diff --git a/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts b/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts index 2ea5be16a..afea6d558 100644 --- a/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts @@ -1,6 +1,12 @@ import { npcInteractionActionHandler } from '@engine/world/action/npc-interaction.action'; import { randomBetween } from '@engine/util/num'; import { dialogue, Emote, execute, goto } from '@engine/world/actor/dialogue'; +import { widgets } from '@engine/config'; +import { schedule } from '@engine/world/task'; +import { Position } from '@engine/world/position'; +import { MinimapState } from '@engine/config/minimap-state'; +import { WorldInstance } from '@engine/world/instances'; +import uuidv4 from 'uuid/v4'; export const romeoDialogue: npcInteractionActionHandler[] = [ async details => { @@ -124,7 +130,78 @@ export const romeoDialogue: npcInteractionActionHandler[] = [ player => [Emote.WONDERING, `Nothing so far, but I need to ask a few questions? `], moreInfo() ]); - } + }, + async details => { + // Placeholder for the cutscene at the end, I was bored so I decided to skip to this one + const { player, npc } = details; + const participants = [player, { npc, key: 'romeo' }]; + const cont = await dialogue(participants, [ + player => [Emote.HAPPY, `Romeo, it's all set. Juliet has drunk the potion and has been taken down into the Crypt...now you just need to pop along and collect her.`], + romeo => [Emote.HAPPY, `Ah, right, the potion! Great...`], + romeo => [Emote.WONDERING, `What potion would that be then?`], + player => [Emote.SHOCKED, `The Cadava potion...you know, the one which will make her appear dead! She's in the crypt, pop along and claim your true love.`], + romeo => [Emote.WORRIED, `But I'm scared...will you come with me?`], + player => [Emote.GENERIC, `Oh , ok...come on! I think I saw the entrance when I visited there last...`] + ]); + + if (!cont) { + return; + } + + const fadeOutAnimation = 3541; + const fadeInAnimation = 2115; + + player.outgoingPackets.playWidgetAnimation(widgets.fade, 0, fadeOutAnimation); + player.interfaceState.openWidget(widgets.fade, { slot: 'screen' }); + + await schedule(4); + + player.outgoingPackets.setMinimapState(MinimapState.BLACK); + player.instance = new WorldInstance(uuidv4()); + player.teleport(new Position(2332, 4645)); + + await dialogue(participants, [ + player => [Emote.WORRIED, `Oh , be quiet...`], + ], { multi: true }); + + await schedule(2); + + await dialogue(participants, [ + romeo => [Emote.WORRIED, `This is pretty scary...`], + ], { multi: true }); + + // player.interfaceState.openWidget(241, { + // slot: 'chatbox', + // multi: true + // }); + // const widgetClosedEvent = await player.interfaceState.widgetClosed('chatbox'); + // + // if(widgetClosedEvent.data === undefined) { + // throw new Error('Dialogue Cancelled.'); + // } + + // player.interfaceState.closeWidget('chatbox'); + + player.outgoingPackets.playWidgetAnimation(widgets.blankScreen, 0, fadeInAnimation); // Fade in + player.outgoingPackets.setMinimapState(MinimapState.NORMAL); // TODO move this until after the cutscene + player.instance = null; + + + // await dialogue(participants, [ + // romeo => [Emote.WORRIED, `This is pretty scary...`] + // ], { multi: true }); + // + // player.interfaceState.closeChatOverlayWidget(); + + // await dialogue(participants, [ + // player => [Emote.WORRIED, `Oh , be quiet...`] + // ], { multi: true }); + // + // player.interfaceState.closeChatOverlayWidget(); + // player.interfaceState.openWidget(widgets.blankScreen, { slot: 'screen' }); + // await schedule(3); + // player.outgoingPackets.playWidgetAnimation(widgets.blankScreen, 0, 2115); // Fade out + }, ]; const moreInfo = () => { From c2dec7fbc5fc372f1d42cf77db43e04bad8ec809 Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Wed, 14 Apr 2021 22:01:11 +0200 Subject: [PATCH 14/30] refactor romeo's dialogue --- src/game-engine/config/quest-config.ts | 4 +- src/game-engine/world/actor/dialogue.ts | 6 +- .../romeo-and-juliet-quest.plugin.ts | 29 +----- .../quests/romeo-and-juliet/romeo-dialogue.ts | 98 +++++++++---------- 4 files changed, 55 insertions(+), 82 deletions(-) diff --git a/src/game-engine/config/quest-config.ts b/src/game-engine/config/quest-config.ts index 5906a5d85..19a6beee6 100644 --- a/src/game-engine/config/quest-config.ts +++ b/src/game-engine/config/quest-config.ts @@ -2,7 +2,6 @@ import { Player } from '@engine/world/actor/player/player'; import { Npc } from '@engine/world/actor/npc/npc'; import { npcInteractionActionHandler } from '@engine/world/action/npc-interaction.action'; import { logger } from '@runejs/core'; -import { handleTutorial } from '@plugins/quests/goblin-diplomacy-tutorial/goblin-diplomacy-quest.plugin'; export type QuestKey = number | 'complete'; @@ -58,7 +57,8 @@ export function questDialogueActionFactory(questId: string, npcDialogueHandler: logger.error(e); } - await handleTutorial(player); + // TODO v this doesn't belong here!! v + // await handleTutorial(player); } }; } diff --git a/src/game-engine/world/actor/dialogue.ts b/src/game-engine/world/actor/dialogue.ts index 99b727ac7..0de8a594e 100644 --- a/src/game-engine/world/actor/dialogue.ts +++ b/src/game-engine/world/actor/dialogue.ts @@ -601,9 +601,6 @@ async function runDialogueAction(player: Player, dialogueAction: string | Dialog const permanent = additionalOptions?.permanent || false; const multi = additionalOptions?.multi == null ? false : additionalOptions?.multi; - // player.interfaceState.clearSlots(); - // player.interfaceState.closeOthers('chatbox'); - if(permanent) { player.interfaceState.openChatOverlayWidget(widgetId); } else { @@ -670,8 +667,7 @@ export async function dialogue(participants: (Player | NpcParticipant)[], dialog try { await run(); - // TODO uncomment the next line - // player.interfaceState.closeAllSlots(); + player.interfaceState.closeAllSlots(); return true; } catch(error) { player.interfaceState.closeAllSlots(); diff --git a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts index b71a09afa..81949f505 100644 --- a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts +++ b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts @@ -6,9 +6,10 @@ import { findItem } from '@engine/config'; // Dialogues import { phillipaDialogue } from './phillipa-dialogue'; import { julietDialogue } from './juliet-dialogue'; -import { romeoDialogue } from './romeo-dialogue'; +import { romeoDialogueHandler } from './romeo-dialogue'; import { draulLeptocDialogue } from './draul-leptoc-dialogue'; import { fatherLawrenceDialogue } from './father-lawrence-dialogue'; +import { questDialogueActionFactory } from '@engine/config/quest-config'; const journalHandler = { 0: `I can start this quest by speaking to Romeo in @@ -49,14 +50,10 @@ export default { hooks: [{ // TODO you can actually start the quest by talking to Juliet first type: 'npc_interaction', - questRequirement: { - questId: 'rs:romeo_and_juliet', - stage: 0 - }, npcs: 'rs:romeo', options: 'talk-to', walkTo: true, - handler: romeoDialogue[0] + handler: questDialogueActionFactory('rs:romeo_and_juliet', romeoDialogueHandler) }, { type: 'npc_interaction', questRequirement: { @@ -97,26 +94,6 @@ export default { options: 'talk-to', walkTo: true, handler: fatherLawrenceDialogue[0] - }, { - type: 'npc_interaction', - questRequirement: { - questId: 'rs:romeo_and_juliet', - stage: 1 - }, - npcs: 'rs:romeo', - options: 'talk-to', - walkTo: true, - handler: romeoDialogue[1] - }, { - type: 'npc_interaction', - questRequirement: { - questId: 'rs:romeo_and_juliet', - stage: 2 - }, - npcs: 'rs:romeo', - options: 'talk-to', - walkTo: true, - handler: romeoDialogue[2] }, { type: 'npc_interaction', questRequirement: { diff --git a/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts b/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts index afea6d558..eb9b8e149 100644 --- a/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts @@ -1,4 +1,3 @@ -import { npcInteractionActionHandler } from '@engine/world/action/npc-interaction.action'; import { randomBetween } from '@engine/util/num'; import { dialogue, Emote, execute, goto } from '@engine/world/actor/dialogue'; import { widgets } from '@engine/config'; @@ -7,10 +6,46 @@ import { Position } from '@engine/world/position'; import { MinimapState } from '@engine/config/minimap-state'; import { WorldInstance } from '@engine/world/instances'; import uuidv4 from 'uuid/v4'; +import { QuestDialogueHandler } from '@engine/config/quest-config'; +import { Player } from '@engine/world/actor/player/player'; +import { Npc } from '@engine/world/actor/npc/npc'; -export const romeoDialogue: npcInteractionActionHandler[] = [ - async details => { - const { player, npc } = details; +const moreInfo = () => { + return (options, tag_MORE_INFO) => [ + `Where can I find Juliet?`, [ + player => [Emote.WONDERING, `Where can I find Juliet?`], + romeo => [Emote.WONDERING, `Why do you ask?`], + player => [Emote.ANGRY, `So that I can try and find her for you!`], + romeo => [Emote.WONDERING, `Ah yes....quite right. Hmmm, let me think now.`], + romeo => [Emote.WONDERING, `She may still be locked away at her Father's house on the sest vide of Warrock.`], + romeo => [Emote.HAPPY, `Oh, I remember how she loved it when I would sing up to her balcony! She would reward me with her own personal items...`], + player => [Emote.WONDERING, `What, she just gave you her stuff?`], + romeo => [Emote.GENERIC, `Well, not exactly give...more like 'throw with considerable force'...she's always a kidder that Juliet!`], + goto('tag_MORE_INFO') + ], + `Is there anything else you can tell me about Juliet?`, [ + player => [Emote.WONDERING, `Is there anything else you can tell me about Juliet?`], + romeo => [Emote.HAPPY, `Oh, there is so much to tell...she is my true love, we intend to spend together forever...I can tell you so much about her..`], + player => [Emote.GENERIC, `Great!`], + romeo => [Emote.WONDERING, `Ermmm.....`], + romeo => [Emote.WONDERING, `So much can I tell you...`], + player => [Emote.HAPPY, `Yes..`], + romeo => [Emote.WONDERING, `So much to tell...why, where do I start!`], + player => [Emote.GENERIC, `Yes..yes! Please go on...don't let me interrupt...`], + romeo => [Emote.WONDERING, `Ermmm.....`], + romeo => [Emote.WONDERING, `...`], + player => [Emote.WONDERING, `You can't remember can you?`], + romeo => [Emote.SAD, `Not a thing sorry....`], + goto('tag_MORE_INFO') + ], + `Ok, thanks.`, [ + player => [Emote.GENERIC, `Ok, thanks.`] + ] + ]; +}; + +export const romeoDialogueHandler: QuestDialogueHandler = { + 0: async (player: Player, npc: Npc) => { const participants = [player, { npc, key: 'romeo' }]; // Romeo starts with a random line @@ -117,8 +152,8 @@ export const romeoDialogue: npcInteractionActionHandler[] = [ ] ]); }, - async details => { - const { player, npc } = details; + + 1: async (player: Player, npc: Npc) => { const participants = [player, { npc, key: 'romeo' }]; await dialogue(participants, [ player => [Emote.GENERIC, `Hello again, remember me?`], @@ -131,9 +166,9 @@ export const romeoDialogue: npcInteractionActionHandler[] = [ moreInfo() ]); }, - async details => { + + 2: async (player: Player, npc: Npc) => { // Placeholder for the cutscene at the end, I was bored so I decided to skip to this one - const { player, npc } = details; const participants = [player, { npc, key: 'romeo' }]; const cont = await dialogue(participants, [ player => [Emote.HAPPY, `Romeo, it's all set. Juliet has drunk the potion and has been taken down into the Crypt...now you just need to pop along and collect her.`], @@ -151,7 +186,7 @@ export const romeoDialogue: npcInteractionActionHandler[] = [ const fadeOutAnimation = 3541; const fadeInAnimation = 2115; - player.outgoingPackets.playWidgetAnimation(widgets.fade, 0, fadeOutAnimation); + // player.outgoingPackets.playWidgetAnimation(widgets.fade, 0, fadeOutAnimation); player.interfaceState.openWidget(widgets.fade, { slot: 'screen' }); await schedule(4); @@ -161,13 +196,11 @@ export const romeoDialogue: npcInteractionActionHandler[] = [ player.teleport(new Position(2332, 4645)); await dialogue(participants, [ - player => [Emote.WORRIED, `Oh , be quiet...`], + romeo => [Emote.WORRIED, `This is pretty scary...`], ], { multi: true }); - await schedule(2); - await dialogue(participants, [ - romeo => [Emote.WORRIED, `This is pretty scary...`], + player => [Emote.WORRIED, `Oh , be quiet...`], ], { multi: true }); // player.interfaceState.openWidget(241, { @@ -182,7 +215,7 @@ export const romeoDialogue: npcInteractionActionHandler[] = [ // player.interfaceState.closeWidget('chatbox'); - player.outgoingPackets.playWidgetAnimation(widgets.blankScreen, 0, fadeInAnimation); // Fade in + player.outgoingPackets.playWidgetAnimation(widgets.fade, 0, fadeInAnimation); // Fade in player.outgoingPackets.setMinimapState(MinimapState.NORMAL); // TODO move this until after the cutscene player.instance = null; @@ -201,38 +234,5 @@ export const romeoDialogue: npcInteractionActionHandler[] = [ // player.interfaceState.openWidget(widgets.blankScreen, { slot: 'screen' }); // await schedule(3); // player.outgoingPackets.playWidgetAnimation(widgets.blankScreen, 0, 2115); // Fade out - }, -]; - -const moreInfo = () => { - return (options, tag_MORE_INFO) => [ - `Where can I find Juliet?`, [ - player => [Emote.WONDERING, `Where can I find Juliet?`], - romeo => [Emote.WONDERING, `Why do you ask?`], - player => [Emote.ANGRY, `So that I can try and find her for you!`], - romeo => [Emote.WONDERING, `Ah yes....quite right. Hmmm, let me think now.`], - romeo => [Emote.WONDERING, `She may still be locked away at her Father's house on the sest vide of Warrock.`], - romeo => [Emote.HAPPY, `Oh, I remember how she loved it when I would sing up to her balcony! She would reward me with her own personal items...`], - player => [Emote.WONDERING, `What, she just gave you her stuff?`], - romeo => [Emote.GENERIC, `Well, not exactly give...more like 'throw with considerable force'...she's always a kidder that Juliet!`], - goto('tag_MORE_INFO') - ], - `Is there anything else you can tell me about Juliet?`, [ - player => [Emote.WONDERING, `Is there anything else you can tell me about Juliet?`], - romeo => [Emote.HAPPY, `Oh, there is so much to tell...she is my true love, we intend to spend together forever...I can tell you so much about her..`], - player => [Emote.GENERIC, `Great!`], romeo => [Emote.WONDERING, `Ermmm.....`], - romeo => [Emote.WONDERING, `So much can I tell you...`], - player => [Emote.HAPPY, `Yes..`], - romeo => [Emote.WONDERING, `So much to tell...why, where do I start!`], - player => [Emote.GENERIC, `Yes..yes! Please go on...don't let me interrupt...`], - romeo => [Emote.WONDERING, `Ermmm.....`], - romeo => [Emote.WONDERING, `...`], - player => [Emote.WONDERING, `You can't remember can you?`], - romeo => [Emote.SAD, `Not a thing sorry....`], - goto('tag_MORE_INFO') - ], - `Ok, thanks.`, [ - player => [Emote.GENERIC, `Ok, thanks.`] - ] - ]; -}; + } +} From d7046c9223f8a6cc4f0d4280ac1ea5f08a2a3aa5 Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Wed, 14 Apr 2021 22:02:48 +0200 Subject: [PATCH 15/30] rename permanent dialogue packet --- src/game-engine/net/outbound-packets.ts | 29 ++++++++++++------- .../world/actor/player/interface-state.ts | 7 +++-- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/game-engine/net/outbound-packets.ts b/src/game-engine/net/outbound-packets.ts index a107bc01c..a8a4eccaf 100644 --- a/src/game-engine/net/outbound-packets.ts +++ b/src/game-engine/net/outbound-packets.ts @@ -217,16 +217,6 @@ export class OutboundPackets { this.queue(packet); } - // Text dialogs = 356, 359, 363, 368, 374 - // Item dialogs = 519 - // Statements (no click to continue) = 210, 211, 212, 213, 214 - public showChatboxWidget(widgetId: number): void { - const packet = new Packet(208); - packet.put(widgetId, 'SHORT'); - - this.queue(packet); - } - public setWidgetNpcHead(widgetId: number, childId: number, modelId: number): void { const packet = new Packet(160); packet.put(modelId, 'SHORT', 'LITTLE_ENDIAN'); @@ -462,7 +452,24 @@ export class OutboundPackets { this.queue(packet); } - public showChatDialogue(widgetId: number): void { + /** + * Show or replace dialogue in the chatbox slot, resetting all the other slots client-side + * This widget will close when walking + * @param widgetId The widget ID + */ + public showChatboxWidget(widgetId: number): void { + const packet = new Packet(208); + packet.put(widgetId, 'SHORT'); + + this.queue(packet); + } + + /** + * Show permanent widget in the dialogue slot that preserves all widgets, even previously opened chatbox widgets + * This widget will NOT close when walking + * @param widgetId The widget ID + */ + public showPermanentDialogueWidget(widgetId: number): void { const packet = new Packet(185); packet.put(widgetId, 'SHORT'); this.queue(packet); diff --git a/src/game-engine/world/actor/player/interface-state.ts b/src/game-engine/world/actor/player/interface-state.ts index cd42ba873..9b2c714ae 100644 --- a/src/game-engine/world/actor/player/interface-state.ts +++ b/src/game-engine/world/actor/player/interface-state.ts @@ -108,12 +108,12 @@ export class InterfaceState { public openChatOverlayWidget(widgetId: number): void { this._chatOverlayWidget = widgetId; - this.player.outgoingPackets.showChatDialogue(widgetId); + this.player.outgoingPackets.showPermanentDialogueWidget(widgetId); } public closeChatOverlayWidget(): void { this._chatOverlayWidget = null; - this.player.outgoingPackets.showChatDialogue(-1); + this.player.outgoingPackets.showPermanentDialogueWidget(-1); } public openScreenOverlayWidget(widgetId: number): void { @@ -145,6 +145,7 @@ export class InterfaceState { return; } + // Permanent chatbox widgets must be closed like this, or else they show forever if (widget.slot === 'chatbox' && widget.multi) { this.closeChatOverlayWidget(); } @@ -250,7 +251,7 @@ export class InterfaceState { } else if(slot === 'chatbox') { if(multi) { // Dialogue Widget - packets.showChatDialogue(widgetId); + packets.showPermanentDialogueWidget(widgetId); } else { // Chatbox Widget packets.showChatboxWidget(widgetId); From 346f1a6ba7984c7bfc0f669dfc2c2bba5726bb3a Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Thu, 15 Apr 2021 03:11:49 +0200 Subject: [PATCH 16/30] finish romeo and juliet last cutscene --- src/game-engine/world/actor/actor.ts | 23 ++++ src/game-engine/world/actor/dialogue.ts | 10 +- .../world/actor/player/cutscenes.ts | 31 ++++- .../world/actor/player/interface-state.ts | 34 ++++- src/game-engine/world/actor/player/player.ts | 20 ++- src/game-engine/world/config/animation-ids.ts | 4 +- .../romeo-and-juliet-quest.plugin.ts | 7 +- .../quests/romeo-and-juliet/romeo-dialogue.ts | 126 ++++++++++++------ 8 files changed, 191 insertions(+), 64 deletions(-) diff --git a/src/game-engine/world/actor/actor.ts b/src/game-engine/world/actor/actor.ts index 74fe8508f..c7b576316 100644 --- a/src/game-engine/world/actor/actor.ts +++ b/src/game-engine/world/actor/actor.ts @@ -13,6 +13,7 @@ import { world } from '@engine/game-server'; import { WorldInstance } from '@engine/world/instances'; import { Player } from '@engine/world/actor/player/player'; import { ActionCancelType, ActionPipeline } from '@engine/world/action'; +import { World } from '@engine/world'; /** * Handles an actor within the game world. @@ -106,6 +107,28 @@ export abstract class Actor { return true; } + /** + * Wait for actor movement to finish in a Promise-like fashion. + * @param timeoutInTicks How long to wait for in ticks before rejecting the promise + */ + public async isIdle(timeoutInTicks = 100): Promise { + return new Promise((resolve, reject) => { + let ticksChecked = 0; + const checkIsMoving = setInterval(() => { + ticksChecked++; + if (!this.walkingQueue.moving()) { + resolve(); + clearInterval(checkIsMoving); + } + + if (ticksChecked >= timeoutInTicks) { + reject(); + clearInterval(checkIsMoving); + } + }, World.TICK_LENGTH) + }); + } + public follow(target: Actor): void { this.face(target, false, false, false); this.metadata['following'] = target; diff --git a/src/game-engine/world/actor/dialogue.ts b/src/game-engine/world/actor/dialogue.ts index 0de8a594e..75764ac43 100644 --- a/src/game-engine/world/actor/dialogue.ts +++ b/src/game-engine/world/actor/dialogue.ts @@ -162,7 +162,6 @@ function parseDialogueFunctionArgs(func: Function): string[] { export type DialogueTree = (Function | DialogueFunction | GoToAction)[]; export interface AdditionalOptions { - closeOnWalk?: boolean; permanent?: boolean; multi?: boolean; } @@ -641,6 +640,7 @@ async function runParsedDialogue(player: Player, dialogueTree: ParsedDialogueTre export async function dialogue(participants: (Player | NpcParticipant)[], dialogueTree: DialogueTree, additionalOptions?: AdditionalOptions): Promise { const player = participants.find(p => p instanceof Player) as Player; + const multi = additionalOptions?.multi == null ? false : additionalOptions.multi; if(!player) { throw new Error('Player instance not provided to dialogue action.'); @@ -667,10 +667,14 @@ export async function dialogue(participants: (Player | NpcParticipant)[], dialog try { await run(); - player.interfaceState.closeAllSlots(); + if (!multi) { + player.interfaceState.closeAllSlots(); + } return true; } catch(error) { - player.interfaceState.closeAllSlots(); + if (!multi) { + player.interfaceState.closeAllSlots(); + } logger.warn(error); return false; } diff --git a/src/game-engine/world/actor/player/cutscenes.ts b/src/game-engine/world/actor/player/cutscenes.ts index 03766334d..b4fdcb4ae 100644 --- a/src/game-engine/world/actor/player/cutscenes.ts +++ b/src/game-engine/world/actor/player/cutscenes.ts @@ -1,7 +1,13 @@ -import { Player } from '@engine/world/actor/player/player'; +import { defaultPlayerTabWidgets, Player } from '@engine/world/actor/player/player'; import { Position } from '@engine/world/position'; -import { MinimapState } from '@engine/config/minimap-state'; +import { tabIndex } from '@engine/world/actor/player/interface-state'; +// Cutscene widgets +export const cutsceneWidgets = [ + tabIndex['friends'], + tabIndex['ignores'], + tabIndex['logout'] +]; /** * Various camera options for cutscenes. @@ -24,7 +30,6 @@ export interface CameraOptions { * Controls a game cutscene for a specific player. */ export class Cutscene { - public readonly player: Player; private _cameraX: number; private _cameraY: number; @@ -40,11 +45,30 @@ export class Cutscene { public constructor(player: Player, options?: CameraOptions) { this.player = player; + // Disable tab area when initializing a cutscene + this.hideTabs(); + if(options) { this.setCamera(options); } } + hideTabs() { + Object.keys(tabIndex).forEach(tab => { + if (cutsceneWidgets.indexOf(tabIndex[tab]) === -1) { + this.player.outgoingPackets.sendTabWidget(tabIndex[tab], null); + } + }); + } + + resetTabs() { + defaultPlayerTabWidgets.forEach((widgetId: number, tabIndex: number) => { + if (widgetId !== -1) { + this.player.setSidebarWidget(tabIndex, widgetId); + } + }); + } + /** * Sets the cutscene camera to the specified options. * @param options The camera options to use. @@ -104,6 +128,7 @@ export class Cutscene { */ public endCutscene(): void { this.player.outgoingPackets.resetCamera(); + this.resetTabs(); this.player.cutscene = null; } diff --git a/src/game-engine/world/actor/player/interface-state.ts b/src/game-engine/world/actor/player/interface-state.ts index 9b2c714ae..47ed2dbfc 100644 --- a/src/game-engine/world/actor/player/interface-state.ts +++ b/src/game-engine/world/actor/player/interface-state.ts @@ -2,6 +2,10 @@ import { Player } from '@engine/world/actor/player/player'; import { ItemContainer } from '@engine/world/items/item-container'; import { lastValueFrom, Subject } from 'rxjs'; import { filter, take } from 'rxjs/operators'; +import { widgets } from '@engine/config'; +import { animationIds } from '@engine/world/config/animation-ids'; +import { schedule } from '@engine/world/task'; +import { MinimapState } from '@engine/config/minimap-state'; export type TabType = 'combat' | 'skills' | 'quests' | 'inventory' | 'equipment' | 'prayers' | @@ -73,7 +77,6 @@ export interface WidgetClosedEvent { * Control's a Player's Game Interface state. */ export class InterfaceState { - public readonly tabs: { [key: string]: Widget | null }; public readonly widgetSlots: { [key: string]: Widget | null }; public readonly closed: Subject = new Subject(); @@ -126,6 +129,35 @@ export class InterfaceState { this.player.outgoingPackets.showScreenOverlayWidget(-1); } + /** + * Fades out the screen and leaves it blank. Call fade in to return it to normal. + */ + public async fadeOutScreen(): Promise { + return new Promise(resolve => { + this.openWidget(widgets.fade, { slot: 'screen' }); + schedule(3).then(() => { + resolve(); + }); + }); + } + + /** + * Fades in the screen. Only works if fade out was called previously + */ + public async fadeInScreen(): Promise { + return new Promise(resolve => { + this.player.outgoingPackets.playWidgetAnimation(widgets.fade, 0, animationIds.fadeIn); + schedule(2).then(() => { + this.closeAllSlots(); + resolve(); + }); + }); + } + + public setMinimapState(minimapState: MinimapState) { + this.player.outgoingPackets.setMinimapState(minimapState); + } + public async widgetClosed(slot: GameInterfaceSlot): Promise { return await lastValueFrom(this.closed.pipe( filter(event => event.widget.slot === slot)).pipe(take(1))); diff --git a/src/game-engine/world/actor/player/player.ts b/src/game-engine/world/actor/player/player.ts index 6a7faf30a..1efd2890f 100644 --- a/src/game-engine/world/actor/player/player.ts +++ b/src/game-engine/world/actor/player/player.ts @@ -53,6 +53,7 @@ import { PlayerQuest, QuestKey } from '@engine/config/quest-config'; import { Quest } from '@engine/world/actor/player/quest'; import { regionChangeActionFactory } from '@engine/world/action/region-change.action'; import { MusicPlayerMode } from '@plugins/music/music-tab.plugin'; +import { ItemConfig } from '@runejs/filestore'; export const playerOptions: { option: string, index: number, placement: 'TOP' | 'BOTTOM' }[] = [ @@ -489,17 +490,22 @@ export class Player extends Actor { } if(questData.onComplete.questCompleteWidget.itemId) { - this.outgoingPackets.updateWidgetModel1(widgets.questReward, 3, - filestore.configStore.itemStore.getItem(questData.onComplete.questCompleteWidget.itemId)?.model2d?.widgetModel); + const questCompleteItem = filestore.configStore.itemStore.getItem(questData.onComplete.questCompleteWidget.itemId); + if (questCompleteItem) { + this.outgoingPackets.updateWidgetModel1(widgets.questReward, 3, questCompleteItem.model2d?.widgetModel); + this.outgoingPackets.setWidgetModelRotationAndZoom(widgets.questReward, 3, + questCompleteItem.model2d?.rotationY || 0, + questCompleteItem.model2d?.rotationX || 0, + questCompleteItem.model2d?.zoom / 2 || 0); + } } else if(questData.onComplete.questCompleteWidget.modelId) { this.outgoingPackets.updateWidgetModel1(widgets.questReward, 3, questData.onComplete.questCompleteWidget.modelId); + this.outgoingPackets.setWidgetModelRotationAndZoom(widgets.questReward, 3, + questData.onComplete.questCompleteWidget.modelRotationX || 0, + questData.onComplete.questCompleteWidget.modelRotationY || 0, + questData.onComplete.questCompleteWidget.modelZoom || 0); } - this.outgoingPackets.setWidgetModelRotationAndZoom(widgets.questReward, 3, - questData.onComplete.questCompleteWidget.modelRotationX || 0, - questData.onComplete.questCompleteWidget.modelRotationY || 0, - questData.onComplete.questCompleteWidget.modelZoom || 0); - this.interfaceState.openWidget(widgets.questReward, { slot: 'screen', multi: false diff --git a/src/game-engine/world/config/animation-ids.ts b/src/game-engine/world/config/animation-ids.ts index 09727f791..b93b151f5 100644 --- a/src/game-engine/world/config/animation-ids.ts +++ b/src/game-engine/world/config/animation-ids.ts @@ -25,5 +25,7 @@ export const animationIds = { stab: 412, slash: 451, armBlock: 424 - } + }, + fadeOut: 3541, + fadeIn: 2115 }; diff --git a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts index 81949f505..c7d859cc9 100644 --- a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts +++ b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts @@ -38,11 +38,8 @@ export default { journalHandler, onComplete: { questCompleteWidget: { - rewardText: ['5 Quest Points'], - itemId: 1891, - modelZoom: 240, - modelRotationY: 180, - modelRotationX: 180 + rewardText: [], + itemId: 756 } } }) diff --git a/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts b/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts index eb9b8e149..5ea1285b9 100644 --- a/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts @@ -1,7 +1,5 @@ import { randomBetween } from '@engine/util/num'; import { dialogue, Emote, execute, goto } from '@engine/world/actor/dialogue'; -import { widgets } from '@engine/config'; -import { schedule } from '@engine/world/task'; import { Position } from '@engine/world/position'; import { MinimapState } from '@engine/config/minimap-state'; import { WorldInstance } from '@engine/world/instances'; @@ -9,6 +7,9 @@ import uuidv4 from 'uuid/v4'; import { QuestDialogueHandler } from '@engine/config/quest-config'; import { Player } from '@engine/world/actor/player/player'; import { Npc } from '@engine/world/actor/npc/npc'; +import { Cutscene } from '@engine/world/actor/player/cutscenes'; +import { world } from '@engine/game-server'; +import { schedule } from '@engine/world/task'; const moreInfo = () => { return (options, tag_MORE_INFO) => [ @@ -183,56 +184,93 @@ export const romeoDialogueHandler: QuestDialogueHandler = { return; } - const fadeOutAnimation = 3541; - const fadeInAnimation = 2115; + await player.interfaceState.fadeOutScreen(); + player.interfaceState.setMinimapState(MinimapState.BLACK); + player.instance = new WorldInstance(uuidv4()); + player.teleport(new Position(2333, 4646)); + const cutsceneRomeo = await world.spawnNpc('rs:romeo', new Position(2333, 4645), 'NORTH', 0, player.instance.instanceId); + player.face(cutsceneRomeo, true, false, false); + + player.cutscene = new Cutscene(player); - // player.outgoingPackets.playWidgetAnimation(widgets.fade, 0, fadeOutAnimation); - player.interfaceState.openWidget(widgets.fade, { slot: 'screen' }); + await dialogue(participants, [ + romeo => [Emote.WORRIED, `This is pretty scary...`], + player => [Emote.WORRIED, `Oh , be quiet...`], + ], { + multi: true + }); - await schedule(4); + await player.interfaceState.fadeInScreen(); - player.outgoingPackets.setMinimapState(MinimapState.BLACK); - player.instance = new WorldInstance(uuidv4()); - player.teleport(new Position(2332, 4645)); + player.cutscene.snapCameraTo(2330, 4641); + player.cutscene.lookAt(2333, 4645, 300); + + await schedule(2); await dialogue(participants, [ - romeo => [Emote.WORRIED, `This is pretty scary...`], - ], { multi: true }); + player => [Emote.HAPPY, `We're here. Look, Juliet is over there!`] + ]); + + player.cutscene.snapCameraTo(2334, 4647, 375, 10, 0); + player.cutscene.lookAt(2322, 4639, 300, 10, 0); + + await schedule(6); + + player.cutscene.snapCameraTo(2324, 4644, 250, 6, 0); + player.cutscene.lookAt(2321, 4640, 250, 6, 0); + + await schedule(10); + + player.cutscene.snapCameraTo(2330, 4641); + player.cutscene.lookAt(2333, 4645, 300); + + await schedule(1); await dialogue(participants, [ - player => [Emote.WORRIED, `Oh , be quiet...`], - ], { multi: true }); - - // player.interfaceState.openWidget(241, { - // slot: 'chatbox', - // multi: true - // }); - // const widgetClosedEvent = await player.interfaceState.widgetClosed('chatbox'); - // - // if(widgetClosedEvent.data === undefined) { - // throw new Error('Dialogue Cancelled.'); - // } - - // player.interfaceState.closeWidget('chatbox'); - - player.outgoingPackets.playWidgetAnimation(widgets.fade, 0, fadeInAnimation); // Fade in - player.outgoingPackets.setMinimapState(MinimapState.NORMAL); // TODO move this until after the cutscene - player.instance = null; + player => [Emote.HAPPY, `You go over to her...and I'll go and wait over here...`], + romeo => [Emote.WORRIED, `Ohhh, ok then...`], + ]); + + player.cutscene.snapCameraTo(2322, 4639, 300); + player.cutscene.lookAt(2324, 4644, 300); + + cutsceneRomeo.walkingQueue.valid = true; + cutsceneRomeo.walkingQueue.add(2329, 4645); + cutsceneRomeo.walkingQueue.add(2327, 4645); + cutsceneRomeo.walkingQueue.add(2325, 4644); + cutsceneRomeo.walkingQueue.add(2323, 4643); + cutsceneRomeo.face(new Position(2322, 4642), false, false, true); + await cutsceneRomeo.isIdle(); + const cutscenePhillipa = await world.spawnNpc('rs:phillipa', new Position(2331, 4645), 'SOUTHWEST', 0, player.instance.instanceId); + cutscenePhillipa.walkingQueue.valid = true; + cutscenePhillipa.walkingQueue.add(2327, 4645); + cutscenePhillipa.walkingQueue.add(2325, 4644); + cutscenePhillipa.face(cutsceneRomeo, false, false, false); + + const finalParticipants = [player, { npc, key: 'romeo' }, { npc: 'rs:phillipa', key: 'phillipa' }]; + await dialogue(finalParticipants, [ + romeo => [Emote.WORRIED, `Hey...Juliet...`], + romeo => [Emote.WORRIED, `Juliet...?`], + romeo => [Emote.SAD, `Oh dear...you seem to be dead.`], + phillipa => [Emote.HAPPY, `Hi Romeo...I'm Phillipa!`], + ]); + + cutsceneRomeo.face(cutscenePhillipa, false, false, false); + + await dialogue(finalParticipants, [ + romeo => [Emote.HAPPY, `Wow! You're a fox!`], + phillipa => [Emote.HAPPY, `It's a shame about Juliet...but perhaps we can meet up later?`], + romeo => [Emote.HAPPY, `Who's Juliet?`], + ]); + + await player.interfaceState.fadeOutScreen(); + player.cutscene.endCutscene(); + player.teleport(new Position(3212, 3424)); + player.instance = null; + player.interfaceState.setMinimapState(MinimapState.NORMAL); + await player.interfaceState.fadeInScreen(); - // await dialogue(participants, [ - // romeo => [Emote.WORRIED, `This is pretty scary...`] - // ], { multi: true }); - // - // player.interfaceState.closeChatOverlayWidget(); - - // await dialogue(participants, [ - // player => [Emote.WORRIED, `Oh , be quiet...`] - // ], { multi: true }); - // - // player.interfaceState.closeChatOverlayWidget(); - // player.interfaceState.openWidget(widgets.blankScreen, { slot: 'screen' }); - // await schedule(3); - // player.outgoingPackets.playWidgetAnimation(widgets.blankScreen, 0, 2115); // Fade out + player.setQuestProgress('rs:romeo_and_juliet', 'complete'); } } From 97eb534f3372e54e487a0145736184a26b9e20be Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Thu, 15 Apr 2021 10:01:00 +0200 Subject: [PATCH 17/30] refactor dialogue handlers --- src/game-engine/config/quest-config.ts | 16 ++-- src/game-engine/world/actor/player/player.ts | 2 +- .../romeo-and-juliet/draul-leptoc-dialogue.ts | 65 +++++++------- .../father-lawrence-dialogue.ts | 26 +++--- .../romeo-and-juliet/juliet-dialogue.ts | 17 ++-- .../romeo-and-juliet/phillipa-dialogue.ts | 17 ++-- .../romeo-and-juliet-quest.plugin.ts | 87 ++++--------------- 7 files changed, 88 insertions(+), 142 deletions(-) diff --git a/src/game-engine/config/quest-config.ts b/src/game-engine/config/quest-config.ts index 19a6beee6..b5aeadbf4 100644 --- a/src/game-engine/config/quest-config.ts +++ b/src/game-engine/config/quest-config.ts @@ -11,7 +11,7 @@ export type QuestStageHandler = { }; export type QuestDialogueHandler = { - [key in QuestKey]?: (player: Player, npc: Npc) => void | Promise; + [key in QuestKey]?: ((player: Player, npc: Npc) => void | Promise) | number; }; export type QuestJournalHandler = { @@ -44,21 +44,19 @@ export class PlayerQuest { export function questDialogueActionFactory(questId: string, npcDialogueHandler: QuestDialogueHandler): npcInteractionActionHandler { return async({ player, npc }) => { const quest = player.getQuest(questId); - if(!quest) { - return; + const progress = quest.progress; + + let dialogueHandler = npcDialogueHandler[progress]; + if (dialogueHandler != null && typeof dialogueHandler === 'number') { + dialogueHandler = npcDialogueHandler[dialogueHandler] } - const progress = quest.progress; - const dialogueHandler = npcDialogueHandler[progress]; - if(dialogueHandler) { + if (dialogueHandler != null && typeof dialogueHandler === 'function') { try { await dialogueHandler(player, npc); } catch(e) { logger.error(e); } - - // TODO v this doesn't belong here!! v - // await handleTutorial(player); } }; } diff --git a/src/game-engine/world/actor/player/player.ts b/src/game-engine/world/actor/player/player.ts index 1efd2890f..f435e5357 100644 --- a/src/game-engine/world/actor/player/player.ts +++ b/src/game-engine/world/actor/player/player.ts @@ -438,7 +438,7 @@ export class Player extends Actor { */ public getQuest(questId: string): PlayerQuest { let playerQuest = this.quests.find(quest => quest.questId === questId); - if(!playerQuest) { + if (!playerQuest) { playerQuest = new PlayerQuest(questId); this.quests.push(playerQuest); } diff --git a/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts b/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts index 62611d97d..2d229d9c4 100644 --- a/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts @@ -1,62 +1,63 @@ -import { npcInteractionActionHandler } from '@engine/world/action/npc-interaction.action'; import { dialogue, Emote } from '@engine/world/actor/dialogue'; import { questItems } from './romeo-and-juliet-quest.plugin'; +import { QuestDialogueHandler } from '@engine/config/quest-config'; +import { Player } from '@engine/world/actor/player/player'; +import { Npc } from '@engine/world/actor/npc/npc'; -export const draulLeptocDialogue: npcInteractionActionHandler[] = [ - async (details) => { - const { player, npc } = details; - const participants = [player, { npc, key: 'draul_leptoc' }]; +export const draulDialogueHandler: QuestDialogueHandler = { + 0: async (player: Player, npc: Npc) => { + const participants = [player, { npc, key: 'draul' }]; await dialogue(participants, [ - draul_leptoc => [Emote.ANGRY, `What are you doing here? Snooping around...`], + draul => [Emote.ANGRY, `What are you doing here? Snooping around...`], player => [Emote.GENERIC, `Oh...just looking around...`], - draul_leptoc => [Emote.ANGRY, `Just looking around! This is MY house! You might have at least 'ASKED' to view my considerably well appointed abode...but no, you've just burst in with all the elegance of a Troll at a tea party.`], + draul => [Emote.ANGRY, `Just looking around! This is MY house! You might have at least 'ASKED' to view my considerably well appointed abode...but no, you've just burst in with all the elegance of a Troll at a tea party.`], player => [Emote.GENERIC, `I can see that you're busy ranting so I'll just nip off and investigate a bit.`] ]); }, - async (details) => { - const { player, npc } = details; - const participants = [player, { npc, key: 'draul_leptoc' }]; + + 1: async (player: Player, npc: Npc) => { + const participants = [player, { npc, key: 'draul' }]; await dialogue(participants, [ - draul_leptoc => [Emote.ANGRY, `What are you doing here? Snooping around... `], + draul => [Emote.ANGRY, `What are you doing here? Snooping around... `], options => [ `I've come to see Juliet on Romeo's behalf.`, [ player => [Emote.GENERIC, `I've come to see Juliet on Romeo's behalf.`], - draul_leptoc => [Emote.ANGRY, `What...what...Romeo! Why that good for nothing swine...he's always trying to get the affections of my daughter..that soppy, half brained nincompoop won't ever have the heart of my daughter.`], - draul_leptoc => [Emote.ANGRY, `She deserves someone of character, wit and repose.`], + draul => [Emote.ANGRY, `What...what...Romeo! Why that good for nothing swine...he's always trying to get the affections of my daughter..that soppy, half brained nincompoop won't ever have the heart of my daughter.`], + draul => [Emote.ANGRY, `She deserves someone of character, wit and repose.`], player => [Emote.WONDERING, `What's so wrong about Romeo?`], - draul_leptoc => [Emote.ANGRY, `Wrong! What's wrong with him...have you actually talked to him? He's nothing but a dim witted upperclass twit, totally useless.`], - draul_leptoc => [Emote.ANGRY, `If he threw a stone at the ground, he'd probably miss! He's totally invisible when it's raining because he's so wet!`], - draul_leptoc => [Emote.ANGRY, `If you started with what's right with him, you'd have much less to consider!`], + draul => [Emote.ANGRY, `Wrong! What's wrong with him...have you actually talked to him? He's nothing but a dim witted upperclass twit, totally useless.`], + draul => [Emote.ANGRY, `If he threw a stone at the ground, he'd probably miss! He's totally invisible when it's raining because he's so wet!`], + draul => [Emote.ANGRY, `If you started with what's right with him, you'd have much less to consider!`], player => [Emote.WONDERING, `Well, I admit, he's probably not the sharpest knife in the cutlery draw...`], - draul_leptoc => [Emote.ANGRY, `Sharp? I've seen keener wit in root vegetables. Anyway, stop changing the subject. Get out of here and don't think you can sneak up those stairs to see Juliet, because I'll catch you and then you'll be for it!`], + draul => [Emote.ANGRY, `Sharp? I've seen keener wit in root vegetables. Anyway, stop changing the subject. Get out of here and don't think you can sneak up those stairs to see Juliet, because I'll catch you and then you'll be for it!`], player => [Emote.WONDERING, `That seems a bit harsh....`], - draul_leptoc => [Emote.ANGRY, `Harsh but fair I think you'll find...now get OUT!`] + draul => [Emote.ANGRY, `Harsh but fair I think you'll find...now get OUT!`] ], `I've just come to have a chat with Juliet.`, [ player => [Emote.GENERIC, `I've just come to have a chat with Juliet.`], - draul_leptoc => [Emote.ANGRY, `What on earth about? I hope you're not in cahoots with that good for nothing Romeo!`], + draul => [Emote.ANGRY, `What on earth about? I hope you're not in cahoots with that good for nothing Romeo!`], player => [Emote.WONDERING, `Err..no of course not....why would I be?`], - draul_leptoc => [Emote.SKEPTICAL, `He's been trying to wooo my daughter for an age. Up until now she's had the good sense to just ignore him. I just don't know what's gotten into her recently so that she would give him the time of day.`], + draul => [Emote.SKEPTICAL, `He's been trying to wooo my daughter for an age. Up until now she's had the good sense to just ignore him. I just don't know what's gotten into her recently so that she would give him the time of day.`], player => [Emote.SHOCKED, `Well, love is mysterious! Perhaps one day someone may even learn to love you!`], - draul_leptoc => [Emote.ANGRY, `What! Someone may fall in love with me...what are you trying to insinuate?`], + draul => [Emote.ANGRY, `What! Someone may fall in love with me...what are you trying to insinuate?`], player => [Emote.WONDERING, `Err...Nothing....I guess I'd better be going now...`] ], `Oh...just looking around...`, [ player => [Emote.GENERIC, `Oh...just looking around...`], - draul_leptoc => [Emote.ANGRY, `Just looking around! This is MY house! You might have at least 'ASKED' to view my considerably well appointed abode...but no, you've just burst in with all the elegance of a Troll at a tea party.`], + draul => [Emote.ANGRY, `Just looking around! This is MY house! You might have at least 'ASKED' to view my considerably well appointed abode...but no, you've just burst in with all the elegance of a Troll at a tea party.`], player => [Emote.GENERIC, `I can see that you're busy ranting so I'll just nip off and investigate a bit.`] ] ], ]); }, - async (details) => { - const { player, npc } = details; - const participants = [player, { npc, key: 'draul_leptoc' }]; + + 2: async (player: Player, npc: Npc) => { + const participants = [player, { npc, key: 'draul' }]; await dialogue(participants, [ - draul_leptoc => [Emote.ANGRY, `What are you doing in my house? Up to no good I shouldn't wonder!`], + draul => [Emote.ANGRY, `What are you doing in my house? Up to no good I shouldn't wonder!`], player => [Emote.GENERIC, `Just a small chore for Juliet, you do have a lovely daughter in her sir.`], - draul_leptoc => [Emote.HAPPY, `Oh...why, thank you...I've always tried to my best...`], - draul_leptoc => [Emote.ANGRY, ` ...Hang on! Enough of that smiley talk. I have a daughter and I know what she's like. Don't even think of carrying on anything behind my back, I have the eyes of a hawk, nothing gets past me!`] + draul => [Emote.HAPPY, `Oh...why, thank you...I've always tried to my best...`], + draul => [Emote.ANGRY, ` ...Hang on! Enough of that smiley talk. I have a daughter and I know what she's like. Don't even think of carrying on anything behind my back, I have the eyes of a hawk, nothing gets past me!`] ]); if (!player.hasItemInInventory(questItems.julietLetter.gameId)) { @@ -65,12 +66,12 @@ export const draulLeptocDialogue: npcInteractionActionHandler[] = [ await dialogue(participants, [ item => [questItems.julietLetter.gameId, `Sir Draul notices the message!`], - draul_leptoc => [Emote.ANGRY, `Hey! What's that in your hands...looks like a message to me...with Juliet's barely legible scrawl on it...`], + draul => [Emote.ANGRY, `Hey! What's that in your hands...looks like a message to me...with Juliet's barely legible scrawl on it...`], player => [Emote.SHOCKED, `Yes, yes, that's probably why I can't read it!`], player => [Emote.SHOCKED, `Sorry, I mean, that's right sir. I'm just popping to the shops to get some groceries for Juliet.`], player => [Emote.WONDERING, `Right, have to be off now...thanks...`], - draul_leptoc => [Emote.ANGRY, `Groceries!`], - draul_leptoc => [Emote.ANGRY, `Groceries!...at a time like this, does that girl know what she's putting me through!`] + draul => [Emote.ANGRY, `Groceries!`], + draul => [Emote.ANGRY, `Groceries!...at a time like this, does that girl know what she's putting me through!`] ]); }, -]; +}; diff --git a/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts b/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts index 28cdf584b..f5d3774e8 100644 --- a/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts @@ -1,5 +1,7 @@ -import { npcInteractionActionHandler } from '@engine/world/action/npc-interaction.action'; import { dialogue, Emote, goto } from '@engine/world/actor/dialogue'; +import { QuestDialogueHandler } from '@engine/config/quest-config'; +import { Player } from '@engine/world/actor/player/player'; +import { Npc } from '@engine/world/actor/npc/npc'; export const lawrenceOptions = () => { return (options, tag_OPTIONS) => [ @@ -26,22 +28,16 @@ export const lawrenceOptions = () => { player => [Emote.GENERIC, `Ok, thanks`] ] ]; -} +}; -export const fatherLawrenceDialogue: npcInteractionActionHandler[] = [ - async (details) => { - const { player, npc } = details; - const participants = [player, { npc, key: 'father_lawrence' }]; +export const lawrenceDialogueHandler: QuestDialogueHandler = { + 0: async (player: Player, npc: Npc) => { + const participants = [player, { npc, key: 'lawrence' }]; await dialogue(participants, [ - father_lawrence => [Emote.GENERIC, `Hello adventurer, do you seek a quest?`], + lawrence => [Emote.GENERIC, `Hello adventurer, do you seek a quest?`], lawrenceOptions() ]); }, - async details => { - const { player, npc } = details; - const participants = [player, { npc, key: 'father_lawrence' }]; - await dialogue(participants, [ - - ]); - } -]; + 1: 0, + 2: 0 +}; diff --git a/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts b/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts index 16d526131..79b3f4e8e 100644 --- a/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts @@ -1,10 +1,13 @@ -import { npcInteractionActionHandler } from '@engine/world/action/npc-interaction.action'; import { dialogue, Emote } from '@engine/world/actor/dialogue'; import { questItems } from './romeo-and-juliet-quest.plugin'; +import { QuestDialogueHandler } from '@engine/config/quest-config'; +import { Player } from '@engine/world/actor/player/player'; +import { Npc } from '@engine/world/actor/npc/npc'; -export const julietDialogue: npcInteractionActionHandler[] = [ - async details => { - const { player, npc } = details; +export const julietDialogueHandler: QuestDialogueHandler = { + 0: 0, // TODO you can actually start the quest by talking to juliet first + + 1: async (player: Player, npc: Npc) => { const participants = [player, { npc, key: 'juliet' }]; await dialogue(participants, [ player => [Emote.GENERIC, `Juliet, I come from Romeo. He begs me to tell you that he cares still.`], @@ -28,8 +31,8 @@ export const julietDialogue: npcInteractionActionHandler[] = [ ]); } }, - async details => { - const { player, npc } = details; + + 2: async (player: Player, npc: Npc) => { const participants = [player, { npc, key: 'juliet' }]; const hasLetter = player.hasItemInInventory(questItems.julietLetter.gameId) || player.hasItemInBank(questItems.julietLetter.gameId); @@ -61,4 +64,4 @@ export const julietDialogue: npcInteractionActionHandler[] = [ } } } -]; +}; diff --git a/src/plugins/quests/romeo-and-juliet/phillipa-dialogue.ts b/src/plugins/quests/romeo-and-juliet/phillipa-dialogue.ts index fec17a6a3..3982fac07 100644 --- a/src/plugins/quests/romeo-and-juliet/phillipa-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/phillipa-dialogue.ts @@ -1,9 +1,10 @@ -import { npcInteractionActionHandler } from '@engine/world/action/npc-interaction.action'; import { dialogue, Emote } from '@engine/world/actor/dialogue'; +import { QuestDialogueHandler } from '@engine/config/quest-config'; +import { Player } from '@engine/world/actor/player/player'; +import { Npc } from '@engine/world/actor/npc/npc'; -export const phillipaDialogue: npcInteractionActionHandler[] = [ - async (details) => { - const { player, npc } = details; +export const phillipaDialogueHandler: QuestDialogueHandler = { + 0: async (player: Player, npc: Npc) => { const participants = [player, { npc, key: 'phillipa' }]; await dialogue(participants, [ player => [Emote.GENERIC, `Hello`], @@ -11,8 +12,10 @@ export const phillipaDialogue: npcInteractionActionHandler[] = [ phillipa => [Emote.GENERIC, `He'd do it you know... he's ever so dashing, and cavalier, in a wet blanket sort of way.`] ]); }, - async details => { - const { player, npc } = details; + + 1: 0, + + 2: async (player: Player, npc: Npc) => { const participants = [player, { npc, key: 'phillipa' }]; await dialogue(participants, [ phillipa => [Emote.HAPPY, `Oh, hello. Juliet has told me what you're doing for her and Romeo, and I have to say I'm very grateful to you. Juliet deserves a bit of happiness in her life.`], @@ -21,4 +24,4 @@ export const phillipaDialogue: npcInteractionActionHandler[] = [ player => [Emote.GENERIC, `Oh, thanks. I like to do my cupid bit.`] ]); } -]; +} diff --git a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts index c7d859cc9..5c9c4f69e 100644 --- a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts +++ b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts @@ -4,18 +4,18 @@ import { NpcInteractionActionHook } from '@engine/world/action/npc-interaction.a import { findItem } from '@engine/config'; // Dialogues -import { phillipaDialogue } from './phillipa-dialogue'; -import { julietDialogue } from './juliet-dialogue'; +import { phillipaDialogueHandler } from './phillipa-dialogue'; +import { julietDialogueHandler } from './juliet-dialogue'; import { romeoDialogueHandler } from './romeo-dialogue'; -import { draulLeptocDialogue } from './draul-leptoc-dialogue'; -import { fatherLawrenceDialogue } from './father-lawrence-dialogue'; +import { draulDialogueHandler } from './draul-leptoc-dialogue'; +import { lawrenceDialogueHandler } from './father-lawrence-dialogue'; import { questDialogueActionFactory } from '@engine/config/quest-config'; const journalHandler = { 0: `I can start this quest by speaking to Romeo in Varrock central square by the fountain.`, - 1: `I have agreed to find Juliet for Romeo and tell her how he feels.\nFor some reason he can't just do this by himself.\n + 1: `I have agreed to find Juliet for Romeo and tell her how he feels. For some reason he can't just do this by himself. I should go and speak to Juliet. I can find her west of Varrock.`, 2: `I have agreed to find Juliet for Romeo and tell her how he feels. For some reason he can't just do this himself. @@ -27,11 +27,13 @@ export const questItems = { julietLetter: findItem('rs:juliet_letter') } +export const questKey = 'rs:romeo_and_juliet'; + export default { - pluginId: 'rs:romeo_and_juliet', + pluginId: questKey, quests: [ new Quest({ - id: 'rs:romeo_and_juliet', + id: questKey, questTabId: 37, name: `Romeo & Juliet`, points: 5, @@ -45,91 +47,34 @@ export default { }) ], hooks: [{ - // TODO you can actually start the quest by talking to Juliet first type: 'npc_interaction', npcs: 'rs:romeo', options: 'talk-to', walkTo: true, - handler: questDialogueActionFactory('rs:romeo_and_juliet', romeoDialogueHandler) + handler: questDialogueActionFactory(questKey, romeoDialogueHandler) }, { type: 'npc_interaction', - questRequirement: { - questId: 'rs:romeo_and_juliet', - stage: 0 - }, - npcs: 'rs:draul_leptoc', - options: 'talk-to', - walkTo: true, - handler: draulLeptocDialogue[0] - }, { - type: 'npc_interaction', - questRequirement: { - questId: 'rs:romeo_and_juliet', - stage: 1 - }, - npcs: 'rs:draul_leptoc', - options: 'talk-to', - walkTo: true, - handler: draulLeptocDialogue[1] - }, { - type: 'npc_interaction', - questRequirement: { - questId: 'rs:romeo_and_juliet', - stage: 2 - }, - npcs: 'rs:draul_leptoc', - options: 'talk-to', - walkTo: true, - handler: draulLeptocDialogue[2] - }, { - type: 'npc_interaction', - questRequirement: { - questId: 'rs:romeo_and_juliet', - stages: [0, 1, 2] - }, - npcs: 'rs:father_lawrence', - options: 'talk-to', - walkTo: true, - handler: fatherLawrenceDialogue[0] - }, { - type: 'npc_interaction', - questRequirement: { - questId: 'rs:romeo_and_juliet', - stage: 1 - }, npcs: 'rs:juliet', options: 'talk-to', walkTo: true, - handler: julietDialogue[0] + handler: questDialogueActionFactory(questKey, julietDialogueHandler) }, { type: 'npc_interaction', - questRequirement: { - questId: 'rs:romeo_and_juliet', - stages: [0, 1] - }, - npcs: 'rs:phillipa', + npcs: 'rs:draul_leptoc', options: 'talk-to', walkTo: true, - handler: phillipaDialogue[0] + handler: questDialogueActionFactory(questKey, draulDialogueHandler) }, { type: 'npc_interaction', - questRequirement: { - questId: 'rs:romeo_and_juliet', - stage: 2 - }, npcs: 'rs:phillipa', options: 'talk-to', walkTo: true, - handler: phillipaDialogue[1] + handler: questDialogueActionFactory(questKey, phillipaDialogueHandler) }, { type: 'npc_interaction', - questRequirement: { - questId: 'rs:romeo_and_juliet', - stage: 2 - }, - npcs: 'rs:juliet', + npcs: 'rs:father_lawrence', options: 'talk-to', walkTo: true, - handler: julietDialogue[1] + handler: questDialogueActionFactory(questKey, lawrenceDialogueHandler) }] }; From 3cd446dd699c8ccf8b602a650a4c72bdb9ccf046 Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Thu, 15 Apr 2021 11:33:14 +0200 Subject: [PATCH 18/30] fix setting face direction after npc spawn --- src/game-engine/world/index.ts | 1 + src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/game-engine/world/index.ts b/src/game-engine/world/index.ts index ec76298e7..77ed794f5 100644 --- a/src/game-engine/world/index.ts +++ b/src/game-engine/world/index.ts @@ -285,6 +285,7 @@ export class World { position, movementRadius, face), instanceId); await this.registerNpc(npc); + await schedule(1); return npc; } diff --git a/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts b/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts index 5ea1285b9..731109c9a 100644 --- a/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts @@ -242,11 +242,11 @@ export const romeoDialogueHandler: QuestDialogueHandler = { cutsceneRomeo.face(new Position(2322, 4642), false, false, true); await cutsceneRomeo.isIdle(); - const cutscenePhillipa = await world.spawnNpc('rs:phillipa', new Position(2331, 4645), 'SOUTHWEST', 0, player.instance.instanceId); + const cutscenePhillipa = await world.spawnNpc('rs:phillipa', new Position(2333, 4645), 'SOUTHWEST', 0, player.instance.instanceId); cutscenePhillipa.walkingQueue.valid = true; cutscenePhillipa.walkingQueue.add(2327, 4645); cutscenePhillipa.walkingQueue.add(2325, 4644); - cutscenePhillipa.face(cutsceneRomeo, false, false, false); + cutscenePhillipa.face(cutsceneRomeo.position, false, false, true); const finalParticipants = [player, { npc, key: 'romeo' }, { npc: 'rs:phillipa', key: 'phillipa' }]; await dialogue(finalParticipants, [ From b0e6f4f08f136c33cb38fba02ef01cd0a2634fb7 Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Thu, 15 Apr 2021 11:33:20 +0200 Subject: [PATCH 19/30] fix dialogues --- .../romeo-and-juliet/draul-leptoc-dialogue.ts | 8 ++++++-- .../romeo-and-juliet/father-lawrence-dialogue.ts | 14 +++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts b/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts index 2d229d9c4..af1a0b84e 100644 --- a/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts @@ -29,7 +29,9 @@ export const draulDialogueHandler: QuestDialogueHandler = { draul => [Emote.ANGRY, `If he threw a stone at the ground, he'd probably miss! He's totally invisible when it's raining because he's so wet!`], draul => [Emote.ANGRY, `If you started with what's right with him, you'd have much less to consider!`], player => [Emote.WONDERING, `Well, I admit, he's probably not the sharpest knife in the cutlery draw...`], - draul => [Emote.ANGRY, `Sharp? I've seen keener wit in root vegetables. Anyway, stop changing the subject. Get out of here and don't think you can sneak up those stairs to see Juliet, because I'll catch you and then you'll be for it!`], + // TODO next 2 lines are shown as 1 in the OSRS wiki transcript + draul => [Emote.ANGRY, `Sharp? I've seen keener wit in root vegetables. Anyway, stop changing the subject.`], + draul => [Emote.ANGRY, `Get out of here and don't think you can sneak up those stairs to see Juliet, because I'll catch you and then you'll be for it!`], player => [Emote.WONDERING, `That seems a bit harsh....`], draul => [Emote.ANGRY, `Harsh but fair I think you'll find...now get OUT!`] ], @@ -37,7 +39,9 @@ export const draulDialogueHandler: QuestDialogueHandler = { player => [Emote.GENERIC, `I've just come to have a chat with Juliet.`], draul => [Emote.ANGRY, `What on earth about? I hope you're not in cahoots with that good for nothing Romeo!`], player => [Emote.WONDERING, `Err..no of course not....why would I be?`], - draul => [Emote.SKEPTICAL, `He's been trying to wooo my daughter for an age. Up until now she's had the good sense to just ignore him. I just don't know what's gotten into her recently so that she would give him the time of day.`], + // TODO next 2 lines are shown as 1 in the OSRS wiki transcript + draul => [Emote.SKEPTICAL, `He's been trying to wooo my daughter for an age. Up until now she's had the good sense to just ignore him.`], + draul => [Emote.SKEPTICAL, `I just don't know what's gotten into her recently so that she would give him the time of day.`], player => [Emote.SHOCKED, `Well, love is mysterious! Perhaps one day someone may even learn to love you!`], draul => [Emote.ANGRY, `What! Someone may fall in love with me...what are you trying to insinuate?`], player => [Emote.WONDERING, `Err...Nothing....I guess I'd better be going now...`] diff --git a/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts b/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts index f5d3774e8..78476d1d8 100644 --- a/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts @@ -7,21 +7,21 @@ export const lawrenceOptions = () => { return (options, tag_OPTIONS) => [ `I am always looking for a quest.`, [ player => [Emote.HAPPY, `I am always looking for a quest.`], - father_lawrence => [Emote.GENERIC, `Well, I see poor Romeo wandering around the square. I think he may need help.`], - father_lawrence => [Emote.SAD, `I was helping him and Juliet to meet, but it became impossible.`], - father_lawrence => [Emote.HAPPY, `I am sure he can use some help.`], + lawrence => [Emote.GENERIC, `Well, I see poor Romeo wandering around the square. I think he may need help.`], + lawrence => [Emote.SAD, `I was helping him and Juliet to meet, but it became impossible.`], + lawrence => [Emote.HAPPY, `I am sure he can use some help.`], goto('tag_OPTIONS') ], `No, I prefer just to kill things.`, [ player => [Emote.HAPPY, `No, I prefer just to kill things.`], - father_lawrence => [Emote.HAPPY, `That's a fine career in these lands. There is more that needs killing every day.`], + lawrence => [Emote.HAPPY, `That's a fine career in these lands. There is more that needs killing every day.`], goto('tag_OPTIONS') ], `Can you recommend a good bar?`, [ player => [Emote.GENERIC, `Can you recommend a good bar?`], - father_lawrence => [Emote.ANGRY, `Drinking will be the death of you.`], - father_lawrence => [Emote.GENERIC, `But the Blue Moon in the city is cheap enough.`], - father_lawrence => [Emote.HAPPY, `And providing you buy one drink an hour they let you stay all night.`], + lawrence => [Emote.ANGRY, `Drinking will be the death of you.`], + lawrence => [Emote.GENERIC, `But the Blue Moon in the city is cheap enough.`], + lawrence => [Emote.HAPPY, `And providing you buy one drink an hour they let you stay all night.`], goto('tag_OPTIONS') ], `Ok, thanks`, [ From 54da115818d2336045985688cea5a220908fbecf Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Thu, 15 Apr 2021 11:52:16 +0200 Subject: [PATCH 20/30] add cutscene options --- .../world/actor/player/cutscenes.ts | 77 +++++++++++++------ .../quests/romeo-and-juliet/romeo-dialogue.ts | 5 +- 2 files changed, 55 insertions(+), 27 deletions(-) diff --git a/src/game-engine/world/actor/player/cutscenes.ts b/src/game-engine/world/actor/player/cutscenes.ts index b4fdcb4ae..20e83d113 100644 --- a/src/game-engine/world/actor/player/cutscenes.ts +++ b/src/game-engine/world/actor/player/cutscenes.ts @@ -1,6 +1,7 @@ import { defaultPlayerTabWidgets, Player } from '@engine/world/actor/player/player'; import { Position } from '@engine/world/position'; import { tabIndex } from '@engine/world/actor/player/interface-state'; +import { MinimapState } from '@engine/config/minimap-state'; // Cutscene widgets export const cutsceneWidgets = [ @@ -25,6 +26,13 @@ export interface CameraOptions { lookAcceleration?: number; } +/** + * Cutscene options. + */ +export interface CutsceneOptions { + hideMinimap?: boolean; + hideTabs?: boolean; +} /** * Controls a game cutscene for a specific player. @@ -41,41 +49,40 @@ export class Cutscene { private _lookHeight: number; private _lookMovementSpeed: number; private _lookAcceleration: number; + private cutsceneOptions: CutsceneOptions = { + hideTabs: false, + hideMinimap: false + } - public constructor(player: Player, options?: CameraOptions) { + public constructor(player: Player, cutsceneOptions: CutsceneOptions = {}, cameraOptions?: CameraOptions) { this.player = player; + this.setCutsceneOptions(cutsceneOptions); - // Disable tab area when initializing a cutscene - this.hideTabs(); - - if(options) { - this.setCamera(options); + if (cameraOptions) { + this.setCamera(cameraOptions); } } - hideTabs() { - Object.keys(tabIndex).forEach(tab => { - if (cutsceneWidgets.indexOf(tabIndex[tab]) === -1) { - this.player.outgoingPackets.sendTabWidget(tabIndex[tab], null); - } - }); - } + setCutsceneOptions(cutsceneOptions: CutsceneOptions) { + this.cutsceneOptions = { ...this.cutsceneOptions, ...cutsceneOptions }; + const { hideMinimap, hideTabs } = this.cutsceneOptions; - resetTabs() { - defaultPlayerTabWidgets.forEach((widgetId: number, tabIndex: number) => { - if (widgetId !== -1) { - this.player.setSidebarWidget(tabIndex, widgetId); - } - }); + if (hideMinimap) { + this.player.interfaceState.setMinimapState(MinimapState.BLACK); + } + + if (hideTabs) { + this.hideTabs(); + } } /** * Sets the cutscene camera to the specified options. - * @param options The camera options to use. + * @param cameraOptions The camera options to use. */ - public setCamera(options: CameraOptions): void { + public setCamera(cameraOptions: CameraOptions): void { const { cameraX, cameraY, cameraHeight, cameraMovementSpeed, cameraAcceleration, - lookX, lookY, lookHeight, lookMovementSpeed, lookAcceleration } = options; + lookX, lookY, lookHeight, lookMovementSpeed, lookAcceleration } = cameraOptions; if(cameraX && cameraY) { this.snapCameraTo(cameraX, cameraY, cameraHeight || 400, @@ -128,7 +135,15 @@ export class Cutscene { */ public endCutscene(): void { this.player.outgoingPackets.resetCamera(); - this.resetTabs(); + + if (this.cutsceneOptions.hideTabs) { + this.resetTabs(); + } + + if (this.cutsceneOptions.hideMinimap) { + this.player.interfaceState.setMinimapState(MinimapState.NORMAL); + } + this.player.cutscene = null; } @@ -172,4 +187,20 @@ export class Cutscene { public get lookAcceleration(): number { return this._lookAcceleration; } + + private hideTabs() { + Object.keys(tabIndex).forEach(tab => { + if (cutsceneWidgets.indexOf(tabIndex[tab]) === -1) { + this.player.outgoingPackets.sendTabWidget(tabIndex[tab], null); + } + }); + } + + private resetTabs() { + defaultPlayerTabWidgets.forEach((widgetId: number, tabIndex: number) => { + if (widgetId !== -1) { + this.player.setSidebarWidget(tabIndex, widgetId); + } + }); + } } diff --git a/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts b/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts index 731109c9a..c85344dc9 100644 --- a/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts @@ -185,14 +185,12 @@ export const romeoDialogueHandler: QuestDialogueHandler = { } await player.interfaceState.fadeOutScreen(); - player.interfaceState.setMinimapState(MinimapState.BLACK); player.instance = new WorldInstance(uuidv4()); player.teleport(new Position(2333, 4646)); + player.cutscene = new Cutscene(player, { hideTabs: true, hideMinimap: true }); const cutsceneRomeo = await world.spawnNpc('rs:romeo', new Position(2333, 4645), 'NORTH', 0, player.instance.instanceId); player.face(cutsceneRomeo, true, false, false); - player.cutscene = new Cutscene(player); - await dialogue(participants, [ romeo => [Emote.WORRIED, `This is pretty scary...`], player => [Emote.WORRIED, `Oh , be quiet...`], @@ -268,7 +266,6 @@ export const romeoDialogueHandler: QuestDialogueHandler = { player.cutscene.endCutscene(); player.teleport(new Position(3212, 3424)); player.instance = null; - player.interfaceState.setMinimapState(MinimapState.NORMAL); await player.interfaceState.fadeInScreen(); player.setQuestProgress('rs:romeo_and_juliet', 'complete'); From f40643da467a0d6251a28212e1ef404d57ac054e Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Fri, 16 Apr 2021 04:31:10 +0200 Subject: [PATCH 21/30] add proper parent npc spawn for juliet --- data/config/npc-spawns/varrock/varrock-general.json | 2 +- data/config/npcs/varrock.json | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/data/config/npc-spawns/varrock/varrock-general.json b/data/config/npc-spawns/varrock/varrock-general.json index 4c9d3e645..ad949132b 100644 --- a/data/config/npc-spawns/varrock/varrock-general.json +++ b/data/config/npc-spawns/varrock/varrock-general.json @@ -61,7 +61,7 @@ "movement_radius": 5 }, { - "npc": "rs:juliet", + "npc": "rs:juliet:0", "spawn_x": 3158, "spawn_y": 3425, "spawn_level": 1, diff --git a/data/config/npcs/varrock.json b/data/config/npcs/varrock.json index 60a235bba..466d10e5a 100644 --- a/data/config/npcs/varrock.json +++ b/data/config/npcs/varrock.json @@ -36,7 +36,13 @@ "game_id": 3325 }, "rs:juliet": { - "game_id": 637 + "game_id": 637, + "variations": [ + { + "suffix": "0", + "game_id": 3323 + } + ] }, "rs:father_lawrence": { "game_id": 640 From c1fcf751d228578288443943d5ca890a1ec07172 Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Fri, 16 Apr 2021 21:39:17 +0200 Subject: [PATCH 22/30] calculate juliet's visibility based on player settings --- src/game-engine/util/varbits.ts | 43 ++++++++++++------- src/game-engine/world/actor/player/player.ts | 1 + .../romeo-and-juliet/juliet-dialogue.ts | 12 +++++- .../romeo-and-juliet-quest.plugin.ts | 14 ++++-- 4 files changed, 51 insertions(+), 19 deletions(-) diff --git a/src/game-engine/util/varbits.ts b/src/game-engine/util/varbits.ts index 9b955d351..bba859535 100644 --- a/src/game-engine/util/varbits.ts +++ b/src/game-engine/util/varbits.ts @@ -1,32 +1,45 @@ import { filestore } from '@engine/game-server'; -import { findNpc } from '@engine/config'; -import { logger } from '@runejs/core'; -import { Npc } from '@engine/world/actor/npc/npc'; -import { Player } from '@engine/world/actor/player/player'; - -const varbitMasks = []; /** - * Returns the index to morph actor/object into, based on set config - * @param varbitId - * @param playerConfig - * @return index to morph into + * Calculate the varbit masks + * @returns an array of varbit masks */ -export function getVarbitMorphIndex(varbitId, playerConfig) { - if(varbitMasks.length === 0) { +export function calculateVarbitMasks() { + const varbitMasks = []; + if (varbitMasks.length === 0) { let i = 2; for (let i_7_ = 0; i_7_ < 32; i_7_++) { varbitMasks[i_7_] = -1 + i; i += i; } } + return varbitMasks; +} + +/** + * Returns the index to morph actor/object into, based on set config + * @param varbitId + * @param playerConfig + * @return index to morph into + */ +export function getVarbitMorphIndex(varbitId, playerConfig) { + const varbitMasks = calculateVarbitMasks(); const varbitDefinition = filestore.configStore.varbitStore.getVarbit(varbitId); const mostSignificantBit = varbitDefinition.mostSignificantBit; const configId = varbitDefinition.index; const leastSignificantBit = varbitDefinition.leastSignificantBit; - // TODO: Unknown - const i_8_ = varbitMasks[mostSignificantBit - leastSignificantBit]; + const varbitMask = varbitMasks[mostSignificantBit - leastSignificantBit]; const configValue = playerConfig && playerConfig[configId] ? playerConfig[configId] : 0; - return ((configValue) >> leastSignificantBit & i_8_); + return ((configValue) >> leastSignificantBit & varbitMask); +} + +/** + * Returns the setting/config index from a varbitId + * @param varbitId + * @return the config ID for the varbit + */ +export function getVarbitConfigId(varbitId) { + const varbitDefinition = filestore.configStore.varbitStore.getVarbit(varbitId); + return varbitDefinition.index; } diff --git a/src/game-engine/world/actor/player/player.ts b/src/game-engine/world/actor/player/player.ts index 29c45426e..052564f1f 100644 --- a/src/game-engine/world/actor/player/player.ts +++ b/src/game-engine/world/actor/player/player.ts @@ -946,6 +946,7 @@ export class Player extends Actor { /** * Returns the morphed NPC details for a specific player based on his client settings * @param originalNpc + * @returns the morphed NPC, or null if there is none */ public getMorphedNpcDetails(originalNpc: Npc) { if (!originalNpc.childrenIds) { diff --git a/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts b/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts index 79b3f4e8e..5d0b37606 100644 --- a/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts @@ -1,8 +1,18 @@ import { dialogue, Emote } from '@engine/world/actor/dialogue'; -import { questItems } from './romeo-and-juliet-quest.plugin'; +import { questItems, questKey } from './romeo-and-juliet-quest.plugin'; import { QuestDialogueHandler } from '@engine/config/quest-config'; import { Player } from '@engine/world/actor/player/player'; import { Npc } from '@engine/world/actor/npc/npc'; +import { findNpc } from '@engine/config'; +import { getVarbitConfigId } from '@engine/util/varbits'; + +export const calculateJulietVisibility = (player: Player) => { + const julietParentNpc = findNpc('rs:juliet:0'); + const configId = getVarbitConfigId(julietParentNpc.varbitId); + + const hideJulietForPlayer = player.getQuest(questKey).progress === 4 ? 1 : 0; // TODO fix this value + player.outgoingPackets.updateClientConfig(configId, hideJulietForPlayer); +} export const julietDialogueHandler: QuestDialogueHandler = { 0: 0, // TODO you can actually start the quest by talking to juliet first diff --git a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts index 5c9c4f69e..20228d9be 100644 --- a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts +++ b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts @@ -2,14 +2,15 @@ import { Quest } from '@engine/world/actor/player/quest'; import { ContentPlugin } from '@engine/plugins/content-plugin'; import { NpcInteractionActionHook } from '@engine/world/action/npc-interaction.action'; import { findItem } from '@engine/config'; +import { questDialogueActionFactory } from '@engine/config/quest-config'; +import { playerInitActionHandler, PlayerInitActionHook } from '@engine/world/action/player-init.action'; // Dialogues import { phillipaDialogueHandler } from './phillipa-dialogue'; -import { julietDialogueHandler } from './juliet-dialogue'; +import { calculateJulietVisibility, julietDialogueHandler } from './juliet-dialogue'; import { romeoDialogueHandler } from './romeo-dialogue'; import { draulDialogueHandler } from './draul-leptoc-dialogue'; import { lawrenceDialogueHandler } from './father-lawrence-dialogue'; -import { questDialogueActionFactory } from '@engine/config/quest-config'; const journalHandler = { 0: `I can start this quest by speaking to Romeo in @@ -29,6 +30,10 @@ export const questItems = { export const questKey = 'rs:romeo_and_juliet'; +const playerInitHook: playerInitActionHandler = details => { + calculateJulietVisibility(details.player); +}; + export default { pluginId: questKey, quests: [ @@ -46,7 +51,10 @@ export default { } }) ], - hooks: [{ + hooks: [{ + type: 'player_init', + handler: playerInitHook + }, { type: 'npc_interaction', npcs: 'rs:romeo', options: 'talk-to', From 3b5a4581330555d6e01a7c378669bed16acb753b Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Sun, 18 Apr 2021 01:28:40 +0200 Subject: [PATCH 23/30] fix some dialogues --- .../romeo-and-juliet/draul-leptoc-dialogue.ts | 8 +++--- .../romeo-and-juliet/juliet-dialogue.ts | 8 +++++- .../romeo-and-juliet/phillipa-dialogue.ts | 14 ++++++++-- .../quests/romeo-and-juliet/romeo-dialogue.ts | 28 +++++++++---------- 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts b/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts index af1a0b84e..233928341 100644 --- a/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts @@ -8,10 +8,10 @@ export const draulDialogueHandler: QuestDialogueHandler = { 0: async (player: Player, npc: Npc) => { const participants = [player, { npc, key: 'draul' }]; await dialogue(participants, [ - draul => [Emote.ANGRY, `What are you doing here? Snooping around...`], - player => [Emote.GENERIC, `Oh...just looking around...`], - draul => [Emote.ANGRY, `Just looking around! This is MY house! You might have at least 'ASKED' to view my considerably well appointed abode...but no, you've just burst in with all the elegance of a Troll at a tea party.`], - player => [Emote.GENERIC, `I can see that you're busy ranting so I'll just nip off and investigate a bit.`] + draul => [Emote.ANGRY, `What are you doing in my house...why the impertinence...the sheer cheek...how dare you violate my personal lodgings....`], + player => [Emote.GENERIC, `I..I was just looking around....`], + draul => [Emote.ANGRY, `Well get out! Get out....this is my house....and don't go near my daughter Juliet...she's grounded in her room to keep her away from that good for nothing Romeo.`], + player => [Emote.GENERIC, `Yes....sir....`] ]); }, diff --git a/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts b/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts index 5d0b37606..a647e9704 100644 --- a/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts @@ -15,7 +15,13 @@ export const calculateJulietVisibility = (player: Player) => { } export const julietDialogueHandler: QuestDialogueHandler = { - 0: 0, // TODO you can actually start the quest by talking to juliet first + 0: async (player: Player, npc: Npc) => { + const participants = [player, { npc, key: 'juliet' }]; + await dialogue(participants, [ + juliet => [Emote.SAD, `Romeo, Romeo, wherefore art thou Romeo?`], + text => `She seems to be lost in thought.` + ]); + }, 1: async (player: Player, npc: Npc) => { const participants = [player, { npc, key: 'juliet' }]; diff --git a/src/plugins/quests/romeo-and-juliet/phillipa-dialogue.ts b/src/plugins/quests/romeo-and-juliet/phillipa-dialogue.ts index 3982fac07..f2cb53e72 100644 --- a/src/plugins/quests/romeo-and-juliet/phillipa-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/phillipa-dialogue.ts @@ -5,6 +5,18 @@ import { Npc } from '@engine/world/actor/npc/npc'; export const phillipaDialogueHandler: QuestDialogueHandler = { 0: async (player: Player, npc: Npc) => { + const participants = [player, { npc, key: 'phillipa' }]; + await dialogue(participants, [ + player => [Emote.GENERIC, `Hello, who are you?`], + phillipa => [Emote.HAPPY, ` Hi, I'm Phillipa, Juliet's cousin. I like to keep an eye on her, make sure that dashing young Romeo doesn't just steal her away under our plain old noses!`], + phillipa => [Emote.HAPPY, `He'd do it, you know - he's ever so dashing, and cavalier, in a wet blanket sort of way.`], + player => [Emote.GENERIC, `Romeo? Where would I find him then?`], + phillipa => [Emote.HAPPY, `Well, that's a good question! Who knows where his head's at most of the time? In the clouds, most likely!`], + phillipa => [Emote.HAPPY, `But he's probably chasing the ladies who frequent Varrock market. He does like a bit of kiss chase, so I've heard!`], + ]); + }, + + 1: async (player: Player, npc: Npc) => { const participants = [player, { npc, key: 'phillipa' }]; await dialogue(participants, [ player => [Emote.GENERIC, `Hello`], @@ -13,8 +25,6 @@ export const phillipaDialogueHandler: QuestDialogueHandler = { ]); }, - 1: 0, - 2: async (player: Player, npc: Npc) => { const participants = [player, { npc, key: 'phillipa' }]; await dialogue(participants, [ diff --git a/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts b/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts index c85344dc9..78473c564 100644 --- a/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts @@ -54,37 +54,37 @@ export const romeoDialogueHandler: QuestDialogueHandler = { switch (randomDialog) { case 0: await dialogue(participants, [ - romeo => [Emote.WORRIED, `Blub! Blub...where is my Juliet? Have you seen her?`] + romeo => [Emote.SAD, `Blub! Blub...where is my Juliet? Have you seen her?`] ]); break; case 1: await dialogue(participants, [ - romeo => [Emote.WORRIED, `Looking for a blonde girl, goes by the name of Juliet..quite pretty...haven't seen her have you?`] + romeo => [Emote.SAD, `Looking for a blonde girl, goes by the name of Juliet..quite pretty...haven't seen her have you?`] ]); break; case 2: await dialogue(participants, [ - romeo => [Emote.WORRIED, `Juliet, Juliet, wherefore art thou Juliet? Have you seen my Juliet?`] + romeo => [Emote.SAD, `Juliet, Juliet, wherefore art thou Juliet? Have you seen my Juliet?`] ]); break; case 3: await dialogue(participants, [ - romeo => [Emote.WORRIED, `Oh woe is me that I cannot find my Juliet! You haven't seen Juliet have you?`] + romeo => [Emote.SAD, `Oh woe is me that I cannot find my Juliet! You haven't seen Juliet have you?`] ]); break; case 4: await dialogue(participants, [ - romeo => [Emote.WORRIED, `Sadness surrounds me now that Juliet's father forbids us to meet. Have you seen my Juliet?`] + romeo => [Emote.SAD, `Sadness surrounds me now that Juliet's father forbids us to meet. Have you seen my Juliet?`] ]); break; case 5: await dialogue(participants, [ - romeo => [Emote.WORRIED, `What is to become of me and my darling Juliet, I cannot find her anywhere, have you seen her?`] + romeo => [Emote.SAD, `What is to become of me and my darling Juliet, I cannot find her anywhere, have you seen her?`] ]); break; } @@ -92,13 +92,13 @@ export const romeoDialogueHandler: QuestDialogueHandler = { await dialogue(participants, [ options => [ `Yes, I have seen her actually!`, [ - player => [Emote.GENERIC, `Yes, I have seen her actually!`], - player => [Emote.WONDERING, `At least, I think it was her... Blonde? A bit stressed?`], - romeo => [Emote.SHOCKED, `Golly...yes, yes...you make her sound very interesting!`], - romeo => [Emote.WONDERING, `And I'll bet she's a bit of a fox!`], - player => [Emote.WONDERING, `Well, I guess she could be considered attractive...`], - romeo => [Emote.HAPPY, `I'll bet she is! Wooooooooo!`], - romeo => [Emote.GENERIC, `Sorry, all that jubilation has made me forget what we were talking about.`], + player => [Emote.HAPPY, `Yes, I have seen her actually!`], + player => [Emote.SKEPTICAL, `At least, I think it was her... Blonde? A bit stressed?`], + romeo => [Emote.LAUGH, `Golly...yes, yes...you make her sound very interesting!`], + romeo => [Emote.SKEPTICAL, `And I'll bet she's a bit of a fox!`], + player => [Emote.SKEPTICAL, `Well, I guess she could be considered attractive...`], + romeo => [Emote.LAUGH, `I'll bet she is! Wooooooooo!`], + romeo => [Emote.POMPOUS, `Sorry, all that jubilation has made me forget what we were talking about.`], player => [Emote.GENERIC, `You were asking me about Juliet? You seemed to know her?`], romeo => [Emote.HAPPY, `Oh yes, Juliet!`], romeo => [Emote.HAPPY, `The fox...could you tell her that she is the love of my long and that I life to be with her?`], @@ -116,7 +116,7 @@ export const romeoDialogueHandler: QuestDialogueHandler = { romeo => [Emote.HAPPY, `Oh yeah...what you said...tell her that, it sounds much better! Oh you're so good at this!`] ], `Perhaps I could help to find her for you? `, [ - player => [Emote.WONDERING, `Perhaps I can help find her for you? What does she look like?`], + player => [Emote.HAPPY, `Perhaps I can help find her for you? What does she look like?`], romeo => [Emote.HAPPY, `Oh would you? That would be great! She has this sort of hair...`], player => [Emote.WONDERING, `Hair...check..`], romeo => [Emote.HAPPY, `...and she these...great lips...`], From 847647376c650f5b428f54be444f7d65171a7325 Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Sun, 18 Apr 2021 02:28:52 +0200 Subject: [PATCH 24/30] add apothecary initial dialogue --- .../npc-spawns/varrock/varrock-general.json | 6 +++ data/config/npcs/varrock.json | 3 ++ .../romeo-and-juliet/apothecary-dialogue.ts | 45 +++++++++++++++++++ .../romeo-and-juliet-quest.plugin.ts | 7 +++ 4 files changed, 61 insertions(+) create mode 100644 src/plugins/quests/romeo-and-juliet/apothecary-dialogue.ts diff --git a/data/config/npc-spawns/varrock/varrock-general.json b/data/config/npc-spawns/varrock/varrock-general.json index ad949132b..3a8303b65 100644 --- a/data/config/npc-spawns/varrock/varrock-general.json +++ b/data/config/npc-spawns/varrock/varrock-general.json @@ -47,6 +47,12 @@ "spawn_y": 3485, "movement_radius": 3 }, + { + "npc": "rs:apothecary", + "spawn_x": 3196, + "spawn_y": 3404, + "movement_radius": 3 + }, { "npc": "rs:man", "spawn_x": 3153, diff --git a/data/config/npcs/varrock.json b/data/config/npcs/varrock.json index 466d10e5a..50690f211 100644 --- a/data/config/npcs/varrock.json +++ b/data/config/npcs/varrock.json @@ -44,6 +44,9 @@ } ] }, + "rs:apothecary": { + "game_id": 638 + }, "rs:father_lawrence": { "game_id": 640 }, diff --git a/src/plugins/quests/romeo-and-juliet/apothecary-dialogue.ts b/src/plugins/quests/romeo-and-juliet/apothecary-dialogue.ts new file mode 100644 index 000000000..b681f9d81 --- /dev/null +++ b/src/plugins/quests/romeo-and-juliet/apothecary-dialogue.ts @@ -0,0 +1,45 @@ +import { dialogue, Emote, goto } from '@engine/world/actor/dialogue'; +import { QuestDialogueHandler } from '@engine/config/quest-config'; +import { Player } from '@engine/world/actor/player/player'; +import { Npc } from '@engine/world/actor/npc/npc'; + +export const apothecaryOptions = (player: Player) => { + return (options, tag_OPTIONS) => { + const firstOption = [`Have you got any decent gossip to share?`, [ + player => [Emote.POMPOUS, `Have you got any decent gossip to share?`], + apothecary => [Emote.GENERIC, `Well I hear young Romeo's having a little woman trouble but other than that all's quiet on the eastern front. Can I do something for you?`], + goto('tag_OPTIONS') + ]]; + + const restOptions = [ + `Do you know a potion to make hair fall out?`, [ + player => [Emote.HAPPY, `Do you know a potion to make hair fall out?`], + apothecary => [Emote.HAPPY, `I do indeed. I gave it to my mother. That's why I now live alone.`], + apothecary => [Emote.GENERIC, `But can I do something for you?`], + goto('tag_OPTIONS') + ], + `Have you got any good potions to give away?`, [ + player => [Emote.HAPPY, `Have you got any good potions to give away?`], + apothecary => [Emote.SAD, `Sorry, charity is not my strong point. Do you need anything else?`], + goto('tag_OPTIONS') + ], + `No thanks.`, [ + player => [Emote.VERY_SAD, `No thanks.`] + ] + ]; + + return [...firstOption, ...restOptions]; + }; +}; + +export const apothecaryDialogueHandler: QuestDialogueHandler = { + 0: async (player: Player, npc: Npc) => { + const participants = [player, { npc, key: 'apothecary' }]; + await dialogue(participants, [ + apothecary => [Emote.GENERIC, `I am the Apothecary. I brew potions. Do you need anything specific?`], + apothecaryOptions(player) + ]); + }, + 1: 0, + 2: 0 +}; diff --git a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts index 20228d9be..4a1200240 100644 --- a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts +++ b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts @@ -11,6 +11,7 @@ import { calculateJulietVisibility, julietDialogueHandler } from './juliet-dialo import { romeoDialogueHandler } from './romeo-dialogue'; import { draulDialogueHandler } from './draul-leptoc-dialogue'; import { lawrenceDialogueHandler } from './father-lawrence-dialogue'; +import { apothecaryDialogueHandler } from './apothecary-dialogue'; const journalHandler = { 0: `I can start this quest by speaking to Romeo in @@ -84,5 +85,11 @@ export default { options: 'talk-to', walkTo: true, handler: questDialogueActionFactory(questKey, lawrenceDialogueHandler) + }, { + type: 'npc_interaction', + npcs: 'rs:apothecary', + options: 'talk-to', + walkTo: true, + handler: questDialogueActionFactory(questKey, apothecaryDialogueHandler) }] }; From 9cc71fde5b1c9229424495c34c3710027096c96c Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Sun, 18 Apr 2021 02:45:43 +0200 Subject: [PATCH 25/30] add resident sleeper npcs in the church --- .../npc-spawns/varrock/varrock-general.json | 16 +++++++++++++++- data/config/npcs/varrock.json | 6 ++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/data/config/npc-spawns/varrock/varrock-general.json b/data/config/npc-spawns/varrock/varrock-general.json index 3a8303b65..aa01c9c06 100644 --- a/data/config/npc-spawns/varrock/varrock-general.json +++ b/data/config/npc-spawns/varrock/varrock-general.json @@ -45,7 +45,21 @@ "npc": "rs:father_lawrence", "spawn_x": 3254, "spawn_y": 3485, - "movement_radius": 3 + "movement_radius": 2 + }, + { + "npc": "rs:martina_scorsby", + "spawn_x": 3257, + "spawn_y": 3481, + "movement_radius": 0, + "face": "NORTH" + }, + { + "npc": "rs:jeremy_clerksin", + "spawn_x": 3253, + "spawn_y": 3477, + "movement_radius": 0, + "face": "NORTH" }, { "npc": "rs:apothecary", diff --git a/data/config/npcs/varrock.json b/data/config/npcs/varrock.json index 50690f211..188b86062 100644 --- a/data/config/npcs/varrock.json +++ b/data/config/npcs/varrock.json @@ -50,6 +50,12 @@ "rs:father_lawrence": { "game_id": 640 }, + "rs:martina_scorsby": { + "game_id": 3326 + }, + "rs:jeremy_clerksin": { + "game_id": 3327 + }, "rs:varrock_shop_keeper": { "game_id": 520 }, From 8f0193e77883d4d2aedac9657d5b8b7e33cf1bdb Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Sun, 18 Apr 2021 04:04:54 +0200 Subject: [PATCH 26/30] add stage 2 - 3 --- .../romeo-and-juliet/apothecary-dialogue.ts | 3 +- .../romeo-and-juliet/draul-leptoc-dialogue.ts | 7 ++ .../father-lawrence-dialogue.ts | 11 +- .../romeo-and-juliet/juliet-dialogue.ts | 16 ++- .../romeo-and-juliet/phillipa-dialogue.ts | 3 +- .../romeo-and-juliet-quest.plugin.ts | 7 +- .../quests/romeo-and-juliet/romeo-dialogue.ts | 114 +++++++++++++++++- 7 files changed, 155 insertions(+), 6 deletions(-) diff --git a/src/plugins/quests/romeo-and-juliet/apothecary-dialogue.ts b/src/plugins/quests/romeo-and-juliet/apothecary-dialogue.ts index b681f9d81..c2b5ee450 100644 --- a/src/plugins/quests/romeo-and-juliet/apothecary-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/apothecary-dialogue.ts @@ -41,5 +41,6 @@ export const apothecaryDialogueHandler: QuestDialogueHandler = { ]); }, 1: 0, - 2: 0 + 2: 0, + 3: 0 }; diff --git a/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts b/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts index 233928341..4f5ce0fd3 100644 --- a/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/draul-leptoc-dialogue.ts @@ -78,4 +78,11 @@ export const draulDialogueHandler: QuestDialogueHandler = { draul => [Emote.ANGRY, `Groceries!...at a time like this, does that girl know what she's putting me through!`] ]); }, + + 3: async (player: Player, npc: Npc) => { + const participants = [player, { npc, key: 'draul' }]; + await dialogue(participants, [ + draul => [Emote.ANGRY, `Do you live here? If so, how's about a couple of hundred gold towards the rent eh? Pay your share I say...you don't want to be like that freeloading Romeo!`], + ]); + }, }; diff --git a/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts b/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts index 78476d1d8..71c46a81d 100644 --- a/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts @@ -39,5 +39,14 @@ export const lawrenceDialogueHandler: QuestDialogueHandler = { ]); }, 1: 0, - 2: 0 + 2: 0, + + 3: async (player: Player, npc: Npc) => { + const participants = [player, { npc, key: 'lawrence' }]; + // TODO + await dialogue(participants, [ + lawrence => [Emote.GENERIC, `Hello adventurer, do you seek a quest?`], + lawrenceOptions() + ]); + }, }; diff --git a/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts b/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts index a647e9704..1b636c0b0 100644 --- a/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts @@ -79,5 +79,19 @@ export const julietDialogueHandler: QuestDialogueHandler = { ]); } } - } + }, + + 3: async (player: Player, npc: Npc) => { + const participants = [player, { npc, key: 'juliet' }]; + await dialogue(participants, [ + player => [Emote.HAPPY, `Hi Juliet, I have passed your message on to Romeo..he's scared half out of his wits at the news that your father wants to kill him.`], + juliet => [Emote.SAD, `Yes, unfortunately my father is quite the hunter, you may have seen some the animal head trophies on the wall. And it would be so awful to see Romeo's head up there with them!`], + player => [Emote.SAD, `I know what you mean...`], + player => [Emote.POMPOUS, `...his hair colour will clash terribly with the rest of the decoration.`], + juliet => [Emote.ANGRY, `That's not what I was suggesting at all...`], + player => [Emote.HAPPY, `I know, I know...I was just kidding.`], + player => [Emote.HAPPY, `Anyway, don't worry because I'm on the case. I'm going to get some help from Father Lawrence.`], + juliet => [Emote.HAPPY, `Oh yes, I'm sure that Father Lawrence will come up with a solution. I hope you find him soon.`], + ]); + }, }; diff --git a/src/plugins/quests/romeo-and-juliet/phillipa-dialogue.ts b/src/plugins/quests/romeo-and-juliet/phillipa-dialogue.ts index f2cb53e72..b2dab61b2 100644 --- a/src/plugins/quests/romeo-and-juliet/phillipa-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/phillipa-dialogue.ts @@ -33,5 +33,6 @@ export const phillipaDialogueHandler: QuestDialogueHandler = { phillipa => [Emote.HAPPY, `He always brings a tear to my eyes - tears of happiness at his foolish antics!`], player => [Emote.GENERIC, `Oh, thanks. I like to do my cupid bit.`] ]); - } + }, + 3: 2 } diff --git a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts index 4a1200240..0adc9cd5e 100644 --- a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts +++ b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts @@ -22,7 +22,12 @@ const journalHandler = { 2: `I have agreed to find Juliet for Romeo and tell her how he feels. For some reason he can't just do this himself. I found Juliet on the Western edge of Varrock, and told her about Romeo. She gave me a message to take back. - I should take the message from Juliet to Romeo in Varrock central square.` + I should take the message from Juliet to Romeo in Varrock central square.`, + + 3: `I have agreed to find Juliet for Romeo and tell her how he feels. For some reason he can't just do this himself. + I found Juliet on the Western edge of Varrock, and told her about Romeo. She gave me a message to take back. + I delivered the message to Romeo, and he was sad to hear that Juliet's father opposed their marriage. However, he said that Father Lawrence, might be able to overcome this. + I should find Father Lawrence and see how we can help. I can find him in his church in the north-east of Varrock.` }; export const questItems = { diff --git a/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts b/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts index 78473c564..5ca1e39b8 100644 --- a/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts @@ -1,7 +1,6 @@ import { randomBetween } from '@engine/util/num'; import { dialogue, Emote, execute, goto } from '@engine/world/actor/dialogue'; import { Position } from '@engine/world/position'; -import { MinimapState } from '@engine/config/minimap-state'; import { WorldInstance } from '@engine/world/instances'; import uuidv4 from 'uuid/v4'; import { QuestDialogueHandler } from '@engine/config/quest-config'; @@ -10,6 +9,38 @@ import { Npc } from '@engine/world/actor/npc/npc'; import { Cutscene } from '@engine/world/actor/player/cutscenes'; import { world } from '@engine/game-server'; import { schedule } from '@engine/world/task'; +import { questItems, questKey } from './romeo-and-juliet-quest.plugin'; + +const findingFatherLawrence = () => { + return (options, tag_FATHER_LAWRENCE) => [ + `How are you?`, [ + player => [Emote.POMPOUS, `How are you?`], + romeo => [Emote.SAD, `Not so good my friend...I miss Judi..., Junie..., Joopie...`], + player => [Emote.POMPOUS, `Juliet?`], + romeo => [Emote.SKEPTICAL, `Juliet! I miss Juliet, terribly!`], + player => [Emote.SKEPTICAL, `Hmmm, so I see!`], + goto('tag_FATHER_LAWRENCE') + ], + `Where can I find Father Lawrence?`, [ + player => [Emote.POMPOUS, `Where can I find Father Lawrence?`], + romeo => [Emote.HAPPY, `Lather Fawrence! Oh he's...`], + romeo => [Emote.SKEPTICAL, `You know he's not my 'real' Father don't you?`], + player => [Emote.VERY_SAD, `I think I suspected that he wasn't.`], + romeo => [Emote.HAPPY, `Well anyway...he tells these song, loring bermons...and keeps these here Carrockian vitizens snoring in his church to the East North.`], + goto('tag_FATHER_LAWRENCE') + ], + `Have you heard anything from Juliet?`, [ + player => [Emote.POMPOUS, `Have you heard anything from Juliet?`], + romeo => [Emote.SAD, `Sadly not my friend! And what's worse, her Father has threatened to kill me if he sees me. I mean, that seems a bit harsh!`], + player => [Emote.POMPOUS, `Well, I shouldn't worry too much...you can always run away if you see him...`], + romeo => [Emote.SAD, `I just wish I could remember what he looks like! I live in fear of every man I see!`], + goto('tag_FATHER_LAWRENCE') + ], + `Ok, thanks.`, [ + player => [Emote.GENERIC, `Ok, thanks.`] + ] + ]; +} const moreInfo = () => { return (options, tag_MORE_INFO) => [ @@ -169,6 +200,87 @@ export const romeoDialogueHandler: QuestDialogueHandler = { }, 2: async (player: Player, npc: Npc) => { + const participants = [player, { npc, key: 'romeo' }]; + + const hasLetterInInventory = player.hasItemInInventory(questItems.julietLetter.gameId); + if (!hasLetterInInventory) { + await dialogue(participants, [ + player => [Emote.HAPPY, `Romeo...great news...I've been in touch with Juliet!`], + romeo => [Emote.HAPPY, `Oh great! That is great news! Well done...well done... what a total success!`], + player => [Emote.HAPPY, `Yes, and she gave me a message to give you...`], + romeo => [Emote.HAPPY, `Ohhh great! A message....wow!`], + player => [Emote.HAPPY, `Yes!`], + romeo => [Emote.HAPPY, `A message...oh, I can't wait to read what my dear Juliet has to say....`], + player => [Emote.HAPPY, `I know...it's exciting isn't it...?`], + romeo => [Emote.HAPPY, `Yes...yes...`], + romeo => [Emote.BLANK_STARE, `...`], + romeo => [Emote.SKEPTICAL, `You've lost the message haven't you?`], + player => [Emote.GENERIC, `Yep, haven't got a clue where it is.`] + ]); + return; + } + + const completedDialogue = await dialogue(participants, [ + player => [Emote.HAPPY, `Romeo...great news...I've been in touch with Juliet! She's written a message for you...`], + item => [questItems.julietLetter.gameId, `You hand over Juliet's message to Romeo.`], + romeo => [Emote.HAPPY, `Oh, a message! A message! I've never had a message before...`], + player => [Emote.POMPOUS, `Really?`], + romeo => [Emote.HAPPY, `No, no, not one!`], + romeo => [Emote.POMPOUS, `Oh, well, except for the occasional court summons.`], + romeo => [Emote.HAPPY, `But they're not really 'nice' messages. Not like this one! I'm sure that this message will be lovely.`], + player => [Emote.POMPOUS, `Well are you going to open it or not?`], + romeo => [Emote.HAPPY, `Oh yes, yes, of course! 'Dearest Romeo, I am very pleased that you sent ${player.username} to look for me and to tell me that you still hold affliction...', Affliction! She thinks I'm diseased?`], + player => [Emote.VERY_SAD, `'Affection?'`], + romeo => [Emote.SAD, `Ahh yes...'still hold affection for me. I still feel great affection for you, but unfortunately my Father opposes our marriage.'`], + player => [Emote.SAD, `Oh dear...that doesn't sound too good.`], + romeo => [Emote.HAPPY, `What? '...great affection for you. Father opposes our..'`], + romeo => [Emote.SAD, `'...marriage and will...`], + romeo => [Emote.SHOCKED, `...will kill you if he sees you again!'`], + player => [Emote.SKEPTICAL, `I have to be honest, it's not getting any better...`], + romeo => [Emote.SAD, `'Our only hope is that Father Lawrence, our long time confidant, can help us in some way.'`], + item => [questItems.julietLetter.gameId, `Romeo folds the message away.`], + romeo => [Emote.SAD, `Well, that's it then...we haven't got a chance...`], + player => [Emote.SAD, `What about Father Lawrence?`], + romeo => [Emote.SAD, `...our love is over...the great romance, the life of my love...`], + player => [Emote.POMPOUS, `...or you could speak to Father Lawrence!`], + romeo => [Emote.SAD, `Oh, my aching, breaking, heart...how useless the situation is now...we have no one to turn to...`], + player => [Emote.ANGRY, `FATHER LAWRENCE!`] + ]); + + if (!completedDialogue) { + return; + } + + const slotRemoved = player.removeFirstItem(questItems.julietLetter.gameId); + if (slotRemoved === -1) { + return; + } + + player.setQuestProgress(questKey, 3); + + await dialogue(participants, [ + romeo => [Emote.SHOCKED, `Father Lawrence?`], + // TODO the next 2 lines should be 1 dialogue + romeo => [Emote.HAPPY, `Oh yes, Father Lawrence...he's our long time confidant, he might have a solution!`], + romeo => [Emote.HAPPY, `Yes, yes, you have to go and talk to Lather Fawrence for us and ask him if he's got any suggestions for our predicament?`], + player => [Emote.POMPOUS, `Where can I find Father Lawrence?`], + romeo => [Emote.HAPPY, `Lather Fawrence! Oh he's...`], + romeo => [Emote.SKEPTICAL, `You know he's not my 'real' Father don't you?`], + player => [Emote.VERY_SAD, `I think I suspected that he wasn't.`], + romeo => [Emote.HAPPY, `Well anyway...he tells these song, loring bermons...and keeps these here Carrockian vitizens snoring in his church to the East North.`], + findingFatherLawrence() + ]); + }, + + 3: async (player: Player, npc: Npc) => { + const participants = [player, { npc, key: 'romeo' }]; + await dialogue(participants, [ + player => [Emote.POMPOUS, `Hey again Romeo!`], + moreInfo() + ]); + }, + + 4: async (player: Player, npc: Npc) => { // Placeholder for the cutscene at the end, I was bored so I decided to skip to this one const participants = [player, { npc, key: 'romeo' }]; const cont = await dialogue(participants, [ From 5cd0e39fcf6f1ecb45ce0e53af270c53fd7193de Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Sun, 18 Apr 2021 14:24:39 +0200 Subject: [PATCH 27/30] set player as busy during a cutscene, add partial father lawrence cutscene --- .../npc-spawns/varrock/varrock-general.json | 2 +- .../net/inbound-packets/walk-packet.js | 5 ++++ src/game-engine/world/actor/dialogue.ts | 5 +++- .../world/actor/player/cutscenes.ts | 14 ++++++++-- .../father-lawrence-dialogue.ts | 26 ++++++++++++++++--- .../quests/romeo-and-juliet/romeo-dialogue.ts | 2 +- 6 files changed, 46 insertions(+), 8 deletions(-) diff --git a/data/config/npc-spawns/varrock/varrock-general.json b/data/config/npc-spawns/varrock/varrock-general.json index aa01c9c06..a70c0c7d0 100644 --- a/data/config/npc-spawns/varrock/varrock-general.json +++ b/data/config/npc-spawns/varrock/varrock-general.json @@ -49,7 +49,7 @@ }, { "npc": "rs:martina_scorsby", - "spawn_x": 3257, + "spawn_x": 3256, "spawn_y": 3481, "movement_radius": 0, "face": "NORTH" diff --git a/src/game-engine/net/inbound-packets/walk-packet.js b/src/game-engine/net/inbound-packets/walk-packet.js index ffba42094..0b293e519 100644 --- a/src/game-engine/net/inbound-packets/walk-packet.js +++ b/src/game-engine/net/inbound-packets/walk-packet.js @@ -1,6 +1,11 @@ const walkPacket = (player, packet) => { const { buffer, packetSize, packetId } = packet; + // Don't add to the walking queue if busy. If this poses problems, feel free to move it somewhere else. + if (player.busy) { + return; + } + let size = packetSize; if(packetId === 236) { size -= 14; diff --git a/src/game-engine/world/actor/dialogue.ts b/src/game-engine/world/actor/dialogue.ts index 75764ac43..969ef7590 100644 --- a/src/game-engine/world/actor/dialogue.ts +++ b/src/game-engine/world/actor/dialogue.ts @@ -29,7 +29,8 @@ export enum Emote { BLANK_STARE = 'BLANK_STARE', SINGLE_WORD = 'SINGLE_WORD', EVIL_STARE = 'EVIL_STARE', - LAUGH_EVIL = 'LAUGH_EVIL' + LAUGH_EVIL = 'LAUGH_EVIL', + SLEEPING = 'SLEEPING', } // A big thanks to Dust R I P for all these emotes! @@ -102,6 +103,7 @@ enum EmoteAnimation { EASTER_BUNNY_2LINE = 1825, EASTER_BUNNY_3LINE = 1826, EASTER_BUNNY_4LINE = 1827, + SLEEPING_1LINE = 3321, } const nonLineEmotes = [ Emote.BLANK_STARE, Emote.SINGLE_WORD, Emote.EVIL_STARE, Emote.LAUGH_EVIL ]; @@ -580,6 +582,7 @@ async function runDialogueAction(player: Player, dialogueAction: string | Dialog if(dialogueAction.type === 'NPC') { widgetId = npcWidgetIds[lines.length - 1]; player.outgoingPackets.setWidgetNpcHead(widgetId, 0, npcId as number); + // TODO make a generic way to change the NPC name player.outgoingPackets.updateWidgetString(widgetId, 1, filestore.configStore.npcStore.getNpc(npcId as number).name); } else { diff --git a/src/game-engine/world/actor/player/cutscenes.ts b/src/game-engine/world/actor/player/cutscenes.ts index 20e83d113..99c5b05b4 100644 --- a/src/game-engine/world/actor/player/cutscenes.ts +++ b/src/game-engine/world/actor/player/cutscenes.ts @@ -32,6 +32,7 @@ export interface CameraOptions { export interface CutsceneOptions { hideMinimap?: boolean; hideTabs?: boolean; + setBusy?: boolean; } /** @@ -51,7 +52,8 @@ export class Cutscene { private _lookAcceleration: number; private cutsceneOptions: CutsceneOptions = { hideTabs: false, - hideMinimap: false + hideMinimap: false, + setBusy: true } public constructor(player: Player, cutsceneOptions: CutsceneOptions = {}, cameraOptions?: CameraOptions) { @@ -65,7 +67,7 @@ export class Cutscene { setCutsceneOptions(cutsceneOptions: CutsceneOptions) { this.cutsceneOptions = { ...this.cutsceneOptions, ...cutsceneOptions }; - const { hideMinimap, hideTabs } = this.cutsceneOptions; + const { hideMinimap, hideTabs, setBusy } = this.cutsceneOptions; if (hideMinimap) { this.player.interfaceState.setMinimapState(MinimapState.BLACK); @@ -74,6 +76,10 @@ export class Cutscene { if (hideTabs) { this.hideTabs(); } + + if (setBusy) { + this.player.busy = true; + } } /** @@ -144,6 +150,10 @@ export class Cutscene { this.player.interfaceState.setMinimapState(MinimapState.NORMAL); } + if (this.cutsceneOptions.setBusy) { + this.player.busy = false; + } + this.player.cutscene = null; } diff --git a/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts b/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts index 71c46a81d..e3dfbc1d7 100644 --- a/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts @@ -2,6 +2,7 @@ import { dialogue, Emote, goto } from '@engine/world/actor/dialogue'; import { QuestDialogueHandler } from '@engine/config/quest-config'; import { Player } from '@engine/world/actor/player/player'; import { Npc } from '@engine/world/actor/npc/npc'; +import { Cutscene } from '@engine/world/actor/player/cutscenes'; export const lawrenceOptions = () => { return (options, tag_OPTIONS) => [ @@ -43,10 +44,29 @@ export const lawrenceDialogueHandler: QuestDialogueHandler = { 3: async (player: Player, npc: Npc) => { const participants = [player, { npc, key: 'lawrence' }]; - // TODO await dialogue(participants, [ - lawrence => [Emote.GENERIC, `Hello adventurer, do you seek a quest?`], - lawrenceOptions() + lawrence => [Emote.HAPPY, `''...and let Saradomin light the way for you... '' Urgh!`], + lawrence => [Emote.ANGRY, `Can't you see that I'm in the middle of a Sermon?!`], + player => [Emote.ANGRY, `But Romeo sent me!`], + lawrence => [Emote.ANGRY, `But I'm busy delivering a sermon to my congregation!`], + ]); + + player.cutscene = new Cutscene(player, { hideTabs: false, hideMinimap: false }); + player.cutscene.snapCameraTo(3254, 3486, 330); + player.cutscene.lookAt(3255, 3479, 300); + + const congregation = [player, { npc: 'rs:jeremy_clerksin', key: 'congregation' }] + await dialogue(congregation, [ + congregation => [Emote.SLEEPING, `Zzzzzzzzz`], + player => [Emote.ANGRY, `Yes, well, it certainly seems like you have a captive audience!`] ]); + + player.cutscene.endCutscene(); + + await dialogue(participants, [ + lawrence => [Emote.HAPPY, `Ok, ok...what do you want so I can get rid of you and continue with my sermon?`] + ]); + + // TODO }, }; diff --git a/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts b/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts index 5ca1e39b8..c13041085 100644 --- a/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/romeo-dialogue.ts @@ -276,7 +276,7 @@ export const romeoDialogueHandler: QuestDialogueHandler = { const participants = [player, { npc, key: 'romeo' }]; await dialogue(participants, [ player => [Emote.POMPOUS, `Hey again Romeo!`], - moreInfo() + findingFatherLawrence() ]); }, From 650c99ab346a6c659319f39a12aab19af6b3a0d2 Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Mon, 19 Apr 2021 11:01:26 +0200 Subject: [PATCH 28/30] rename juliet parent/child --- data/config/npc-spawns/varrock/varrock-general.json | 2 +- data/config/npcs/varrock.json | 6 +++--- src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts | 2 +- .../romeo-and-juliet/romeo-and-juliet-quest.plugin.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/data/config/npc-spawns/varrock/varrock-general.json b/data/config/npc-spawns/varrock/varrock-general.json index a70c0c7d0..8479c7c71 100644 --- a/data/config/npc-spawns/varrock/varrock-general.json +++ b/data/config/npc-spawns/varrock/varrock-general.json @@ -81,7 +81,7 @@ "movement_radius": 5 }, { - "npc": "rs:juliet:0", + "npc": "rs:juliet", "spawn_x": 3158, "spawn_y": 3425, "spawn_level": 1, diff --git a/data/config/npcs/varrock.json b/data/config/npcs/varrock.json index 188b86062..a2ca4fcde 100644 --- a/data/config/npcs/varrock.json +++ b/data/config/npcs/varrock.json @@ -36,11 +36,11 @@ "game_id": 3325 }, "rs:juliet": { - "game_id": 637, + "game_id": 3323, "variations": [ { - "suffix": "0", - "game_id": 3323 + "suffix": "visible", + "game_id": 637 } ] }, diff --git a/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts b/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts index 1b636c0b0..934d4689b 100644 --- a/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/juliet-dialogue.ts @@ -7,7 +7,7 @@ import { findNpc } from '@engine/config'; import { getVarbitConfigId } from '@engine/util/varbits'; export const calculateJulietVisibility = (player: Player) => { - const julietParentNpc = findNpc('rs:juliet:0'); + const julietParentNpc = findNpc('rs:juliet'); const configId = getVarbitConfigId(julietParentNpc.varbitId); const hideJulietForPlayer = player.getQuest(questKey).progress === 4 ? 1 : 0; // TODO fix this value diff --git a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts index 0adc9cd5e..875fe40ea 100644 --- a/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts +++ b/src/plugins/quests/romeo-and-juliet/romeo-and-juliet-quest.plugin.ts @@ -68,7 +68,7 @@ export default { handler: questDialogueActionFactory(questKey, romeoDialogueHandler) }, { type: 'npc_interaction', - npcs: 'rs:juliet', + npcs: 'rs:juliet:visible', options: 'talk-to', walkTo: true, handler: questDialogueActionFactory(questKey, julietDialogueHandler) From d0d3ab02b3ea1a79d840df499acadc0fac152891 Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Mon, 19 Apr 2021 11:41:04 +0200 Subject: [PATCH 29/30] add generic way to change npc name in dialogues --- src/game-engine/world/actor/dialogue.ts | 23 ++++++++++--------- .../father-lawrence-dialogue.ts | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/game-engine/world/actor/dialogue.ts b/src/game-engine/world/actor/dialogue.ts index 969ef7590..42ae439d8 100644 --- a/src/game-engine/world/actor/dialogue.ts +++ b/src/game-engine/world/actor/dialogue.ts @@ -107,8 +107,8 @@ enum EmoteAnimation { } const nonLineEmotes = [ Emote.BLANK_STARE, Emote.SINGLE_WORD, Emote.EVIL_STARE, Emote.LAUGH_EVIL ]; -export const playerWidgetIds = [ 64, 65, 66, 67 ]; -export const npcWidgetIds = [ 241, 242, 243, 244 ]; +export const playerWidgetIds = [ 64, 65, 66, 67 ]; // TODO: Non 'click here to continue' player widgets are missing! +export const npcWidgetIds = [ 241, 242, 243, 244 ]; // TODO: Non 'click here to continue' npc widgets are missing! export const optionWidgetIds = [ 228, 230, 232, 234, 235 ]; export const continuableTextWidgetIds = [ 210, 211, 212, 213, 214 ]; export const textWidgetIds = [ 215, 216, 217, 218, 219 ]; @@ -198,6 +198,7 @@ class GoToAction implements DialogueAction { interface ActorDialogueAction extends DialogueAction { animation: number; lines: string[]; + customName?: string; } interface NpcDialogueAction extends ActorDialogueAction { @@ -346,7 +347,7 @@ function parseDialogueTree(player: Player, npcParticipants: NpcParticipant[], di } else { // Player or Npc dialogue. - let dialogueDetails: [ Emote, string ]; + let dialogueDetails: [ Emote, string, string? ]; let npc: Npc | number | string; if(dialogueType !== 'player') { @@ -372,18 +373,19 @@ function parseDialogueTree(player: Player, npcParticipants: NpcParticipant[], di const emote = dialogueDetails[0] as Emote; const text = dialogueDetails[1] as string; + const customName = dialogueDetails[2] as string; const lines = wrapDialogueText(text, 'ACTOR'); const animation = nonLineEmotes.indexOf(emote) !== -1 ? EmoteAnimation[emote] : EmoteAnimation[`${emote}_${lines.length}LINE`]; if(dialogueType !== 'player') { const npcDialogueAction: NpcDialogueAction = { - npcId: npc as number, animation, lines, tag, type: 'NPC' + npcId: npc as number, animation, lines, tag, type: 'NPC', customName }; parsedDialogueTree.push(npcDialogueAction); } else { const playerDialogueAction: PlayerDialogueAction = { - player, animation, lines, tag, type: 'PLAYER' + player, animation, lines, tag, type: 'PLAYER', customName }; parsedDialogueTree.push(playerDialogueAction); @@ -580,15 +582,15 @@ async function runDialogueAction(player: Player, dialogueAction: string | Dialog const animation = actorDialogueAction.animation; if(dialogueAction.type === 'NPC') { + const name = actorDialogueAction.customName || filestore.configStore.npcStore.getNpc(npcId as number).name; widgetId = npcWidgetIds[lines.length - 1]; player.outgoingPackets.setWidgetNpcHead(widgetId, 0, npcId as number); - // TODO make a generic way to change the NPC name - player.outgoingPackets.updateWidgetString(widgetId, 1, - filestore.configStore.npcStore.getNpc(npcId as number).name); + player.outgoingPackets.updateWidgetString(widgetId, 1, name); } else { + const name = actorDialogueAction.customName || player.username; widgetId = playerWidgetIds[lines.length - 1]; player.outgoingPackets.setWidgetPlayerHead(widgetId, 0); - player.outgoingPackets.updateWidgetString(widgetId, 1, player.username); + player.outgoingPackets.updateWidgetString(widgetId, 1, name); } player.outgoingPackets.playWidgetAnimation(widgetId, 0, animation); @@ -678,8 +680,7 @@ export async function dialogue(participants: (Player | NpcParticipant)[], dialog if (!multi) { player.interfaceState.closeAllSlots(); } - logger.warn(error); - return false; + throw error; } } diff --git a/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts b/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts index e3dfbc1d7..b3c23f855 100644 --- a/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts @@ -57,7 +57,7 @@ export const lawrenceDialogueHandler: QuestDialogueHandler = { const congregation = [player, { npc: 'rs:jeremy_clerksin', key: 'congregation' }] await dialogue(congregation, [ - congregation => [Emote.SLEEPING, `Zzzzzzzzz`], + congregation => [Emote.SLEEPING, `Zzzzzzzzz`, `Congregation`], player => [Emote.ANGRY, `Yes, well, it certainly seems like you have a captive audience!`] ]); From d91858e524800e1f729e9eaf502f6a9158377e42 Mon Sep 17 00:00:00 2001 From: Uwe Wiemer Date: Tue, 20 Apr 2021 01:39:49 +0200 Subject: [PATCH 30/30] fix emotes --- .../quests/romeo-and-juliet/father-lawrence-dialogue.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts b/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts index b3c23f855..f000c9700 100644 --- a/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts +++ b/src/plugins/quests/romeo-and-juliet/father-lawrence-dialogue.ts @@ -58,13 +58,13 @@ export const lawrenceDialogueHandler: QuestDialogueHandler = { const congregation = [player, { npc: 'rs:jeremy_clerksin', key: 'congregation' }] await dialogue(congregation, [ congregation => [Emote.SLEEPING, `Zzzzzzzzz`, `Congregation`], - player => [Emote.ANGRY, `Yes, well, it certainly seems like you have a captive audience!`] + player => [Emote.VERY_SAD, `Yes, well, it certainly seems like you have a captive audience!`] ]); player.cutscene.endCutscene(); await dialogue(participants, [ - lawrence => [Emote.HAPPY, `Ok, ok...what do you want so I can get rid of you and continue with my sermon?`] + lawrence => [Emote.WONDERING, `Ok, ok...what do you want so I can get rid of you and continue with my sermon?`] ]); // TODO