じ☆ve冰风 发表于 2026-4-9 02:34:52

装备变更行走图 Ace 版 附属脚本—— 染色与涂装

RUBY 代码
#==============================================================================#
#** 染色与涂装系统 —— 装备变更行走图 Ace 版 附属脚本
#------------------------------------------------------------------------------#
#作者:MrRuigi
#功能:
#    · 在物品备注中定义染料和涂装,通过菜单对已装备的武器/防具进行染色/涂装
#    · 染色采用色相旋转(hue_change),仅改变非透明像素的色调,保持透明背景
#    · 涂装采用正片叠底(Multiply)混合,平铺小图案,仅作用于装备非透明区域
#    · 装备外观变化会实时反映在玩家行走图中
#    · 无可用物品时自动返回上一级,避免卡操作感
#==============================================================================#

#==============================================================================#
# ■ 配置模块
#==============================================================================#
module FalDyePaint
# 菜单中显示的选项名称
COMMAND_NAME = "染色/涂装"

# 操作音效
DYE_SE      = "Heal6"      # 染色成功
PAINT_SE    = "Item3"      # 涂装成功
CANCEL_SE   = "Cancel2"    # 取消操作
ERROR_SE    = "Buzzer1"    # 错误提示

# 默认颜色(无染色时视为白色,不影响色调)
DEFAULT_COLOR = Color.new(255, 255, 255)

# 预览窗口尺寸
PREVIEW_WIDTH= 100
PREVIEW_HEIGHT = 100

# 染色模式:true = 色相旋转(推荐),false = 像素颜色混合(旧版效果)
USE_HUE_CHANGE = true

# 像素混合模式下的染色强度 (0-255),仅在 USE_HUE_CHANGE = false 时生效
DYE_OPACITY = 160

# 涂装图片存放文件夹(相对于 Graphics 目录)
PAINTING_DIR = "Painting"
end

#==============================================================================#
# ■ 装备变更行走图相关配置
#==============================================================================#
module EquipDisplay
# 各个朝向对应的显示优先级,靠后的显示在最前
Priority = {
    0 => [:body, :armor, :helmet, :weapon, :shield], # 下
    1 => [:weapon, :body, :armor, :helmet, :shield], # 左
    2 => [:shield, :body, :armor, :helmet, :weapon], # 右
    3 => [:body, :weapon, :shield, :armor, :helmet]# 上
}
end

#==============================================================================#
# ■ RPG::EquipItem (扩展)
#==============================================================================#
classRPG::EquipItem < RPG::BaseItem
# 获取此装备当前的染色颜色(从系统数据中读取)
def dye_color
    $game_system.dye_data(self)rescuenil
end

# 获取此装备当前的涂装图案文件名
def paint_pattern
    $game_system.paint_data(self)rescuenil
end

# 获取行走图装备备注中的图片名
def display_equip_part
    @note =~ //
    return $1
end
end

#==============================================================================#
# ■ RPG::Item (扩展)
#==============================================================================#
classRPG::Item < RPG::UsableItem
# 判断是否为染料物品,并返回解析出的颜色(Color 对象)
def dye_color
    @note =~ //i
    returnnilunless $1 && $2 && $3
    Color.new($1.to_i, $2.to_i, $3.to_i)
end

# 判断是否为涂装物品,并返回图案文件名
def paint_pattern
    @note =~ //i
    return $1
end

# 是否为可用的外观物品(染料或涂装)
def appearance_item?
    dye_color || paint_pattern
end
end

#==============================================================================#
# ■ Game_System (扩展)
#==============================================================================#
class Game_System
# 初始化染色/涂装数据容器
alias dye_paint_initialize initialize
def initialize
    dye_paint_initialize
    @dye_data   = {}   # 格式: { equip_id => Color }
    @paint_data = {}   # 格式: { equip_id => "pattern_name" }
end

# 获取装备的染色颜色
def dye_data(equip)
    @dye_data || FalDyePaint::DEFAULT_COLOR
end

# 设置装备的染色颜色
def set_dye_data(equip, color)
    @dye_data = color
end

# 获取装备的涂装图案
def paint_data(equip)
    @paint_data
end

# 设置装备的涂装图案
def set_paint_data(equip, pattern)
    @paint_data = pattern
end

# 清除装备的染色数据
def clear_dye_data(equip)
    @dye_data.delete(equip_id(equip))
end

# 清除装备的涂装数据
def clear_paint_data(equip)
    @paint_data.delete(equip_id(equip))
end

private

# 生成装备的唯一标识符(类别 + ID)
def equip_id(equip)
    "#{equip.class.name}_#{equip.id}"
end
end

#==============================================================================#
# ■ Game_Actor (扩展)
#==============================================================================#
class Game_Actor
attr_accessor :equip_changed

alias dye_paint_change_equip change_equip
def change_equip(slot_id, item)
    dye_paint_change_equip(slot_id, item)
    @equip_changed = true
end
end

#==============================================================================#
# ■ Window_DyePaintHelp (自定义帮助窗口)
#==============================================================================#
class Window_DyePaintHelp < Window_Help
def initialize(line_number = 2)
    super(line_number)
end

def set_actor(actor)
    set_text(actor ? "#{actor.name} 的装备" : "")
end

def set_equip(equip)
    text = equip ? equip.name : ""
    if equip
      dye = $game_system.dye_data(equip)
      pattern = $game_system.paint_data(equip)
      extra = []
      extra0
end

definclude?(item)
    case@mode
    when:dye
      item.is_a?(RPG::Item) && item.dye_color != nil
    when:paint
      item.is_a?(RPG::Item) && item.paint_pattern != nil
    else
      false
    end
end

def enable?(item)
    $game_party.item_number(item) > 0
end

def update_help
    @help_window.set_item(item)if@help_window
end
end

#==============================================================================#
# ■ Window_DyePaintPreview (预览窗口)
#==============================================================================#
class Window_DyePaintPreview < Window_Base
def initialize(x, y)
    super(x, y, FalDyePaint::PREVIEW_WIDTH + 32, FalDyePaint::PREVIEW_HEIGHT + 32)
    @equip = nil
    @dye_color = nil
    @paint_pattern = nil
    refresh
end

def set_preview(equip, dye_color = nil, paint_pattern = nil)
    returnif@equip == equip && @dye_color == dye_color && @paint_pattern == paint_pattern
    @equip = equip
    @dye_color = dye_color
    @paint_pattern = paint_pattern
    refresh
end

def clear
    @equip = nil
    @dye_color = nil
    @paint_pattern = nil
    refresh
end

def refresh
    contents.clear
    returnunless@equip

    draw_icon(@equip.icon_index, 0, 0)

    if@dye_color
      rect = Rect.new(24, 0, 24, 24)
      contents.fill_rect(rect, @dye_color)
    end

    if@paint_pattern
      contents.font.size = 16
      contents.draw_text(48, 0, contents_width - 48, 24, @paint_pattern)
    end

    contents.font.size = Font.default_size
    contents.draw_text(0, 30, contents_width, 24, "当前效果", 1)
end
end

#==============================================================================#
# ■ Scene_DyePaint (染色/涂装主场景)
#==============================================================================#
class Scene_DyePaint < Scene_MenuBase
def start
    super
    create_help_window
    create_command_window
    create_actor_window
    create_equip_window
    create_item_window
    create_preview_window

    @help_window.height = 72
    @command_window.activate
    @mode = :dye
end

def create_help_window
    @help_window = Window_DyePaintHelp.new
    @help_window.viewport = @viewport
end

def create_command_window
    @command_window = Window_DyePaintCommand.new
    @command_window.viewport = @viewport
    @command_window.y = @help_window.height
    @command_window.set_handler(:dye,   method(:command_dye))
    @command_window.set_handler(:paint, method(:command_paint))
    @command_window.set_handler(:cancel, method(:return_scene))
end

def create_actor_window
    y = @command_window.y + @command_window.height
    @actor_window = Window_DyePaintActor.new(0, y)
    @actor_window.viewport = @viewport
    @actor_window.help_window = @help_window
    @actor_window.set_handler(:ok,   method(:on_actor_ok))
    @actor_window.set_handler(:cancel, method(:on_actor_cancel))
    @actor_window.deactivate
end

def create_equip_window
    x = @actor_window.width
    y = @actor_window.y
    @equip_window = Window_DyePaintEquip.new(x, y)
    @equip_window.viewport = @viewport
    @equip_window.help_window = @help_window
    @equip_window.set_handler(:ok,   method(:on_equip_ok))
    @equip_window.set_handler(:cancel, method(:on_equip_cancel))
end

def create_item_window
    x = @actor_window.width + @equip_window.width
    y = @actor_window.y
    w = Graphics.width - x
    h = Graphics.height - y
    @item_window = Window_DyePaintItem.new(x, y, w, h)
    @item_window.viewport = @viewport
    @item_window.help_window = @help_window
    @item_window.set_handler(:ok,   method(:on_item_ok))
    @item_window.set_handler(:cancel, method(:on_item_cancel))
    @item_window.mode = :dye
end

def create_preview_window
    x = Graphics.width - FalDyePaint::PREVIEW_WIDTH - 32
    y = @command_window.y
    @preview_window = Window_DyePaintPreview.new(x, y)
    @preview_window.viewport = @viewport
end

#--------------------------------------------------------------------------
# * 命令处理
#--------------------------------------------------------------------------
def command_dye
    @mode = :dye
    @command_window.deactivate
    @actor_window.activate
    @item_window.mode = :dye
    @item_window.refresh
    update_preview
end

def command_paint
    @mode = :paint
    @command_window.deactivate
    @actor_window.activate
    @item_window.mode = :paint
    @item_window.refresh
    update_preview
end

#--------------------------------------------------------------------------
# * 角色窗口处理
#--------------------------------------------------------------------------
def on_actor_ok
    @equip_window.actor = @actor_window.actor
    @actor_window.deactivate
    if@equip_window.item_max > 0
      @equip_window.activate
    else
      Sound.play_buzzer
      @actor_window.activate
    end
    update_preview
end

def on_actor_cancel
    @actor_window.deactivate
    @command_window.activate
    @preview_window.clear
end

#--------------------------------------------------------------------------
# * 装备窗口处理
#--------------------------------------------------------------------------
def on_equip_ok
    @item_window.refresh
    if@item_window.item_max == 0
      Sound.play_buzzer
      @help_window.set_text("没有可用的#{@mode == :dye ? '染料' : '涂装'}物品")
      @equip_window.deactivate
      @actor_window.activate
      @preview_window.clear
      return
    end
    @equip_window.deactivate
    @item_window.activate
    update_preview
end

def on_equip_cancel
    @equip_window.deactivate
    @actor_window.activate
    @preview_window.clear
    @item_window.unselect
end

#--------------------------------------------------------------------------
# * 物品窗口处理
#--------------------------------------------------------------------------
def on_item_ok
    item = @item_window.item
    equip = @equip_window.equip
    if item.nil?
      on_item_cancel
      return
    end

    if(@mode == :dye && item.dye_color) || (@mode == :paint && item.paint_pattern)
      apply_appearance(equip, item)
    else
      Sound.play_buzzer
    end
end

def on_item_cancel
    @item_window.deactivate
    @equip_window.activate
    @preview_window.set_preview(@equip_window.equip,
                              $game_system.dye_data(@equip_window.equip),
                              $game_system.paint_data(@equip_window.equip))
end

#--------------------------------------------------------------------------
# * 应用染色/涂装
#--------------------------------------------------------------------------
def apply_appearance(equip, item)
    if@mode == :dye
      color = item.dye_color
      $game_system.set_dye_data(equip, color)
      RPG::SE.new(FalDyePaint::DYE_SE, 80).play
    else
      pattern = item.paint_pattern
      $game_system.set_paint_data(equip, pattern)
      RPG::SE.new(FalDyePaint::PAINT_SE, 80).play
    end

    $game_party.lose_item(item, 1)

    @item_window.refresh
    @equip_window.refresh
    update_preview

    @item_window.deactivate
    @equip_window.activate
    @equip_window.select(@equip_window.index)

    $game_player.refresh
end

#--------------------------------------------------------------------------
# * 更新预览窗口
#--------------------------------------------------------------------------
def update_preview
    equip = @equip_window.equip
    if equip
      dye = $game_system.dye_data(equip)
      paint = $game_system.paint_data(equip)
      @preview_window.set_preview(equip, dye, paint)
    else
      @preview_window.clear
    end
end

#--------------------------------------------------------------------------
# * 帧更新
#--------------------------------------------------------------------------
def update
    super
    if@item_window.active && @item_window.item
      item = @item_window.item
      equip = @equip_window.equip
      if equip
      if@mode == :dye && item.dye_color
          @preview_window.set_preview(equip, item.dye_color,
                                    $game_system.paint_data(equip))
      elsif@mode == :paint && item.paint_pattern
          @preview_window.set_preview(equip,
                                    $game_system.dye_data(equip),
                                    item.paint_pattern)
      else
          @preview_window.set_preview(equip,
                                    $game_system.dye_data(equip),
                                    $game_system.paint_data(equip))
      end
      end
    end
end
end

#==============================================================================#
# ■ Window_MenuCommand (在菜单中添加命令)
#==============================================================================#
class Window_MenuCommand < Window_Command
alias dye_paint_add_original_commands add_original_commands
def add_original_commands
    dye_paint_add_original_commands
    add_command(FalDyePaint::COMMAND_NAME, :dyepaint, true)
end
end

#==============================================================================#
# ■ Scene_Menu (增加菜单选项)
#==============================================================================#
class Scene_Menu < Scene_MenuBase
alias dye_paint_create_command_window create_command_window
def create_command_window
    dye_paint_create_command_window
    @command_window.set_handler(:dyepaint, method(:command_dyepaint))
end

def command_dyepaint
    SceneManager.call(Scene_DyePaint)
end
end

#==============================================================================#
# ■ Sprite_Player (装备变更行走图 + 染色/涂装叠加)
#==============================================================================#
class Sprite_Player < Sprite_Character
EquipDisplayPriority = EquipDisplay::Priority

def graphic_changed?
    super || equip_changed?
end

def equip_changed?
    @character.actor && @character.actor.equip_changed
end

def set_character_bitmap
    super
    setup_bitmap_display_equips
end

def setup_bitmap_display_equips
    w = @cw * 3
    h = @ch * 4
    i = @character.character_index
    orig_bitmap = Bitmap.new(w, h)
    orig_bitmap.blt(0, 0, self.bitmap, Rect.new(i%4*w, i/4*h, w, h))
    self.bitmap = Bitmap.new(w, h)
    bitmap_draw_equips(orig_bitmap)if@character.actor
    orig_bitmap.dispose
end

def bitmap_draw_equips(orig_bitmap)
    @character.actor.equip_changed = false
    temp_equip_bitmaps = []
    @character.actor.equips.each_with_indexdo |equip, index|
      img_name = equip ? equip.display_equip_part : nil
      if img_name
      base_bmp = equip_bitmap(img_name, index == 1 && equip.is_a?(RPG::Weapon))
      if base_bmp
          colored_bmp = apply_dye_and_paint(base_bmp, equip)
          temp_equip_bitmaps.push(colored_bmp)
      else
          temp_equip_bitmaps.push(nil)
      end
      else
      temp_equip_bitmaps.push(nil)
      end
    end

    btmp_rect = Rect.new(0, 0, @cw*3, @ch)
    4.timesdo |i|
      btmp_rect.y = @ch * i
      EquipDisplayPriority.eachdo |part|
      btmp = equip_part_bitmap(part, temp_equip_bitmaps, orig_bitmap)
      self.bitmap.blt(0, btmp_rect.y, btmp, btmp_rect)if btmp
      end
    end

    temp_equip_bitmaps.each{|b| b.disposeif b}
end

#--------------------------------------------------------------------------
# * 应用染色和涂装(染色:色相旋转;涂装:正片叠底 + 平铺)
#--------------------------------------------------------------------------
def apply_dye_and_paint(src_bitmap, equip)
    return src_bitmap unless equip
    new_bmp = Bitmap.new(src_bitmap.width, src_bitmap.height)

    # ----- 染色处理(色相旋转)-----
    dye_color = equip.dye_color
    if dye_color && dye_color != FalDyePaint::DEFAULT_COLOR
      ifFalDyePaint::USE_HUE_CHANGE
      hue = rgb_to_hue(dye_color.red, dye_color.green, dye_color.blue)
      new_bmp.blt(0, 0, src_bitmap, src_bitmap.rect)
      new_bmp.hue_change(hue)
      else
      new_bmp.blt(0, 0, src_bitmap, src_bitmap.rect)
      (0...new_bmp.width).eachdo |x|
          (0...new_bmp.height).eachdo |y|
            color = new_bmp.get_pixel(x, y)
            nextif color.alpha == 0
            r = (color.red   * (255 - FalDyePaint::DYE_OPACITY) + dye_color.red   * FalDyePaint::DYE_OPACITY) / 255
            g = (color.green * (255 - FalDyePaint::DYE_OPACITY) + dye_color.green * FalDyePaint::DYE_OPACITY) / 255
            b = (color.blue* (255 - FalDyePaint::DYE_OPACITY) + dye_color.blue* FalDyePaint::DYE_OPACITY) / 255
            new_bmp.set_pixel(x, y, Color.new(r, g, b, color.alpha))
          end
      end
      end
    else
      new_bmp.blt(0, 0, src_bitmap, src_bitmap.rect)
    end

    # ----- 涂装处理(正片叠底 + 平铺,仅作用于非透明区域)-----
    paint_pattern = equip.paint_pattern
    if paint_pattern
      paint_file = "Graphics/#{FalDyePaint::PAINTING_DIR}/#{paint_pattern}.png"
      ifFile.exist?(paint_file)
      paint_bmp = Bitmap.new(paint_file)

      # 创建平铺图层
      tile_bmp = Bitmap.new(new_bmp.width, new_bmp.height)
      tile_x = (new_bmp.width.to_f / paint_bmp.width).ceil
      tile_y = (new_bmp.height.to_f / paint_bmp.height).ceil
      tile_y.timesdo |y|
          tile_x.timesdo |x|
            tile_bmp.blt(x * paint_bmp.width, y * paint_bmp.height,
                         paint_bmp, paint_bmp.rect)
          end
      end

      # 逐像素正片叠底
      (0...new_bmp.width).eachdo |x|
          (0...new_bmp.height).eachdo |y|
            src_color = src_bitmap.get_pixel(x, y)
            nextif src_color.alpha == 0          # 透明区域不处理

            paint_color = tile_bmp.get_pixel(x, y)
            nextif paint_color.alpha == 0      # 涂装透明像素跳过

            base_color = new_bmp.get_pixel(x, y)

            # 正片叠底公式: Result = (Base * Overlay) / 255
            r = (base_color.red * paint_color.red) / 255
            g = (base_color.green * paint_color.green) / 255
            b = (base_color.blue * paint_color.blue) / 255

            # 处理涂装的透明度(Alpha 混合)
            if paint_color.alpha < 255
            alpha_factor = paint_color.alpha / 255.0
            r = (base_color.red * (1 - alpha_factor) + r * alpha_factor).to_i
            g = (base_color.green * (1 - alpha_factor) + g * alpha_factor).to_i
            b = (base_color.blue * (1 - alpha_factor) + b * alpha_factor).to_i
            end

            new_bmp.set_pixel(x, y, Color.new(r, g, b, base_color.alpha))
          end
      end

      tile_bmp.dispose
      paint_bmp.dispose
      end
    end

    new_bmp
end

#--------------------------------------------------------------------------
# * RGB 转色相 (0~360)
#--------------------------------------------------------------------------
def rgb_to_hue(r, g, b)
    r /= 255.0
    g /= 255.0
    b /= 255.0
    max = .max
    min = .min
    delta = max - min

    hue = 0.0
    if delta != 0
      case max
      when r
      hue = 60.0 * ((g - b) / delta % 6)
      when g
      hue = 60.0 * ((b - r) / delta + 2)
      when b
      hue = 60.0 * ((r - g) / delta + 4)
      end
    end
    hue = 0if hue < 0
    hue.to_i
end

def equip_part_bitmap(part, temp_equip_bitmaps, orig_bitmap)
    case part
    when:body;   orig_bitmap
    when:weapon;   temp_equip_bitmaps
    when:shield;   temp_equip_bitmaps
    when:armor;    temp_equip_bitmaps
    when:helmet;   temp_equip_bitmaps
    end
end

def equip_bitmap(img_name, dual_weapon)
    if img_name
      img_file = "Graphics/DisplayEquips/#{img_name}"
      if dual_weapon
      img_dual = img_file + "_dual.png"
      File.exist?(img_dual) ? Bitmap.new(img_dual) : nil
      else
      Bitmap.new(img_file + ".png")rescuenil
      end
    else
      nil
    end
end

def update_src_rect
    pattern = @character.pattern < 3 ? @character.pattern : 1
    sx = pattern * @cw
    sy = (@character.direction - 2) / 2 * @ch
    self.src_rect.set(sx, sy, @cw, @ch)
end

def dispose
    bitmap.disposeif bitmap
    super
end
end

#==============================================================================#
# ■ Spriteset_Map (覆盖角色精灵创建)
#==============================================================================#
class Spriteset_Map
def create_characters
    @character_sprites = []
    $game_map.events.values.eachdo |event|
      @character_sprites.push(Sprite_Character.new(@viewport1, event))
    end
    $game_map.vehicles.eachdo |vehicle|
      @character_sprites.push(Sprite_Character.new(@viewport1, vehicle))
    end
    $game_player.followers.reverse_eachdo |follower|
      @character_sprites.push(Sprite_Player.new(@viewport1, follower))
    end
    @character_sprites.push(Sprite_Player.new(@viewport1, $game_player))
    @map_id = $game_map.map_id
end
end






注意:由于未知问题,不适用URGE,会导致无报错闪退




            本帖来自P1论坛作者Ruigi,因Project1站服务器在国外有时候访问缓慢不方便作者交流学习,经联系P1站长fux2同意署名转载一起分享游戏制作经验,共同为国内独立游戏作者共同创造良好交流环境,原文地址:https://rpg.blue/forum.php?mod=viewthread&tid=498805若有侵权,发帖作者可联系底部站长QQ在线咨询功能删除,谢谢。
页: [1]
查看完整版本: 装备变更行走图 Ace 版 附属脚本—— 染色与涂装