装备变更行走图 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]