You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
232 lines
5.9 KiB
232 lines
5.9 KiB
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 {changePlayerBallState} 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,
|
|
actions: [],
|
|
}
|
|
}
|
|
|
|
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, true)
|
|
}
|
|
|
|
courtObject = {
|
|
type: BALL_TYPE,
|
|
id: BALL_ID,
|
|
rightRatio: x,
|
|
bottomRatio: y,
|
|
actions: [],
|
|
}
|
|
break
|
|
|
|
default:
|
|
throw new Error("unknown court object " + rackedObject.key)
|
|
}
|
|
|
|
return {
|
|
...content,
|
|
components: [...content.components, courtObject],
|
|
}
|
|
}
|
|
|
|
export function dropBallOnComponent(
|
|
targetedComponentIdx: number,
|
|
content: TacticContent,
|
|
setAsOrigin: boolean
|
|
): TacticContent {
|
|
const component = content.components[targetedComponentIdx]
|
|
|
|
if ((component.type == 'player' || component.type == 'phantom')) {
|
|
const newState = setAsOrigin
|
|
? (component.ballState === BallState.PASSED || component.ballState === BallState.PASSED_ORIGIN) ? BallState.PASSED_ORIGIN : BallState.HOLDS_ORIGIN
|
|
: BallState.HOLDS_BY_PASS
|
|
|
|
content = changePlayerBallState(component, newState, content)
|
|
}
|
|
|
|
return removeBall(content)
|
|
}
|
|
|
|
export function removeBall(content: TacticContent): TacticContent {
|
|
const ballObjIdx = content.components.findIndex((o) => o.type == "ball")
|
|
|
|
if (ballObjIdx == -1) {
|
|
return content
|
|
}
|
|
|
|
return {
|
|
...content,
|
|
components: content.components.toSpliced(ballObjIdx, 1),
|
|
}
|
|
}
|
|
|
|
export function placeBallAt(
|
|
refBounds: DOMRect,
|
|
courtBounds: DOMRect,
|
|
content: TacticContent,
|
|
): TacticContent {
|
|
if (!overlaps(courtBounds, refBounds)) {
|
|
return removeBall(content)
|
|
}
|
|
const playerCollidedIdx = getComponentCollided(
|
|
refBounds,
|
|
content.components,
|
|
BALL_ID,
|
|
)
|
|
|
|
if (playerCollidedIdx != -1) {
|
|
return dropBallOnComponent(playerCollidedIdx, content, true)
|
|
}
|
|
|
|
const ballIdx = content.components.findIndex((o) => o.type == "ball")
|
|
|
|
const {x, y} = ratioWithinBase(refBounds, courtBounds)
|
|
|
|
const ball: Ball = {
|
|
type: BALL_TYPE,
|
|
id: BALL_ID,
|
|
rightRatio: x,
|
|
bottomRatio: y,
|
|
actions: [],
|
|
}
|
|
|
|
let components = content.components
|
|
|
|
if (ballIdx != -1) {
|
|
components = components.toSpliced(ballIdx, 1, ball)
|
|
} else {
|
|
components = components.concat(ball)
|
|
}
|
|
|
|
return {
|
|
...content,
|
|
components,
|
|
}
|
|
}
|
|
|
|
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),
|
|
}
|
|
}
|
|
|
|
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}))
|
|
}
|