|
|
@ -1,11 +1,21 @@
|
|
|
|
import { BallState, Player, PlayerPhantom } from "../model/tactic/Player"
|
|
|
|
import { BallState, Player, PlayerPhantom } from "../model/tactic/Player"
|
|
|
|
import { ratioWithinBase } from "../geo/Pos"
|
|
|
|
import { ratioWithinBase } from "../geo/Pos"
|
|
|
|
import {ComponentId, TacticComponent, TacticContent,} from "../model/tactic/Tactic"
|
|
|
|
import {
|
|
|
|
|
|
|
|
ComponentId,
|
|
|
|
|
|
|
|
TacticComponent,
|
|
|
|
|
|
|
|
TacticContent,
|
|
|
|
|
|
|
|
} from "../model/tactic/Tactic"
|
|
|
|
import { overlaps } from "../geo/Box"
|
|
|
|
import { overlaps } from "../geo/Box"
|
|
|
|
import { Action, ActionKind, moves } from "../model/tactic/Action"
|
|
|
|
import { Action, ActionKind, moves } from "../model/tactic/Action"
|
|
|
|
import { removeBall, updateComponent } from "./TacticContentDomains"
|
|
|
|
import { removeBall, updateComponent } from "./TacticContentDomains"
|
|
|
|
import {areInSamePath, changePlayerBallState, getOrigin, isNextInPath, removePlayer} from "./PlayerDomains"
|
|
|
|
import {
|
|
|
|
import {BALL_TYPE} from "../model/tactic/CourtObjects";
|
|
|
|
areInSamePath,
|
|
|
|
|
|
|
|
changePlayerBallState,
|
|
|
|
|
|
|
|
getOrigin,
|
|
|
|
|
|
|
|
isNextInPath,
|
|
|
|
|
|
|
|
removePlayer,
|
|
|
|
|
|
|
|
} from "./PlayerDomains"
|
|
|
|
|
|
|
|
import { BALL_TYPE } from "../model/tactic/CourtObjects"
|
|
|
|
|
|
|
|
|
|
|
|
export function getActionKind(
|
|
|
|
export function getActionKind(
|
|
|
|
target: TacticComponent | null,
|
|
|
|
target: TacticComponent | null,
|
|
|
@ -14,9 +24,7 @@ export function getActionKind(
|
|
|
|
switch (ballState) {
|
|
|
|
switch (ballState) {
|
|
|
|
case BallState.HOLDS_ORIGIN:
|
|
|
|
case BallState.HOLDS_ORIGIN:
|
|
|
|
case BallState.HOLDS_BY_PASS:
|
|
|
|
case BallState.HOLDS_BY_PASS:
|
|
|
|
return target
|
|
|
|
return target ? ActionKind.SHOOT : ActionKind.DRIBBLE
|
|
|
|
? ActionKind.SHOOT
|
|
|
|
|
|
|
|
: ActionKind.DRIBBLE
|
|
|
|
|
|
|
|
case BallState.PASSED_ORIGIN:
|
|
|
|
case BallState.PASSED_ORIGIN:
|
|
|
|
case BallState.PASSED:
|
|
|
|
case BallState.PASSED:
|
|
|
|
case BallState.NONE:
|
|
|
|
case BallState.NONE:
|
|
|
@ -26,23 +34,38 @@ export function getActionKind(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function getActionKindBetween(origin: Player | PlayerPhantom, target: TacticComponent | null, state: BallState): ActionKind {
|
|
|
|
export function getActionKindBetween(
|
|
|
|
|
|
|
|
origin: Player | PlayerPhantom,
|
|
|
|
|
|
|
|
target: TacticComponent | null,
|
|
|
|
|
|
|
|
state: BallState,
|
|
|
|
|
|
|
|
): ActionKind {
|
|
|
|
//remove the target if the target is a phantom that is within the origin's path
|
|
|
|
//remove the target if the target is a phantom that is within the origin's path
|
|
|
|
if (target != null && target.type == 'phantom' && areInSamePath(origin, target)) {
|
|
|
|
if (
|
|
|
|
target = null;
|
|
|
|
target != null &&
|
|
|
|
|
|
|
|
target.type == "phantom" &&
|
|
|
|
|
|
|
|
areInSamePath(origin, target)
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
target = null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return getActionKind(target, state)
|
|
|
|
return getActionKind(target, state)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function isActionValid(origin: TacticComponent, target: TacticComponent | null, components: TacticComponent[]): boolean {
|
|
|
|
export function isActionValid(
|
|
|
|
|
|
|
|
origin: TacticComponent,
|
|
|
|
|
|
|
|
target: TacticComponent | null,
|
|
|
|
|
|
|
|
components: TacticComponent[],
|
|
|
|
|
|
|
|
): boolean {
|
|
|
|
/// action is valid if the origin is neither a phantom nor a player
|
|
|
|
/// action is valid if the origin is neither a phantom nor a player
|
|
|
|
if (origin.type != "phantom" && origin.type != "player") {
|
|
|
|
if (origin.type != "phantom" && origin.type != "player") {
|
|
|
|
return true
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// action is invalid if the origin already moves (unless the origin holds a ball which will lead to a ball pass)
|
|
|
|
// action is invalid if the origin already moves (unless the origin holds a ball which will lead to a ball pass)
|
|
|
|
if (origin.actions.find(a => moves(a.type)) && origin.ballState != BallState.HOLDS_BY_PASS) {
|
|
|
|
if (
|
|
|
|
|
|
|
|
origin.actions.find((a) => moves(a.type)) &&
|
|
|
|
|
|
|
|
origin.ballState != BallState.HOLDS_BY_PASS
|
|
|
|
|
|
|
|
) {
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//Action is valid if the target is null
|
|
|
|
//Action is valid if the target is null
|
|
|
@ -56,23 +79,26 @@ export function isActionValid(origin: TacticComponent, target: TacticComponent |
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// action is invalid if the target already moves and is not indirectly bound with origin
|
|
|
|
// action is invalid if the target already moves and is not indirectly bound with origin
|
|
|
|
if (target.actions.find(a => moves(a.type)) && (hasBoundWith(target, origin, components) || hasBoundWith(origin, target, components))) {
|
|
|
|
if (
|
|
|
|
|
|
|
|
target.actions.find((a) => moves(a.type)) &&
|
|
|
|
|
|
|
|
(hasBoundWith(target, origin, components) ||
|
|
|
|
|
|
|
|
hasBoundWith(origin, target, components))
|
|
|
|
|
|
|
|
) {
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Action is invalid if there is already an action between origin and target.
|
|
|
|
// Action is invalid if there is already an action between origin and target.
|
|
|
|
if (origin.actions.find(a => a.target === target?.id) || target?.actions.find(a => a.target === origin.id)) {
|
|
|
|
if (
|
|
|
|
|
|
|
|
origin.actions.find((a) => a.target === target?.id) ||
|
|
|
|
|
|
|
|
target?.actions.find((a) => a.target === origin.id)
|
|
|
|
|
|
|
|
) {
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Action is invalid if there is already an anterior action within the target's path
|
|
|
|
// Action is invalid if there is already an anterior action within the target's path
|
|
|
|
if (target.type == "phantom" || target.type == "player") {
|
|
|
|
if (target.type == "phantom" || target.type == "player") {
|
|
|
|
|
|
|
|
|
|
|
|
// cant have an action with current path
|
|
|
|
// cant have an action with current path
|
|
|
|
if (areInSamePath(origin, target))
|
|
|
|
if (areInSamePath(origin, target)) return false
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (alreadyHasAnAnteriorActionWith(origin, target, components)) {
|
|
|
|
if (alreadyHasAnAnteriorActionWith(origin, target, components)) {
|
|
|
|
return false
|
|
|
|
return false
|
|
|
@ -82,21 +108,25 @@ export function isActionValid(origin: TacticComponent, target: TacticComponent |
|
|
|
|
return true
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function hasBoundWith(origin: TacticComponent, target: TacticComponent, components: TacticComponent[]): boolean {
|
|
|
|
function hasBoundWith(
|
|
|
|
|
|
|
|
origin: TacticComponent,
|
|
|
|
|
|
|
|
target: TacticComponent,
|
|
|
|
|
|
|
|
components: TacticComponent[],
|
|
|
|
|
|
|
|
): boolean {
|
|
|
|
const toVisit = [origin.id]
|
|
|
|
const toVisit = [origin.id]
|
|
|
|
const visited: string[] = []
|
|
|
|
const visited: string[] = []
|
|
|
|
|
|
|
|
|
|
|
|
let itemId: string | undefined
|
|
|
|
let itemId: string | undefined
|
|
|
|
while ((itemId = toVisit.pop())) {
|
|
|
|
while ((itemId = toVisit.pop())) {
|
|
|
|
|
|
|
|
if (visited.indexOf(itemId) !== -1) continue
|
|
|
|
if (visited.indexOf(itemId) !== -1)
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
visited.push(itemId)
|
|
|
|
visited.push(itemId)
|
|
|
|
|
|
|
|
|
|
|
|
const item = components.find(c => c.id === itemId)!
|
|
|
|
const item = components.find((c) => c.id === itemId)!
|
|
|
|
|
|
|
|
|
|
|
|
const itemBounds = item.actions.flatMap(a => typeof a.target == "string" ? [a.target] : [])
|
|
|
|
const itemBounds = item.actions.flatMap((a) =>
|
|
|
|
|
|
|
|
typeof a.target == "string" ? [a.target] : [],
|
|
|
|
|
|
|
|
)
|
|
|
|
if (itemBounds.indexOf(target.id) !== -1) {
|
|
|
|
if (itemBounds.indexOf(target.id) !== -1) {
|
|
|
|
return true
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -107,30 +137,58 @@ function hasBoundWith(origin: TacticComponent, target: TacticComponent, componen
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function alreadyHasAnAnteriorActionWith(origin: Player | PlayerPhantom, target: Player | PlayerPhantom, components: TacticComponent[]): boolean {
|
|
|
|
function alreadyHasAnAnteriorActionWith(
|
|
|
|
const targetOrigin = target.type === "phantom" ? getOrigin(target, components) : target
|
|
|
|
origin: Player | PlayerPhantom,
|
|
|
|
const targetOriginPath = [targetOrigin.id, ...(targetOrigin.path?.items ?? [])]
|
|
|
|
target: Player | PlayerPhantom,
|
|
|
|
|
|
|
|
components: TacticComponent[],
|
|
|
|
const originOrigin = origin.type === "phantom" ? getOrigin(origin, components) : origin
|
|
|
|
): boolean {
|
|
|
|
const originOriginPath = [originOrigin.id, ...(originOrigin.path?.items ?? [])]
|
|
|
|
const targetOrigin =
|
|
|
|
|
|
|
|
target.type === "phantom" ? getOrigin(target, components) : target
|
|
|
|
|
|
|
|
const targetOriginPath = [
|
|
|
|
|
|
|
|
targetOrigin.id,
|
|
|
|
|
|
|
|
...(targetOrigin.path?.items ?? []),
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const originOrigin =
|
|
|
|
|
|
|
|
origin.type === "phantom" ? getOrigin(origin, components) : origin
|
|
|
|
|
|
|
|
const originOriginPath = [
|
|
|
|
|
|
|
|
originOrigin.id,
|
|
|
|
|
|
|
|
...(originOrigin.path?.items ?? []),
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
const targetIdx = targetOriginPath.indexOf(target.id)
|
|
|
|
const targetIdx = targetOriginPath.indexOf(target.id)
|
|
|
|
for (let i = targetIdx; i < targetOriginPath.length; i++) {
|
|
|
|
for (let i = targetIdx; i < targetOriginPath.length; i++) {
|
|
|
|
const phantom = components.find(c => c.id === targetOriginPath[i])! as (Player | PlayerPhantom)
|
|
|
|
const phantom = components.find(
|
|
|
|
if (phantom.actions.find(a => typeof a.target === "string" && (originOriginPath.indexOf(a.target) !== -1))) {
|
|
|
|
(c) => c.id === targetOriginPath[i],
|
|
|
|
return true;
|
|
|
|
)! as Player | PlayerPhantom
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
|
|
phantom.actions.find(
|
|
|
|
|
|
|
|
(a) =>
|
|
|
|
|
|
|
|
typeof a.target === "string" &&
|
|
|
|
|
|
|
|
originOriginPath.indexOf(a.target) !== -1,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const originIdx = originOriginPath.indexOf(origin.id)
|
|
|
|
const originIdx = originOriginPath.indexOf(origin.id)
|
|
|
|
for (let i = 0; i <= originIdx; i++) {
|
|
|
|
for (let i = 0; i <= originIdx; i++) {
|
|
|
|
const phantom = components.find(c => c.id === originOriginPath[i])! as (Player | PlayerPhantom)
|
|
|
|
const phantom = components.find(
|
|
|
|
if (phantom.actions.find(a => typeof a.target === "string" && targetOriginPath.indexOf(a.target) > targetIdx)) {
|
|
|
|
(c) => c.id === originOriginPath[i],
|
|
|
|
return true;
|
|
|
|
)! as Player | PlayerPhantom
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
|
|
phantom.actions.find(
|
|
|
|
|
|
|
|
(a) =>
|
|
|
|
|
|
|
|
typeof a.target === "string" &&
|
|
|
|
|
|
|
|
targetOriginPath.indexOf(a.target) > targetIdx,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function createAction(
|
|
|
|
export function createAction(
|
|
|
@ -143,7 +201,7 @@ export function createAction(
|
|
|
|
* Creates a new phantom component.
|
|
|
|
* Creates a new phantom component.
|
|
|
|
* Be aware that this function will reassign the `content` parameter.
|
|
|
|
* Be aware that this function will reassign the `content` parameter.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
function createPhantom(originState: BallState): ComponentId {
|
|
|
|
function createPhantom(forceHasBall: boolean): ComponentId {
|
|
|
|
const { x, y } = ratioWithinBase(arrowHead, courtBounds)
|
|
|
|
const { x, y } = ratioWithinBase(arrowHead, courtBounds)
|
|
|
|
|
|
|
|
|
|
|
|
let itemIndex: number
|
|
|
|
let itemIndex: number
|
|
|
@ -177,7 +235,9 @@ export function createAction(
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
let phantomState: BallState
|
|
|
|
let phantomState: BallState
|
|
|
|
switch (originState) {
|
|
|
|
if (forceHasBall) phantomState = BallState.HOLDS_ORIGIN
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
switch (origin.ballState) {
|
|
|
|
case BallState.HOLDS_ORIGIN:
|
|
|
|
case BallState.HOLDS_ORIGIN:
|
|
|
|
phantomState = BallState.HOLDS_BY_PASS
|
|
|
|
phantomState = BallState.HOLDS_BY_PASS
|
|
|
|
break
|
|
|
|
break
|
|
|
@ -186,7 +246,7 @@ export function createAction(
|
|
|
|
phantomState = BallState.NONE
|
|
|
|
phantomState = BallState.NONE
|
|
|
|
break
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
phantomState = originState
|
|
|
|
phantomState = origin.ballState
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const phantom: PlayerPhantom = {
|
|
|
|
const phantom: PlayerPhantom = {
|
|
|
@ -241,7 +301,7 @@ export function createAction(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const phantomId = createPhantom(origin.ballState)
|
|
|
|
const phantomId = createPhantom(false)
|
|
|
|
|
|
|
|
|
|
|
|
const action: Action = {
|
|
|
|
const action: Action = {
|
|
|
|
target: phantomId,
|
|
|
|
target: phantomId,
|
|
|
@ -279,29 +339,39 @@ export function removeAllActionsTargeting(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function removeAction(
|
|
|
|
export function removeAction(origin: TacticComponent, action: Action, actionIdx: number, content: TacticContent): TacticContent {
|
|
|
|
origin: TacticComponent,
|
|
|
|
|
|
|
|
action: Action,
|
|
|
|
|
|
|
|
actionIdx: number,
|
|
|
|
|
|
|
|
content: TacticContent,
|
|
|
|
|
|
|
|
): TacticContent {
|
|
|
|
origin = {
|
|
|
|
origin = {
|
|
|
|
...origin,
|
|
|
|
...origin,
|
|
|
|
actions: origin.actions.toSpliced(actionIdx, 1),
|
|
|
|
actions: origin.actions.toSpliced(actionIdx, 1),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
content = updateComponent(
|
|
|
|
content = updateComponent(origin, content)
|
|
|
|
origin,
|
|
|
|
|
|
|
|
content,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (action.target == null) return content
|
|
|
|
if (action.target == null) return content
|
|
|
|
|
|
|
|
|
|
|
|
const target = content.components.find(
|
|
|
|
const target = content.components.find((c) => action.target == c.id)!
|
|
|
|
(c) => action.target == c.id,
|
|
|
|
|
|
|
|
)!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// if the removed action is a shoot, set the origin as holding the ball
|
|
|
|
// if the removed action is a shoot, set the origin as holding the ball
|
|
|
|
if (action.type == ActionKind.SHOOT && (origin.type === "player" || origin.type === "phantom")) {
|
|
|
|
if (
|
|
|
|
|
|
|
|
action.type == ActionKind.SHOOT &&
|
|
|
|
|
|
|
|
(origin.type === "player" || origin.type === "phantom")
|
|
|
|
|
|
|
|
) {
|
|
|
|
if (origin.ballState === BallState.PASSED)
|
|
|
|
if (origin.ballState === BallState.PASSED)
|
|
|
|
content = changePlayerBallState(origin, BallState.HOLDS_BY_PASS, content)
|
|
|
|
content = changePlayerBallState(
|
|
|
|
|
|
|
|
origin,
|
|
|
|
|
|
|
|
BallState.HOLDS_BY_PASS,
|
|
|
|
|
|
|
|
content,
|
|
|
|
|
|
|
|
)
|
|
|
|
else if (origin.ballState === BallState.PASSED_ORIGIN)
|
|
|
|
else if (origin.ballState === BallState.PASSED_ORIGIN)
|
|
|
|
content = changePlayerBallState(origin, BallState.HOLDS_ORIGIN, content)
|
|
|
|
content = changePlayerBallState(
|
|
|
|
|
|
|
|
origin,
|
|
|
|
|
|
|
|
BallState.HOLDS_ORIGIN,
|
|
|
|
|
|
|
|
content,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if (target.type === "player" || target.type === "phantom")
|
|
|
|
if (target.type === "player" || target.type === "phantom")
|
|
|
|
content = changePlayerBallState(target, BallState.NONE, content)
|
|
|
|
content = changePlayerBallState(target, BallState.NONE, content)
|
|
|
@ -315,16 +385,11 @@ export function removeAction(origin: TacticComponent, action: Action, actionIdx:
|
|
|
|
path = getOrigin(origin, content.components).path
|
|
|
|
path = getOrigin(origin, content.components).path
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
if (path != null && path.items.find((c) => c == target.id)) {
|
|
|
|
path != null &&
|
|
|
|
|
|
|
|
path.items.find((c) => c == target.id)
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
content = removePlayer(target, content)
|
|
|
|
content = removePlayer(target, content)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return content
|
|
|
|
return content
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -335,14 +400,18 @@ export function removeAction(origin: TacticComponent, action: Action, actionIdx:
|
|
|
|
* @param newState
|
|
|
|
* @param newState
|
|
|
|
* @param content
|
|
|
|
* @param content
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
export function spreadNewStateFromOriginStateChange(origin: Player | PlayerPhantom, newState: BallState, content: TacticContent): TacticContent {
|
|
|
|
export function spreadNewStateFromOriginStateChange(
|
|
|
|
|
|
|
|
origin: Player | PlayerPhantom,
|
|
|
|
|
|
|
|
newState: BallState,
|
|
|
|
|
|
|
|
content: TacticContent,
|
|
|
|
|
|
|
|
): TacticContent {
|
|
|
|
if (origin.ballState === newState) {
|
|
|
|
if (origin.ballState === newState) {
|
|
|
|
return content
|
|
|
|
return content
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
origin = {
|
|
|
|
origin = {
|
|
|
|
...origin,
|
|
|
|
...origin,
|
|
|
|
ballState: newState
|
|
|
|
ballState: newState,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
content = updateComponent(origin, content)
|
|
|
|
content = updateComponent(origin, content)
|
|
|
@ -350,32 +419,53 @@ export function spreadNewStateFromOriginStateChange(origin: Player | PlayerPhant
|
|
|
|
for (let i = 0; i < origin.actions.length; i++) {
|
|
|
|
for (let i = 0; i < origin.actions.length; i++) {
|
|
|
|
const action = origin.actions[i]
|
|
|
|
const action = origin.actions[i]
|
|
|
|
if (typeof action.target !== "string") {
|
|
|
|
if (typeof action.target !== "string") {
|
|
|
|
continue;
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const actionTarget = content.components.find(c => action.target === c.id)! as Player | PlayerPhantom;
|
|
|
|
const actionTarget = content.components.find(
|
|
|
|
|
|
|
|
(c) => action.target === c.id,
|
|
|
|
|
|
|
|
)! as Player | PlayerPhantom
|
|
|
|
|
|
|
|
|
|
|
|
let targetState: BallState = actionTarget.ballState
|
|
|
|
let targetState: BallState = actionTarget.ballState
|
|
|
|
let deleteAction = false
|
|
|
|
let deleteAction = false
|
|
|
|
|
|
|
|
|
|
|
|
if (isNextInPath(origin, actionTarget, content.components)) {
|
|
|
|
if (isNextInPath(origin, actionTarget, content.components)) {
|
|
|
|
/// If the target is the next phantom from the origin, its state is propagated.
|
|
|
|
switch (newState) {
|
|
|
|
targetState = (newState === BallState.PASSED || newState === BallState.PASSED_ORIGIN) ? BallState.NONE : newState
|
|
|
|
case BallState.PASSED:
|
|
|
|
} else if (newState === BallState.NONE && action.type === ActionKind.SHOOT) {
|
|
|
|
case BallState.PASSED_ORIGIN:
|
|
|
|
|
|
|
|
targetState = BallState.NONE
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
case BallState.HOLDS_ORIGIN:
|
|
|
|
|
|
|
|
targetState = BallState.HOLDS_BY_PASS
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
targetState = newState
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (
|
|
|
|
|
|
|
|
newState === BallState.NONE &&
|
|
|
|
|
|
|
|
action.type === ActionKind.SHOOT
|
|
|
|
|
|
|
|
) {
|
|
|
|
/// if the new state removes the ball from the player, remove all actions that were meant to shoot the ball
|
|
|
|
/// if the new state removes the ball from the player, remove all actions that were meant to shoot the ball
|
|
|
|
deleteAction = true
|
|
|
|
deleteAction = true
|
|
|
|
targetState = BallState.NONE // then remove the ball for the target as well
|
|
|
|
targetState = BallState.NONE // then remove the ball for the target as well
|
|
|
|
} else if ((newState === BallState.HOLDS_BY_PASS || newState === BallState.HOLDS_ORIGIN) && action.type === ActionKind.SCREEN) {
|
|
|
|
} else if (
|
|
|
|
|
|
|
|
(newState === BallState.HOLDS_BY_PASS ||
|
|
|
|
|
|
|
|
newState === BallState.HOLDS_ORIGIN) &&
|
|
|
|
|
|
|
|
action.type === ActionKind.SCREEN
|
|
|
|
|
|
|
|
) {
|
|
|
|
targetState = BallState.HOLDS_BY_PASS
|
|
|
|
targetState = BallState.HOLDS_BY_PASS
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (deleteAction) {
|
|
|
|
if (deleteAction) {
|
|
|
|
content = removeAction(origin, action, i, content)
|
|
|
|
content = removeAction(origin, action, i, content)
|
|
|
|
origin = content.components.find(c => c.id === origin.id)! as Player | PlayerPhantom
|
|
|
|
origin = content.components.find((c) => c.id === origin.id)! as
|
|
|
|
i--; // step back
|
|
|
|
| Player
|
|
|
|
|
|
|
|
| PlayerPhantom
|
|
|
|
|
|
|
|
i-- // step back
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
// do not change the action type if it is a shoot action
|
|
|
|
// do not change the action type if it is a shoot action
|
|
|
|
const type = action.type == ActionKind.SHOOT
|
|
|
|
const type =
|
|
|
|
|
|
|
|
action.type == ActionKind.SHOOT
|
|
|
|
? ActionKind.SHOOT
|
|
|
|
? ActionKind.SHOOT
|
|
|
|
: getActionKindBetween(origin, actionTarget, newState)
|
|
|
|
: getActionKindBetween(origin, actionTarget, newState)
|
|
|
|
|
|
|
|
|
|
|
@ -383,13 +473,17 @@ export function spreadNewStateFromOriginStateChange(origin: Player | PlayerPhant
|
|
|
|
...origin,
|
|
|
|
...origin,
|
|
|
|
actions: origin.actions.toSpliced(i, 1, {
|
|
|
|
actions: origin.actions.toSpliced(i, 1, {
|
|
|
|
...action,
|
|
|
|
...action,
|
|
|
|
type
|
|
|
|
type,
|
|
|
|
})
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
content = updateComponent(origin, content)
|
|
|
|
content = updateComponent(origin, content)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
content = spreadNewStateFromOriginStateChange(actionTarget, targetState, content)
|
|
|
|
content = spreadNewStateFromOriginStateChange(
|
|
|
|
|
|
|
|
actionTarget,
|
|
|
|
|
|
|
|
targetState,
|
|
|
|
|
|
|
|
content,
|
|
|
|
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return content
|
|
|
|
return content
|
|
|
|