• THIS BELOW IS A 500 LINE TEXT WALL, KEEP THAT IN MIND THIS IS THE ENTIRE FILE
  • Hack 1 : Create Your Own Dialogue Set
  • Hack 2: Apply to Multiple NPCs
  • Homework:
  • Lesson: Implenting a Randomized Dialogue System with good UI for adventure Game

    Files Involved:

    - DialogueSystem.js (added)

    - Current Adventure Game (modified)

    Part 1: Dialogue System Module Setup

    The system should:

    • Hold multiple possible lines.
    • Randomize which line gets shown.
    • Use HTML + CSS for a custom visual popup
    Below is the complete code of DialogueSystem.js. This is a module you have to import in your main game engine in order to implent on your adventure game.

    MAKE SURE IT IS IN THE SAME LEVEL AS YOUR OTHER GAME FILES, NOT IN THE GAME ENGINE FOLDER

    THIS BELOW IS A 500 LINE TEXT WALL, KEEP THAT IN MIND THIS IS THE ENTIRE FILE

    // DialogueSystem.js - Fixed version that addresses both issues
    // 1. Unique instances for each NPC to prevent button conflicts
    // 2. Works with Eye of Ender collection
    
    class DialogueSystem {
      constructor(options = {}) {
        // Default dialogue arrays
        this.dialogues = options.dialogues || [
          "You've come far, traveler. The skies whisper your name.",
          "The End holds secrets only the brave dare uncover.",
          "Retrieve the elytra and embrace your destiny!"
        ];
        
        // Create a unique ID for this dialogue system
        this.id = options.id || "dialogue_" + Math.random().toString(36).substr(2, 9);
        
        // Track the last shown dialogue to avoid repetition
        this.lastShownIndex = -1;
        
        // Create necessary DOM elements
        this.dialogueBox = null;
        this.dialogueText = null;
        this.closeBtn = null;
        
        // Sound effect option
        this.enableSound = options.enableSound !== undefined ? options.enableSound : false;
        this.soundUrl = options.soundUrl || "./sounds/dialogue.mp3";
        this.sound = this.enableSound ? new Audio(this.soundUrl) : null;
        
        // Create the dialogue box
        this.createDialogueBox();
        
        // Keep track of whether the dialogue is currently open
        this.isOpen = false;
      }
    
      createDialogueBox() {
        // Create the main dialogue container with unique ID
        this.dialogueBox = document.createElement("div");
        this.dialogueBox.id = "custom-dialogue-box-" + this.id;
        
        // Set styles for the dialogue box
        Object.assign(this.dialogueBox.style, {
          position: "fixed",
          bottom: "100px",
          left: "50%",
          transform: "translateX(-50%)",
          padding: "20px",
          maxWidth: "80%",
          background: "rgba(0, 0, 0, 0.85)",
          color: "#00FFFF",
          fontFamily: "'Press Start 2P', cursive, monospace",
          fontSize: "14px",
          textAlign: "center",
          border: "2px solid #4a86e8",
          borderRadius: "12px",
          zIndex: "9999",
          boxShadow: "0 0 20px rgba(0, 255, 255, 0.7)",
          display: "none"
        });
    
        // Create the avatar container for character portraits
        const avatarContainer = document.createElement("div");
        avatarContainer.id = "dialogue-avatar-" + this.id;
        Object.assign(avatarContainer.style, {
          width: "50px",
          height: "50px",
          marginRight: "15px",
          backgroundSize: "contain",
          backgroundRepeat: "no-repeat",
          backgroundPosition: "center",
          display: "none" // Hidden by default
        });
    
        // Create the header with character name
        const speakerName = document.createElement("div");
        speakerName.id = "dialogue-speaker-" + this.id;
        Object.assign(speakerName.style, {
          fontWeight: "bold",
          color: "#4a86e8",
          marginBottom: "10px",
          fontSize: "16px"
        });
    
        // Create the text content area
        this.dialogueText = document.createElement("div");
        this.dialogueText.id = "dialogue-text-" + this.id;
        Object.assign(this.dialogueText.style, {
          marginBottom: "15px",
          lineHeight: "1.5"
        });
    
        // Create close button
        this.closeBtn = document.createElement("button");
        this.closeBtn.innerText = "Close";
        Object.assign(this.closeBtn.style, {
          marginTop: "15px",
          padding: "10px 20px",
          background: "#4a86e8",
          color: "#fff",
          border: "none",
          borderRadius: "5px",
          cursor: "pointer",
          fontFamily: "'Press Start 2P', cursive, monospace",
          fontSize: "12px"
        });
        
        // Add click handler
        this.closeBtn.onclick = () => {
          this.closeDialogue();
        };
    
        // Create content container to hold text and avatar side by side
        const contentContainer = document.createElement("div");
        contentContainer.style.display = "flex";
        contentContainer.style.alignItems = "flex-start";
        contentContainer.style.marginBottom = "10px";
        contentContainer.appendChild(avatarContainer);
        
        // Create text container for speaker + dialogue
        const textContainer = document.createElement("div");
        textContainer.style.flexGrow = "1";
        textContainer.appendChild(speakerName);
        textContainer.appendChild(this.dialogueText);
        contentContainer.appendChild(textContainer);
    
        // Assemble the dialogue box
        this.dialogueBox.appendChild(contentContainer);
        this.dialogueBox.appendChild(this.closeBtn);
        
        // Add to the document
        document.body.appendChild(this.dialogueBox);
        
        // Also listen for Escape key to close dialogue
        document.addEventListener("keydown", (e) => {
          if (e.key === "Escape" && this.isOpen) {
            this.closeDialogue();
          }
        });
      }
    
      // Show a specific dialogue message
      showDialogue(message, speaker = "", avatarSrc = null) {
        // Set the content (with unique element IDs)
        const speakerElement = document.getElementById("dialogue-speaker-" + this.id);
        if (speakerElement) {
          speakerElement.textContent = speaker;
          speakerElement.style.display = speaker ? "block" : "none";
        }
        
        // Set avatar if provided
        const avatarElement = document.getElementById("dialogue-avatar-" + this.id);
        if (avatarElement) {
          if (avatarSrc) {
            avatarElement.style.backgroundImage = `url('${avatarSrc}')`;
            avatarElement.style.display = "block";
          } else {
            avatarElement.style.display = "none";
          }
        }
        
        // Set the dialogue text directly
        this.dialogueText.textContent = message;
        
        // Show the dialogue box
        this.dialogueBox.style.display = "block";
        
        // Play sound effect if enabled
        if (this.sound) {
          this.sound.currentTime = 0;
          this.sound.play().catch(e => console.log("Sound play error:", e));
        }
        
        this.isOpen = true;
        
        // Return the dialogue box element for custom button addition
        return this.dialogueBox;
      }
    
      // Show a random dialogue from the dialogues array
      showRandomDialogue(speaker = "", avatarSrc = null) {
        if (this.dialogues.length === 0) return;
        
        // Pick a random index that's different from the last one
        let randomIndex;
        if (this.dialogues.length > 1) {
          do {
            randomIndex = Math.floor(Math.random() * this.dialogues.length);
          } while (randomIndex === this.lastShownIndex);
        } else {
          randomIndex = 0; // Only one dialogue available
        }
        
        // Store the current index to avoid repetition next time
        this.lastShownIndex = randomIndex;
        
        // Show the dialogue
        const randomDialogue = this.dialogues[randomIndex];
        return this.showDialogue(randomDialogue, speaker, avatarSrc);
      }
    
      // Close the dialogue box
      closeDialogue() {
        if (!this.isOpen) return;
        
        // Hide the dialogue box
        this.dialogueBox.style.display = "none";
        this.isOpen = false;
        
        // Remove any custom buttons
        const buttonContainers = this.dialogueBox.querySelectorAll('div[style*="display: flex"]');
        buttonContainers.forEach(container => {
          // Skip the main content container
          if (container.contains(document.getElementById("dialogue-avatar-" + this.id))) {
            return;
          }
          container.remove();
        });
      }
    
      // Check if dialogue is currently open
      isDialogueOpen() {
        return this.isOpen;
      }
      
      // Add buttons to the dialogue
      addButtons(buttons) {
          if (!this.isOpen || !buttons || !Array.isArray(buttons) || buttons.length === 0) return;
          
          const buttonContainer = document.createElement('div');
          buttonContainer.style.display = 'flex';
          buttonContainer.style.justifyContent = 'space-between';
          buttonContainer.style.marginTop = '10px';
          
          // Add each button
          buttons.forEach(button => {
              if (!button || !button.text) return;
              
              const btn = document.createElement('button');
              btn.textContent = button.text;
              btn.style.padding = '8px 15px';
              btn.style.background = button.primary ? '#4a86e8' : '#666';
              btn.style.color = 'white';
              btn.style.border = 'none';
              btn.style.borderRadius = '5px';
              btn.style.cursor = 'pointer';
              btn.style.marginRight = '10px';
              
              // Add click handler
              btn.onclick = () => {
                  if (button.action && typeof button.action === 'function') {
                      button.action();
                  }
              };
              
              buttonContainer.appendChild(btn);
          });
          
          // Insert before the close button
          if (buttonContainer.children.length > 0) {
              this.dialogueBox.insertBefore(buttonContainer, this.closeBtn);
          }
      }
    }
    
    export default DialogueSystem;
    
    <IPython.core.display.Javascript object>
    

    In order for the Randomized Dialogue to be visible and implented on your adventure game you must need to

    • Replace the current reaction and interact method of your current npc(s)
    • Import this file called DialogueSystem.js into your adventure game
    • Double Check to make sure that DialogueSystem.js methods are properly referenced when implenting certain methods into the npc keys pair values

    Example of Implementation (Endship is the NPC)

    const sprite_data_endship = {
      id: 'Endship',
      greeting: sprite_greet_endship,
      src: sprite_src_endship,
      SCALE_FACTOR: 5,
      ANIMATION_RATE: 1000000,
      pixels: {height: 982, width: 900},
      INIT_POSITION: { x: (width / 2), y: (height / 2) },
      orientation: {rows: 1, columns: 1 },
      down: {row: 0, start: 0, columns: 1 },
      hitbox: { widthPercentage: 0.1, heightPercentage: 0.2 },
      zIndex: 10,  // Same z-index as player
      dialogues: [
        "The end ship looms before you...",
        "The end ship seems to beckon you to loot the treasure within...",
        "funny purple spaceship heheheheheh",
        // Add more later?
      ],
      reaction: function() {
        dialogueSystem.showRandomDialogue(); // Using Dialogue system instead of alert
      },
      interact: function() {
        let quiz = new Quiz();
        quiz.initialize();
        quiz.openPanel(sprite_data_endship);
      }
    };
    
    
    <IPython.core.display.Javascript object>
    

    Hack 1 : Create Your Own Dialogue Set

    Create a custom DialogueSystem instance with at least 4 unique lines. Replace the default quotes with your own themed around your game (sci-fi, fantasy, mystery, etc.).

    Hack 2: Apply to Multiple NPCs

    Modify at least 2 different NPCs in your game to each have their own unique DialogueSystem instance and call showRandomDialogue() when the player interacts with them.

    Make sure the dialogue themes match the personality of the character!

    Homework:

    • At least two functioning NPCs with randomized dialogues.

    • Your game UI showing the custom dialogue box.

    • Brief reflection: What was the biggest challenge in integrating this system?