Fixed bugs, fixed AI

This commit is contained in:
AlexMamontow 2021-08-11 19:19:36 +03:00
parent 0eef64947d
commit 0a5f964099
8 changed files with 173 additions and 442 deletions

View File

@ -8587,7 +8587,7 @@ MonoBehaviour:
m_Arguments: m_Arguments:
m_ObjectArgument: {fileID: 0} m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
m_IntArgument: 1 m_IntArgument: 0
m_FloatArgument: 0 m_FloatArgument: 0
m_StringArgument: m_StringArgument:
m_BoolArgument: 0 m_BoolArgument: 0
@ -15379,8 +15379,8 @@ Transform:
- {fileID: 280329356} - {fileID: 280329356}
- {fileID: 531088200} - {fileID: 531088200}
- {fileID: 564569809} - {fileID: 564569809}
m_Father: {fileID: 1904687149} m_Father: {fileID: 0}
m_RootOrder: 2 m_RootOrder: 27
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1186423015 --- !u!114 &1186423015
MonoBehaviour: MonoBehaviour:
@ -15399,8 +15399,11 @@ MonoBehaviour:
rightInput: {x: 0, y: 0} rightInput: {x: 0, y: 0}
agressiveTime: 5 agressiveTime: 5
attackTime: 2 attackTime: 2
updateBehaviourIn: 1 updateBehaviourIn: 0.6
neutralCapDistance: 10
detectPlayerDistance: 4
_currentEnemy: {fileID: 0} _currentEnemy: {fileID: 0}
_currentFollowingPath: []
--- !u!114 &1186423016 --- !u!114 &1186423016
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -23589,8 +23592,8 @@ Transform:
- {fileID: 896468143} - {fileID: 896468143}
- {fileID: 1996574566} - {fileID: 1996574566}
- {fileID: 622020284} - {fileID: 622020284}
m_Father: {fileID: 1904687149} m_Father: {fileID: 0}
m_RootOrder: 0 m_RootOrder: 25
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1801060023 --- !u!114 &1801060023
MonoBehaviour: MonoBehaviour:
@ -24040,8 +24043,8 @@ Transform:
- {fileID: 774590129} - {fileID: 774590129}
- {fileID: 842327024} - {fileID: 842327024}
- {fileID: 1981449834} - {fileID: 1981449834}
m_Father: {fileID: 1904687149} m_Father: {fileID: 0}
m_RootOrder: 1 m_RootOrder: 26
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1819395263 --- !u!114 &1819395263
MonoBehaviour: MonoBehaviour:
@ -24164,8 +24167,11 @@ MonoBehaviour:
rightInput: {x: 0, y: 0} rightInput: {x: 0, y: 0}
agressiveTime: 5 agressiveTime: 5
attackTime: 2 attackTime: 2
updateBehaviourIn: 1 updateBehaviourIn: 0.6
neutralCapDistance: 10
detectPlayerDistance: 4
_currentEnemy: {fileID: 0} _currentEnemy: {fileID: 0}
_currentFollowingPath: []
--- !u!1 &1826148571 stripped --- !u!1 &1826148571 stripped
GameObject: GameObject:
m_CorrespondingSourceObject: {fileID: 4991598031878838981, guid: fa5ecf8ffbe22c7459c44494cee176d9, m_CorrespondingSourceObject: {fileID: 4991598031878838981, guid: fa5ecf8ffbe22c7459c44494cee176d9,
@ -25409,12 +25415,9 @@ Transform:
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: m_Children: []
- {fileID: 1801060022}
- {fileID: 1819395262}
- {fileID: 1186423014}
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_RootOrder: 25 m_RootOrder: 28
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1001 &1911590196 --- !u!1001 &1911590196
PrefabInstance: PrefabInstance:

View File

@ -11,21 +11,29 @@ public class AI_Input : MonoBehaviour
public float agressiveTime = 5f; public float agressiveTime = 5f;
public float attackTime = 2f; public float attackTime = 2f;
public float updateBehaviourIn = 1f; public float updateBehaviourIn = 1f;
public float neutralCapDistance = 20f;
public float detectPlayerDistance = 4f;
public Action OnTouchDown, OnTouchUp; public Action OnTouchDown, OnTouchUp;
public Action OnCurrentPathFinished, OnAttack; public Action OnCurrentPathFinished, OnAttack;
public PlayerState _currentEnemy; public PlayerState _currentEnemy;
private List<TileInfo> _currentFollowingPath; public List<TileInfo> _currentFollowingPath;
private PlayerState _playerState; private PlayerState _playerState;
private TileMovement _tileMovement; private TileMovement _tileMovement;
private PlayerActionManager _actionManager; private PlayerActionManager _actionManager;
private CaptureController _captureController;
private IEnumerator _attackCoroutine;
private Vector3 _startBotPoint;
private void Awake() private void Awake()
{ {
_playerState = GetComponent<PlayerState>(); _playerState = GetComponent<PlayerState>();
_tileMovement = GetComponent<TileMovement>(); _tileMovement = GetComponent<TileMovement>();
_actionManager = GetComponent<PlayerActionManager>(); _actionManager = GetComponent<PlayerActionManager>();
_captureController = GetComponent<CaptureController>();
_actionManager.OnActionSuccess += BackToPatrol; _actionManager.OnActionSuccess += BackToPatrol;
_actionManager.OnActionStart += OnActionStart; _actionManager.OnActionStart += OnActionStart;
@ -36,21 +44,47 @@ public class AI_Input : MonoBehaviour
OnCurrentPathFinished += StartPatrolBehaviour; OnCurrentPathFinished += StartPatrolBehaviour;
PlayerDeathController.OnPlayerDeath += StopAllActions; _playerState.OnDeath += StopAllActions;
//_captureController.OnCaptureEnd += SetNewTarget;
_startBotPoint = transform.position;
//Debug.Log(_startBotPoint);
} }
private void StopAllActions(PlayerState player) /*private void SetNewTarget(TileInfo tile, float capTime)
{ {
if (player.name == gameObject.name) StartPatrolBehaviour();
}*/
private void StopAllActions()
{ {
StopAllCoroutines(); StopAllCoroutines();
} CancelInvoke();
} }
private void Start() private void Start()
{ {
InvokeRepeating("CheckState", UnityEngine.Random.Range(0.5f, 1f), updateBehaviourIn); //to make not the same start
}
private IEnumerator CheckBotState(float updateTime)
{
yield return new WaitForSeconds(UnityEngine.Random.Range(0.5f, 1f));
while (true)
{
CheckState();
yield return new WaitForSeconds(updateTime);
}
}
private void OnEnable()
{
//Debug.Log("bot " + gameObject.name + " started");
//InvokeRepeating("CheckState", UnityEngine.Random.Range(0.5f, 1f), updateBehaviourIn); //to make not the same start
StartCoroutine(CheckBotState(updateBehaviourIn));
} }
private void OnActionStart(ActionType arg1, CharacterState arg2) private void OnActionStart(ActionType arg1, CharacterState arg2)
@ -61,8 +95,9 @@ public class AI_Input : MonoBehaviour
private void BackToPatrol() private void BackToPatrol()
{ {
Debug.Log("attack ended"); Debug.Log("attack ended");
StopAllCoroutines(); StopCoroutine(_attackCoroutine);
StartPatrolBehaviour(); StartPatrolBehaviour();
//StartCoroutine(CheckBotState(updateBehaviourIn));
} }
@ -84,7 +119,7 @@ public class AI_Input : MonoBehaviour
_currentEnemy = null; _currentEnemy = null;
botState = BotState.Patrol; botState = BotState.Patrol;
//TileInfo targetTile = TileManagment.GetRandomOtherTile(_playerState.ownerIndex); //TileInfo targetTile = TileManagment.GetRandomOtherTile(_playerState.ownerIndex);
TileInfo targetTile = TileManagment.GetNearestNeutralTile(_playerState.currentTile, _playerState.ownerIndex); TileInfo targetTile = TileManagment.GetNearestOtherTile(_playerState.currentTile, _playerState.ownerIndex, neutralCapDistance, _startBotPoint);
var startTile = _playerState.currentTile; var startTile = _playerState.currentTile;
_currentFollowingPath = Pathfinding.FindPath(startTile, targetTile, TileManagment.levelTiles, TileManagment.tileOffset); _currentFollowingPath = Pathfinding.FindPath(startTile, targetTile, TileManagment.levelTiles, TileManagment.tileOffset);
if (_currentFollowingPath == null) if (_currentFollowingPath == null)
@ -92,18 +127,20 @@ public class AI_Input : MonoBehaviour
StartPatrolBehaviour(); StartPatrolBehaviour();
return; return;
} }
MoveTo(_currentFollowingPath[1]); //MoveTo(_currentFollowingPath[1]);
} }
private void CheckState(/*ActionType newType, CharacterState newState*/) private void CheckState(/*ActionType newType, CharacterState newState*/)
{ {
//Debug.Log("Check state");
if (_playerState.currentState == CharacterState.Dead) if (_playerState.currentState == CharacterState.Dead)
return; return;
foreach (PlayerState enemy in _playerState.enemies) foreach (PlayerState enemy in _playerState.enemies)
{ {
//Debug.Log("Check near enemy");
if (Vector3.Distance(enemy.transform.position, transform.position) <= TileManagment.tileOffset*1.1f) if (Vector3.Distance(enemy.transform.position, transform.position) <= TileManagment.tileOffset*1.1f)
{ {
//Debug.Log("attack state");
botState = BotState.Attack; botState = BotState.Attack;
_currentEnemy = enemy; _currentEnemy = enemy;
break; break;
@ -120,6 +157,14 @@ public class AI_Input : MonoBehaviour
botState = BotState.Agressive; botState = BotState.Agressive;
_currentEnemy = enemy; _currentEnemy = enemy;
StartCoroutine(CalmDown(agressiveTime)); StartCoroutine(CalmDown(agressiveTime));
//Debug.Log("aggressive state");
}
else if ((enemy.transform.position - transform.position).magnitude < detectPlayerDistance)
{
botState = BotState.Agressive;
_currentEnemy = enemy;
StartCoroutine(CalmDown(agressiveTime));
//Debug.Log("aggressive state");
} }
} }
if (_currentEnemy != null) if (_currentEnemy != null)
@ -154,9 +199,8 @@ public class AI_Input : MonoBehaviour
//Debug.Log("attacking"); //Debug.Log("attacking");
leftInput = Vector2.zero; leftInput = Vector2.zero;
_currentFollowingPath.Clear(); _currentFollowingPath.Clear();
//_actionManager.AttackEnemyOnTile(currentEnemy.currentTile); _attackCoroutine = TryToAttack(0.2f);
StartCoroutine(TryToAttack(0.2f)); StartCoroutine(_attackCoroutine);
//StartCoroutine(CalmDown(attackTime));
} }
private void MoveToEnemy(PlayerState currentEnemy) private void MoveToEnemy(PlayerState currentEnemy)
@ -190,7 +234,10 @@ public class AI_Input : MonoBehaviour
private void MoveToNextPoint() private void MoveToNextPoint()
{ {
if (_currentFollowingPath.Count > 0) //when stop movement, calculating path that begins from target tile Debug.Log("try to move next point");
if (_currentFollowingPath != null) //when stop movement, calculating path that begins from target tile
{
if (_playerState.currentTile.tileOwnerIndex == _playerState.ownerIndex)
{ {
if (_playerState.currentTile == _currentFollowingPath[_currentFollowingPath.Count - 1]) if (_playerState.currentTile == _currentFollowingPath[_currentFollowingPath.Count - 1])
{ {
@ -201,7 +248,7 @@ public class AI_Input : MonoBehaviour
var endTile = _currentFollowingPath[_currentFollowingPath.Count - 1]; var endTile = _currentFollowingPath[_currentFollowingPath.Count - 1];
if (!endTile.canMove) if (!endTile.canMove)
{ {
endTile = TileManagment.GetNearestNeutralTile(_playerState.currentTile, _playerState.ownerIndex); endTile = TileManagment.GetNearestOtherTile(_playerState.currentTile, _playerState.ownerIndex, neutralCapDistance, _startBotPoint);
//endTile = TileManagment.GetRandomOtherTile(_playerState.ownerIndex); //endTile = TileManagment.GetRandomOtherTile(_playerState.ownerIndex);
//Debug.Log("changed target"); //Debug.Log("changed target");
} }
@ -209,8 +256,11 @@ public class AI_Input : MonoBehaviour
_currentFollowingPath.Clear(); _currentFollowingPath.Clear();
_currentFollowingPath = Pathfinding.FindPath(currentTile, endTile, TileManagment.levelTiles, TileManagment.tileOffset); _currentFollowingPath = Pathfinding.FindPath(currentTile, endTile, TileManagment.levelTiles, TileManagment.tileOffset);
MoveTo(_currentFollowingPath[1]); MoveTo(_currentFollowingPath[1]);
Debug.Log("moving");
} }
else
}
else if (_playerState.currentTile.tileOwnerIndex == _playerState.ownerIndex)
{ {
OnCurrentPathFinished?.Invoke(); OnCurrentPathFinished?.Invoke();
} }
@ -222,7 +272,7 @@ public class AI_Input : MonoBehaviour
yield return new WaitForSeconds(time); yield return new WaitForSeconds(time);
_currentEnemy = null; _currentEnemy = null;
StartPatrolBehaviour(); StartPatrolBehaviour();
StopAllCoroutines(); //StopAllCoroutines();
} }
private IEnumerator TryToAttack(float attackCoolDown) private IEnumerator TryToAttack(float attackCoolDown)
@ -237,7 +287,7 @@ public class AI_Input : MonoBehaviour
} }
} }
BackToPatrol(); BackToPatrol();
StopAllCoroutines(); //StopAllCoroutines();
} }
public enum BotState public enum BotState

View File

@ -18,7 +18,7 @@ public class PlayerDeathController : MonoBehaviour
private float updateTime = 1f; private float updateTime = 1f;
private int spawnSafezone = 1; private int spawnSafezone = 1;
public static Action<PlayerState> OnPlayerDeath; //public static Action<PlayerState> OnPlayerDeath;
private void Awake() private void Awake()
{ {
@ -48,24 +48,22 @@ public class PlayerDeathController : MonoBehaviour
foreach (var player in alivePlayers) foreach (var player in alivePlayers)
{ {
var playerTile = TileManagment.GetTile(player.transform.position); var playerTile = TileManagment.GetTile(player.transform.position);
var adjacentTiles = TileManagment.GetAllAdjacentTiles(playerTile); var myAdjacentTiles = TileManagment.GetAllAdjacentTiles(playerTile, player.ownerIndex);
if (playerTile.tileOwnerIndex != player.ownerIndex) if (playerTile.tileOwnerIndex != player.ownerIndex)
{ {
int otherTileCounter = 0;
int cantStandTilesCounter = 0;
int tileCounter = 0; int tileCounter = 0;
foreach (var tile in adjacentTiles) foreach (var tile in myAdjacentTiles)
{
if (tile)
{ {
tileCounter++; tileCounter++;
if (tile.tileOwnerIndex != player.ownerIndex || !tile.canMove) if (!tile.canMove)
{ {
otherTileCounter++; cantStandTilesCounter++;
}
} }
} }
if (otherTileCounter >= adjacentTiles.Count) if (cantStandTilesCounter >= myAdjacentTiles.Count)
{ {
thisIterationDeadPlayers.Add(player); thisIterationDeadPlayers.Add(player);
} }
@ -85,7 +83,7 @@ public class PlayerDeathController : MonoBehaviour
alivePlayers.Remove(player); alivePlayers.Remove(player);
deadPlayers.Add(player); deadPlayers.Add(player);
OnPlayerDeath.Invoke(player); //OnPlayerDeath?.Invoke(player);
PlayerDeadActions(player); PlayerDeadActions(player);
} }
@ -93,7 +91,7 @@ public class PlayerDeathController : MonoBehaviour
{ {
List<PlayerState> needResPlayers = new List<PlayerState>(); List<PlayerState> needResPlayers = new List<PlayerState>();
foreach (var player in deadPlayers) foreach (PlayerState player in deadPlayers)
{ {
int playerIndex = players.IndexOf(player); int playerIndex = players.IndexOf(player);
if (Time.time > resurrectTime + lastDeadTime[playerIndex]) if (Time.time > resurrectTime + lastDeadTime[playerIndex])
@ -101,7 +99,7 @@ public class PlayerDeathController : MonoBehaviour
needResPlayers.Add(player); needResPlayers.Add(player);
} }
} }
foreach (var player in needResPlayers) foreach (PlayerState player in needResPlayers)
{ {
ResPlayer(player); ResPlayer(player);
} }
@ -123,7 +121,8 @@ public class PlayerDeathController : MonoBehaviour
Instantiate(deathParticles, player.transform.position, deathParticles.transform.rotation); Instantiate(deathParticles, player.transform.position, deathParticles.transform.rotation);
} }
player.SetNewState(ActionType.Attack, CharacterState.Dead); //player.SetNewState(ActionType.Attack, CharacterState.Dead);
player.OnDeathActions();
List<TileInfo> playerTiles = TileManagment.charTiles[(int)player.ownerIndex]; List<TileInfo> playerTiles = TileManagment.charTiles[(int)player.ownerIndex];
TileInfo currentTile = TileManagment.GetTile(player.transform.position); TileInfo currentTile = TileManagment.GetTile(player.transform.position);
currentTile.canMove = true; currentTile.canMove = true;
@ -141,8 +140,6 @@ public class PlayerDeathController : MonoBehaviour
private void PlayerResActions(PlayerState player) private void PlayerResActions(PlayerState player)
{ {
List<TileInfo> playerTiles = TileManagment.charTiles[(int)player.ownerIndex]; List<TileInfo> playerTiles = TileManagment.charTiles[(int)player.ownerIndex];
foreach (TileInfo tile in playerTiles) foreach (TileInfo tile in playerTiles)
@ -150,11 +147,14 @@ public class PlayerDeathController : MonoBehaviour
tile.easyCaptureFor.Clear(); tile.easyCaptureFor.Clear();
} }
player.transform.position = GetAvailableResPos(player, playerTiles);
TileInfo currentTile = TileManagment.GetTile(player.transform.position);
currentTile.canMove = false;
player.gameObject.SetActive(true); player.gameObject.SetActive(true);
player.SetStartParams(); //player.SetNewState(ActionType.Attack, CharacterState.Idle);
player.currentTile = GetAvailableResPos(player, playerTiles);
player.transform.position = player.currentTile.tilePosition;
player.currentTile.canMove = false;
//player.transform.position = TileManagment.GetRandomOtherTile(player.ownerIndex).tilePosition;
//player.SetStartParams();
Debug.Log("player " + player.name + " res"); Debug.Log("player " + player.name + " res");
if (resParticles) if (resParticles)
@ -163,7 +163,7 @@ public class PlayerDeathController : MonoBehaviour
} }
} }
private Vector3 GetAvailableResPos(PlayerState player, List<TileInfo> playerTiles) private TileInfo GetAvailableResPos(PlayerState player, List<TileInfo> playerTiles)
{ {
foreach (TileInfo tile in playerTiles) foreach (TileInfo tile in playerTiles)
{ {
@ -172,12 +172,12 @@ public class PlayerDeathController : MonoBehaviour
var myNeighbourTiles = TileManagment.GetAllAdjacentTiles(tile, player.ownerIndex); var myNeighbourTiles = TileManagment.GetAllAdjacentTiles(tile, player.ownerIndex);
if (myNeighbourTiles.Count >= spawnSafezone) if (myNeighbourTiles.Count >= spawnSafezone)
{ {
return tile.tilePosition; return tile;
} }
} }
} }
Debug.Log("nowhere to spawn"); Debug.Log("nowhere to spawn");
return Vector3.zero; return null;
} }
public void OnKillBtnClick(int playerIndex) public void OnKillBtnClick(int playerIndex)

View File

@ -75,7 +75,7 @@ public class PlayerState : MonoBehaviour
} }
} }
public void SetNewState(ActionType actionType, CharacterState newState) public void SetNewState(ActionType newAction, CharacterState newState)
{ {
if (currentState != newState) if (currentState != newState)
{ {
@ -86,18 +86,18 @@ public class PlayerState : MonoBehaviour
else else
{ {
CaptureState(false); CaptureState(false);
if (newState == CharacterState.Dead) /*if (newState == CharacterState.Dead)
{ {
OnDeath?.Invoke(); OnDeath?.Invoke();
} }*/
} }
currentState = newState; currentState = newState;
OnCharStateChanged?.Invoke(newState, actionType); OnCharStateChanged?.Invoke(newState, newAction);
} }
if (currentAction != actionType ) if (currentAction != newAction )
{ {
currentAction = actionType; currentAction = newAction;
OnActionChanged?.Invoke(actionType); OnActionChanged?.Invoke(newAction);
} }
} }
@ -116,6 +116,11 @@ public class PlayerState : MonoBehaviour
{ {
return (currentTile.tileOwnerIndex == ownerIndex) && (currentState == CharacterState.Idle); return (currentTile.tileOwnerIndex == ownerIndex) && (currentState == CharacterState.Idle);
} }
public void OnDeathActions()
{
OnDeath?.Invoke();
}
} }
public enum CharacterState public enum CharacterState

View File

@ -191,25 +191,32 @@ public class TileManagment : MonoBehaviour
return otherTile; return otherTile;
} }
public static TileInfo GetNearestNeutralTile(TileInfo currentTile, TileOwner owner) public static TileInfo GetNearestOtherTile(TileInfo currentTile, TileOwner owner, float capRadius, Vector3 startPoint)
{ {
var neutralTiles = charTiles[(int)TileOwner.Neutral]; var neutralTiles = charTiles[(int)TileOwner.Neutral];
Debug.Log("neutral tiles " + neutralTiles.Count); //Debug.Log("neutral tiles " + neutralTiles.Count);
TileInfo closestTile = GetRandomOtherTile(owner); TileInfo closestTile = GetRandomOtherTile(owner);
/*foreach (TileInfo tile in neutralTiles) foreach (TileInfo tile in levelTiles)
{ {
if (tile) if (tile.canMove && tile!=currentTile && tile.tileOwnerIndex!=owner)
{ {
float distOld = Vector3.Distance(currentTile.tilePosition, closestTile.tilePosition); float distOld = Vector3.Distance(startPoint, closestTile.tilePosition);
float distNew = Vector3.Distance(currentTile.tilePosition, tile.tilePosition);
if (distNew < distOld && tile.canMove) float distNew = Vector3.Distance(startPoint, tile.tilePosition);
//Debug.Log("new distance " + distNew);
if ((distNew < distOld) && (distNew < capRadius))
{ {
closestTile = tile; closestTile = tile;
} }
} }
}*/ }
//float dist = Vector3.Distance(startPoint, closestTile.tilePosition);
//Debug.Log(startPoint);
//Debug.Log(closestTile);
//Debug.Log("start point " + startPoint);
return closestTile; return closestTile;
} }

View File

@ -88,7 +88,7 @@ public class TileMovement : MonoBehaviour
private void FinishMovementActions( TileInfo currentTile) private void FinishMovementActions( TileInfo currentTile)
{ {
_playerState.currentTile = currentTile; _playerState.currentTile = currentTile;
//_playerState.currentTile.canMove = false; _playerState.currentTile.canMove = false;
OnFinishMovement?.Invoke(ActionType.Attack, CharacterState.Idle); OnFinishMovement?.Invoke(ActionType.Attack, CharacterState.Idle);
} }
} }

File diff suppressed because one or more lines are too long