import {Pos, ratioWithinBase} from "../geo/Pos" import {BallState, Player, PlayerInfo, PlayerTeam} from "../model/tactic/Player" import {Ball, BALL_ID, BALL_TYPE, CourtObject} from "../model/tactic/CourtObjects" import {ComponentId, TacticComponent, TacticContent,} from "../model/tactic/Tactic" import {overlaps} from "../geo/Box" import {RackedCourtObject, RackedPlayer} from "./RackedItems" import {refreshAllActions} from "./ActionsDomains" import {getOrigin} from "./PlayerDomains"; export function placePlayerAt( refBounds: DOMRect, courtBounds: DOMRect, element: RackedPlayer, ): Player { const {x, y} = ratioWithinBase(refBounds, courtBounds) return { type: "player", id: "player-" + element.key + "-" + element.team, team: element.team, role: element.key, rightRatio: x, bottomRatio: y, ballState: BallState.NONE, path: null, } } export function placeObjectAt( refBounds: DOMRect, courtBounds: DOMRect, rackedObject: RackedCourtObject, content: TacticContent, ): TacticContent { const {x, y} = ratioWithinBase(refBounds, courtBounds) let courtObject: CourtObject switch (rackedObject.key) { case BALL_TYPE: const playerCollidedIdx = getComponentCollided( refBounds, content.components, BALL_ID, ) if (playerCollidedIdx != -1) { return dropBallOnComponent(playerCollidedIdx, content) } courtObject = { type: BALL_TYPE, id: BALL_ID, rightRatio: x, bottomRatio: y, } break default: throw new Error("unknown court object " + rackedObject.key) } return { ...content, components: [...content.components, courtObject], } } export function dropBallOnComponent( targetedComponentIdx: number, content: TacticContent, ): TacticContent { let components = content.components let component = components[targetedComponentIdx] let origin let isPhantom: boolean if (component.type == 'phantom') { isPhantom = true origin = getOrigin(component, components) } else if (component.type == 'player') { isPhantom = false origin = component } else { return content } components = components.toSpliced(targetedComponentIdx, 1, { ...component, ballState: BallState.HOLDS, }) if (origin.path != null) { const phantoms = origin.path!.items const headingPhantoms = isPhantom ? phantoms.slice(phantoms.indexOf(component.id)) : phantoms components = components.map(c => headingPhantoms.indexOf(c.id) != -1 ? { ...c, hasBall: true } : c) } const ballObj = components.findIndex((p) => p.type == BALL_TYPE) // Maybe the ball is not present on the court as an object component // if so, don't bother removing it from the court. // This can occur if the user drags and drop the ball from a player that already has the ball // to another component if (ballObj != -1) { components.splice(ballObj, 1) } return { ...content, actions: refreshAllActions(content.actions, components), components, } } export function removeBall(content: TacticContent): TacticContent { const ballObj = content.components.findIndex((o) => o.type == "ball") const components = content.components.map((c) => (c.type == 'player' || c.type == 'phantom') ? { ...c, hasBall: false, } : c, ) // if the ball is already not on the court, do nothing if (ballObj != -1) { components.splice(ballObj, 1) } return { ...content, actions: refreshAllActions(content.actions, components), components, } } export function placeBallAt( refBounds: DOMRect, courtBounds: DOMRect, content: TacticContent, ): { newContent: TacticContent removed: boolean } { if (!overlaps(courtBounds, refBounds)) { return {newContent: removeBall(content), removed: true} } const playerCollidedIdx = getComponentCollided( refBounds, content.components, BALL_ID, ) if (playerCollidedIdx != -1) { return { newContent: dropBallOnComponent(playerCollidedIdx, { ...content, components: content.components.map((c) => c.type == "player" || c.type == 'phantom' ? { ...c, hasBall: false, } : c, ), }), removed: false, } } const ballIdx = content.components.findIndex((o) => o.type == "ball") const {x, y} = ratioWithinBase(refBounds, courtBounds) const components = content.components.map((c) => c.type == "player" || c.type == "phantom" ? { ...c, hasBall: false, } : c, ) const ball: Ball = { type: BALL_TYPE, id: BALL_ID, rightRatio: x, bottomRatio: y, } if (ballIdx != -1) { components.splice(ballIdx, 1, ball) } else { components.push(ball) } return { newContent: { ...content, actions: refreshAllActions(content.actions, components), components, }, removed: false, } } export function moveComponent( newPos: Pos, component: TacticComponent, info: PlayerInfo, courtBounds: DOMRect, content: TacticContent, removed: (content: TacticContent) => TacticContent, ): TacticContent { const playerBounds = document .getElementById(info.id)! .getBoundingClientRect() // if the piece is no longer on the court, remove it if (!overlaps(playerBounds, courtBounds)) { return removed(content) } return updateComponent( { ...component, rightRatio: newPos.x, bottomRatio: newPos.y, }, content, ) } export function removeComponent( componentId: ComponentId, content: TacticContent, ): TacticContent { const componentIdx = content.components.findIndex( (c) => c.id == componentId, ) return { ...content, components: content.components.toSpliced(componentIdx, 1), actions: content.actions.filter( (a) => a.toId !== componentId && a.fromId !== componentId, ), } } export function updateComponent( component: TacticComponent, content: TacticContent, ): TacticContent { const componentIdx = content.components.findIndex( (c) => c.id == component.id, ) return { ...content, components: content.components.toSpliced(componentIdx, 1, component), } } export function getComponentCollided( bounds: DOMRect, components: TacticComponent[], ignore?: ComponentId, ): number | -1 { for (let i = 0; i < components.length; i++) { const component = components[i] if (component.id == ignore) continue const playerBounds = document .getElementById(component.id)! .getBoundingClientRect() if (overlaps(playerBounds, bounds)) { return i } } return -1 } export function getRackPlayers( team: PlayerTeam, components: TacticComponent[], ): RackedPlayer[] { return ["1", "2", "3", "4", "5"] .filter( (role) => components.findIndex( (c) => c.type == "player" && c.team == team && c.role == role, ) == -1, ) .map((key) => ({team, key})) }