扫描二维码关注官方公众号
返回列表
+ 发新帖
查看: 102|回复: 0

[转载发布] 仿Undertale 风格敌人对话气泡

[复制链接]
累计送礼:
0 个
累计收礼:
1 个
  • TA的每日心情
    开心
    2026-5-3 18:58
  • 签到天数: 195 天

    连续签到: 1 天

    [LV.7]常住居民III

    2612

    主题

    810

    回帖

    2万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    VIP
    7
    卡币
    18009
    OK点
    16
    推广点
    0
    同能卷
    50
    积分
    21461

    灌水之王

    发表于 昨天 02:33 | 显示全部楼层 |阅读模式
    RUBY 代码
    [code]#==============================================================================
    # ■ Undertale 风格敌人对话气泡
    #==============================================================================
    #作者:ruigi
    #==============================================================================
    # ■ 使用说明
    #==============================================================================
    # 【1】在敌人备注中写入对话标签
    #   格式:
    #   如:
    #   可写入多行,每次随机抽取一句。
    #
    # 【2】手动换行(仅支持事件脚本)
    #   使用  标签,例如:
    #   自动换行已启用,超宽文字会自动折行。
    #
    # 【3】事件脚本指令(在战斗事件的[脚本]中使用)
    #   set_enemy_dialogue(索引, "台词")   # 为敌人设置自定义对话(下次行动时显示)
    #   force_enemy_dialogue(索引)         # 立即强制显示该敌人的对话气泡
    #   clear_enemy_dialogue(索引)         # 清除自定义对话,恢复从备注随机读取
    #   dismiss_enemy_dialogue             # 立即关闭当前显示的气泡
    #
    # 【4】可调参数(见模块 Enemy_Dialogue)
    #   ENABLED           : 总开关(false 完全关闭功能)
    #   TEXT_TYPEWRITER   : 打字机效果开关
    #   TYPE_SPEED        : 打字速度(每 N 帧显示一个字)
    #   EXTRA_PAUSE      : 打字完成后额外停留帧数
    #   DELAY_ACTION     : 是否等气泡消失后再发动攻击
    #   SIMULTANEOUS_BUBBLES        : 是否所有敌人同时显示气泡
    #   SIMULTANEOUS_BUBBLES_WAIT   : 全体气泡是否等待结束后才继续行动
    #   REPEAT_DIALOGUE : 是否每回合都可能说话
    #   Z 键(确认键)可跳过当前正在显示的对话(打字或停留)。
    #
    # 【5】资源文件
    #   请在 Graphics/System/ 下放置气泡图片,文件名见 DIALOGUE_BUBBLE_IMAGE。
    #==============================================================================
    module Enemy_Dialogue
      ENABLED                = true
      DIALOGUE_BUBBLE_IMAGE  = "Enemy_Dialogue_Bubble"
      DURATION_IN_FRAMES     = 200

      X_OFFSET   = -30
      Y_OFFSET   = -290
      Z_VALUE    = 100

      TEXT_COLOR        = Color.new(255, 255, 255, 255)
      TEXT_SIZE         = 20
      TEXT_BOLD         = true
      TEXT_SHADOW_COLOR = Color.new(0, 0, 0, 160)

      TEXT_TYPEWRITER  = true
      TYPE_SPEED       = 2
      EXTRA_PAUSE      = 40

      AUTO_WRAP = true
      MANUAL_BREAK_TAG = //i
      TEXT_PADDING_LEFT  = 12
      TEXT_PADDING_RIGHT = 12
      LINE_SPACING = 24

      BUBBLE_ZOOM_X  = 1.0
      BUBBLE_ZOOM_Y  = 1.0
      SHOW_IN_LOG    = false

      REPEAT_DIALOGUE = true
      DELAY_ACTION    = true
      SIMULTANEOUS_BUBBLES      = true
      SIMULTANEOUS_BUBBLES_WAIT = true
    end

    #==============================================================================
    # ■ RPG::Enemy
    #==============================================================================
    classRPG::Enemy < RPG::BaseItem
      def dialogue_lines
        @dialogue_lines ||= load_dialogue_lines
      end

      def random_dialogue
        lines = dialogue_lines
        lines.empty? ? nil : lines[rand(lines.size)]
      end

      private

      def load_dialogue_lines
        lines = []
        note.scan(//i)do |match|
          lines.push(match[0].strip)unless match[0].strip.empty?
        end
        lines
      end
    end

    #==============================================================================
    # ■ Game_Enemy
    #==============================================================================
    class Game_Enemy < Game_Battler
      attr_accessor :custom_dialogue_text
      attr_accessor :dialogue_displayed
      attr_accessor :just_spoke_in_group

      alias enemy_dialogue_initialize initialize
      def initialize(index, enemy_id)
        enemy_dialogue_initialize(index, enemy_id)
        @custom_dialogue_text = nil
        @dialogue_displayed   = false
        @just_spoke_in_group  = false
      end

      def active_dialogue_text
        return@custom_dialogue_textif@custom_dialogue_text
        enemy.random_dialogue
      end

      def has_dialogue?
        returntrueif@custom_dialogue_text
        enemy.dialogue_lines.any?
      end

      def custom_dialogue_active?
        !@custom_dialogue_text.nil?
      end
    end

    #==============================================================================
    # ■ Window_Enemy_Dialogue_Bubble
    #==============================================================================
    class Window_Enemy_Dialogue_Bubble < Window_Base
      attr_reader :enemy, :text
      attr_reader :typing_finished

      def initialize(enemy, raw_text)
        @enemy = enemy
        @raw_text = raw_text
        @processed_text = @raw_text.gsub(Enemy_Dialogue::MANUAL_BREAK_TAG, "\n")
        @char_index = 0
        @type_timer = 0
        @typing_finished = false

        super(0, 0, 210, 80)
        self.opacity          = 0
        self.back_opacity     = 0
        self.contents_opacity = 255
        self.z = Enemy_Dialogue::Z_VALUE

        create_contents
        max_width = max_text_width
        @total_lines = calculate_lines(@processed_text, max_width)
        line_height = Enemy_Dialogue::LINE_SPACING
        w = [210, [280, max_width + 40].min].max
        h = [80, @total_lines.size * line_height + 32].max

        self.width = w
        self.height = h
        create_contents

        create_bubble_sprite
        update_display_text
        set_position
      end

      # 自动换行计算
      def full_text_width
        bmp = Bitmap.new(1, 1)
        bmp.font.size = Enemy_Dialogue::TEXT_SIZE
        bmp.font.bold = Enemy_Dialogue::TEXT_BOLD
        w = bmp.text_size(@processed_text).width
        bmp.dispose
        w
      end

      def max_text_width
        self.width - 32 - Enemy_Dialogue::TEXT_PADDING_LEFT - Enemy_Dialogue::TEXT_PADDING_RIGHT
      end

      def create_bubble_sprite
        @bubble_sprite = Sprite.new
        @bubble_sprite.bitmap = Cache.system(Enemy_Dialogue::DIALOGUE_BUBBLE_IMAGE)
        @bubble_sprite.z      = self.z - 100
        @bubble_sprite.zoom_x = Enemy_Dialogue::BUBBLE_ZOOM_X
        @bubble_sprite.zoom_y = Enemy_Dialogue::BUBBLE_ZOOM_Y
      end

      def create_contents
        self.contents.disposeifself.contents
        self.contents = Bitmap.new(width - 32, height - 32)
        self.contents.clear
      end

      def calculate_lines(text, max_width)
        lines = []
        paragraphs = text.split("\n")
        bmp = self.contents
        bmp.font.size = Enemy_Dialogue::TEXT_SIZE
        bmp.font.bold = Enemy_Dialogue::TEXT_BOLD

        paragraphs.eachdo |para|
          if para.empty?
            lines  max_width
              lines
    该会员没有填写今日想说内容.
    回复 送礼论坛版权

    使用道具 举报

    文明发言,和谐互动
    文明发言,和谐互动
    高级模式
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    关闭

    幸运抽奖

    社区每日抽奖来袭,快来试试你是欧皇还是非酋~

    立即查看

    聊天机器人
    Loading...

    QQ|Archiver|手机版|小黑屋|同能RPG制作大师 ( 沪ICP备12027754号-3 )

    GMT+8, 2026-5-12 23:17 , Processed in 0.140631 second(s), 51 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2020, Tencent Cloud.

    快速回复 返回顶部 返回列表