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.
197 lines
5.5 KiB
197 lines
5.5 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 } {
|
|
/**
|
|
* 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 = {
|
|
actions: [],
|
|
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)) {
|
|
let toId = component.id
|
|
|
|
if (component.type == "ball") {
|
|
toId = createPhantom(true)
|
|
content = removeBall(content)
|
|
}
|
|
|
|
const action: Action = {
|
|
target: toId,
|
|
type: getActionKind(true, origin.ballState),
|
|
segments: [{ next: component.id }],
|
|
}
|
|
|
|
return {
|
|
newContent: updateComponent(
|
|
{
|
|
...origin,
|
|
actions: [...origin.actions, action],
|
|
},
|
|
content,
|
|
),
|
|
createdAction: action,
|
|
}
|
|
}
|
|
}
|
|
|
|
const phantomId = createPhantom(origin.ballState == BallState.HOLDS)
|
|
|
|
const action: Action = {
|
|
target: phantomId,
|
|
type: getActionKind(false, origin.ballState),
|
|
segments: [{ next: phantomId }],
|
|
}
|
|
return {
|
|
newContent: updateComponent(
|
|
{
|
|
...content.components.find((c) => c.id == origin.id)!,
|
|
actions: [...origin.actions, action],
|
|
},
|
|
content,
|
|
),
|
|
createdAction: action,
|
|
}
|
|
}
|
|
|
|
export function removeAllActionsTargeting(
|
|
componentId: ComponentId,
|
|
content: TacticContent,
|
|
): TacticContent {
|
|
let components = []
|
|
for (let i = 0; i < content.components.length; i++) {
|
|
const component = content.components[i]
|
|
components.push({
|
|
...component,
|
|
actions: component.actions.filter((a) => a.target != componentId),
|
|
})
|
|
}
|
|
|
|
return {
|
|
...content,
|
|
components,
|
|
}
|
|
}
|