2023-01-23 00:55:30 +00:00
|
|
|
package funkin.play.event;
|
|
|
|
|
2024-02-28 06:29:40 +00:00
|
|
|
import flixel.tweens.FlxEase;
|
2023-09-08 21:46:44 +00:00
|
|
|
// Data from the chart
|
|
|
|
import funkin.data.song.SongData;
|
|
|
|
import funkin.data.song.SongData.SongEventData;
|
|
|
|
// Data from the event schema
|
2023-06-02 18:35:28 +00:00
|
|
|
import funkin.play.event.SongEvent;
|
2024-01-04 02:10:14 +00:00
|
|
|
import funkin.data.event.SongEventSchema;
|
|
|
|
import funkin.data.event.SongEventSchema.SongEventFieldType;
|
2023-01-23 00:55:30 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This class represents a handler for a type of song event.
|
|
|
|
* It is used by the ScriptedSongEvent class to handle user-defined events.
|
2023-06-08 20:30:45 +00:00
|
|
|
*
|
2023-01-23 00:55:30 +00:00
|
|
|
* Example: Focus on Boyfriend:
|
|
|
|
* ```
|
|
|
|
* {
|
|
|
|
* "e": "FocusCamera",
|
|
|
|
* "v": {
|
|
|
|
* "char": 0,
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* ```
|
2023-06-08 20:30:45 +00:00
|
|
|
*
|
2023-01-23 00:55:30 +00:00
|
|
|
* Example: Focus on 10px above Girlfriend:
|
|
|
|
* ```
|
|
|
|
* {
|
|
|
|
* "e": "FocusCamera",
|
|
|
|
* "v": {
|
|
|
|
* "char": 2,
|
|
|
|
* "y": -10,
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* ```
|
2023-06-08 20:30:45 +00:00
|
|
|
*
|
2023-01-23 00:55:30 +00:00
|
|
|
* Example: Focus on (100, 100):
|
|
|
|
* ```
|
|
|
|
* {
|
|
|
|
* "e": "FocusCamera",
|
|
|
|
* "v": {
|
|
|
|
* "char": -1,
|
|
|
|
* "x": 100,
|
|
|
|
* "y": 100,
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* ```
|
|
|
|
*/
|
|
|
|
class FocusCameraSongEvent extends SongEvent
|
|
|
|
{
|
|
|
|
public function new()
|
|
|
|
{
|
|
|
|
super('FocusCamera');
|
|
|
|
}
|
|
|
|
|
2023-02-22 01:58:15 +00:00
|
|
|
public override function handleEvent(data:SongEventData):Void
|
2023-01-23 00:55:30 +00:00
|
|
|
{
|
|
|
|
// Does nothing if there is no PlayState camera or stage.
|
2023-01-23 03:25:45 +00:00
|
|
|
if (PlayState.instance == null || PlayState.instance.currentStage == null) return;
|
2023-01-23 00:55:30 +00:00
|
|
|
|
2024-03-13 01:35:55 +00:00
|
|
|
// Does nothing if we are minimal mode.
|
2024-03-14 04:27:09 +00:00
|
|
|
if (PlayState.instance.isMinimalMode) return;
|
2024-03-13 01:35:55 +00:00
|
|
|
|
2023-02-22 01:58:15 +00:00
|
|
|
var posX:Null<Float> = data.getFloat('x');
|
2023-01-23 03:25:45 +00:00
|
|
|
if (posX == null) posX = 0.0;
|
2023-02-22 01:58:15 +00:00
|
|
|
var posY:Null<Float> = data.getFloat('y');
|
2023-01-23 03:25:45 +00:00
|
|
|
if (posY == null) posY = 0.0;
|
2023-01-23 00:55:30 +00:00
|
|
|
|
2023-02-22 01:58:15 +00:00
|
|
|
var char:Null<Int> = data.getInt('char');
|
2023-01-23 00:55:30 +00:00
|
|
|
|
2023-01-23 03:25:45 +00:00
|
|
|
if (char == null) char = cast data.value;
|
2023-01-23 00:55:30 +00:00
|
|
|
|
2024-02-28 06:29:40 +00:00
|
|
|
var duration:Null<Float> = data.getFloat('duration');
|
|
|
|
if (duration == null) duration = 4.0;
|
|
|
|
var ease:Null<String> = data.getString('ease');
|
2024-03-28 19:34:13 +00:00
|
|
|
if (ease == null) ease = 'CLASSIC';
|
|
|
|
|
|
|
|
var currentStage = PlayState.instance.currentStage;
|
|
|
|
|
|
|
|
// Get target position based on char.
|
|
|
|
var targetX:Float = posX;
|
|
|
|
var targetY:Float = posY;
|
2024-02-28 06:29:40 +00:00
|
|
|
|
2023-01-23 00:55:30 +00:00
|
|
|
switch (char)
|
|
|
|
{
|
2024-03-28 19:34:13 +00:00
|
|
|
case -1: // Position ("focus" on origin)
|
2023-01-23 00:55:30 +00:00
|
|
|
trace('Focusing camera on static position.');
|
|
|
|
|
2024-03-28 19:34:13 +00:00
|
|
|
case 0: // Boyfriend (focus on player)
|
|
|
|
if (currentStage.getBoyfriend() == null)
|
2023-02-22 01:58:15 +00:00
|
|
|
{
|
|
|
|
trace('No BF to focus on.');
|
|
|
|
return;
|
|
|
|
}
|
2023-01-23 00:55:30 +00:00
|
|
|
trace('Focusing camera on player.');
|
2024-03-28 19:34:13 +00:00
|
|
|
var bfPoint = currentStage.getBoyfriend().cameraFocusPoint;
|
|
|
|
targetX += bfPoint.x;
|
|
|
|
targetY += bfPoint.y;
|
2023-01-23 00:55:30 +00:00
|
|
|
|
2024-03-28 19:34:13 +00:00
|
|
|
case 1: // Dad (focus on opponent)
|
|
|
|
if (currentStage.getDad() == null)
|
2023-02-22 01:58:15 +00:00
|
|
|
{
|
|
|
|
trace('No dad to focus on.');
|
|
|
|
return;
|
|
|
|
}
|
2024-03-28 19:34:13 +00:00
|
|
|
trace('Focusing camera on opponent.');
|
|
|
|
var dadPoint = currentStage.getDad().cameraFocusPoint;
|
|
|
|
targetX += dadPoint.x;
|
|
|
|
targetY += dadPoint.y;
|
|
|
|
|
|
|
|
case 2: // Girlfriend (focus on girlfriend)
|
|
|
|
if (currentStage.getGirlfriend() == null)
|
2023-02-22 01:58:15 +00:00
|
|
|
{
|
|
|
|
trace('No GF to focus on.');
|
|
|
|
return;
|
|
|
|
}
|
2023-01-23 00:55:30 +00:00
|
|
|
trace('Focusing camera on girlfriend.');
|
2024-03-28 19:34:13 +00:00
|
|
|
var gfPoint = currentStage.getGirlfriend().cameraFocusPoint;
|
|
|
|
targetX += gfPoint.x;
|
|
|
|
targetY += gfPoint.y;
|
2023-01-23 00:55:30 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
trace('Unknown camera focus: ' + data);
|
|
|
|
}
|
2024-02-28 06:29:40 +00:00
|
|
|
|
2024-03-28 19:34:13 +00:00
|
|
|
// Apply tween based on ease.
|
|
|
|
switch (ease)
|
2024-02-28 06:29:40 +00:00
|
|
|
{
|
2024-03-28 19:34:13 +00:00
|
|
|
case 'CLASSIC': // Old-school. No ease. Just set follow point.
|
2024-04-29 01:22:33 +00:00
|
|
|
PlayState.instance.resetCamera(false, true);
|
2024-03-28 19:34:13 +00:00
|
|
|
PlayState.instance.cameraFollowPoint.setPosition(targetX, targetY);
|
|
|
|
case 'INSTANT': // Instant ease. Duration is automatically 0.
|
|
|
|
PlayState.instance.tweenCameraToPosition(targetX, targetY, 0);
|
|
|
|
default:
|
|
|
|
var durSeconds = Conductor.instance.stepLengthMs * duration / 1000;
|
|
|
|
var easeFunction:Null<Float->Float> = Reflect.field(FlxEase, ease);
|
|
|
|
if (easeFunction == null)
|
|
|
|
{
|
|
|
|
trace('Invalid ease function: $ease');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
PlayState.instance.tweenCameraToPosition(targetX, targetY, durSeconds, easeFunction);
|
2024-02-28 06:29:40 +00:00
|
|
|
}
|
2023-01-23 00:55:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public override function getTitle():String
|
|
|
|
{
|
2023-02-22 01:58:15 +00:00
|
|
|
return 'Focus Camera';
|
2023-01-23 00:55:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ```
|
|
|
|
* {
|
|
|
|
* "char": ENUM, // Which character to point to
|
|
|
|
* "x": FLOAT, // Optional x offset
|
|
|
|
* "y": FLOAT, // Optional y offset
|
|
|
|
* }
|
|
|
|
* @return SongEventSchema
|
|
|
|
*/
|
|
|
|
public override function getEventSchema():SongEventSchema
|
|
|
|
{
|
2024-01-04 02:10:14 +00:00
|
|
|
return new SongEventSchema([
|
2023-01-23 00:55:30 +00:00
|
|
|
{
|
|
|
|
name: "char",
|
2024-01-25 03:31:25 +00:00
|
|
|
title: "Target",
|
2023-01-23 00:55:30 +00:00
|
|
|
defaultValue: 0,
|
|
|
|
type: SongEventFieldType.ENUM,
|
2024-01-25 03:31:25 +00:00
|
|
|
keys: ["Position" => -1, "Player" => 0, "Opponent" => 1, "Girlfriend" => 2]
|
2023-01-23 00:55:30 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "x",
|
|
|
|
title: "X Position",
|
|
|
|
defaultValue: 0,
|
|
|
|
step: 10.0,
|
|
|
|
type: SongEventFieldType.FLOAT,
|
2024-01-27 01:51:36 +00:00
|
|
|
units: "px"
|
2023-01-23 00:55:30 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "y",
|
|
|
|
title: "Y Position",
|
|
|
|
defaultValue: 0,
|
|
|
|
step: 10.0,
|
|
|
|
type: SongEventFieldType.FLOAT,
|
2024-01-27 01:51:36 +00:00
|
|
|
units: "px"
|
2024-02-28 06:29:40 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'duration',
|
|
|
|
title: 'Duration',
|
|
|
|
defaultValue: 4.0,
|
|
|
|
step: 0.5,
|
|
|
|
type: SongEventFieldType.FLOAT,
|
|
|
|
units: 'steps'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'ease',
|
|
|
|
title: 'Easing Type',
|
|
|
|
defaultValue: 'linear',
|
|
|
|
type: SongEventFieldType.ENUM,
|
|
|
|
keys: [
|
|
|
|
'Linear' => 'linear',
|
2024-03-28 19:34:13 +00:00
|
|
|
'Sine In' => 'sineIn',
|
|
|
|
'Sine Out' => 'sineOut',
|
|
|
|
'Sine In/Out' => 'sineInOut',
|
2024-02-28 06:29:40 +00:00
|
|
|
'Quad In' => 'quadIn',
|
|
|
|
'Quad Out' => 'quadOut',
|
|
|
|
'Quad In/Out' => 'quadInOut',
|
|
|
|
'Cube In' => 'cubeIn',
|
|
|
|
'Cube Out' => 'cubeOut',
|
|
|
|
'Cube In/Out' => 'cubeInOut',
|
|
|
|
'Quart In' => 'quartIn',
|
|
|
|
'Quart Out' => 'quartOut',
|
|
|
|
'Quart In/Out' => 'quartInOut',
|
|
|
|
'Quint In' => 'quintIn',
|
|
|
|
'Quint Out' => 'quintOut',
|
|
|
|
'Quint In/Out' => 'quintInOut',
|
2024-03-28 19:34:13 +00:00
|
|
|
'Expo In' => 'expoIn',
|
|
|
|
'Expo Out' => 'expoOut',
|
|
|
|
'Expo In/Out' => 'expoInOut',
|
2024-02-28 06:29:40 +00:00
|
|
|
'Smooth Step In' => 'smoothStepIn',
|
|
|
|
'Smooth Step Out' => 'smoothStepOut',
|
|
|
|
'Smooth Step In/Out' => 'smoothStepInOut',
|
|
|
|
'Elastic In' => 'elasticIn',
|
|
|
|
'Elastic Out' => 'elasticOut',
|
|
|
|
'Elastic In/Out' => 'elasticInOut',
|
2024-03-28 19:34:13 +00:00
|
|
|
'Instant (Ignores duration)' => 'INSTANT',
|
|
|
|
'Classic (Ignores duration)' => 'CLASSIC'
|
2024-02-28 06:29:40 +00:00
|
|
|
]
|
2023-01-23 00:55:30 +00:00
|
|
|
}
|
2024-01-04 02:10:14 +00:00
|
|
|
]);
|
2023-01-23 00:55:30 +00:00
|
|
|
}
|
|
|
|
}
|