Anim Editor: Added smart angle property to particles

This commit is contained in:
Maruno17
2024-05-11 00:33:56 +01:00
parent 34741ea840
commit 5495bf565c
11 changed files with 329 additions and 219 deletions

View File

@@ -50,6 +50,11 @@ module GameData
"RandomDirectionGravity" => :random_direction_gravity,
"RandomUpDirectionGravity" => :random_up_direction_gravity
}
ANGLE_OVERRIDES = {
"None" => :none,
"InitialAngleToFocus" => :initial_angle_to_focus,
"AlwaysPointAtFocus" => :always_point_at_focus
}
# Properties that apply to the animation in general, not to individual
# particles. They don't change during the animation.
@@ -76,6 +81,7 @@ module GameData
"Spawner" => [:spawner, "e", SPAWNER_TYPES],
"SpawnQuantity" => [:spawn_quantity, "v"],
"RandomFrameMax" => [:random_frame_max, "u"],
"AngleOverride" => [:angle_override, "e", ANGLE_OVERRIDES],
# All properties below are "SetXYZ" or "MoveXYZ". "SetXYZ" has the
# keyframe and the value, and "MoveXYZ" has the keyframe, duration and the
# value. All have "^" in their schema. "SetXYZ" is turned into "MoveXYZ"
@@ -129,7 +135,8 @@ module GameData
:foe_flip => false,
:spawner => :none,
:spawn_quantity => 1,
:random_frame_max => 0
:random_frame_max => 0,
:angle_override => :none
}
# NOTE: Particles are invisible until their first command, and automatically
@@ -335,6 +342,10 @@ module GameData
ret = nil if ret && ret <= 1
when "RandomFrameMax"
ret = nil if ret == 0
when "AngleOverride"
ret = nil if ret == :none
ret = nil if !FOCUS_TYPES_WITH_USER.include?(@particles[index][:focus]) &&
!FOCUS_TYPES_WITH_TARGET.include?(@particles[index][:focus])
when "AllCommands"
# Get translations of all properties to their names as seen in PBS
# animation files

View File

@@ -203,6 +203,14 @@ module Compiler
particle[:name]) + "\n" + FileLineData.linereport
end
end
# Ensure that only particles that have an entity as a focus can have a
# smart angle
if (particle[:angle_override] || :none) != :none &&
!GameData::Animation::FOCUS_TYPES_WITH_USER.include?(particle[:focus]) &&
!GameData::Animation::FOCUS_TYPES_WITH_TARGET.include?(particle[:focus])
raise _INTL("Particle \"{1}\" can't set \"AngleOverride\" if its focus isn't a specific thing(s).",
particle[:name]) + "\n" + FileLineData.linereport
end
# Ensure that a particle with a user's/target's graphic doesn't have any
# :frame commands
if !["User", "Target", "SE"].include?(particle[:name]) &&

View File

@@ -562,6 +562,28 @@ AnimationEditor::SidePanes.add_property(:particle_pane, :spawn_quantity, {
}
})
AnimationEditor::SidePanes.add_property(:particle_pane, :angle_override, {
:new => proc { |pane, editor|
values = {
:none => _INTL("None"),
:initial_angle_to_focus => _INTL("Initial angle to focus"),
:always_point_at_focus => _INTL("Always point at focus")
}
pane.add_labelled_dropdown_list(:angle_override, _INTL("Smart angle"), values, :none)
},
:refresh_value => proc { |control, editor|
focus = editor.anim[:particles][editor.particle_index][:focus]
if !GameData::Animation::FOCUS_TYPES_WITH_USER.include?(focus) &&
!GameData::Animation::FOCUS_TYPES_WITH_TARGET.include?(focus)
editor.anim[:particles][editor.particle_index][:angle_override] = :none
control.value = :none
control.disable
else
control.enable
end
}
})
AnimationEditor::SidePanes.add_property(:particle_pane, :opposing_label, {
:new => proc { |pane, editor|
pane.add_label(:opposing_label, _INTL("If on opposing side..."))

View File

@@ -497,7 +497,23 @@ class AnimationEditor::Canvas < Sprite
# Set various other properties
spr.zoom_x = values[:zoom_x] / 100.0
spr.zoom_y = values[:zoom_y] / 100.0
spr.angle = values[:angle]
case particle[:angle_override]
when :initial_angle_to_focus
target_x = (focus_xy.length == 2) ? focus_xy[1][0] : focus_xy[0][0]
target_x += offset_xy[0]
target_y = (focus_xy.length == 2) ? focus_xy[1][1] : focus_xy[0][1]
target_y += offset_xy[1]
spr.angle = AnimationPlayer::Helper.initial_angle_between(particle, focus_xy, offset_xy)
when :always_point_at_focus
target_x = (focus_xy.length == 2) ? focus_xy[1][0] : focus_xy[0][0]
target_x += offset_xy[0]
target_y = (focus_xy.length == 2) ? focus_xy[1][1] : focus_xy[0][1]
target_y += offset_xy[1]
spr.angle = AnimationPlayer::Helper.angle_between(spr.x, spr.y, target_x, target_y)
else
spr.angle = 0
end
spr.angle += values[:angle]
spr.mirror = values[:flip]
spr.mirror = !spr.mirror if relative_to_index >= 0 && relative_to_index.odd? && particle[:foe_flip]
spr.blend_type = values[:blending]

View File

@@ -127,6 +127,17 @@ class AnimationPlayer
particle_sprite.foe_invert_y = particle[:foe_invert_y]
particle_sprite.foe_flip = particle[:foe_flip]
end
particle_sprite.base_angle = 0
case particle[:angle_override]
when :initial_angle_to_focus
target_x = (focus_xy.length == 2) ? focus_xy[1][0] : focus_xy[0][0]
target_x += offset_xy[0]
target_y = (focus_xy.length == 2) ? focus_xy[1][1] : focus_xy[0][1]
target_y += offset_xy[1]
particle_sprite.base_angle = AnimationPlayer::Helper.initial_angle_between(particle, focus_xy, offset_xy)
when :always_point_at_focus
particle_sprite.angle_override = particle[:angle_override] if relative_to_index >= 0
end
# Find earliest command and add a "make visible" command then
delay = AnimationPlayer::Helper.get_particle_delay(particle, instance)
if sprite && !particle_sprite.battler_sprite?

View File

@@ -5,6 +5,7 @@
class AnimationPlayer::ParticleSprite
attr_accessor :sprite
attr_accessor :focus_xy, :offset_xy, :focus_z
attr_accessor :base_angle, :angle_override
attr_accessor :foe_invert_x, :foe_invert_y, :foe_flip
FRAMES_PER_SECOND = 20.0
@@ -109,16 +110,24 @@ class AnimationPlayer::ParticleSprite
value *= -1 if @foe_invert_x
AnimationPlayer::Helper.apply_xy_focus_to_sprite(@sprite, :x, value, @focus_xy)
@sprite.x += @offset_xy[0]
update_angle_pointing_at_focus
when :y
value = value.round
value *= -1 if @foe_invert_y
AnimationPlayer::Helper.apply_xy_focus_to_sprite(@sprite, :y, value, @focus_xy)
@sprite.y += @offset_xy[1]
update_angle_pointing_at_focus
when :z
AnimationPlayer::Helper.apply_z_focus_to_sprite(@sprite, value, @focus_z)
when :zoom_x then @sprite.zoom_x = value / 100.0
when :zoom_y then @sprite.zoom_y = value / 100.0
when :angle then @sprite.angle = value
when :angle
if @angle_override == :always_point_at_focus
update_angle_pointing_at_focus
@sprite.angle += value
else
@sprite.angle = value + (@base_angle || 0)
end
when :visible then @sprite.visible = value
when :opacity then @sprite.opacity = value
when :color_red then @sprite.color.red = value
@@ -132,6 +141,21 @@ class AnimationPlayer::ParticleSprite
end
end
# This assumes vertically up is an angle of 0, and the angle increases
# anticlockwise.
def update_angle_pointing_at_focus
return if @angle_override != :always_point_at_focus
# Get coordinates
sprite_x = @sprite.x
sprite_y = @sprite.y
target_x = (@focus_xy.length == 2) ? @focus_xy[1][0] : @focus_xy[0][0]
target_x += @offset_xy[0]
target_y = (@focus_xy.length == 2) ? @focus_xy[1][1] : @focus_xy[0][1]
target_y += @offset_xy[1]
@sprite.angle = AnimationPlayer::Helper.angle_between(sprite_x, sprite_y, target_x, target_y)
@sprite.angle += (@base_angle || 0)
end
def update(elapsed_time)
frame = (elapsed_time * FRAMES_PER_SECOND).floor
changed_properties = []

View File

@@ -77,7 +77,7 @@ module AnimationPlayer::Helper
when :user_side_foreground, :user_side_background
ret = [Battle::Scene.pbBattlerPosition(user_index)]
when :target_side_foreground, :target_side_background
ret = [Battle::Scene.pbBattlerPosition(target_idx)]
ret = [Battle::Scene.pbBattlerPosition(target_index)]
end
return ret
end
@@ -168,6 +168,48 @@ module AnimationPlayer::Helper
#-----------------------------------------------------------------------------
def angle_between(x1, y1, x2, y2)
diff_x = x1 - x2
diff_y = y1 - y2
ret = Math.atan(diff_x.to_f / diff_y) * 180 / Math::PI
ret += 180 if diff_y < 0
return ret
end
def initial_angle_between(particle, focus, offset)
x1 = 0
y1 = 0
x2 = (focus.length == 2) ? focus[1][0] : focus[0][0]
y2 = (focus.length == 2) ? focus[1][1] : focus[0][1]
[:x, :y].each do |property|
next if !particle[property]
particle[property].each do |cmd|
break if cmd[1] > 0
if property == :x
x1 = cmd[2]
else
y1 = cmd[2]
end
break
end
end
if focus
if focus.length == 2
distance = GameData::Animation::USER_AND_TARGET_SEPARATION
x1 = focus[0][0] + ((x1.to_f / distance[0]) * (focus[1][0] - focus[0][0])).to_i
y1 = focus[0][1] + ((y1.to_f / distance[1]) * (focus[1][1] - focus[0][1])).to_i
else
x1 += focus[0][0]
y1 += focus[0][1]
end
end
x1 += offset[0]
y1 += offset[1]
return angle_between(x1, y1, x2, y2)
end
#-----------------------------------------------------------------------------
# user_sprites, target_sprites = [front sprite, back sprite]
def set_bitmap_and_origin(particle, sprite, user_index, target_index, user_sprites, target_sprites)
return if sprite&.is_a?(Battle::Scene::BattlerSprite)