#==============================================================================# # ** 染色与涂装系统 —— 装备变更行走图 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 (扩展) #==============================================================================# class RPG::EquipItem < RPG::BaseItem # 获取此装备当前的染色颜色(从系统数据中读取) def dye_color $game_system.dye_data(self) rescue nil end # 获取此装备当前的涂装图案文件名 def paint_pattern $game_system.paint_data(self) rescue nil end # 获取行走图装备备注中的图片名 def display_equip_part @note =~ /<display.(\S+)>/ return $1 end end #==============================================================================# # ■ RPG::Item (扩展) #==============================================================================# class RPG::Item < RPG::UsableItem # 判断是否为染料物品,并返回解析出的颜色(Color 对象) def dye_color @note =~ /<染料:\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*>/i return nil unless $1 && $2 && $3 Color.new($1.to_i, $2.to_i, $3.to_i) end # 判断是否为涂装物品,并返回图案文件名 def paint_pattern @note =~ /<涂装:\s*(.+?)\s*>/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[equip_id(equip)] || FalDyePaint::DEFAULT_COLOR end # 设置装备的染色颜色 def set_dye_data(equip, color) @dye_data[equip_id(equip)] = color end # 获取装备的涂装图案 def paint_data(equip) @paint_data[equip_id(equip)] end # 设置装备的涂装图案 def set_paint_data(equip, pattern) @paint_data[equip_id(equip)] = 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 = [] extra << "染色" if dye != FalDyePaint::DEFAULT_COLOR extra << "涂装" if pattern text += " (#{extra.join('、')})" unless extra.empty? end set_text(text) end def set_item(item) text = item ? item.name : "" if item if item.dye_color c = item.dye_color text += sprintf(" RGB(%d,%d,%d)", c.red, c.green, c.blue) elsif item.paint_pattern text += " 图案: #{item.paint_pattern}" end end set_text(text) end end #==============================================================================# # ■ Window_DyePaintCommand (命令窗口) #==============================================================================# class Window_DyePaintCommand < Window_HorzCommand def initialize super(0, 0) deactivate end def window_width Graphics.width end def col_max 2 end def make_command_list add_command("染色", :dye) add_command("涂装", :paint) end end #==============================================================================# # ■ Window_DyePaintActor (角色选择窗口) #==============================================================================# class Window_DyePaintActor < Window_Selectable attr_reader :actor def initialize(x, y) super(x, y, 160, Graphics.height - y) refresh activate select(0) end def item_max $game_party.members.size end def actor $game_party.members[@index] end def refresh contents.clear $game_party.members.each_with_index do |actor, i| draw_actor_name(actor, 4, i * line_height) end end def draw_actor_name(actor, x, y) change_color(normal_color) draw_text(x, y, contents_width, line_height, actor.name) end def update_help @help_window.set_actor(actor) if @help_window end end #==============================================================================# # ■ Window_DyePaintEquip (装备选择窗口) #==============================================================================# class Window_DyePaintEquip < Window_Selectable attr_reader :equip def initialize(x, y) super(x, y, 200, Graphics.height - y) @actor = nil deactivate end def actor=(actor) return if @actor == actor @actor = actor refresh select(0) if item_max > 0 end def item_max @actor ? @actor.equips.compact.size : 0 end def equip @actor ? @actor.equips.compact[@index] : nil end def refresh contents.clear return unless @actor @actor.equips.compact.each_with_index do |equip, i| draw_item_name(equip, 4, i * line_height, true, contents_width - 8) end end def update_help @help_window.set_equip(equip) if @help_window end end #==============================================================================# # ■ Window_DyePaintItem (染料/涂装物品窗口) #==============================================================================# class Window_DyePaintItem < Window_ItemList def initialize(x, y, width, height) super(x, y, width, height) @mode = :dye deactivate end def mode=(mode) return if @mode == mode @mode = mode refresh select(0) if item_max > 0 end def include?(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) return if @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 return unless @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_index do |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.times do |i| btmp_rect.y = @ch * i EquipDisplayPriority[i].each do |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.dispose if 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 if FalDyePaint::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).each do |x| (0...new_bmp.height).each do |y| color = new_bmp.get_pixel(x, y) next if 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" if File.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.times do |y| tile_x.times do |x| tile_bmp.blt(x * paint_bmp.width, y * paint_bmp.height, paint_bmp, paint_bmp.rect) end end # 逐像素正片叠底 (0...new_bmp.width).each do |x| (0...new_bmp.height).each do |y| src_color = src_bitmap.get_pixel(x, y) next if src_color.alpha == 0 # 透明区域不处理 paint_color = tile_bmp.get_pixel(x, y) next if 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 = [r, g, b].max min = [r, g, b].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 = 0 if 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[0] when :shield; temp_equip_bitmaps[1] when :armor; temp_equip_bitmaps[2] when :helmet; temp_equip_bitmaps[3] 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") rescue nil 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.dispose if bitmap super end end #==============================================================================# # ■ Spriteset_Map (覆盖角色精灵创建) #==============================================================================# class Spriteset_Map def create_characters @character_sprites = [] $game_map.events.values.each do |event| @character_sprites.push(Sprite_Character.new(@viewport1, event)) end $game_map.vehicles.each do |vehicle| @character_sprites.push(Sprite_Character.new(@viewport1, vehicle)) end $game_player.followers.reverse_each do |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
W{IA1W9[IP}40H3%X@7{HC3.png (198.55 KB, 下载次数: 18)
`XIQE4(FNF8L`YOA72_88[2.png (137.63 KB, 下载次数: 18)
DEYTSI$GR%}I5C`41RV1B{8.png (16.67 KB, 下载次数: 20)
{)R{$MHEM9(V}0IGTPON%VV.png (8.52 KB, 下载次数: 21)
| 欢迎光临 Project1 (https://rpg.blue/) | Powered by Discuz! X3.1 |