Skip to content

Commit

Permalink
feat: Show casts and interrupted abilities (#4)
Browse files Browse the repository at this point in the history
In the previous behavior casted abilities would only show up in the timeline after their cast finished.

This meant that there would be an uneven spacing inbetween abilities that were insta-casted and those that had cast times.

In this branch, casted abilities are now shown in the timeline at the instant they are started in a semi-transparent state,
and they become full opaque when they are finished. If they are interrupted, then they disappear from the timeline.

![casts and intrruptions skillsdisplay](https://github.com/reyronald/SkillDisplay/assets/7514993/cceac273-9b07-46dc-b22d-ee6890347979)
  • Loading branch information
reyronald authored Feb 2, 2024
1 parent aa0c844 commit 454b211
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 21 deletions.
8 changes: 7 additions & 1 deletion src/Action.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,12 @@ const actionOverrides = new Map([

const actionMap = new Map()

export default function Action({ actionId, ability, additionalClasses }) {
export default function Action({
actionId,
casting,
ability,
additionalClasses,
}) {
const [apiData, setApiData] = React.useState()

React.useEffect(() => {
Expand Down Expand Up @@ -120,6 +125,7 @@ export default function Action({ actionId, ability, additionalClasses }) {
className={
isGCD ? `gcd ${additionalClasses}` : `ogcd ${additionalClasses}`
}
style={casting ? { opacity: 0.5 } : undefined}
src={`https://xivapi.com/${apiData.Icon}`}
alt={apiData.Name || ""}
/>
Expand Down
70 changes: 52 additions & 18 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,18 @@ import "./css/App.css"
import Action from "./Action"
import RotationContainer from "./Rotation"
import ReactDOM from "react-dom"
import { SPRINT_ACTION_ID } from "./constants"

const handleCodes = new Set(["00", "01", "02", "21", "22", "33"])
import { SPRINT_ACTION_ID, LINE_ID } from "./constants"

const handleCodes = new Set([
LINE_ID.LogLine,
LINE_ID.ChangeZone,
LINE_ID.ChangePrimaryPlayer,
LINE_ID.NetworkStartsCasting,
LINE_ID.NetworkAbility,
LINE_ID.NetworkAOEAbility,
LINE_ID.NetworkCancelAbility,
LINE_ID.ActorControl,
])

export default function App() {
const [actionList, setActionList] = React.useState([])
Expand All @@ -18,7 +27,8 @@ export default function App() {
let lastAction = -1
let currentZone = "Unknown"

let lastKey = 1
let lastKey = 0
let timeoutId = null

let closeFn = listenToACT((...logSplit) => {
const openNewEncounter = () => {
Expand Down Expand Up @@ -58,18 +68,18 @@ export default function App() {
if (!handleCodes.has(logCode)) return

switch (logCode) {
case "00":
case LINE_ID.LogLine:
if (logParameter1 === "0038" && logParameter3 === "end")
openNewEncounter()
return
case "01":
case LINE_ID.ChangeZone:
currentZone = logParameter2
return
case "02":
case LINE_ID.ChangePrimaryPlayer:
selfId = parseInt(logParameter1, 16)
openNewEncounter()
return
case "33":
case LINE_ID.ActorControl:
if (logParameter2 === "40000012" || logParameter2 === "40000010")
openNewEncounter()
return
Expand Down Expand Up @@ -102,48 +112,72 @@ export default function App() {
lastTimestamp = logTimestamp
lastAction = action

const key = (lastKey % 256) + 1
lastKey = key
let keyToRemove = null

// This is pretty silly but it's the neatest way to handle the updates going
// out at the same time, without finding some way to merge the action lists....
ReactDOM.unstable_batchedUpdates(() => {
setActionList((actionList) =>
actionList.concat({ action, ability, key }),
)
setActionList((actionList) => {
const lastAction = actionList.at(-1)

keyToRemove = lastAction?.key ?? null

if (logCode === LINE_ID.NetworkCancelAbility) {
return actionList.slice(0, -1)
} else if (lastAction?.action === action && lastAction?.casting) {
return actionList.with(-1, { ...lastAction, casting: false })
} else {
const key = (lastKey % 256) + 1
lastKey = key
return actionList.concat({
action,
ability,
key,
casting: logCode === LINE_ID.NetworkStartsCasting,
})
}
})
setEncounterList((encounterList) => {
if (logCode !== LINE_ID.NetworkAbility) return encounterList

if (!encounterList[0]) {
encounterList[0] = {
name: currentZone,
rotation: [],
}
}

encounterList[0].rotation.push(action)
encounterList[0].rotation.push({ action, ability })

return encounterList
})
})

setTimeout(() => {
setActionList((actionList) => actionList.slice(1))
}, 10000)
if (keyToRemove != null) {
timeoutId = setTimeout(() => {
setActionList((actionList) =>
actionList.filter((action) => action.key !== keyToRemove),
)
}, 10000)
}
})

return () => {
closeFn()
clearTimeout(timeoutId)
}
}, [])

return (
<>
<div className="container">
<div className="actions">
{actionList.map(({ action, ability, key }) => (
{actionList.map(({ action, ability, key, casting }) => (
<Action
key={key}
actionId={action}
ability={ability}
casting={casting}
additionalClasses="action-move"
/>
))}
Expand Down
9 changes: 7 additions & 2 deletions src/Rotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,13 @@ function RotationContents({ expanded, actionList }) {

return (
<div className="rotation-list">
{actionList.map((action, i) => (
<Action key={i} actionId={action} additionalClasses="action-rotation" />
{actionList.map(({ action, ability }, i) => (
<Action
key={i}
actionId={action}
ability={ability}
additionalClasses="action-rotation"
/>
))}
</div>
)
Expand Down
12 changes: 12 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
@@ -1 +1,13 @@
export const SPRINT_ACTION_ID = 3

// https://github.com/OverlayPlugin/cactbot/blob/main/docs/LogGuide.md
export const LINE_ID = {
LogLine: "00",
ChangeZone: "01",
ChangePrimaryPlayer: "02",
NetworkStartsCasting: "20",
NetworkAbility: "21",
NetworkAOEAbility: "22",
NetworkCancelAbility: "23",
ActorControl: "33",
}

0 comments on commit 454b211

Please sign in to comment.