设为首页收藏本站同能贴吧 切换语言 繁体中文
开启辅助访问 切换到窄版
搜索附件  
同能RPG制作大师 › 附件中心 › 同能RM技术讨论区 › RPG Maker VA 讨论区 › 射击系统脚本New: 011110d6kicq1466w3qv74.png

射击系统脚本New: 011110d6kicq1466w3qv74.png

 

射击系统脚本New:
[Ruby] 纯文本查看 复制代码
#==============================================================================
# ▼ 射击系统脚本 (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
    @Bullet    = 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









射击系统脚本New: 011110d6kicq1466w3qv74.png
         同一主题附件:
    6acbdad7a5cc7.png
    6acbdae0d635d.png
    6acbdae8e1aed.png
    6acbdb0110649.png
    6acbdb11525d3.png
    6acbdb19d6145.png
    010206gbtkazakbruoihik.gif
    010510x0vduemd9avwmhze.png
    010657fvp3n0pevqne1pvh.png
    010922ej7ewis9ws9kbkea.png
    010939d84ibc04twtelbiw.png
    011110d6kicq1466w3qv74.png
简体中文
繁體中文
English(英语)
日本語(日语)
Deutsch(德语)
Русский язык(俄语)
بالعربية(阿拉伯语)
Türkçe(土耳其语)
Português(葡萄牙语)
ภาษาไทย(泰国语)
한어(朝鲜语/韩语)
Français(法语)
关闭

幸运抽奖

社区每日抽奖来袭,快来试试你是欧皇还是非酋~

立即查看

Loading...

QQ|Archiver|手机版|小黑屋|同能RPG制作大师 ( 沪ICP备12027754号-3 )

GMT+8, 2026-10-24 21:00 , Processed in 0.049760 second(s), 21 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

返回顶部