| 赞 | 5 |
| VIP | |
| 好人卡 | |
| 积分 | 22 |
| 经验 | |
| 最后登录 | 2026-10-12 |
| 在线时间 | 60 小时 |
- 梦石
- 1
- 星屑
- 1219
- 在线时间
- 60 小时
- 注册时间
- 2023-9-8
- 回帖
- 20
|
加入我们,或者,欢迎回来。
您需要 登录 才可以下载或查看,没有账号?注册会员
×
- #==============================================================================
- # ▼ 射击系统脚本 (Shooting System)
- # 作者:Ruigi
- # -- 放在 Main 之前
- #==============================================================================
- # 标签(写在技能/物品的「备注」栏):
- # <gun> 标记为射击类
- # <guntime:数字> 射击模式持续时间(帧),默认 300
- # <bullet:物品编号> 每次射击消耗的物品ID
- # <kind:动画编号> 持枪状态动画(持续循环播放)
- # <shoot:动画编号> 射击命中敌人时对目标播放的动画
- # <Impact:动画编号> 准星位置播放的弹孔/火花(无论命中与否都播放)
- # <Precision:数字> 准星平滑度(1 = 不滑,越大越滑越难瞄)
- # <Automatic:秒数> 自动挡模式(支持小数)
- # <Xrecoil:幅度> 水平后坐力(像素)
- # <Yrecoil:幅度> 垂直后坐力(像素)
- #
- # ★ 射击技能的「使用效果」要设置伤害公式(如 a.atk * 4 - b.def * 2)
- #==============================================================================
- module Shooting_Config
- # ---- 基础 ----
- CROSSHAIR_IMAGE = "crosshair"
- CROSSHAIR_SIZE = [48, 48]
- DEFAULT_GUNTIME = 300
- SHOOT_KEY = :C
- HIT_SOUND = "gun1"
- EXIT_KEYS = [:B]
- USE_MOUSE_LEFT = true
- SHOT_COOLDOWN = 8
- KEYBOARD_SPEED = 6
- CHECK_BULLET_ON_ENTER = true
- HIT_RECT_PADDING = 0
- # ---- 爆头机制 ----
- HEADSHOT_ENABLED = true
- HEADSHOT_RATIO = 0.35
- HEADSHOT_MULTIPLIER = 2.0
- HEADSHOT_SOUND = "Critical"
- HEADSHOT_TEXT = "爆头!"
- # ---- 准星平滑 ----
- DEFAULT_PRECISION = 3
- # ---- 后坐力 ----
- RECOIL_DECAY = 0.82
- # ---- 弹孔 / 空枪反馈 ----
- IMPACT_OFFSET_X = 0
- IMPACT_OFFSET_Y = 0
- IMPACT_Z = 8000
- # ★ 每次射击(命中或未命中)都重播一次 <kind:> 动画
- # true = 每次开火都重播(更有"枪口抖动"感)
- # false = 只在未命中时重播(默认,避免命中时视觉过乱)
- REPLAY_KIND_ON_HIT = false
- # ---- 子弹 HUD ----
- HUD_X = 0
- HUD_Y = 0
- HUD_WIDTH = 200
- HUD_Z = 9000
- HUD_SHOW_NAME = true
- HUD_LOW_COLOR = true
- HUD_LOW_THRESHOLD = 3
- # ---- 自动挡 ----
- AUTO_HUD_LABEL = "[自动]"
- # ---- 调试 ----
- DEBUG_AUTO = false
- end
- #==============================================================================
- # ■ Spriteset_Battle - 开放精灵数组读取
- #==============================================================================
- class Spriteset_Battle
- attr_reader :actor_sprites, :enemy_sprites
- end
- #==============================================================================
- # ■ NoteNum - 宽容的标签数字解析
- #==============================================================================
- module NoteNum
- FULLWIDTH_DIGITS = {
- "0"=>"0","1"=>"1","2"=>"2","3"=>"3","4"=>"4",
- "5"=>"5","6"=>"6","7"=>"7","8"=>"8","9"=>"9"
- }
- def self.extract(note, tag)
- m = note.match(/<#{tag}\s*:\s*([^>\r\n]*)/i)
- return nil unless m
- raw = m[1].strip
- num = raw[/[0-90-9..,,]+/]
- return nil unless num
- s = num.each_char.map { |c| FULLWIDTH_DIGITS[c] || c }.join
- s = s.tr(".,,", ".")
- s.to_f
- end
- end
- #==============================================================================
- # ■ RPG::UsableItem - 标签解析
- #==============================================================================
- class RPG::UsableItem < RPG::BaseItem
- def shooting?
- return @shooting unless @shooting.nil?
- @shooting = !note[/<gun>/i].nil?
- end
- def gun_time
- return @gun_time unless @gun_time.nil?
- v = NoteNum.extract(note, "guntime")
- @gun_time = v ? v.to_i : Shooting_Config::DEFAULT_GUNTIME
- end
- def bullet_id
- return @bullet_id unless @bullet_id.nil?
- v = NoteNum.extract(note, "bullet")
- @bullet_id = v ? v.to_i : 0
- end
- def kind_animation_id
- return @kind_anim unless @kind_anim.nil?
- v = NoteNum.extract(note, "kind")
- @kind_anim = v ? v.to_i : 0
- end
- def shoot_animation_id
- return @shoot_anim unless @shoot_anim.nil?
- v = NoteNum.extract(note, "shoot")
- @shoot_anim = v ? v.to_i : 0
- end
- def impact_animation_id
- return @impact_anim unless @impact_anim.nil?
- v = NoteNum.extract(note, "Impact")
- @impact_anim = v ? v.to_i : 0
- end
- def precision
- return @precision unless @precision.nil?
- v = NoteNum.extract(note, "Precision")
- @precision = v ? v.to_i : Shooting_Config::DEFAULT_PRECISION
- end
- def automatic_interval
- return @auto_interval unless @auto_interval.nil?
- v = NoteNum.extract(note, "Automatic")
- @auto_interval = v && v > 0 ? v : 0.0
- end
- def automatic_interval_frames
- return 0 if automatic_interval <= 0
- [(automatic_interval * 60).round, 1].max
- end
- def automatic?
- automatic_interval > 0
- end
- def x_recoil
- return @x_recoil unless @x_recoil.nil?
- v = NoteNum.extract(note, "Xrecoil")
- @x_recoil = v && v > 0 ? v : 0.0
- end
- def y_recoil
- return @y_recoil unless @y_recoil.nil?
- v = NoteNum.extract(note, "Yrecoil")
- @y_recoil = v && v > 0 ? v : 0.0
- end
- end
- #==============================================================================
- # ■ Mouse - 鼠标辅助
- #==============================================================================
- module Mouse
- WIN32_AVAILABLE = defined?(::Win32API) ? true : false
- if WIN32_AVAILABLE
- @get_cursor_pos = ::Win32API.new("user32", "GetCursorPos", "P", "I")
- @screen_to_client = ::Win32API.new("user32", "ScreenToClient", "LP", "I")
- @find_window = ::Win32API.new("user32", "FindWindow", "PP", "L")
- @get_async_key = ::Win32API.new("user32", "GetAsyncKeyState", "i", "i")
- end
- @cached_pos = [320, 240]
- @prev_left = false
- def self.available?
- WIN32_AVAILABLE
- end
- def self.find_hwnd
- return 0 unless WIN32_AVAILABLE
- ["RGSS Player", "RGSS3 Player", "RPG Maker VX Ace", "Game"].each do |t|
- hwnd = @find_window.call(nil, t)
- return hwnd if hwnd != 0
- end
- 0
- end
- def self.position
- return @cached_pos unless WIN32_AVAILABLE
- hwnd = find_hwnd
- return @cached_pos if hwnd == 0
- pos = [0, 0].pack("ll")
- @get_cursor_pos.call(pos)
- @screen_to_client.call(hwnd, pos)
- @cached_pos = pos.unpack("ll")
- end
- def self.left_held?
- return false unless WIN32_AVAILABLE
- (@get_async_key.call(0x01) & 0x8000) != 0
- end
- def self.left_click?
- now = left_held?
- result = now && !@prev_left
- @prev_left = now
- result
- end
- end
- #==============================================================================
- # ■ Sprite_Crosshair - 准星精灵
- #==============================================================================
- class Sprite_Crosshair < Sprite
- attr_accessor :precision
- def initialize(viewport = nil)
- super(viewport)
- @active = false
- @precision = Shooting_Config::DEFAULT_PRECISION
- @target_x = 320
- @target_y = 240
- @current_x = 320
- @current_y = 240
- @recoil_x = 0.0
- @recoil_y = 0.0
- begin
- self.bitmap = Cache.picture(Shooting_Config::CROSSHAIR_IMAGE)
- rescue
- self.bitmap = nil
- end
- if self.bitmap
- self.zoom_x = Shooting_Config::CROSSHAIR_SIZE[0].to_f / self.bitmap.width
- self.zoom_y = Shooting_Config::CROSSHAIR_SIZE[1].to_f / self.bitmap.height
- else
- self.bitmap = Bitmap.new(32, 32)
- c = Color.new(255, 0, 0)
- self.bitmap.draw_line(16, 0, 16, 12, c)
- self.bitmap.draw_line(16, 20, 16, 32, c)
- self.bitmap.draw_line(0, 16, 12, 16, c)
- self.bitmap.draw_line(20, 16, 32, 16, c)
- self.bitmap.draw_line(6, 6, 12, 12, c)
- self.bitmap.draw_line(26, 6, 20, 12, c)
- self.bitmap.draw_line(6, 26, 12, 20, c)
- self.bitmap.draw_line(26, 26, 20, 20, c)
- end
- self.visible = false
- self.z = 9999
- end
- def activate
- @active = true
- self.visible = true
- if Mouse.available?
- @target_x, @target_y = Mouse.position
- else
- @target_x = 320
- @target_y = 240
- end
- @current_x = @target_x
- @current_y = @target_y
- @recoil_x = 0.0
- @recoil_y = 0.0
- update_position
- end
- def deactivate
- @active = false
- self.visible = false
- end
- def active?
- @active
- end
- def screen_pos
- [@current_x + @recoil_x, @current_y + @recoil_y]
- end
- def add_recoil(x_amp, y_amp)
- @recoil_x += (rand * 2.0 - 1.0) * x_amp if x_amp && x_amp > 0
- @recoil_y += (rand * 2.0 - 1.0) * y_amp if y_amp && y_amp > 0
- end
- def update_position
- return unless @active
- if Mouse.available?
- @target_x, @target_y = Mouse.position
- else
- @target_x -= Shooting_Config::KEYBOARD_SPEED if Input.press?(:LEFT)
- @target_x += Shooting_Config::KEYBOARD_SPEED if Input.press?(:RIGHT)
- @target_y -= Shooting_Config::KEYBOARD_SPEED if Input.press?(:UP)
- @target_y += Shooting_Config::KEYBOARD_SPEED if Input.press?(:DOWN)
- @target_x = [[@target_x, 0].max, 640].min
- @target_y = [[@target_y, 0].max, 480].min
- end
- factor = 1.0 / [@precision, 1].max
- @current_x += (@target_x - @current_x) * factor
- @current_y += (@target_y - @current_y) * factor
- @current_x = @target_x if (@target_x - @current_x).abs < 0.5
- @current_y = @target_y if (@target_y - @current_y).abs < 0.5
- @recoil_x *= Shooting_Config::RECOIL_DECAY
- @recoil_y *= Shooting_Config::RECOIL_DECAY
- @recoil_x = 0.0 if @recoil_x.abs < 0.1
- @recoil_y = 0.0 if @recoil_y.abs < 0.1
- draw_x = @current_x + @recoil_x
- draw_y = @current_y + @recoil_y
- w = self.bitmap ? self.bitmap.width * zoom_x : 0
- h = self.bitmap ? self.bitmap.height * zoom_y : 0
- self.x = draw_x - w / 2
- self.y = draw_y - h / 2
- end
- end
- #==============================================================================
- # ■ Sprite_Impact - 弹孔动画精灵
- #==============================================================================
- class Sprite_Impact < Sprite_Base
- def initialize(viewport = nil)
- super(viewport)
- self.visible = false
- self.z = Shooting_Config::IMPACT_Z
- @playing = false
- end
- def play(x, y, animation_id)
- return if animation_id.nil? || animation_id <= 0
- animation = $data_animations[animation_id]
- return unless animation
- self.x = x + Shooting_Config::IMPACT_OFFSET_X
- self.y = y + Shooting_Config::IMPACT_OFFSET_Y
- @playing = true
- self.visible = true
- start_animation(animation)
- end
- def update
- super
- if @playing && !animation?
- @playing = false
- self.visible = false
- end
- end
- end
- #==============================================================================
- # ■ Window_BulletCounter - 左上角子弹数量 HUD
- #==============================================================================
- class Window_BulletCounter < Window_Base
- def initialize
- super(Shooting_Config::HUD_X, Shooting_Config::HUD_Y,
- Shooting_Config::HUD_WIDTH, fitting_height(1))
- self.z = Shooting_Config::HUD_Z
- self.visible = false
- self.openness = 255
- [url=home.php?mod=space&uid=363652]@Bullet[/url] = nil
- @count = 0
- @automatic = false
- end
- def show_bullet(item, automatic = false)
- return unless item
- @bullet = item
- @automatic = automatic
- refresh
- self.visible = true
- end
- def hide_bullet
- self.visible = false
- @bullet = nil
- end
- def update_count
- return unless @bullet
- new_count = $game_party.item_number(@bullet)
- return if new_count == @count
- @count = new_count
- refresh
- end
- def refresh
- contents.clear
- return unless @bullet
- @count = $game_party.item_number(@bullet)
- draw_icon(@bullet.icon_index, 0, 0)
- offset = 28
- if @automatic && Shooting_Config::AUTO_HUD_LABEL && !Shooting_Config::AUTO_HUD_LABEL.empty?
- change_color(Color.new(120, 200, 255))
- draw_text(offset, 0, 48, line_height, Shooting_Config::AUTO_HUD_LABEL)
- change_color(normal_color)
- offset += 48
- end
- if Shooting_Config::HUD_SHOW_NAME
- change_color(normal_color)
- draw_text(offset, 0, 120 - (offset - 28), line_height, @bullet.name)
- count_x = 148
- count_w = contents.width - count_x
- else
- count_x = offset
- count_w = contents.width - count_x
- end
- if Shooting_Config::HUD_LOW_COLOR && @count <= Shooting_Config::HUD_LOW_THRESHOLD
- change_color(Color.new(255, 80, 80))
- else
- change_color(system_color)
- end
- draw_text(count_x, 0, count_w, line_height, "x#{@count}", 2)
- change_color(normal_color)
- end
- end
- #==============================================================================
- # ■ Game_Battler - 射击状态管理 + 爆头伤害加成
- #==============================================================================
- class Game_Battler < Game_BattlerBase
- attr_accessor :gun_active
- attr_accessor :gun_time_remaining
- attr_accessor :gun_item
- attr_accessor :shooting_headshot
- alias shooting_init_game_battler initialize
- def initialize
- shooting_init_game_battler
- @gun_active = false
- @gun_time_remaining = 0
- @gun_item = nil
- @shooting_headshot = false
- end
- def start_gun(item)
- @gun_active = true
- @gun_time_remaining = item.gun_time
- @gun_item = item
- end
- def stop_gun
- @gun_active = false
- @gun_time_remaining = 0
- @gun_item = nil
- end
- def update_gun
- return unless @gun_active
- @gun_time_remaining -= 1
- stop_gun if @gun_time_remaining <= 0
- end
- def gun_active?
- @gun_active
- end
- alias shooting_make_damage_value make_damage_value
- def make_damage_value(user, item)
- shooting_make_damage_value(user, item)
- if @shooting_headshot && Shooting_Config::HEADSHOT_ENABLED && @result.hp_damage > 0
- multiplier = Shooting_Config::HEADSHOT_MULTIPLIER
- @result.hp_damage = (@result.hp_damage * multiplier).to_i
- end
- end
- end
- #==============================================================================
- # ■ Game_Enemy - 命中判定后备矩形
- #==============================================================================
- # 注意:不覆盖 screen_x / screen_y / screen_z —— 保留引擎原版定义,
- # 让 $data_troops 里手动摆放的敌人坐标继续生效。
- # 这里只新增一个 screen_rect 后备方法,仅在找不到精灵时使用。
- class Game_Enemy < Game_Battler
- def screen_rect
- # 引擎原版的 screen_x / screen_y 是精灵的绘制坐标(左上角)
- # 真实尺寸由精灵决定,找不到精灵时给一个宽松的默认矩形
- Rect.new(screen_x, screen_y, 64, 64)
- end
- end
- #==============================================================================
- # ■ Scene_Battle - 射击模式集成
- #==============================================================================
- class Scene_Battle < Scene_Base
- alias shooting_start start
- def start
- shooting_start
- @shooting_mode = false
- @shooting_item = nil
- @crosshair = Sprite_Crosshair.new
- @impact = Sprite_Impact.new
- @shot_cooldown = 0
- @bullet_window = Window_BulletCounter.new
- @dbg_counter = 0
- end
- alias shooting_terminate terminate
- def terminate
- shooting_terminate
- @bullet_window.dispose if @bullet_window
- @impact.dispose if @impact
- end
- alias shooting_update update
- def update
- if @shooting_mode
- update_basic
- @spriteset.update if @spriteset
- @impact.update if @impact
- update_shooting
- else
- shooting_update
- @impact.update if @impact
- update_shooting
- end
- end
- #--------------------------------------------------------------------------
- # ● 每帧射击相关更新
- #--------------------------------------------------------------------------
- def update_shooting
- @crosshair.update_position if @crosshair.active?
- @shot_cooldown -= 1 if @shot_cooldown > 0
- $game_party.members.each { |m| m.update_gun }
- $game_troop.members.each { |m| m.update_gun }
- @bullet_window.update_count if @shooting_mode
- return unless @shooting_mode
- update_gun_animation
- if Shooting_Config::DEBUG_AUTO
- @dbg_counter += 1
- if @dbg_counter % 30 == 0
- p "[AUTO DBG] automatic?=#{auto_mode?} interval=#{@shooting_item.automatic_interval rescue 'nil'} " +
- "frames=#{@shooting_item.automatic_interval_frames rescue 'nil'} " +
- "cooldown=#{@shot_cooldown} " +
- "kbd_press=#{Input.press?(Shooting_Config::SHOOT_KEY)} " +
- "mouse_held=#{Shooting_Config::USE_MOUSE_LEFT && Mouse.available? ? Mouse.left_held? : false}"
- end
- end
- if @shooting_item && $game_party.members.none? { |m| m.gun_active? }
- exit_shooting_mode
- return
- end
- if @shot_cooldown <= 0
- if auto_mode?
- perform_shot if shoot_key_held?
- else
- perform_shot if shoot_key_triggered?
- end
- end
- Shooting_Config::EXIT_KEYS.each do |key|
- if Input.trigger?(key)
- exit_shooting_mode
- return
- end
- end
- end
- def auto_mode?
- @shooting_item && @shooting_item.automatic?
- end
- def shoot_key_held?
- return true if Input.press?(Shooting_Config::SHOOT_KEY)
- if Shooting_Config::USE_MOUSE_LEFT && Mouse.available?
- return true if Mouse.left_held?
- end
- false
- end
- def shoot_key_triggered?
- return true if Input.trigger?(Shooting_Config::SHOOT_KEY)
- if Shooting_Config::USE_MOUSE_LEFT && Mouse.available?
- return true if Mouse.left_click?
- end
- false
- end
- def shot_interval
- return Shooting_Config::SHOT_COOLDOWN unless @shooting_item
- frames = @shooting_item.automatic_interval_frames
- frames > 0 ? frames : Shooting_Config::SHOT_COOLDOWN
- end
- #--------------------------------------------------------------------------
- # ● 拦截 process_action
- #--------------------------------------------------------------------------
- alias shooting_process_action process_action
- def process_action
- return if scene_changing?
- if !@subject || !@subject.current_action
- @subject = BattleManager.next_subject
- end
- return turn_end unless @subject
- if @subject.current_action
- @subject.current_action.prepare
- if @subject.current_action.valid?
- @status_window.open
- item = @subject.current_action.item
- if item && item.shooting? && @subject.is_a?(Game_Actor)
- if Shooting_Config::CHECK_BULLET_ON_ENTER && !has_bullet?(item)
- Sound.play_buzzer
- @log_window.add_text(sprintf("%s 的弹药不足!", @subject.name)) if @log_window
- @subject.remove_current_action
- @status_window.close rescue nil
- return
- end
- @subject.use_item(item)
- refresh_status
- @subject.remove_current_action
- enter_shooting_mode(item)
- return
- end
- execute_action
- end
- @subject.remove_current_action
- end
- process_action_end unless @subject.current_action
- end
- def has_bullet?(item)
- return true if item.bullet_id <= 0
- bullet = $data_items[item.bullet_id]
- return false if bullet.nil?
- $game_party.item_number(bullet) > 0
- end
- #--------------------------------------------------------------------------
- # ● 进入 / 退出射击模式
- #--------------------------------------------------------------------------
- def enter_shooting_mode(item)
- @shooting_mode = true
- @shooting_item = item
- @shot_cooldown = 0
- @crosshair.precision = item.precision
- @crosshair.activate
- $game_party.members.each { |m| m.start_gun(item) }
- play_actor_animation(item.kind_animation_id)
- if item.bullet_id > 0
- bullet = $data_items[item.bullet_id]
- @bullet_window.show_bullet(bullet, item.automatic?) if bullet
- end
- if Shooting_Config::DEBUG_AUTO
- p "=== 进入射击模式:#{item.name} ==="
- p " shooting? = #{item.shooting?}"
- p " bullet_id = #{item.bullet_id}"
- p " precision = #{item.precision}"
- p " automatic? = #{item.automatic?}"
- p " auto_interval = #{item.automatic_interval}"
- p " auto_frames = #{item.automatic_interval_frames}"
- p " impact_anim = #{item.impact_animation_id}"
- p " kind_anim = #{item.kind_animation_id}"
- p " x_recoil = #{item.x_recoil}"
- p " y_recoil = #{item.y_recoil}"
- p " note = #{item.note.inspect}"
- end
- end
- def exit_shooting_mode
- @shooting_mode = false
- @shooting_item = nil
- @crosshair.deactivate
- $game_party.members.each { |m| m.stop_gun }
- @bullet_window.hide_bullet
- end
- #--------------------------------------------------------------------------
- # ● 执行射击
- #--------------------------------------------------------------------------
- def perform_shot
- return unless @shooting_item
- bullet_id = @shooting_item.bullet_id
- if bullet_id > 0
- bullet_item = $data_items[bullet_id]
- if bullet_item.nil? || $game_party.item_number(bullet_item) <= 0
- Sound.play_buzzer
- @log_window.add_text("弹药耗尽!") if @log_window
- exit_shooting_mode
- return
- end
- end
- # 拍快照:本次射击用当前准星位置判定(含已有后坐力)
- shot_x, shot_y = @crosshair.screen_pos
- # 扣弹药
- if bullet_id > 0
- $game_party.lose_item($data_items[bullet_id], 1)
- end
- # 准星位置播放弹孔/火花动画(无论命中与否)
- impact_id = @shooting_item.impact_animation_id
- if impact_id > 0 && @impact
- @impact.play(shot_x, shot_y, impact_id)
- end
- # 施加后坐力(视觉立即偏移,影响下一发)
- @crosshair.add_recoil(@shooting_item.x_recoil, @shooting_item.y_recoil)
- # 用快照位置判定目标
- target = find_enemy_at(shot_x, shot_y)
- # =========================================================
- # 空枪:播放开火音效 + 重播 kind 动画(开火动作反馈)
- # =========================================================
- unless target
- Sound.play_use_skill rescue nil
- # ★ 未命中时重新播放角色身上的持枪动画
- play_actor_animation(@shooting_item.kind_animation_id)
- @log_window.add_text("未命中...") if @log_window
- @shot_cooldown = shot_interval
- if bullet_id > 0 && $game_party.item_number($data_items[bullet_id]) <= 0
- exit_shooting_mode
- end
- return
- end
- # =========================================================
- # 命中:敌人播 <shoot:>,可选重播 kind
- # =========================================================
- headshot = headshot_hit_at?(target, shot_x, shot_y)
- Sound.play_use_skill rescue nil
- # 可选:命中时也重播 kind 动画(由配置控制)
- if Shooting_Config::REPLAY_KIND_ON_HIT
- play_actor_animation(@shooting_item.kind_animation_id)
- end
- play_enemy_animation(target, @shooting_item.shoot_animation_id)
- if Shooting_Config::HIT_SOUND && !Shooting_Config::HIT_SOUND.empty?
- Audio.se_play("Audio/SE/" + Shooting_Config::HIT_SOUND, 80, 100) rescue nil
- end
- if headshot
- if Shooting_Config::HEADSHOT_SOUND && !Shooting_Config::HEADSHOT_SOUND.empty?
- Audio.se_play("Audio/SE/" + Shooting_Config::HEADSHOT_SOUND, 90, 100) rescue nil
- end
- if @log_window && Shooting_Config::HEADSHOT_TEXT && !Shooting_Config::HEADSHOT_TEXT.empty?
- @log_window.add_text(Shooting_Config::HEADSHOT_TEXT) rescue nil
- end
- end
- target.shooting_headshot = headshot
- apply_shot_effect(target, @shooting_item)
- target.shooting_headshot = false
- @shot_cooldown = shot_interval
- if bullet_id > 0 && $game_party.item_number($data_items[bullet_id]) <= 0
- exit_shooting_mode
- end
- end
- #--------------------------------------------------------------------------
- # ● 命中检测 / 爆头判定
- #--------------------------------------------------------------------------
- def find_enemy_at(mx, my)
- $game_troop.alive_members.each do |enemy|
- sprite = find_enemy_sprite(enemy)
- rect = enemy_hit_rect(enemy, sprite)
- return enemy if mx >= rect.x && mx <= rect.x + rect.width &&
- my >= rect.y && my <= rect.y + rect.height
- end
- nil
- end
- def enemy_hit_rect(enemy, sprite)
- pad = Shooting_Config::HIT_RECT_PADDING
- if sprite && sprite.bitmap
- w = sprite.bitmap.width * sprite.zoom_x
- h = sprite.bitmap.height * sprite.zoom_y
- x = sprite.x - sprite.ox * sprite.zoom_x - pad
- y = sprite.y - sprite.oy * sprite.zoom_y - pad
- Rect.new(x, y, w + pad * 2, h + pad * 2)
- else
- r = enemy.screen_rect
- Rect.new(r.x - pad, r.y - pad, r.width + pad * 2, r.height + pad * 2)
- end
- end
- def headshot_hit_at?(enemy, mx, my)
- return false unless Shooting_Config::HEADSHOT_ENABLED
- sprite = find_enemy_sprite(enemy)
- rect = enemy_hit_rect(enemy, sprite)
- return false unless mx >= rect.x && mx <= rect.x + rect.width
- return false unless my >= rect.y && my <= rect.y + rect.height
- head_bottom = rect.y + rect.height * Shooting_Config::HEADSHOT_RATIO
- my <= head_bottom
- end
- def apply_shot_effect(target, item)
- user = $game_party.members[0]
- return unless user
- target.item_apply(user, item)
- if @log_window
- @log_window.display_action_results(target, item) rescue nil
- end
- end
- def play_actor_animation(anim_id)
- return if anim_id <= 0 || !@spriteset
- animation = $data_animations[anim_id]
- return unless animation
- $game_party.members.each do |actor|
- sprite = find_actor_sprite(actor)
- sprite.start_animation(animation) if sprite
- end
- end
- def play_enemy_animation(target, anim_id)
- return if anim_id <= 0 || !@spriteset
- animation = $data_animations[anim_id]
- return unless animation
- sprite = find_enemy_sprite(target)
- sprite.start_animation(animation) if sprite
- end
- def update_gun_animation
- return unless @shooting_item
- anim_id = @shooting_item.kind_animation_id
- return if anim_id <= 0 || !@spriteset
- animation = $data_animations[anim_id]
- return unless animation
- $game_party.members.each do |actor|
- sprite = find_actor_sprite(actor)
- next unless sprite
- next if sprite.animation?
- sprite.start_animation(animation)
- end
- end
- def find_actor_sprite(actor)
- return nil unless @spriteset
- return nil unless @spriteset.respond_to?(:actor_sprites)
- @spriteset.actor_sprites.find { |s| s.battler == actor }
- end
- def find_enemy_sprite(target)
- return nil unless @spriteset
- return nil unless @spriteset.respond_to?(:enemy_sprites)
- @spriteset.enemy_sprites.find { |s| s.battler == target }
- end
- end
- #==============================================================================
- # ■ 战斗窗口中射击技能/物品的图标标记
- #==============================================================================
- class Window_BattleSkill < Window_SkillList
- alias shooting_draw_item draw_item
- def draw_item(index)
- shooting_draw_item(index)
- skill = @data[index]
- return unless skill
- if skill.shooting?
- rect = item_rect(index)
- draw_icon(132, rect.x + rect.width - 24, rect.y, true) rescue nil
- end
- end
- end
- class Window_BattleItem < Window_ItemList
- alias shooting_draw_item draw_item
- def draw_item(index)
- shooting_draw_item(index)
- item = @data[index]
- return unless item
- if item.shooting?
- rect = item_rect(index)
- draw_icon(132, rect.x + rect.width - 24, rect.y, true) rescue nil
- end
- end
- end
复制代码
|
|