const COLLISION_WITH_PLAYER_STATE = { WALKING_COLLIDABLE: 0, STILL_NEAR_SELF_PLAYER_ONLY_PLAYING_ANIM: 1, STILL_NEAR_SELF_PLAYER_ONLY_PLAYED_ANIM: 2, STILL_NEAR_OTHER_PLAYER_ONLY_PLAYING_ANIM: 3, STILL_NEAR_OTHER_PLAYER_ONLY_PLAYED_ANIM: 4, STILL_NEAR_SELF_PLAYER_NEAR_OTHER_PLAYER_PLAYING_ANIM: 5, STILL_NEAR_SELF_PLAYER_NEAR_OTHER_PLAYER_PLAYED_ANIM: 6, WALKING_COLLIDABLE_WITH_SELF_PLAYER_BUT_NOT_OTHER_PLAYER: 7, STILL_NEAR_NOBODY_PLAYING_ANIM: 8, }; const STILL_NEAR_SELF_PLAYER_STATE_SET = new Set(); STILL_NEAR_SELF_PLAYER_STATE_SET.add(COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_ONLY_PLAYING_ANIM); STILL_NEAR_SELF_PLAYER_STATE_SET.add(COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_ONLY_PLAYED_ANIM); STILL_NEAR_SELF_PLAYER_STATE_SET.add(COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_NEAR_OTHER_PLAYER_PLAYING_ANIM); STILL_NEAR_SELF_PLAYER_STATE_SET.add(COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_NEAR_OTHER_PLAYER_PLAYED_ANIM); const STILL_NEAR_OTHER_PLAYER_STATE_SET = new Set(); STILL_NEAR_OTHER_PLAYER_STATE_SET.add(COLLISION_WITH_PLAYER_STATE.STILL_NEAR_OTHER_PLAYER_ONLY_PLAYING_ANIM); STILL_NEAR_OTHER_PLAYER_STATE_SET.add(COLLISION_WITH_PLAYER_STATE.STILL_NEAR_OTHER_PLAYER_ONLY_PLAYED_ANIM); STILL_NEAR_OTHER_PLAYER_STATE_SET.add(COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_NEAR_OTHER_PLAYER_PLAYING_ANIM); STILL_NEAR_OTHER_PLAYER_STATE_SET.add(COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_NEAR_OTHER_PLAYER_PLAYED_ANIM); const STILL_SHOULD_NOT_PLAY_STUNNED_ANIM_SET = new Set(); STILL_SHOULD_NOT_PLAY_STUNNED_ANIM_SET.add(COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_ONLY_PLAYING_ANIM); STILL_SHOULD_NOT_PLAY_STUNNED_ANIM_SET.add(COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_ONLY_PLAYED_ANIM); STILL_SHOULD_NOT_PLAY_STUNNED_ANIM_SET.add(COLLISION_WITH_PLAYER_STATE.STILL_NEAR_OTHER_PLAYER_ONLY_PLAYING_ANIM); STILL_SHOULD_NOT_PLAY_STUNNED_ANIM_SET.add(COLLISION_WITH_PLAYER_STATE.STILL_NEAR_OTHER_PLAYER_ONLY_PLAYED_ANIM); STILL_SHOULD_NOT_PLAY_STUNNED_ANIM_SET.add(COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_NEAR_OTHER_PLAYER_PLAYING_ANIM); STILL_SHOULD_NOT_PLAY_STUNNED_ANIM_SET.add(COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_NEAR_OTHER_PLAYER_PLAYED_ANIM); STILL_SHOULD_NOT_PLAY_STUNNED_ANIM_SET.add(COLLISION_WITH_PLAYER_STATE.STILL_NEAR_NOBODY_PLAYING_ANIM); function transitWalkingConditionallyCollidableToUnconditionallyCollidable(currentCollisionWithPlayerState) { switch (currentCollisionWithPlayerState) { case COLLISION_WITH_PLAYER_STATE.WALKING_COLLIDABLE_WITH_SELF_PLAYER_BUT_NOT_OTHER_PLAYER: return COLLISION_WITH_PLAYER_STATE.WALKING_COLLIDABLE; } return currentCollisionWithPlayerState; } function transitUponSelfPlayerLeftProximityArea(currentCollisionWithPlayerState) { switch (currentCollisionWithPlayerState) { case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_ONLY_PLAYING_ANIM: return COLLISION_WITH_PLAYER_STATE.STILL_NEAR_NOBODY_PLAYING_ANIM; case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_ONLY_PLAYED_ANIM: return COLLISION_WITH_PLAYER_STATE.WALKING_COLLIDABLE; case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_NEAR_OTHER_PLAYER_PLAYING_ANIM: return COLLISION_WITH_PLAYER_STATE.STILL_NEAR_OTHER_PLAYER_ONLY_PLAYING_ANIM; case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_NEAR_OTHER_PLAYER_PLAYED_ANIM: return COLLISION_WITH_PLAYER_STATE.WALKING_COLLIDABLE_WITH_SELF_PLAYER_BUT_NOT_OTHER_PLAYER; } return currentCollisionWithPlayerState; } function transitDueToNoBodyInProximityArea(currentCollisionWithPlayerState) { switch (currentCollisionWithPlayerState) { case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_ONLY_PLAYING_ANIM: case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_OTHER_PLAYER_ONLY_PLAYING_ANIM: case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_NEAR_OTHER_PLAYER_PLAYING_ANIM: return COLLISION_WITH_PLAYER_STATE.STILL_NEAR_NOBODY_PLAYING_ANIM; case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_ONLY_PLAYED_ANIM: case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_OTHER_PLAYER_ONLY_PLAYED_ANIM: case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_NEAR_OTHER_PLAYER_PLAYED_ANIM: return COLLISION_WITH_PLAYER_STATE.WALKING_COLLIDABLE_WITH_SELF_PLAYER_BUT_NOT_OTHER_PLAYER; } return currentCollisionWithPlayerState; } function transitToPlayingStunnedAnim(currentCollisionWithPlayerState, dueToSelfPlayer, dueToOtherPlayer) { if (dueToSelfPlayer) { switch (currentCollisionWithPlayerState) { case COLLISION_WITH_PLAYER_STATE.WALKING_COLLIDABLE: case COLLISION_WITH_PLAYER_STATE.WALKING_COLLIDABLE_WITH_SELF_PLAYER_BUT_NOT_OTHER_PLAYER: return COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_ONLY_PLAYING_ANIM; } } if (dueToOtherPlayer) { switch (currentCollisionWithPlayerState) { case COLLISION_WITH_PLAYER_STATE.WALKING_COLLIDABLE: return COLLISION_WITH_PLAYER_STATE.STILL_NEAR_OTHER_PLAYER_ONLY_PLAYING_ANIM; } } // TODO: Any error to throw? return currentCollisionWithPlayerState; } function transitDuringPlayingStunnedAnim(currentCollisionWithPlayerState, dueToSelfPlayerComesIntoProximity, dueToOtherPlayerComesIntoProximity) { if (dueToSelfPlayerComesIntoProximity) { switch (currentCollisionWithPlayerState) { case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_OTHER_PLAYER_ONLY_PLAYING_ANIM: return COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_NEAR_OTHER_PLAYER_PLAYING_ANIM; case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_NOBODY_PLAYING_ANIM: return COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_ONLY_PLAYING_ANIM; } } if (dueToOtherPlayerComesIntoProximity) { switch (currentCollisionWithPlayerState) { case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_ONLY_PLAYING_ANIM: return COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_NEAR_OTHER_PLAYER_PLAYING_ANIM; case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_NOBODY_PLAYING_ANIM: return COLLISION_WITH_PLAYER_STATE.STILL_NEAR_OTHER_PLAYER_ONLY_PLAYING_ANIM; } } // TODO: Any error to throw? return currentCollisionWithPlayerState; } function transitStunnedAnimPlayingToPlayed(currentCollisionWithPlayerState, forceNotCollidableWithOtherPlayer) { switch (currentCollisionWithPlayerState) { case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_ONLY_PLAYING_ANIM: return COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_ONLY_PLAYED_ANIM; case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_OTHER_PLAYER_ONLY_PLAYING_ANIM: return COLLISION_WITH_PLAYER_STATE.STILL_NEAR_OTHER_PLAYER_ONLY_PLAYED_ANIM; case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_NEAR_OTHER_PLAYER_PLAYING_ANIM: return COLLISION_WITH_PLAYER_STATE.STILL_NEAR_SELF_PLAYER_NEAR_OTHER_PLAYER_PLAYED_ANIM; case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_NOBODY_PLAYING_ANIM: return (true == forceNotCollidableWithOtherPlayer ? COLLISION_WITH_PLAYER_STATE.WALKING_COLLIDABLE_WITH_SELF_PLAYER_BUT_NOT_OTHER_PLAYER : COLLISION_WITH_PLAYER_STATE.WALKING_COLLIDABLE); } // TODO: Any error to throw? return currentCollisionWithPlayerState; } function transitStunnedAnimPlayedToWalking(currentCollisionWithPlayerState) { /* * Intentionally NOT transiting for * * - STILL_NEAR_SELF_PLAYER_NEAR_OTHER_PLAYER_PLAYED_ANIM, or * - STILL_NEAR_SELF_PLAYER_ONLY_PLAYED_ANIM, * * which should be transited upon leaving of "SelfPlayer". */ switch (currentCollisionWithPlayerState) { case COLLISION_WITH_PLAYER_STATE.STILL_NEAR_OTHER_PLAYER_ONLY_PLAYED_ANIM: return COLLISION_WITH_PLAYER_STATE.WALKING_COLLIDABLE_WITH_SELF_PLAYER_BUT_NOT_OTHER_PLAYER; } // TODO: Any error to throw? return currentCollisionWithPlayerState; } const BasePlayer = require("./BasePlayer"); cc.Class({ extends: BasePlayer, // LIFE-CYCLE CALLBACKS: start() { BasePlayer.prototype.start.call(this); this.scheduleNewDirection(this._generateRandomDirectionExcluding(0, 0)); }, onLoad() { BasePlayer.prototype.onLoad.call(this); const self = this; this.collisionWithPlayerState = COLLISION_WITH_PLAYER_STATE.WALKING_COLLIDABLE; this.clips = { '01': 'FlatHeadSisterRunTop', '0-1': 'FlatHeadSisterRunBottom', '-20': 'FlatHeadSisterRunLeft', '20': 'FlatHeadSisterRunRight', '-21': 'FlatHeadSisterRunTopLeft', '21': 'FlatHeadSisterRunTopRight', '-2-1': 'FlatHeadSisterRunBottomLeft', '2-1': 'FlatHeadSisterRunBottomRight' }; self.onStunnedAnimPlayedSafe = () => { const oldCollisionWithPlayerState = self.collisionWithPlayerState; self.collisionWithPlayerState = transitStunnedAnimPlayingToPlayed(this.collisionWithPlayerState, true); if (oldCollisionWithPlayerState == self.collisionWithPlayerState || !self.node) return; // TODO: Be more specific with "toExcludeDx" and "toExcludeDy". self.scheduleNewDirection(self._generateRandomDirectionExcluding(0, 0)); self.collisionWithPlayerState = transitStunnedAnimPlayedToWalking(self.collisionWithPlayerState); setTimeout(() => { self.collisionWithPlayerState = transitWalkingConditionallyCollidableToUnconditionallyCollidable(self.collisionWithPlayerState); }, 5000); }; self.onStunnedAnimPlayedSafeAction = cc.callFunc(self.onStunnedAnimPlayedSafe, self); self.playStunnedAnim = () => { let colliededAction1 = cc.rotateTo(0.2, -15); let colliededAction2 = cc.rotateTo(0.3, 15); let colliededAction3 = cc.rotateTo(0.2, 0); self.node.runAction(cc.sequence( cc.callFunc(() => { self.player.pause() }, self), colliededAction1, colliededAction2, colliededAction3, cc.callFunc(() => { self.player.resume() }, self), self.onStunnedAnimPlayedSafeAction )); // NOTE: Use .on('stop', self.onStunnedAnimPlayedSafe) if necessary. } }, _canMoveBy(vecToMoveBy) { if (COLLISION_WITH_PLAYER_STATE.WALKING_COLLIDABLE_WITH_SELF_PLAYER_BUT_NOT_OTHER_PLAYER != this.collisionWithPlayerState && COLLISION_WITH_PLAYER_STATE.WALKING_COLLIDABLE != this.collisionWithPlayerState) { return false; } const superRet = BasePlayer.prototype._canMoveBy.call(this, vecToMoveBy); const self = this; const computedNewDifferentPosLocalToParentWithinCurrentFrame = self.node.position.add(vecToMoveBy); const currentSelfColliderCircle = self.node.getComponent("cc.CircleCollider"); let nextSelfColliderCircle = null; if (0 < self.contactedBarriers.length || 0 < self.contactedNPCPlayers.length || 0 < self.contactedControlledPlayers) { /* To avoid unexpected buckling. */ const mutatedVecToMoveBy = vecToMoveBy.mul(2); nextSelfColliderCircle = { position: self.node.position.add(vecToMoveBy.mul(2)).add(currentSelfColliderCircle.offset), radius: currentSelfColliderCircle.radius, }; } else { nextSelfColliderCircle = { position: computedNewDifferentPosLocalToParentWithinCurrentFrame.add(currentSelfColliderCircle.offset), radius: currentSelfColliderCircle.radius, }; } for (let aCircleCollider of self.contactedControlledPlayers) { let contactedCircleLocalToParentWithinCurrentFrame = { position: aCircleCollider.node.position.add(aCircleCollider.offset), radius: aCircleCollider.radius, }; if (cc.Intersection.circleCircle(contactedCircleLocalToParentWithinCurrentFrame, nextSelfColliderCircle)) { return false; } } return superRet; }, update(dt) { const self = this; BasePlayer.prototype.update.call(this, dt); if (0 < self.contactedBarriers.length) { self.scheduleNewDirection(self._generateRandomDirectionExcluding(self.scheduledDirection.dx, self.scheduledDirection.dy)); } if (tileCollisionManager.isOutOfMapNode(self.mapNode, self.computedNewDifferentPosLocalToParentWithinCurrentFrame)) { self.scheduleNewDirection(self._generateRandomDirectionExcluding(self.scheduledDirection.dx, self.scheduledDirection.dy)); } }, onCollisionEnter(other, self) { BasePlayer.prototype.onCollisionEnter.call(this, other, self); const playerScriptIns = self.getComponent(self.node.name); switch (other.node.name) { case "SelfPlayer": playerScriptIns._addContactedControlledPlayers(other); if (1 == playerScriptIns.contactedControlledPlayers.length) { // When "SelfPlayer" comes into proximity area. if (!STILL_SHOULD_NOT_PLAY_STUNNED_ANIM_SET.has(playerScriptIns.collisionWithPlayerState)) { playerScriptIns.collisionWithPlayerState = transitToPlayingStunnedAnim(playerScriptIns.collisionWithPlayerState, true, false); playerScriptIns.playStunnedAnim(); } else { playerScriptIns.collisionWithPlayerState = transitDuringPlayingStunnedAnim(playerScriptIns.collisionWithPlayerState, true, false); } } break; case "NPCPlayer": if (1 == playerScriptIns.contactedNPCPlayers.length) { // When one of the other "OtherPlayer"s comes into proximity area. if (!STILL_SHOULD_NOT_PLAY_STUNNED_ANIM_SET.has(playerScriptIns.collisionWithPlayerState)) { const oldState = playerScriptIns.collisionWithPlayerState; playerScriptIns.collisionWithPlayerState = transitToPlayingStunnedAnim(oldState, false, true); if (playerScriptIns.collisionWithPlayerState != oldState) { playerScriptIns.playStunnedAnim(); } } else { playerScriptIns.collisionWithPlayerState = transitDuringPlayingStunnedAnim(playerScriptIns.collisionWithPlayerState, false, true); } } break; default: break; } }, onCollisionStay(other, self) { // TBD. }, onCollisionExit(other, self) { BasePlayer.prototype.onCollisionExit.call(this, other, self); const playerScriptIns = self.getComponent(self.node.name); switch (other.node.name) { case "SelfPlayer": playerScriptIns._removeContactedControlledPlayer(other); if (0 == playerScriptIns.contactedControlledPlayers.length) { // Special release step. if (STILL_NEAR_SELF_PLAYER_STATE_SET.has(playerScriptIns.collisionWithPlayerState)) { playerScriptIns.collisionWithPlayerState = transitUponSelfPlayerLeftProximityArea(playerScriptIns.collisionWithPlayerState); } } if (0 == playerScriptIns.contactedControlledPlayers.length && 0 == playerScriptIns.contactedNPCPlayers.length) { transitDueToNoBodyInProximityArea(playerScriptIns.collisionWithPlayerState); } break; case "NPCPlayer": if (0 == playerScriptIns.contactedControlledPlayers.length && 0 == playerScriptIns.contactedNPCPlayers.length) { transitDueToNoBodyInProximityArea(playerScriptIns.collisionWithPlayerState); } break; default: break; } }, });