HitFX, Early/Late display with ms #35

Merged
hersi merged 16 commits from domdoc-develop into master 2022-05-27 11:17:12 +02:00
8 changed files with 366 additions and 76 deletions

View File

@ -69,6 +69,27 @@
"default": true "default": true
}, },
"gameplay_earlyLateFor": {
"label": "Show Early/Late display for",
"type": "selection",
"default": "NEAR (or worse)",
"values": ["CRITICAL (or worse)", "NEAR (or worse)", "OFF"]
},
"gameplay_earlyLatePosition": {
"label": "Early/Late display position",
"type": "selection",
"default": "STANDARD",
"values": ["UPPER+", "UPPER", "STANDARD", "LOWER"]
},
"gameplay_msFor": {
"label": "Show millisecond display for",
"type": "selection",
"default": "NEAR (or worse)",
"values": ["ALL", "CRITICAL (or worse)", "NEAR (or worse)", "NONE"]
},
"separator_f": {}, "separator_f": {},
"Debug": { "type": "label" }, "Debug": { "type": "label" },

Binary file not shown.

View File

@ -18,6 +18,7 @@ local Chain = require('gameplay.chain')
local LaserAlert = require('gameplay.laser_alert') local LaserAlert = require('gameplay.laser_alert')
local HitFX = require 'gameplay.hitfx' local HitFX = require 'gameplay.hitfx'
local EarlyLate = require 'gameplay.earlylate'
local TrackEnd = require('gameplay.track_end') local TrackEnd = require('gameplay.track_end')
@ -59,15 +60,26 @@ function render(deltaTime)
Chain.render(deltaTime, gameplay.comboState, chain, gameplay.critLine.x, gameplay.critLine.y); Chain.render(deltaTime, gameplay.comboState, chain, gameplay.critLine.x, gameplay.critLine.y);
LaserAlert.render(deltaTime); LaserAlert.render(deltaTime);
EarlyLate.render(deltaTime)
end end
function render_crit_base(deltaTime) function render_crit_base(deltaTime)
CritLine.renderBase(deltaTime, gameplay.critLine.x, gameplay.critLine.y, -gameplay.critLine.rotation, gameplay.critLine.cursors); local cl = gameplay.critLine
HitFX.render(deltaTime, gameplay.critLine.x, gameplay.critLine.y, -gameplay.critLine.rotation, gameplay.critLine.cursors);
Console.render(deltaTime, gameplay.critLine.x, gameplay.critLine.y, -gameplay.critLine.rotation); CritLine.renderBase(deltaTime, cl.x, cl.y, -cl.rotation);
Console.render(deltaTime, cl.x, cl.y, -cl.rotation);
end end
function render_crit_overlay(deltaTime) function render_crit_overlay(deltaTime)
local cl = gameplay.critLine
local centerX = cl.x
local centerY = cl.y
local rot = -cl.rotation
HitFX.renderButtons(deltaTime, centerX, centerY, rot);
HitFX.renderLasers(deltaTime, centerX, centerY, rot, cl.cursors);
CritLine.renderOverlay(deltaTime, centerX, centerY, rot, cl.cursors, gameplay.laserActive)
end end
function render_intro(deltaTime) function render_intro(deltaTime)
@ -109,10 +121,14 @@ function button_hit(button, rating, delta)
if (showHitAnims) then if (showHitAnims) then
if (rating == 1) then if (rating == 1) then
HitFX.TriggerAnimation("Near", button + 1) HitFX.TriggerAnimation("Near", button + 1)
else elseif (rating == 2) then
HitFX.TriggerAnimation("Crit", button + 1) HitFX.TriggerAnimation("Crit", button + 1)
end end
end end
if 0 < rating and rating < 3 then
EarlyLate.TriggerAnimation(rating, delta)
end
end end
function laser_slam_hit(slamLength, startPos, endPost, index) function laser_slam_hit(slamLength, startPos, endPost, index)

View File

@ -17,6 +17,10 @@ local cursorGlowTopImages = {
gfx.CreateSkinImage("gameplay/crit_line/cursor_glow_top_left.png", 0), gfx.CreateSkinImage("gameplay/crit_line/cursor_glow_top_left.png", 0),
gfx.CreateSkinImage("gameplay/crit_line/cursor_glow_top_right.png", 0), gfx.CreateSkinImage("gameplay/crit_line/cursor_glow_top_right.png", 0),
} }
local cursorTailImages = {
gfx.CreateSkinImage("gameplay/crit_line/cursor_tail_l.png", 0),
gfx.CreateSkinImage("gameplay/crit_line/cursor_tail_r.png", 0),
}
local CRITBAR_W = 1080 local CRITBAR_W = 1080
local CRITBAR_H = 251 local CRITBAR_H = 251
@ -24,10 +28,15 @@ local CRITBAR_H = 251
local scale = 1; local scale = 1;
local isLandscape = false; local isLandscape = false;
local drawCursors = function (centerX, centerY,cursors) local drawCursors = function (centerX, centerY,cursors, laserActive)
local cursorW = 598 * 0.165; local cursorW = 598 * 0.165;
local cursorH = 673 * 0.14; local cursorH = 673 * 0.14;
local tailW = cursorW * 9
local tailH = cursorH * 9
for i = 0, 1, 1 do for i = 0, 1, 1 do
local luaIndex = i + 1
local cursor = cursors[i]; local cursor = cursors[i];
gfx.Save(); gfx.Save();
@ -39,6 +48,18 @@ local drawCursors = function (centerX, centerY,cursors)
local cursorX = cursor.pos * (1 / scale) - cursorW / 2; local cursorX = cursor.pos * (1 / scale) - cursorW / 2;
local cursorY = -cursorH / 2; local cursorY = -cursorH / 2;
if laserActive[luaIndex] then
gfx.ImageRect(
cursor.pos - tailW / 2,
- tailH / 2,
tailW,
tailH,
cursorTailImages[luaIndex],
cursor.alpha / 2,
0
)
end
gfx.ImageRect( gfx.ImageRect(
cursorX, cursorX,
cursorY, cursorY,
@ -54,7 +75,7 @@ local drawCursors = function (centerX, centerY,cursors)
cursorY, cursorY,
cursorW, cursorW,
cursorH, cursorH,
cursorGlowBottomImages[i+1], cursorGlowBottomImages[luaIndex],
cursor.alpha, cursor.alpha,
0 0
); );
@ -74,7 +95,7 @@ local drawCursors = function (centerX, centerY,cursors)
cursorY, cursorY,
cursorW, cursorW,
cursorH, cursorH,
cursorGlowTopImages[i+1], cursorGlowTopImages[luaIndex],
cursor.alpha, cursor.alpha,
0 0
); );
@ -83,7 +104,7 @@ local drawCursors = function (centerX, centerY,cursors)
end end
end end
local renderBase = function (deltaTime, centerX, centerY, rotation, cursors) local renderBase = function (deltaTime, centerX, centerY, rotation)
scale, isLandscape = Dimensions.setUpTransforms(centerX, centerY, rotation) scale, isLandscape = Dimensions.setUpTransforms(centerX, centerY, rotation)
gfx.BeginPath() gfx.BeginPath()
@ -99,13 +120,15 @@ local renderBase = function (deltaTime, centerX, centerY, rotation, cursors)
gfx.ImageRect(-CRITBAR_W/2, -CRITBAR_H/2, CRITBAR_W, CRITBAR_H, baseImage, 1, 0); gfx.ImageRect(-CRITBAR_W/2, -CRITBAR_H/2, CRITBAR_W, CRITBAR_H, baseImage, 1, 0);
end end
drawCursors(centerX, centerY, cursors)
gfx.ResetTransform() gfx.ResetTransform()
end end
local renderOverlay = function (deltaTime) local renderOverlay = function (deltaTime, centerX, centerY, rotation, cursors, laserActive)
scale, isLandscape = Dimensions.setUpTransforms(centerX, centerY, rotation)
drawCursors(centerX, centerY, cursors, laserActive)
gfx.ResetTransform()
end end
return { return {

View File

@ -1,30 +1,109 @@
local Dimensions = require "common.dimensions"
local desw = 1080; -- Used for comparing button_hit()'s delta parameter with the
local desh = 1920; -- gameplay_earlyLateFor/gameplay_msDisplay skin settings values.
-- If the number is <= delta then the EarlyLate/ms should be shown
local transitionExistScale = 0; local compare = {
["ALL"] = 2,
local tickTransitions = function (deltaTime) ["CRITICAL (or worse)"] = 2,
["NEAR (or worse)"] = 1,
if transitionExistScale < 1 then ["NONE"] = -1,
transitionExistScale = transitionExistScale + deltaTime / 2 -- transition should last for that time in seconds ["OFF"] = -1
end
end
local render = function (deltaTime, comboState, combo, critLineCenterX, critLineCenterY)
tickTransitions(deltaTime)
if (transitionExistScale >= 1) then
return;
end
end
local trigger = function ()
end
return {
render=render
} }
local portraitHeightFractions = {
["UPPER+"] = 2.4,
["UPPER"] = 3,
["STANDARD"] = 4.2,
["LOWER"] = 5.3,
}
local landscapeHeightFractions = {
["UPPER+"] = 1.5,
["UPPER"] = 2.7,
["STANDARD"] = 4.1,
["LOWER"] = 6.7,
}
local earlyLateFor = compare[game.GetSkinSetting("gameplay_earlyLateFor")]
local msFor = compare[game.GetSkinSetting("gameplay_msFor")]
local earlyLatePosition = game.GetSkinSetting("gameplay_earlyLatePosition")
local EarlyLate = {
timer = 0,
color = {},
earlyLateText = "",
millisecText = ""
}
function EarlyLate.render(deltaTime)
if EarlyLate.timer <= 0 then
return
end
EarlyLate.timer = EarlyLate.timer - deltaTime * 100
local screenW, screenH = Dimensions.screen.width, Dimensions.screen.height
local screenCenterX = screenW / 2
local desh, fractionTable
if screenH > screenW then
desh = 1600
fractionTable = portraitHeightFractions
else
desh = 1080
fractionTable = landscapeHeightFractions
end
local scale = screenH / desh
local y = screenH / 8 * fractionTable[earlyLatePosition]
gfx.BeginPath()
gfx.LoadSkinFont("Digital-Serial-ExtraBold.ttf")
gfx.FontSize(20 * scale)
local color = EarlyLate.color
gfx.FillColor(color[1], color[2], color[3])
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_BASELINE)
gfx.FastText(EarlyLate.earlyLateText, screenCenterX - 100 * scale, y)
gfx.FastText(EarlyLate.millisecText, screenCenterX + 100 * scale, y)
end
function EarlyLate.TriggerAnimation(rating, millisec)
local showEarlyLate = rating <= earlyLateFor
local showMillisec = rating <= msFor
local isEarly = millisec < 0
if millisec == 0 then return end
if not showEarlyLate and not showMillisec then return end
if showEarlyLate then
EarlyLate.earlyLateText = isEarly and "EARLY" or "LATE"
else
EarlyLate.earlyLateText = ""
end
if showMillisec then
local millisecText = string.format("%dms", millisec)
-- prepend + sign for lates
millisecText = isEarly and millisecText or "+"..millisecText
EarlyLate.millisecText = millisecText
else
EarlyLate.millisecText = ""
end
if isEarly then
EarlyLate.color = {206, 94, 135}
else
EarlyLate.color = {53, 102, 197}
end
EarlyLate.timer = 120
end
return EarlyLate

View File

@ -13,41 +13,166 @@ local Animations = {
Near = Animation.new('gameplay/hit_animation_frames/near_taps', { Near = Animation.new('gameplay/hit_animation_frames/near_taps', {
centered = true, centered = true,
}), }),
HoldCrit = Animation.new('gameplay/hit_animation_frames/hold_critical', {
centered = true,
loop = true,
}),
HoldDome = Animation.new('gameplay/hit_animation_frames/hold_dome', {
centered = true,
loop = true,
loopPoint = 10
}),
HoldEnd = Animation.new('gameplay/hit_animation_frames/hold_end', {
centered = true,
}),
HoldInner = Animation.new('gameplay/hit_animation_frames/hold_inner', {
centered = true,
loop = true,
}),
LaserCrit = Animation.new('gameplay/hit_animation_frames/laser_critical', {
loop = true,
}),
LaserDome = Animation.new('gameplay/hit_animation_frames/laser_dome', {
loop = true,
}),
LaserEndOuter = Animation.new('gameplay/hit_animation_frames/laser_end_outer', {}),
LaserEndLeft = Animation.new('gameplay/hit_animation_frames/laser_end_l_inner', {}),
LaserEndRight = Animation.new('gameplay/hit_animation_frames/laser_end_r_inner', {}),
}; };
local animationStates = { ---@class LaserStateTable
---@field Crit AnimationState
---@field Dome AnimationState
---@field EndInner AnimationState
---@field EndOuter AnimationState
---@type LaserStateTable[]
local laserStateTables = {
{
Crit = Animations.LaserCrit:createState(),
Dome = Animations.LaserDome:createState(),
EndInner = Animations.LaserEndLeft:createState(),
EndOuter = Animations.LaserEndOuter:createState()
},
{
Crit = Animations.LaserCrit:createState(),
Dome = Animations.LaserDome:createState(),
EndInner = Animations.LaserEndRight:createState(),
EndOuter = Animations.LaserEndOuter:createState()
}
}
---@class HoldStateTable
---@field Crit AnimationState
---@field Dome AnimationState
---@field End AnimationState
---@field Inner AnimationState
---@type HoldStateTable[]
local holdStateTables = {}
for i = 1, 6 do
holdStateTables[i] = {
Crit = Animations.HoldCrit:createState(),
Dome = Animations.HoldDome:createState(),
End = Animations.HoldEnd:createState(),
Inner = Animations.HoldInner:createState()
}
end
---@type AnimationState[] ---@type AnimationState[]
Hold = { }, local tapStates = {}
---@type AnimationState[]
Tap = { }
};
local HitFX = { }; local HitFX = { };
local LanePositions = { local function setUpTransform(critCenterX, critCenterY, critRotation, xScalar)
1.5 / 6, local critLine = gameplay.critLine
2.5 / 6, local x = critCenterX + (critLine.line.x2 - critLine.line.x1) * xScalar
3.5 / 6, local y = critCenterY + (critLine.line.y2 - critLine.line.y1) * xScalar
4.5 / 6,
1 / 3,
2 / 3
};
local function setupLaneTransform(lanePosition, critCenterX, critCenterY) Dimensions.setUpTransforms(x, y, critRotation)
local critLine = gameplay.critLine;
local x = critCenterX + (critLine.line.x2 - critLine.line.x1) * lanePosition;
local y = critCenterY + (critLine.line.y2 - critLine.line.y1) * lanePosition;
Dimensions.setUpTransforms(x, y, -critLine.rotation);
end end
function HitFX.render(deltaTime, critCenterX, critCenterY, critRotation, cursors) function HitFX.renderLasers(deltaTime, critCenterX, critCenterY, critRotation, cursors)
local baseHitSize = 325; local hitSize = 406
-- Lasers
for laser = 1, 2 do
-- Update
local isActive = gameplay.laserActive[laser]
local laserState = laserStateTables[laser]
local isAnimationPlaying = laserState.Dome.running
if isActive and not isAnimationPlaying then
laserState.Crit:restart()
laserState.Dome:restart()
end
if not isActive and isAnimationPlaying then
laserState.Crit:stop()
laserState.Dome:stop()
laserState.EndInner:restart()
laserState.EndOuter:restart()
end
-- Render
local laserColor = {game.GetLaserColor(laser - 1)}
local x = cursors[laser - 1].pos
Dimensions.setUpTransforms(critCenterX, critCenterY, critRotation)
laserState.Dome:render(deltaTime, {
centered = true,
width = hitSize,
height = hitSize,
color = laserColor,
x = x,
})
laserState.Crit:render(deltaTime, {
centered = true,
width = hitSize,
height = hitSize,
x = x,
})
laserState.EndInner:render(deltaTime, {
centered = true,
width = hitSize,
height = hitSize,
x = x,
})
laserState.EndOuter:render(deltaTime, {
centered = true,
width = hitSize,
height = hitSize,
color = laserColor,
x = x,
})
end
end
function HitFX.renderButtons(deltaTime, critCenterX, critCenterY, critRotation)
--local baseHitSize = 325;
local hitSize = 406
-- BT + FX
for i = 1, 6 do for i = 1, 6 do
--[[
local hitSize = baseHitSize; local hitSize = baseHitSize;
if (i > 4) then if (i > 4) then
hitSize = hitSize * 1.5; hitSize = hitSize * 1.5;
end end
]]
local laneWidth = (track.GetCurrentLaneXPos(2) - track.GetCurrentLaneXPos(1)) * (i <= 4 and 1 or 2); local laneWidth = (track.GetCurrentLaneXPos(2) - track.GetCurrentLaneXPos(1)) * (i <= 4 and 1 or 2);
local lanePosition = track.GetCurrentLaneXPos(i) + laneWidth / 2 local lanePosition = track.GetCurrentLaneXPos(i) + laneWidth / 2
@ -55,40 +180,66 @@ function HitFX.render(deltaTime, critCenterX, critCenterY, critRotation, cursors
lanePosition = -track.GetCurrentLaneXPos(6) - laneWidth / 2 lanePosition = -track.GetCurrentLaneXPos(6) - laneWidth / 2
end end
local holdState = animationStates.Hold[i]; -- Update Holds
local tapState = animationStates.Tap[i]; local isHeld = gameplay.noteHeld[i]
local holdStates = holdStateTables[i]
local isAnimationPlaying = holdStates.Dome.running
local isHeld = gameplay.noteHeld[i]; if isHeld and not isAnimationPlaying then
if (isHeld) then holdStates.Crit:restart()
if (holdState) then holdStates.Dome:restart()
if (not holdState.running) then holdStates.Inner:restart()
holdState:restart();
end end
setupLaneTransform(lanePosition, critCenterX, critCenterY); if not isHeld and isAnimationPlaying then
holdState:render(deltaTime); holdStates.Crit:stop()
gfx.ResetTransform(); holdStates.Dome:stop()
end holdStates.Inner:stop()
else
if (holdState and holdState.running) then holdStates.End:restart()
holdState:restart();
end end
if (tapState and tapState.running) then -- Render holds
setupLaneTransform(lanePosition, critCenterX, critCenterY); setUpTransform(critCenterX, critCenterY, critRotation, lanePosition)
holdStates.Inner:render(deltaTime, {
centered = true,
width = hitSize,
height = hitSize
})
holdStates.Dome:render(deltaTime, {
centered = true,
width = hitSize,
height = hitSize
})
holdStates.Crit:render(deltaTime, {
centered = true,
width = hitSize,
height = hitSize
})
holdStates.End:render(deltaTime, {
centered = true,
width = hitSize,
height = hitSize
})
-- Render Taps
local tapState = tapStates[i]
if tapState then
tapState:render(deltaTime, { tapState:render(deltaTime, {
centered = true, centered = true,
width = hitSize, width = hitSize,
height = hitSize, height = hitSize,
}); });
gfx.ResetTransform();
end
end end
end end
gfx.ResetTransform()
end end
function HitFX.TriggerAnimation(name, lane) function HitFX.TriggerAnimation(name, lane)
animationStates.Tap[lane] = Animations[name]:start(); tapStates[lane] = Animations[name]:start();
end end
return HitFX; return HitFX;

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB