查看: 13|回复: 0

[原创发布] 仿半条命/反恐精英喷绘系统

[复制链接]
梦石
1
星屑
1219
在线时间
60 小时
注册时间
2023-9-8
回帖
20
发表于 4 小时前 | 显示全部楼层 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有账号?注册会员

×
本帖最后由 Ruigi 于 2026-10-12 01:23 编辑
  1. #==============================================================================
  2. # ** HL1 喷绘系统
  3. #------------------------------------------------------------------------------
  4. #  半条命风格喷绘系统,支持菜单选择图案、颜色调整和快速喷绘
  5. #  作者:Ruigi
  6. #  - 喷绘图片放在 Graphics/Sprays/ 文件夹(PNG 格式)
  7. #  - 按 A 键快速喷绘
  8. #  - 按住 Shift 同时喷绘,喷绘出现在玩家前方一格(墙上喷绘)
  9. #  - 主菜单 -> 喷绘 进入设置界面
  10. #  - 可配置喷绘混合模式(BLEND_TYPE)
  11. #==============================================================================

  12. module HL1_Spray
  13.   #--------------------------------------------------------------------------
  14.   # * 配置
  15.   #--------------------------------------------------------------------------
  16.   FOLDER_PATH = "Graphics/Sprays/"   # 喷绘图片文件夹
  17.   SE_NAME     = "Spray"              # 喷绘音效文件名(不含扩展名)
  18.   SE_VOLUME   = 80                   # 音量 (0-100)
  19.   SE_PITCH    = 100                  # 音调 (50-150)
  20.   MAX_SPRAYS  = 10                   # 地图上最多保留喷绘数量(超出移除最旧)
  21.   SPRAY_OFFSET_X = 0                 # 原地喷绘相对于玩家屏幕坐标的 X 偏移
  22.   SPRAY_OFFSET_Y = 0               # 原地喷绘 Y 偏移(负值向上,通常放在脚底)
  23.   SPRAY_Z     = 99                  # Z 坐标(高于角色,低于天气)
  24.   SPRAY_KEY   = 0x48                 # 虚拟键码 0x48 = H 键
  25.   FALLBACK_KEY = Input::X            # Win32API 不可用时的备用键(X 键)
  26.   COLOR_EDIT_KEY = Input::Y          # 切换颜色编辑的按键(Y 键)
  27.   COLOR_EDIT_KEY_NAME = "Y"          # 颜色编辑键的显示名称
  28.   #--------------------------------------------------------------------------
  29.   # * 混合模式
  30.   #  0 = 正常混合
  31.   #  1 = 加法混合(变亮)
  32.   #  2 = 减法混合(变暗,近似正片叠底)
  33.   #--------------------------------------------------------------------------
  34.   BLEND_TYPE = 0                     # 设置为 2 实现正片叠底效果
  35.   #--------------------------------------------------------------------------
  36.   # * 墙上喷绘偏移(前方一格喷绘时的微调)
  37.   #--------------------------------------------------------------------------
  38.   WALL_SPRAY_OFFSET_X = 0            # 前方一格喷绘的 X 偏移(像素)
  39.   WALL_SPRAY_OFFSET_Y = 0            # 前方一格喷绘的 Y 偏移(像素)
  40.   #--------------------------------------------------------------------------
  41.   # * 内部变量
  42.   #--------------------------------------------------------------------------
  43.   @spray_list = []                   # 喷绘文件名列表(不含路径和扩展名)
  44.   @current_index = 0                 # 当前选中的喷绘索引
  45.   @spray_color = Color.new(255,255,255,255)  # 当前喷绘颜色 (RGBA)
  46.   @initialized = false               # 是否已加载列表
  47.   @h_was_down = false                # 喷绘键上一帧状态(用于触发检测)

  48.   # 尝试加载 Win32API(用于检测 H 键)
  49.   begin
  50.     require 'Win32API'
  51.     GET_ASYNC_KEY_STATE = Win32API.new('user32', 'GetAsyncKeyState', 'i', 'i')
  52.     USE_WIN32API = true
  53.   rescue LoadError
  54.     GET_ASYNC_KEY_STATE = nil
  55.     USE_WIN32API = false
  56.   end

  57.   class << self
  58.     #------------------------------------------------------------------------
  59.     # * 加载喷绘文件列表
  60.     #------------------------------------------------------------------------
  61.     def load_sprays
  62.       @spray_list.clear
  63.       Dir.glob(FOLDER_PATH + "*.png").sort.each do |path|
  64.         @spray_list << File.basename(path, ".png")
  65.       end
  66.       @initialized = true
  67.       p "[喷绘系统] 已加载 #{@spray_list.size} 个喷绘图片。" if $TEST
  68.     end

  69.     #------------------------------------------------------------------------
  70.     # * 获取喷绘名称列表
  71.     #------------------------------------------------------------------------
  72.     def spray_list
  73.       load_sprays unless @initialized
  74.       @spray_list
  75.     end

  76.     #------------------------------------------------------------------------
  77.     # * 获取/设置当前喷绘索引
  78.     #------------------------------------------------------------------------
  79.     def current_index
  80.       load_sprays unless @initialized
  81.       @current_index
  82.     end

  83.     def current_index=(index)
  84.       load_sprays unless @initialized
  85.       @current_index = index.clamp(0, [spray_list.size - 1, 0].max)
  86.     end

  87.     #------------------------------------------------------------------------
  88.     # * 获取/设置当前喷绘颜色
  89.     #------------------------------------------------------------------------
  90.     def spray_color
  91.       @spray_color
  92.     end

  93.     def spray_color=(color)
  94.       @spray_color = color
  95.     end

  96.     #------------------------------------------------------------------------
  97.     # * 获取当前喷绘位图(失败返回 nil)
  98.     #------------------------------------------------------------------------
  99.     def current_bitmap
  100.       return nil if spray_list.empty?
  101.       name = spray_list[current_index]
  102.       return nil unless name
  103.       path = FOLDER_PATH + name + ".png"
  104.       begin
  105.         bitmap = Bitmap.new(path)
  106.         p "[喷绘系统] 成功加载图片: #{path}" if $TEST
  107.         return bitmap
  108.       rescue => e
  109.         p "[喷绘系统] 加载图片失败: #{path} - #{e.message}" if $TEST
  110.         return nil
  111.       end
  112.     end

  113.     #------------------------------------------------------------------------
  114.     # * 设置当前喷绘(兼容旧接口)
  115.     #------------------------------------------------------------------------
  116.     def set_spray(index)
  117.       self.current_index = index
  118.     end

  119.     #------------------------------------------------------------------------
  120.     # * 打开喷绘选择场景(脚本调用兼容)
  121.     #------------------------------------------------------------------------
  122.     def choose_spray
  123.       SceneManager.call(Scene_SpraySelect)
  124.     end

  125.     #------------------------------------------------------------------------
  126.     # * 检测喷绘键是否刚按下(触发)
  127.     #------------------------------------------------------------------------
  128.     def h_trigger?
  129.       if USE_WIN32API
  130.         down = (GET_ASYNC_KEY_STATE.call(SPRAY_KEY) & 0x8000) != 0
  131.         trigger = down && !@h_was_down
  132.         @h_was_down = down
  133.         p "[喷绘系统] 喷绘键按下: #{trigger}" if $TEST && trigger
  134.         trigger
  135.       else
  136.         Input.trigger?(FALLBACK_KEY)
  137.       end
  138.     end

  139.     #------------------------------------------------------------------------
  140.     # * 执行喷绘
  141.     #------------------------------------------------------------------------
  142.     def spray
  143.       return unless SceneManager.scene_is?(Scene_Map)
  144.       p "[喷绘系统] 尝试喷绘..." if $TEST
  145.       bitmap = current_bitmap
  146.       if bitmap.nil?
  147.         RPG::SE.new("Buzzer1", 80, 100).play
  148.         p "[喷绘系统] 没有可用的喷绘图片!" if $TEST
  149.         return
  150.       end

  151.       # 根据 Shift 键状态决定喷绘位置
  152.       if Input.press?(Input::SHIFT)
  153.         # 前方一格喷绘(墙上喷绘)
  154.         tx = $game_player.x
  155.         ty = $game_player.y
  156.         case $game_player.direction
  157.         when 2 then ty += 1   # 下
  158.         when 4 then tx -= 1   # 左
  159.         when 6 then tx += 1   # 右
  160.         when 8 then ty -= 1   # 上
  161.         end
  162.         map_x = tx * 32 + 16 + HL1_Spray::WALL_SPRAY_OFFSET_X
  163.         map_y = ty * 32 + 16 + HL1_Spray::WALL_SPRAY_OFFSET_Y
  164.         p "[喷绘系统] 前方一格喷绘,格子(#{tx}, #{ty})" if $TEST
  165.       else
  166.         # 原地喷绘
  167.         screen_x = $game_player.screen_x + HL1_Spray::SPRAY_OFFSET_X
  168.         screen_y = $game_player.screen_y + HL1_Spray::SPRAY_OFFSET_Y
  169.         map_x = screen_x + $game_map.display_x * 32
  170.         map_y = screen_y + $game_map.display_y * 32
  171.         p "[喷绘系统] 原地喷绘" if $TEST
  172.       end

  173.       spriteset = SceneManager.scene.instance_variable_get(:@spriteset)
  174.       if spriteset
  175.         spriteset.add_spray(map_x, map_y, bitmap, spray_color)
  176.         RPG::SE.new(SE_NAME, SE_VOLUME, SE_PITCH).play
  177.         p "[喷绘系统] 喷绘已添加,坐标: (#{map_x}, #{map_y})" if $TEST
  178.       else
  179.         p "[喷绘系统] 找不到 Spriteset!" if $TEST
  180.         bitmap.dispose
  181.       end
  182.     end
  183.   end
  184. end

  185. #==============================================================================
  186. # ** 喷绘列表窗口(继承自 Window_Selectable,但屏蔽内置输入处理)
  187. #==============================================================================

  188. class Window_SprayList < Window_Selectable
  189.   #--------------------------------------------------------------------------
  190.   # * 初始化
  191.   #--------------------------------------------------------------------------
  192.   def initialize(x, y, width, height)
  193.     super
  194.     @data = HL1_Spray.spray_list
  195.     self.contents = Bitmap.new(width - 32, [height - 32, 1].max)
  196.     refresh
  197.     self.index = HL1_Spray.current_index
  198.     activate
  199.   end

  200.   #--------------------------------------------------------------------------
  201.   # * 获取项目数
  202.   #--------------------------------------------------------------------------
  203.   def item_max
  204.     @data ? @data.size : 0
  205.   end

  206.   #--------------------------------------------------------------------------
  207.   # * 绘制项目
  208.   #--------------------------------------------------------------------------
  209.   def draw_item(index)
  210.     rect = item_rect(index)
  211.     self.contents.clear_rect(rect)
  212.     name = @data[index]
  213.     self.contents.draw_text(rect, name, 1)
  214.   end

  215.   #--------------------------------------------------------------------------
  216.   # * 刷新
  217.   #--------------------------------------------------------------------------
  218.   def refresh
  219.     self.contents.clear
  220.     draw_all_items
  221.   end

  222.   #--------------------------------------------------------------------------
  223.   # * 重写 update,阻止内置方向键等处理
  224.   #--------------------------------------------------------------------------
  225.   def update
  226.     # 留空,不调用 super,完全由场景手动控制
  227.   end
  228. end

  229. #==============================================================================
  230. # ** 喷绘预览窗口
  231. #==============================================================================

  232. class Window_SprayPreview < Window_Base
  233.   #--------------------------------------------------------------------------
  234.   # * 初始化
  235.   #--------------------------------------------------------------------------
  236.   def initialize(x, y, width, height)
  237.     super
  238.     @sprite = Sprite.new
  239.     @sprite.bitmap = nil
  240.     @sprite.x = x + 16
  241.     @sprite.y = y + 16
  242.     @sprite.z = 200
  243.     @sprite.blend_type = HL1_Spray::BLEND_TYPE  # 应用混合模式
  244.     refresh
  245.   end

  246.   #--------------------------------------------------------------------------
  247.   # * 刷新预览
  248.   #--------------------------------------------------------------------------
  249.   def refresh
  250.     bitmap = HL1_Spray.current_bitmap
  251.     if bitmap.nil?
  252.       @sprite.bitmap = nil
  253.       self.contents.clear
  254.       self.contents.font.color = crisis_color
  255.       self.contents.draw_text(0, 0, contents_width, 24, "未找到图片", 1)
  256.       return
  257.     end
  258.     @sprite.bitmap = bitmap
  259.     @sprite.color = HL1_Spray.spray_color
  260.     self.contents.clear
  261.   end

  262.   #--------------------------------------------------------------------------
  263.   # * 更新预览(外部调用)
  264.   #--------------------------------------------------------------------------
  265.   def update_preview
  266.     refresh
  267.   end

  268.   #--------------------------------------------------------------------------
  269.   # * 释放资源
  270.   #--------------------------------------------------------------------------
  271.   def dispose
  272.     @sprite.bitmap.dispose if @sprite.bitmap
  273.     @sprite.dispose
  274.     super
  275.   end
  276. end

  277. #==============================================================================
  278. # ** 颜色调整窗口
  279. #==============================================================================

  280. class Window_SprayColor < Window_Base
  281.   attr_reader :red, :green, :blue
  282.   attr_accessor :index   # 0=红, 1=绿, 2=蓝

  283.   def initialize(x, y, width, height)
  284.     super
  285.     @red, @green, [url=home.php?mod=space&uid=8492]@blue[/url] = HL1_Spray.spray_color.red, HL1_Spray.spray_color.green, HL1_Spray.spray_color.blue
  286.     @index = 0
  287.     self.contents = Bitmap.new(width - 32, height - 32)
  288.     refresh
  289.   end

  290.   def refresh
  291.     self.contents.clear
  292.     labels = ["红:", "绿:", "蓝:"]
  293.     values = [@red, @green, @blue]
  294.     labels.each_with_index do |label, i|
  295.       y = i * 28
  296.       if i == @index
  297.         self.contents.font.color = crisis_color
  298.         self.contents.fill_rect(0, y, contents_width, 24, Color.new(255,255,255,80))
  299.       else
  300.         self.contents.font.color = system_color
  301.       end
  302.       self.contents.draw_text(0, y, 60, 24, label)
  303.       self.contents.font.color = normal_color
  304.       self.contents.draw_text(70, y, 60, 24, values[i].to_s, 2)
  305.       bar_color = case i
  306.                   when 0 then Color.new(@red, 0, 0)
  307.                   when 1 then Color.new(0, @green, 0)
  308.                   when 2 then Color.new(0, 0, @blue)
  309.                   end
  310.       self.contents.fill_rect(140, y + 4, 100, 16, bar_color)
  311.     end
  312.   end

  313.   def change_color(delta)
  314.     case @index
  315.     when 0 then [url=home.php?mod=space&uid=71174]@red[/url] = (@red + delta).clamp(0, 255)
  316.     when 1 then @green = (@green + delta).clamp(0, 255)
  317.     when 2 then @blue = (@blue + delta).clamp(0, 255)
  318.     end
  319.     HL1_Spray.spray_color = Color.new(@red, @green, @blue, 255)
  320.     refresh
  321.   end
  322. end

  323. #==============================================================================
  324. # ** 喷绘设置场景
  325. #==============================================================================

  326. class Scene_SpraySelect < Scene_MenuBase
  327.   def start
  328.     super
  329.     create_background
  330.     create_list_window
  331.     create_preview_window
  332.     create_color_window
  333.     create_help_window
  334.     @list_window.index = HL1_Spray.current_index
  335.     @color_mode = false
  336.     update_preview
  337.     p "[喷绘系统] 设置场景已启动。列表项: #{HL1_Spray.spray_list.size}" if $TEST
  338.   end

  339.   def create_list_window
  340.     @list_window = Window_SprayList.new(0, 48, 240, Graphics.height - 48 - 120)
  341.     @list_window.activate
  342.   end

  343.   def create_preview_window
  344.     @preview_window = Window_SprayPreview.new(240, 48, Graphics.width - 240, 200)
  345.   end

  346.   def create_color_window
  347.     @color_window = Window_SprayColor.new(240, 248, Graphics.width - 240, 120)
  348.   end

  349.   def create_help_window
  350.     @help_window = Window_Help.new(1)
  351.     @help_window.set_text("选择喷绘图案。按 #{HL1_Spray::COLOR_EDIT_KEY_NAME} 或 Enter 编辑颜色。")
  352.   end

  353.   def update_preview
  354.     @preview_window.update_preview
  355.   end

  356.   def update
  357.     super
  358.     @preview_window.update
  359.     @color_window.update
  360.     @help_window.update
  361.     # 注意:不调用 @list_window.update,因为其 update 已被重写为空

  362.     if !@color_mode
  363.       # 列表编辑模式
  364.       if Input.trigger?(Input::UP) || Input.trigger?(Input::DOWN)
  365.         if Input.trigger?(Input::UP)
  366.           @list_window.index = (@list_window.index - 1) % @list_window.item_max
  367.         else
  368.           @list_window.index = (@list_window.index + 1) % @list_window.item_max
  369.         end
  370.         # 同步全局当前索引
  371.         HL1_Spray.current_index = @list_window.index
  372.         @list_window.refresh
  373.         update_preview
  374.         Sound.play_cursor
  375.       end

  376.       if Input.trigger?(Input::C) || Input.trigger?(HL1_Spray::COLOR_EDIT_KEY)
  377.         @color_mode = true
  378.         @list_window.deactivate
  379.         @help_window.set_text("调整颜色:上下键选择分量,左右键调整数值。Enter 保存,Esc 返回。")
  380.       end

  381.       if Input.trigger?(Input::B)
  382.         return_scene
  383.       end
  384.     else
  385.       # 颜色编辑模式
  386.       if Input.trigger?(Input::B)
  387.         @color_mode = false
  388.         @list_window.activate
  389.         @help_window.set_text("选择喷绘图案。按 #{HL1_Spray::COLOR_EDIT_KEY_NAME} 或 Enter 编辑颜色。")
  390.       end

  391.       if Input.trigger?(Input::C)
  392.         HL1_Spray.current_index = @list_window.index  # 保存最终选择
  393.         return_scene
  394.       end

  395.       if Input.trigger?(Input::UP) || Input.trigger?(Input::DOWN)
  396.         if Input.trigger?(Input::UP)
  397.           @color_window.index = (@color_window.index - 1) % 3
  398.         else
  399.           @color_window.index = (@color_window.index + 1) % 3
  400.         end
  401.         @color_window.refresh
  402.         Sound.play_cursor
  403.       end

  404.       if Input.repeat?(Input::RIGHT)
  405.         @color_window.change_color(5)
  406.         update_preview
  407.       elsif Input.repeat?(Input::LEFT)
  408.         @color_window.change_color(-5)
  409.         update_preview
  410.       end
  411.     end
  412.   end

  413.   def terminate
  414.     super
  415.     @list_window.dispose
  416.     @preview_window.dispose
  417.     @color_window.dispose
  418.     @help_window.dispose
  419.   end
  420. end

  421. #==============================================================================
  422. # ** 主菜单场景
  423. #==============================================================================

  424. class Scene_Menu < Scene_MenuBase
  425.   alias hl1_spray_create_command_window create_command_window
  426.   def create_command_window
  427.     hl1_spray_create_command_window
  428.     @command_window.set_handler(:spray, method(:command_spray))
  429.   end

  430.   def command_spray
  431.     SceneManager.call(Scene_SpraySelect)
  432.   end
  433. end

  434. #==============================================================================
  435. # ** 菜单命令窗口
  436. #==============================================================================

  437. class Window_MenuCommand < Window_Command
  438.   alias hl1_spray_make_command_list make_command_list
  439.   def make_command_list
  440.     hl1_spray_make_command_list
  441.     add_command("喷绘", :spray)
  442.   end
  443. end

  444. #==============================================================================
  445. # ** 地图精灵集合
  446. #==============================================================================

  447. class Spriteset_Map
  448.   alias hl1_spray_initialize initialize
  449.   alias hl1_spray_update update
  450.   alias hl1_spray_dispose dispose

  451.   def initialize
  452.     @spray_sprites = []
  453.     hl1_spray_initialize
  454.   end

  455.   def update
  456.     hl1_spray_update
  457.     update_spray_sprites
  458.   end

  459.   def update_spray_sprites
  460.     @spray_sprites.each do |entry|
  461.       sprite, map_x, map_y = entry
  462.       screen_x = map_x - $game_map.display_x * 32
  463.       screen_y = map_y - $game_map.display_y * 32
  464.       sprite.x = screen_x + HL1_Spray::SPRAY_OFFSET_X
  465.       sprite.y = screen_y + HL1_Spray::SPRAY_OFFSET_Y
  466.     end
  467.   end

  468.   def add_spray(map_x, map_y, bitmap, color = Color.new(255,255,255,255))
  469.     sprite = Sprite.new(@viewport1)
  470.     sprite.bitmap = bitmap
  471.     sprite.ox = bitmap.width / 2
  472.     sprite.oy = bitmap.height / 2
  473.     sprite.z = HL1_Spray::SPRAY_Z
  474.     sprite.color = color
  475.     sprite.blend_type = HL1_Spray::BLEND_TYPE  # 应用混合模式
  476.     screen_x = map_x - $game_map.display_x * 32
  477.     screen_y = map_y - $game_map.display_y * 32
  478.     sprite.x = screen_x + HL1_Spray::SPRAY_OFFSET_X
  479.     sprite.y = screen_y + HL1_Spray::SPRAY_OFFSET_Y
  480.     @spray_sprites << [sprite, map_x, map_y]
  481.     while @spray_sprites.size > HL1_Spray::MAX_SPRAYS
  482.       old_entry = @spray_sprites.shift
  483.       old_entry[0].bitmap.dispose
  484.       old_entry[0].dispose
  485.     end
  486.   end

  487.   def dispose
  488.     @spray_sprites.each do |entry|
  489.       entry[0].bitmap.dispose
  490.       entry[0].dispose
  491.     end
  492.     @spray_sprites.clear
  493.     hl1_spray_dispose
  494.   end
  495. end

  496. #==============================================================================
  497. # ** 地图场景
  498. #==============================================================================

  499. class Scene_Map
  500.   alias hl1_spray_update update

  501.   def update
  502.     hl1_spray_update
  503.     if HL1_Spray.h_trigger?
  504.       HL1_Spray.spray
  505.     end
  506.   end
  507. end
复制代码
QQ截图20261012011930.png
QQ截图20261012012037.png
QQ截图20261012012308.png
您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部