say()

in libs/game/sprite.ts [308:437]


    say(text: string, timeOnScreen?: number, textColor = 1, textBoxColor = 0) {

        if (!text) {
            this.updateSay = undefined;
            if (this.sayBubbleSprite) {
                this.sayBubbleSprite.destroy();
            }
            return;
        }

        

        let pixelsOffset = 0;
        let holdTextSeconds = 1.5;
        let bubblePadding = 4;
        let maxTextWidth = 100;
        let font = image.font8;
        let startX = 2;
        let startY = 2;
        let bubbleWidth = text.length * font.charWidth + bubblePadding;
        let maxOffset = text.length * font.charWidth - maxTextWidth;
        let bubbleOffset: number;
        // sets the defaut scroll speed in pixels per second
        let speed = 45;

        // Calculates the speed of the scroll if scrolling is needed and a time is specified
        if (timeOnScreen && maxOffset > 0) {
            speed = (maxOffset + (2 * maxTextWidth)) / (timeOnScreen / 1000);
            speed = Math.max(speed, 45);
            holdTextSeconds = maxTextWidth / speed;
            holdTextSeconds = Math.min(holdTextSeconds, 1.5);
        } 

        if (timeOnScreen) {
            timeOnScreen = timeOnScreen + control.millis();
        }

        if (!this._hitboxes || this._hitboxes.length == 0) {
            bubbleOffset = 0;
        } else {
            bubbleOffset = this._hitboxes[0].top;
            for (let i = 0; i < this._hitboxes.length; i++) {
                bubbleOffset = Math.min(bubbleOffset, this._hitboxes[i].top);
            }

            // Gets the length from sprites location to its highest hitbox
            bubbleOffset = this.y - bubbleOffset;
        }


        if (bubbleWidth > maxTextWidth + bubblePadding) {
            bubbleWidth = maxTextWidth + bubblePadding;
        } else {
            maxOffset = -1;
        }

        // Destroy previous sayBubbleSprite to prevent leaking
        if (this.sayBubbleSprite) {
            this.sayBubbleSprite.destroy();
        }

        this.sayBubbleSprite = sprites.create(image.create(bubbleWidth, font.charHeight + bubblePadding), -1);

        this.sayBubbleSprite.setFlag(SpriteFlag.Ghost, true);
        this.updateSay = (dt, camera) => {
            // Update box stuff as long as timeOnScreen doesn't exist or it can still be on the screen
            if (!timeOnScreen || timeOnScreen > control.millis()) {
                this.sayBubbleSprite.image.fill(textBoxColor);
                // The minus 2 is how much transparent padding there is under the sayBubbleSprite
                this.sayBubbleSprite.y = this.y - bubbleOffset - ((font.charHeight + bubblePadding) >> 1) - 2;
                this.sayBubbleSprite.x = this.x;

                if (!this.isOutOfScreen(camera)) {
                    const ox = camera.offsetX;
                    const oy = camera.offsetY;

                    if (this.sayBubbleSprite.left - ox < 0) {
                        this.sayBubbleSprite.left = 0;
                    }

                    if (this.sayBubbleSprite.right - ox > screen.width) {
                        this.sayBubbleSprite.right = screen.width;
                    }

                    // If sprite bubble above the sprite gets cut off on top, place the bubble below the sprite
                    if (this.sayBubbleSprite.top - oy < 0) {
                        this.sayBubbleSprite.y = (this.sayBubbleSprite.y - 2 * this.y) * -1;
                    }
                }

                // Pauses at beginning of text for holdTextSeconds length
                if (holdTextSeconds > 0) {
                    holdTextSeconds -= game.eventContext().deltaTime;
                    // If scrolling has reached the end, start back at the beginning
                    if (holdTextSeconds <= 0 && pixelsOffset > 0) {
                        pixelsOffset = 0;
                        holdTextSeconds = maxTextWidth / speed;
                    }
                } else {
                    pixelsOffset += dt * speed;

                    // Pause at end of text for holdTextSeconds length
                    if (pixelsOffset >= maxOffset) {
                        pixelsOffset = maxOffset;
                        holdTextSeconds = maxTextWidth / speed;
                    }
                }
                // If maxOffset is negative it won't scroll
                if (maxOffset < 0) {
                    this.sayBubbleSprite.image.print(text, startX, startY, textColor, font);
                } else {
                    this.sayBubbleSprite.image.print(text, startX - pixelsOffset, startY, textColor, font);
                }

                // Left side padding
                this.sayBubbleSprite.image.fillRect(0, 0, bubblePadding >> 1, font.charHeight + bubblePadding, textBoxColor);
                // Right side padding
                this.sayBubbleSprite.image.fillRect(bubbleWidth - (bubblePadding >> 1), 0, bubblePadding >> 1, font.charHeight + bubblePadding, textBoxColor);
                // Corners removed
                this.sayBubbleSprite.image.setPixel(0, 0, 0);
                this.sayBubbleSprite.image.setPixel(bubbleWidth - 1, 0, 0);
                this.sayBubbleSprite.image.setPixel(0, font.charHeight + bubblePadding - 1, 0);
                this.sayBubbleSprite.image.setPixel(bubbleWidth - 1, font.charHeight + bubblePadding - 1, 0);
            } else {
                // If can't update because of timeOnScreen then destroy the sayBubbleSprite and reset updateSay
                this.sayBubbleSprite.destroy();
                this.updateSay = undefined;
            }
        }
    }