import {BallState, Player, PlayerPhantom} from "../model/tactic/Player" import {middlePos, ratioWithinBase} from "../geo/Pos" import {ComponentId, TacticComponent, TacticContent,} from "../model/tactic/Tactic" import {overlaps} from "../geo/Box" import {Action, ActionKind} from "../model/tactic/Action" import {removeBall, updateComponent} from "./TacticContentDomains" import {getOrigin} from "./PlayerDomains" export function refreshAllActions( actions: Action[], components: TacticComponent[], ) { return actions.map((action) => ({ ...action, type: getActionKindFrom(action.fromId, action.toId, components), })) } export function getActionKindFrom( originId: ComponentId, targetId: ComponentId | null, components: TacticComponent[], ): ActionKind { const origin = components.find((p) => p.id == originId)! const target = components.find(p => p.id == targetId) let ballState = BallState.NONE if (origin.type == "player" || origin.type == "phantom") { ballState = origin.ballState } let hasTarget = target ? (target.type != 'phantom' || target.originPlayerId != origin.id) : false return getActionKind(hasTarget, ballState) } export function getActionKind(hasTarget: boolean, ballState: BallState): ActionKind { switch (ballState) { case BallState.HOLDS: return hasTarget ? ActionKind.SHOOT : ActionKind.DRIBBLE case BallState.SHOOTED: return ActionKind.MOVE case BallState.NONE: return hasTarget ? ActionKind.SCREEN : ActionKind.MOVE } } export function placeArrow( origin: Player | PlayerPhantom, courtBounds: DOMRect, arrowHead: DOMRect, content: TacticContent, ): { createdAction: Action, newContent: TacticContent } { const originRef = document.getElementById(origin.id)! const start = ratioWithinBase( middlePos(originRef.getBoundingClientRect()), courtBounds, ) /** * Creates a new phantom component. * Be aware that this function will reassign the `content` parameter. * @param receivesBall */ function createPhantom(receivesBall: boolean): ComponentId { const {x, y} = ratioWithinBase(arrowHead, courtBounds) let itemIndex: number let originPlayer: Player if (origin.type == "phantom") { // if we create a phantom from another phantom, // simply add it to the phantom's path const originPlr = getOrigin(origin, content.components)! itemIndex = originPlr.path!.items.length originPlayer = originPlr } else { // if we create a phantom directly from a player // create a new path and add it into itemIndex = 0 originPlayer = origin } const path = originPlayer.path const phantomId = "phantom-" + itemIndex + "-" + originPlayer.id content = updateComponent( { ...originPlayer, path: { items: path ? [...path.items, phantomId] : [phantomId], }, }, content, ) const ballState = receivesBall ? BallState.HOLDS : origin.ballState == BallState.HOLDS ? BallState.HOLDS : BallState.NONE const phantom: PlayerPhantom = { type: "phantom", id: phantomId, rightRatio: x, bottomRatio: y, originPlayerId: originPlayer.id, ballState } content = { ...content, components: [...content.components, phantom], } return phantom.id } for (const component of content.components) { if (component.id == origin.id) { continue } const componentBounds = document .getElementById(component.id)! .getBoundingClientRect() if (overlaps(componentBounds, arrowHead)) { const targetPos = document .getElementById(component.id)! .getBoundingClientRect() const end = ratioWithinBase(middlePos(targetPos), courtBounds) let toId = component.id if (component.type == "ball") { toId = createPhantom(true) content = removeBall(content) } const action: Action = { fromId: originRef.id, toId, type: getActionKind(true, origin.ballState), moveFrom: start, segments: [{next: end}], } return { newContent: { ...content, actions: [...content.actions, action], }, createdAction: action } } } const phantomId = createPhantom(origin.ballState == BallState.HOLDS) const action: Action = { fromId: originRef.id, toId: phantomId, type: getActionKind(false, origin.ballState), moveFrom: ratioWithinBase( middlePos(originRef.getBoundingClientRect()), courtBounds, ), segments: [ {next: ratioWithinBase(middlePos(arrowHead), courtBounds)}, ], } return { newContent: { ...content, actions: [...content.actions, action], }, createdAction: action } } export function repositionActionsRelatedTo( compId: ComponentId, courtBounds: DOMRect, actions: Action[], ): Action[] { const posRect = document.getElementById(compId)?.getBoundingClientRect() const newPos = posRect != undefined ? ratioWithinBase(middlePos(posRect), courtBounds) : undefined return actions.flatMap((action) => { if (newPos == undefined) { return [] } if (action.fromId == compId) { return [{...action, moveFrom: newPos}] } if (action.toId == compId) { const lastIdx = action.segments.length - 1 const segments = action.segments.toSpliced(lastIdx, 1, { ...action.segments[lastIdx], next: newPos!, }) return [{...action, segments}] } return action }) }