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.
Application-Web/front/editor/ActionsDomains.ts

215 lines
6.3 KiB

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
})
}