diff --git a/.github/README_templates/en.md b/.github/README_templates/en.md index 121db3f6..7f5e7889 100644 --- a/.github/README_templates/en.md +++ b/.github/README_templates/en.md @@ -68,10 +68,15 @@ Target figura version: [0.1.4](https://modrinth.com/mod/figura/version/0.1.4+1.2 ![Birth day](../README_images/birth_day.svg) ## Ex Skill -The familiar Ex skill cut-in from the original game is imitated. To play Ex skill cut-in, press the Ex skill key (default is "V" key) while in the **third-person perspective**. +The familiar Ex skill cut-in from the original game is imitated. To play Ex skill cut-in, press the Ex skill key (default is "G" key) while in the **third-person perspective**. + +> [!IMPORTANT] +> From v1.9.4, the Ex skill action play key has been changed from the "V" key to the "G" key. ![Ex skill](../README_images/ex_skill.jpg) +Some characters have the secondary Ex skill. It can be played by pressing the "H" key. + Ex skill cut-ins are only visual and have no effect. However, some Ex skills leave objects in place after the cut-in (also only visual). > [!NOTE] @@ -82,7 +87,7 @@ Ex skill cut-ins are only visual and have no effect. However, some Ex skills lea Figura provides the action wheel with which players can play some actions (emotes, animations, configs, and etc.). It will be shown when holding the action wheel key (default is B key). This avatar also has some actions. > [!IMPORTANT] -> The Ex skill action has been changed to play on key press. +> From v1.8.4, the Ex skill action has been changed to play on key press. ![Action wheel](../README_images/action_wheel.jpg) diff --git a/.github/README_templates/jp.md b/.github/README_templates/jp.md index 3c411829..86de3218 100644 --- a/.github/README_templates/jp.md +++ b/.github/README_templates/jp.md @@ -68,10 +68,15 @@ ![誕生日](../README_images/birth_day.svg) ## Exスキル -本家でお馴染みのExスキルのカットインが再現されています。Exスキルを再生するには、**三人称視点で**Exスキルのキー(デフォルトは「V」キー)を押してください。 +本家でお馴染みのExスキルのカットインが再現されています。Exスキルを再生するには、**三人称視点で**Exスキルのキー(デフォルトは「G」キー)を押してください。 + +> [!IMPORTANT] +> v1.9.4より、Exスキルのアクションの再生キーが「V」キーから「G」キーに変更されました。 ![Exスキル](../README_images/ex_skill.jpg) +一部の生徒さんは、Exスキルを2つ持っています。2つ目のExスキルは「H」キーで再生できます。 + Exスキルのカットインは見た目だけであり、効果は特にありません。ただし、Exスキルによってはカットインの後にオブジェクトをその場に残すものもあります(こちらも見た目だけです)。 > [!NOTE] @@ -82,7 +87,7 @@ Exスキルのカットインは見た目だけであり、効果は特にあり Figuraには、アクションホイールキー(デフォルトは「B」キー)を押すことで、エモートなどを実行できるリングメニューが実装されています。このレポジトリのアバターには共通したアクションが用意されています。 > [!IMPORTANT] -> Exスキルのアクションはキー押下で再生されるように変更されました。 +> v1.8.4より、Exスキルのアクションはキー押下で再生されるように変更されました。 ![アクションホイール](../README_images/action_wheel.jpg) diff --git a/scripts/action_wheel/action_wheel_gui.lua b/scripts/action_wheel/action_wheel_gui.lua index 282d8ace..b08dcee6 100644 --- a/scripts/action_wheel/action_wheel_gui.lua +++ b/scripts/action_wheel/action_wheel_gui.lua @@ -39,7 +39,7 @@ ActionWheelGui = { bubbleKeyNames[3]:setText("§0"..keybindTable[Language:getTranslate("key_name__bubble_3")]:getKeyName()) bubbleKeyNames[4]:setText("§0"..keybindTable[Language:getTranslate("key_name__bubble_4")]:getKeyName()) bubbleKeyNames[5]:setText("§0"..keybindTable[Language:getTranslate("key_name__bubble_5")]:getKeyName()) - exSkillGuideBody:setText("§0§l\""..Language:getTranslate("action_wheel_gui__ex_skill_guide__ex_skill_"..BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill.."__name").."\"§r\n§0"..Language:getTranslate("action_wheel_gui__ex_skill_guide__key_pre")..keybindTable[Language:getTranslate("key_name__ex_skill")]:getKeyName()..Language:getTranslate("action_wheel_gui__ex_skill_guide__key_post")) + exSkillGuideBody:setText(BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].subExSkill == nil and "§0§l\""..Language:getTranslate("action_wheel_gui__ex_skill_guide__ex_skill_"..BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill.."__name").."\"§r\n§0"..Language:getTranslate("action_wheel_gui__ex_skill_guide__key_pre")..keybindTable[Language:getTranslate("key_name__ex_skill")]:getKeyName()..Language:getTranslate("action_wheel_gui__ex_skill_guide__key_post") or "§0§l\""..Language:getTranslate("action_wheel_gui__ex_skill_guide__ex_skill_"..BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill.."__name").."\"§r§0 - \""..keybindTable[Language:getTranslate("key_name__ex_skill")]:getKeyName().."\"\n§l\""..Language:getTranslate("action_wheel_gui__ex_skill_guide__ex_skill_"..BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].subExSkill.."__name").."\"§r§0 - \""..keybindTable[Language:getTranslate("key_name__ex_skill_sub")]:getKeyName().."\"") end local windowSize = client:getScaledWindowSize() models.models.action_wheel_gui.Gui.BubbleGuide:setPos(windowSize.x * -0.5 + 44, windowSize.y * -0.5 + 5, 0) diff --git a/scripts/blue_archive_character.lua b/scripts/blue_archive_character.lua index 16da2b35..206f5b6f 100644 --- a/scripts/blue_archive_character.lua +++ b/scripts/blue_archive_character.lua @@ -1291,7 +1291,11 @@ BlueArchiveCharacter = { ---コスチュームに対応するExスキルのインデックス番号 ---@type integer - exSkill = 1 + exSkill = 1, + + ---コスチュームに対応するサブExスキルのインデックス番号(任意) + ---@type integer + subExSkill = 1 } ]] diff --git a/scripts/ex_skill/ex_skill.lua b/scripts/ex_skill/ex_skill.lua index 8c97f10d..07762694 100644 --- a/scripts/ex_skill/ex_skill.lua +++ b/scripts/ex_skill/ex_skill.lua @@ -2,15 +2,21 @@ ---| "PRE" ---| "POST" +---Exスキルの自動再生モード +---@alias ExSkill.AutoPlayMode +---| "NONE" 自動再生なし +---| "MAIN" メインExスキル +---| "SUB" サブExスキル + ---@class ExSkill Exスキルのアニメーションを管理するクラス ExSkill = { ---アバター読み込み時に自動的にExスキルが再生される。デバッグ用。 - ---@type boolean - AUTO_PLAY = false, + ---@type ExSkill.AutoPlayMode + AUTO_PLAY = "NONE", - ---そのレンダーで既にレンダー処理したかどうか - ---@type boolean - RenderProcessed = false, + ---現在再生中のExスキルのインデックス番号 + ---@type integer + ExSkillIndex = 1, ---Exスキルのアニメーション再生中に増加するカウンター。-1はアニメーション停止中を示す。 ---@type integer @@ -105,11 +111,11 @@ ExSkill = { local targetCameraPos = vectors.vec3() local targetCameraRot = vectors.vec3() if direction == "PRE" then - targetCameraPos = vectors.rotateAroundAxis(self.BodyYaw[2] * -1 + 180, BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].camera.start.pos, 0, 1):add(0, -1.62) - targetCameraRot = BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].camera.start.rot:copy():add(0, self.BodyYaw[2], 0) + targetCameraPos = vectors.rotateAroundAxis(self.BodyYaw[2] * -1 + 180, BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].camera.start.pos, 0, 1):add(0, -1.62) + targetCameraRot = BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].camera.start.rot:copy():add(0, self.BodyYaw[2], 0) else - targetCameraPos = vectors.rotateAroundAxis(self.BodyYaw[2] * -1 + 180, BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].camera.fin.pos, 0, 1):add(0, -1.62) - targetCameraRot = BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].camera.fin.rot:copy():add(0, self.BodyYaw[2], 0) + targetCameraPos = vectors.rotateAroundAxis(self.BodyYaw[2] * -1 + 180, BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].camera.fin.pos, 0, 1):add(0, -1.62) + targetCameraRot = BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].camera.fin.rot:copy():add(0, self.BodyYaw[2], 0) end if math.abs(cameraRot.y - targetCameraRot.y) >= 180 then if cameraRot.y < targetCameraRot.y then @@ -167,20 +173,30 @@ ExSkill = { end end end - self.RenderProcessed = true end, "ex_skill_transition_render") end, ---アニメーションを再生する。 - play = function (self) + ---@param self ExSkill + ---@param isSubExSkill boolean サブExスキルを再生するかどうか + play = function (self, isSubExSkill) + if isSubExSkill then + if BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].subExSkill ~= nil then + self.ExSkillIndex = BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].subExSkill + else + return + end + else + self.ExSkillIndex = BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill + end Bubble:stop() renderer:setFOV(70 / client:getFOV()) renderer:setRenderHUD(false) CameraManager:setCameraCollisionDenial(true) models.models.ex_skill_frame.Gui:setColor(BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].formationType == "STRIKER" and vectors.vec3(1, 0.75, 0.75) or vectors.vec3(0.75, 1, 1)) sounds:playSound(CompatibilityUtils:checkSound("minecraft:entity.player.levelup"), player:getPos(), 5, 2) - if BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].callbacks.preTransition ~= nil then - BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].callbacks.preTransition() + if BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].callbacks.preTransition ~= nil then + BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].callbacks.preTransition() end models.models.ex_skill_frame.Gui.FrameBar:setScale(1, client:getScaledWindowSize().y * math.sqrt(2) / 16 + 1, 1) events.TICK:register(function () @@ -193,24 +209,23 @@ ExSkill = { for _, itemModel in ipairs({vanilla_model.RIGHT_ITEM, vanilla_model.LEFT_ITEM}) do itemModel:setVisible(false) end - for _, modelPart in ipairs(BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].models) do + for _, modelPart in ipairs(BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].models) do modelPart:setVisible(true) end - for _, modelPart in ipairs(BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].animations) do - animations["models."..modelPart]["ex_skill_"..BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill]:play() + for _, modelPart in ipairs(BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].animations) do + animations["models."..modelPart]["ex_skill_"..self.ExSkillIndex]:play() end - Gun:setGunPosition("NONE") - if BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].callbacks.preAnimation ~= nil then - BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].callbacks.preAnimation() + if BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].callbacks.preAnimation ~= nil then + BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].callbacks.preAnimation() end CameraManager:setThirdPersonCameraDistance(0) events.TICK:register(function () if not client:isPaused() then if self.AnimationCount == self.AnimationLength - 1 then self:stop() - elseif self:canPlayAnimation() and animations["models.main"]["ex_skill_"..BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill]:isPlaying() then - if BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].callbacks.animationTick ~= nil then - BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].callbacks.animationTick(self.AnimationCount) + elseif self:canPlayAnimation() and animations["models.main"]["ex_skill_"..self.ExSkillIndex]:isPlaying() then + if BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].callbacks.animationTick ~= nil then + BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].callbacks.animationTick(self.AnimationCount) end self.AnimationCount = self.AnimationCount > -1 and self.AnimationCount + 1 or self.AnimationCount end @@ -237,7 +252,7 @@ ExSkill = { end self.AnimationCount = 0 Gun:processGunTick() - self.AnimationLength = math.round(animations["models.main"]["ex_skill_"..BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill]:getLength() * 20) + self.AnimationLength = math.round(animations["models.main"]["ex_skill_"..self.ExSkillIndex]:getLength() * 20) end) end, @@ -249,14 +264,14 @@ ExSkill = { for _, itemModel in ipairs({vanilla_model.RIGHT_ITEM, vanilla_model.LEFT_ITEM}) do itemModel:setVisible(true) end - for _, modelPart in ipairs(BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].models) do + for _, modelPart in ipairs(BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].models) do modelPart:setVisible(false) end - for _, modelPart in ipairs(BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].animations) do - animations["models."..modelPart]["ex_skill_"..BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill]:stop() + for _, modelPart in ipairs(BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].animations) do + animations["models."..modelPart]["ex_skill_"..self.ExSkillIndex]:stop() end - if BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].callbacks.postAnimation ~= nil then - BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].callbacks.postAnimation(false) + if BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].callbacks.postAnimation ~= nil then + BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].callbacks.postAnimation(false) end events.TICK:remove("ex_skill_animation_tick") if host:isHost() then @@ -266,8 +281,8 @@ ExSkill = { Physics:enable() renderer:setFOV() self:transition("POST", function () - if BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].callbacks.postTransition ~= nil then - BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].callbacks.postTransition(false) + if BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].callbacks.postTransition ~= nil then + BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].callbacks.postTransition(false) end CameraManager.setCameraPivot() CameraManager.setCameraRot() @@ -283,11 +298,11 @@ ExSkill = { for _, itemModel in ipairs({vanilla_model.RIGHT_ITEM, vanilla_model.LEFT_ITEM}) do itemModel:setVisible(true) end - for _, modelPart in ipairs(BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].models) do + for _, modelPart in ipairs(BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].models) do modelPart:setVisible(false) end - for _, modelPart in ipairs(BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].animations) do - animations["models."..modelPart]["ex_skill_"..BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill]:stop() + for _, modelPart in ipairs(BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].animations) do + animations["models."..modelPart]["ex_skill_"..self.ExSkillIndex]:stop() end for _, eventName in ipairs({"ex_skill_tick", "ex_skill_animation_tick"}) do events.TICK:remove(eventName) @@ -305,11 +320,11 @@ ExSkill = { for _, modelPart in ipairs({models.models.ex_skill_frame.Gui.Frame.FrameTop, models.models.ex_skill_frame.Gui.Frame.FrameLeft, models.models.ex_skill_frame.Gui.Frame.FrameBottom, models.models.ex_skill_frame.Gui.Frame.FrameRight}) do modelPart:setScale(0, 0, 0) end - if self.AnimationCount >= 0 and BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].callbacks.postAnimation ~= nil then - BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].callbacks.postAnimation(true) + if self.AnimationCount >= 0 and BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].callbacks.postAnimation ~= nil then + BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].callbacks.postAnimation(true) end - if BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].callbacks.postTransition ~= nil then - BlueArchiveCharacter.EX_SKILL[BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].exSkill].callbacks.postTransition(true) + if BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].callbacks.postTransition ~= nil then + BlueArchiveCharacter.EX_SKILL[self.ExSkillIndex].callbacks.postTransition(true) end FaceParts:resetEmotion() CameraManager.setCameraPivot() @@ -330,7 +345,7 @@ ExSkill = { exSkill.camera.fin.pos:mul(-1, 1, 1):scale(1 / 16 * 0.9375) end if host:isHost() then - KeyManager:register("ex_skill", Config.loadConfig("keybind.ex_skill", "key.keyboard.v"), function () + KeyManager:register("ex_skill", Config.loadConfig("keybind.ex_skill", "key.keyboard.g"), function () while events.TICK:getRegisteredCount("ex_skill_keypress_tick") > 0 do events.TICK:remove("ex_skill_keypress_tick") end @@ -357,12 +372,20 @@ ExSkill = { self.KeyPressCount = 0 end end) + KeyManager:register("ex_skill_sub", Config.loadConfig("keybind.ex_skill_sub", "key.keyboard.h"), function () + if ExSkill:canPlayAnimation() and ExSkill.AnimationCount == -1 and ExSkill.TransitionCount == 0 and BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].subExSkill ~= nil then + pings.ex_skill_sub() + else + print(Language:getTranslate(BlueArchiveCharacter.COSTUME.costumes[Costume.CurrentCostume].subExSkill == nil and "action_wheel__main__action_6__unavailable" or "key_bind__ex_skill__unavailable"..(renderer:isFirstPerson() and "_firstperson" or ""))) + sounds:playSound(CompatibilityUtils:checkSound("minecraft:block.note_block.bass"), player:getPos(), 1, 0.5) + end + end) end - if self.AUTO_PLAY then + if self.AUTO_PLAY ~= "NONE" then local init = true events.TICK:register(function () if init then - self:play() + self:play(self.AUTO_PLAY == "SUB") init = false end end) @@ -380,7 +403,11 @@ ExSkill = { } function pings.ex_skill() - ExSkill:play() + ExSkill:play(false) +end + +function pings.ex_skill_sub() + ExSkill:play(true) end function pings.ex_skill_removeAll() diff --git a/scripts/language.lua b/scripts/language.lua index ef0c6ea1..fd688aff 100644 --- a/scripts/language.lua +++ b/scripts/language.lua @@ -30,6 +30,7 @@ Language = { action_wheel_gui__ex_skill_guide__key_pre = "Press \"", action_wheel_gui__ex_skill_guide__key_post = "\"key to play", key_name__ex_skill = "Ex skill", + key_name__ex_skill_sub = "Sub Ex skill", key_name__bubble_1 = "Bubble: Good", key_name__bubble_2 = "Bubble: Heart", key_name__bubble_3 = "Bubble: Note", @@ -66,6 +67,7 @@ Language = { action_wheel_gui__ex_skill_guide__key_pre = "\"", action_wheel_gui__ex_skill_guide__key_post = "\"キーで再生", key_name__ex_skill = "Exスキル", + key_name__ex_skill_sub = "サブExスキル", key_name__bubble_1 = "吹き出し:いいね", key_name__bubble_2 = "吹き出し:ハート", key_name__bubble_3 = "吹き出し:音符",