using UnityEngine; using System; using System.Collections; using System.Collections.Generic; using Pathfinding; using Pathfinding.Util; using UnityEngine.Profiling; public class UnitMovement { float velocityRestriction = 20; bool bCollideWhileInAir = true; [HideInInspector] public UnitInfo unitInfo; public Squad squad = null; [HideInInspector] public SteeringManager steering; public MonturaManager monturaManager; [HideInInspector] public Seeker seeker; // [HideInInspector] public CharacterController characterController; [HideInInspector] public Rigidbody rbRigidBody; // [HideInInspector] public UnitHealthBar unitHealthBar; [HideInInspector] public float fScopeCurrent; // [HideInInspector] public float fFlyHeight = 0; [HideInInspector] public bool bDyingAnimation; [HideInInspector] public bool bDyingFalling; public bool bJumpAndStop = false; [HideInInspector] public float fIsThrown = -1; //[HideInInspector] public string sFrontBackSopled = "Back"; [HideInInspector] public bool bFrontBackSopled = false; // [HideInInspector] public float fTrapDamageTimer; // Utilizado por Trap para hacer daño a las unidades cada cierto tiempo [HideInInspector] public CapsuleCollider capsCol; [HideInInspector] public BoxCollider boxCol; [HideInInspector] public float fCapsColRadius; [HideInInspector] public bool bIsMovingPathComplete; //No useis esta variable, hay que arreglar todo para usar la otra private bool bIsMoving; // Recuerda: Hay una funcion llamada IsMoving() public bool bIsMovingGamepad; public Vector3 v3JoystickMovementSpeedForAnimation; // TODO: Old, borrar //private bool bIWannaMove; //private Vector3 prevVelocity; // private bool bAvoidTrap = false; //private Vector3 v3PastTrap; // private bool bWasMoving = false; public UnitRTSUnit rtsUnitEscapeFrom; // private float fCurrentScapeCountdown = 10; Vector3 v3SoploOrigin; Vector3 v3SoploDestination; float fSoploRadius = 0; float fSoploLength = 0; bool bSoploRadius = false; bool bSoploLength = false; Vector3 v3AddedForces; Vector3 v3GamepadForces; public Vector3 v3GroundedHitPosition; public Vector3 v3PrevPositionGroundedSafe; [Header("Obstacles")] public bool bBreakingObstacle = false; MoveToClass moveToClassObstacle; // bool bGhostsThroughObstacles = false; // float fPathLength = 0; // float fPathObstacleLength = 0; [Header("Agujeros")] [HideInInspector] public Vector3 v3EnterJumpPos; public bool isInAgujero; public bool isATravelerOfAgujero; public bool isEnteringAgujero; public bool isJumpingInAgujero; public bool isJumpingOutAgujero; public bool isExitingAgujero; public bool isTapandoAgujero; public bool bParticlesTrap = false; public TrapHole agujeroIn; public TrapHole agujeroOut; public Vector3 targetPos; public Vector3 agujeroInPosition; public Vector3 agujeroEnterPosition; // public GameObject targetGO; public GameObject goGetOutObject; // public MoleScript moleScript = null; // TODONEWMOLE public NewMoleScript newMoleScript = null; [HideInInspector] public bool bInitialized = false; // NavMeshAgent Variables public bool bGrounded = false; public const float FMOVETODIST = 0.06f; public const float FMINVELOCITYSTOP = 0.02f; public const float fRECALCULATEFREQ = 0.5f; // public const float fRECALCULATEFREQ = 999f; public bool bRestoringPosition = false; public bool bIsDeadAnimation; public bool bWalkNotRun = false; public bool bIsThrown = false; public bool bIsStunOrDown = false; public bool bHasJumped = false; public bool bHasSopled = false; public bool bHasSopledHard = false; public bool bIsThrownRecoveringFromGround = false; public bool bHanging = false; public float fHeightToGrounded; public float fUnitGravityMultiplierUnit = 1; public float fUnitGravityMultiplier //= 1; { get { return GameControl.control.testsettings.fGlobalGravityMultiplier * fUnitGravityMultiplierUnit; } set { fUnitGravityMultiplierUnit = value; } } [System.NonSerialized] public bool bManualMovement = false; public void SetManualMovement(bool bValue) { if (bManualMovement != bValue && bValue && IsInAir()) { rbRigidBody.velocity = Vector3.zero; } bManualMovement = bValue; } // public Rigidbody GetRigidbody() // { // if (rbRigidBody == null) // { // Debug.Log("Rigidbody Get! "+unitInfo.unitAI.name); // rbRigidBody = unitInfo.unitAI.GetComponent(); // } // return rbRigidBody; // } // public void InitializeSteer() { steering = new SteeringManager(unitInfo.unitAI.transformAI); // El rtsUnit le pone la maxSpeed en la funcion SetUnitSpeed } public void InitializeStart() { if (!bInitialized) { seeker = unitInfo.unitAI.GetComponent(); // TODO: Esto hay que borrarlo del prefab, cosa que rompe todos los prefabs de unit... CharacterController characterController = unitInfo.unitAI.GetComponent(); if (characterController != null) { Debug.LogError("ERROR: La unidad tiene el componente CharacterController."); characterController.enabled = false; GameObject.Destroy(characterController); } rbRigidBody = unitInfo.unitAI.GetComponent(); InitializeSteer(); monturaManager = new MonturaManager(unitInfo); unitInfo.unitAI.GetMe().SetUnitSpeed(); // REMEMBER: Como alguien ponga el rigidbody kinematic a true en el start lo mato rbRigidBody.useGravity = false; if (seeker != null) { rbRigidBody.isKinematic = false; rbRigidBody.interpolation = RigidbodyInterpolation.Interpolate; } else { rbRigidBody.isKinematic = true; } //rbRigidBody.interpolation = RigidbodyInterpolation.Interpolate; capsCol = unitInfo.unitAI.GetComponent(); boxCol = unitInfo.unitAI.GetComponent(); if (capsCol == null && boxCol == null) { if (unitInfo.unitAI.meProvisionalEditor.v2DimRadius.x * 2 > unitInfo.unitAI.meProvisionalEditor.v2DimRadius.y) { boxCol = unitInfo.unitAI.gameObject.AddComponent(); } else { capsCol = unitInfo.unitAI.gameObject.AddComponent(); } } if (capsCol != null) capsCol.isTrigger = false; else if (boxCol != null) boxCol.isTrigger = false; // Para los personajes que no son estructuras ni obstacles se les inicializa la HealthBar // if(!unitAI.IsTower() && !unitAI.IsObstacle() && !unitAI.IsMuro()) // { // StartHealthBar(); // } // MoveTo Variables bIsMoving = false; SetDestinationLocalTransform(unitInfo.unitAI.transformAI.position, null, true); if (capsCol != null) { fCapsColRadius = capsCol.radius; } else if (boxCol != null) { fCapsColRadius = Mathf.Max(boxCol.bounds.size.x, boxCol.bounds.size.z); } else { fCapsColRadius = 0.5f; } // UseLocalProjGravMult(); // UseLocalUnitGravMult(); StartGrounded(); bInitialized = true; } } // 2020-02-09: He creado esto para forzar a alguien que ya esta en el suelo pero que quiero que salga en el agua pero si no cae no se actualiza public void ResetGroundWater() { bForceHeightAlphaWater = true; // unitInfo.unitAI.GetMe().rtsPositionCurrent = RTSUnit.Position.Suelo; // El FallLevitateUnit(false) ya decide si la current sera Water o lo que sea } public void StartGrounded(bool bOnlyVisual = false) { ResetGroundWater(); if (!bOnlyVisual) { // Nos aseguramos de que se haya hecho una vez el IsGrounded bGrounded = UpdateIsGrounded(); // if (bGrounded) // { // // En el suelo // unitInfo.unitAI.FallLevitateUnit(false); // } UpdateSaltoSoploLanding(true); } unitInfo.unitAI.FallLevitateUnit(false); unitInfo.unitAI.UpdateFallLevitateUnit(true); } public void ForceStandUp() { ResetEndSaltoSoploLayer(); ResetEndSaltoSoploState(); } public void UpdateMovement() { //Debug.Log($"Beginning:{Vector3.Scale(rbRigidBody.velocity, Vector3.one * 1000000)}"); Profiler.BeginSample("Grounded"); // UseLocalProjGravMult(); bGrounded = UpdateIsGrounded(); //Esto es para que cuando saltan no se choquen (ataque salto kofi etc) if (bCollideWhileInAir) { if (!isATravelerOfAgujero || (isATravelerOfAgujero && !isEnteringAgujero && isJumpingOutAgujero && fHeightToGrounded < 2 && fHeightToGrounded > -capsCol.height / 2 && rbRigidBody.velocity.y < 0.1f)) { capsCol.isTrigger = false; } else { capsCol.isTrigger = true; } // if (!bGrounded) // { // capsCol.radius = .1f; // } // else // { // capsCol.radius = unitInfo.unitAI.GetMe().rtsUnit.v2DimRadius.x; // } } else { if (bGrounded) { capsCol.isTrigger = false; } else { capsCol.isTrigger = true; } } Profiler.EndSample(); // if (InputControl.control.GetKey(KeyCode.Space, "Jump", -1, true)) // { // if (unitNavMeshAgent.bGrounded) // { // rbRigidBody.transform.position += Vector3.up * 0.3f; // } // rbRigidBody.velocity = new Vector3(rbRigidBody.velocity.x, 5, rbRigidBody.velocity.z); // } Profiler.BeginSample("Speed"); // Setteamos la Speed de las animaciones inicialmente por si no se entra en el UpdateMoveTo unitInfo.unitAnimations.SetSpeed(1.0f); Profiler.EndSample(); if (unitInfo.unitAI.GetMe() == SceneControl.control.rtsUnitMercebinary1) { GameControl.control.bAnyUnitAllyWalking = bIsMoving; } // Update soplo salto landing UpdateSaltoSoploLanding(); if (!bDyingAnimation && !bIsDeadAnimation) { Profiler.BeginSample("Various"); monturaManager.UpdateRide(); //UpdatePeso(); UpdateEscapeFromUnit(); Profiler.EndSample(); Profiler.BeginSample("Right or Left"); UpdateFacingRightOrLeft(); Profiler.EndSample(); UpdateVelocityRestriction(); UpdateCheckLockedFalling(); // Si la unidad no esta controlada por las fisicas if (!IsInAir()) { // UpdateJumpOutOfNoWalkable(); Profiler.BeginSample("Move To"); if (unitInfo.unitAI.towerScript == null || !unitInfo.unitAI.towerScript.bDieTowerOnlyOnce) { UpdateMoveTo(); } Profiler.EndSample(); Profiler.BeginSample("Steering manager"); UpdateSteeringManager(); Profiler.EndSample(); Profiler.BeginSample("UpdateNotRecalculate"); UpdateRecalculate(); Profiler.EndSample(); if (unitInfo.unitAI.selected && InputControl.control.GetKey(KeyCode.P, InputControl.PermisosInput.Debug, 1, true)) { if (unitInfo.unitAI.bIsNPC == true) unitInfo.unitAI.bIsNPC = false; if (unitInfo.unitAI.iCurrentSide == 1) { SceneControl.control.ChangeUnitSide(unitInfo.unitAI, 2); unitInfo.unitAI.bIsInAgro = true; } else { SceneControl.control.ChangeUnitSide(unitInfo.unitAI, 1); } } } UpdateAgujeroTravel(); } //Debug.Log($"End:{Vector3.Scale(rbRigidBody.velocity, Vector3.one * 1000000)}"); //bGrounded = UpdateIsGrounded(); } /*****************************************************/ /**************** Escape, jabali, huir ***************/ /*****************************************************/ public void UpdateEscapeFromUnit() { if (rtsUnitEscapeFrom != null && (!IsMoving() || !steering.IsFollowingPath()) && !bCalculatingPath) { // Si ha huido porque le han pegado a traves de una valla y no puedo llegar a ese rival, que le siga mirando if (!unitInfo.unitAI.IsPeaceful()) { LookAt(rtsUnitEscapeFrom.unitAI.transformAI.position); } rtsUnitEscapeFrom = null; } } public void StartEscapingFromUnit(UnitRTSUnit unitAttacker) { if (!unitInfo.unitAI.GetMe().IsDead() && CanMove() && rtsUnitEscapeFrom == null) { // fCurrentScapeCountdown = unitInfo.unitAI.GetMe().fSight; rtsUnitEscapeFrom = unitAttacker; Vector3 v3ScapeDirection = (unitInfo.unitAI.transformAI.position - rtsUnitEscapeFrom.unitAI.transformAI.position).normalized; Vector3 v3NewDestEscaping = unitInfo.unitAI.transformAI.position + (v3ScapeDirection * 2f); //unitInfo.unitAI.GetMe().fSight * 0.5f; MoveToClass moveToClass = new MoveToClass(v3NewDestEscaping); // TODO: Añadir algun metodo para que salga huyendo con mas fSpeed que la default de la unidad MoveTo(moveToClass); } } /*****************************************************/ /****************** Jump over Water ******************/ /*****************************************************/ public void CheckJumpWater(GraphNode currentNode, int iShoreTag) { bool bShoreWater = (AstarKofiExtension.iShore == iShoreTag); bool bShoreLava = (AstarKofiExtension.iShoreLava == iShoreTag); if ((bShoreWater || bShoreLava) && CanMovePartial() && IsMoving()) { // Esta funcion no se llama si no estamos en shore (TODO: Puede que haya que optimizarla (y GetPathPositionInDistanceWithTag tambien) ya que estaremos haciendo muchos calculos en shore) bool bPrevGround = IsGroundedGround(); bool bPrevWater = IsGroundedWater(); bool bPrevLava = IsGroundedLava(); bool bWaterGround = (bPrevGround || bPrevWater); bool bLavaWaterGround = (bPrevGround || bPrevWater || bPrevLava); if ((unitInfo.unitAI.GetMe().pPositionUnit == RTSUnit.Position.Agua && bWaterGround) || (unitInfo.unitAI.GetMe().pPositionUnit == RTSUnit.Position.Levita && bLavaWaterGround)) { int iTag = AstarKofiExtension.iBasicGround; if (bPrevGround) { if (bShoreWater) { iTag = AstarKofiExtension.iWater; } else if (bShoreLava) { iTag = AstarKofiExtension.iLava; } } int iJumpNodes = 0; Vector3 v3pos = GetPathPositionInDistanceWithTag(2f, iTag, out iJumpNodes); if (v3pos != default(Vector3)) { // Debug.Log("JUMP " + bPrevGround + " " + bPrevWater); SaltoCambiandoGravedad(1, true, v3pos); steering.iCurrentNode++; //+= iJumpNodes; // TODO: Puede que sea esto lo del salto de nuevo al agua? } } } } /// /// Mira todos los nodos del path y si alguno tiene el tag [iTag] y está a una distancia menor que [fDistanceJump], devuelve su posicion. /// Vector3 GetPathPositionInDistanceWithTag(float fDistanceJump, int iTag, out int iJumpNodes) { iJumpNodes = 0; Vector3 v3Retorn = default(Vector3); Vector3 v3CurrentPos = unitInfo.unitAI.transformAI.position; int iCount = steering.listGraphNodePathPrev.Count; if (steering.arrayV3Path != null && iCount > 0) { // Para el sample position // Vector3 v3LastNodeInPathPosition = default(Vector3); // float fLastNodeInPathDistance = 99999; // Get the closest node to myself // float fMinDistance = 99999; // int iMin = 0; // for (int i = 0; i < iCount; i++) // { // GraphNode graphNode = steering.listGraphNodePathPrev[i]; // // [1] PrevNode based // if (graphNode == unitInfo.unitPenalty.prevNodePublic) // { // iMin = i; // break; // } // // [2] Distance based (por si acaso el nodo actual no esta en el path) // Vector3 v3GraphNodePos = (Vector3)graphNode.position; // float fDist = Vector3.Distance(v3CurrentPos, v3GraphNodePos); // if (fDist < fMinDistance) // Astar tag (not layer) Ground or Water // { // fMinDistance = fDist; // iMin = i; // } // } // // // Get the closest position with iTag in fDistanceJump // for (int i = iMin; i < iCount; i++) // { // GraphNode graphNode = steering.listGraphNodePathPrev[i]; // Vector3 v3GraphNodePos = (Vector3)graphNode.position; // v3LastNodeInPathPosition = v3GraphNodePos; // fLastNodeInPathDistance = Vector3.Distance(v3CurrentPos, v3GraphNodePos); // if (graphNode.TagOriginal == (uint)iTag && fLastNodeInPathDistance <= fDistanceJump) // Astar tag (not layer) Ground or Water // { // Debug.Log("GetPathPositionInDistanceWithTag " + steering.iCurrentNode + " " + iMin + " " + i + " " + v3GraphNodePos + " " + iTag); // v3Retorn = v3GraphNodePos; // break; // } // } // int iNextNode = steering.iCurrentNode; // if(iNextNode < iCount) // { // GraphNode graphNodeNext = steering.listGraphNodePathPrev[iNextNode]; // Vector3 v3GraphNodePos = (Vector3)graphNodeNext.position; // v3LastNodeInPathPosition = v3GraphNodePos; // fLastNodeInPathDistance = Vector3.Distance(v3CurrentPos, v3GraphNodePos); // if (graphNodeNext.Tag == (uint)iTag && fLastNodeInPathDistance <= fDistanceJump) // Astar tag (not layer) Ground or Water // { // v3Retorn = v3GraphNodePos; // } // } // Get the closest node to myself, porque no hay un indicador del nodo actual en el que estamos, solo de los puntos posibles (no los nodos) float fMinDistance = 99999; int iMin = 0; for (int i = 0; i < iCount; i++) { GraphNode graphNode = steering.listGraphNodePathPrev[i]; // [1] PrevNode based if (graphNode == unitInfo.unitPenalty.prevNodePublic) { iMin = i; break; } // [2] Distance based (por si acaso el nodo actual no esta en el path) Vector3 v3GraphNodePos = (Vector3)graphNode.position; float fDist = (v3CurrentPos - v3GraphNodePos).sqrMagnitude; //Vector3.Distance(v3CurrentPos, v3GraphNodePos); if (fDist < fMinDistance * fMinDistance) // Astar tag (not layer) Ground or Water { fMinDistance = fDist; iMin = i; } } //string sDebug = ""; GraphNode graphNodeFound = null; for (int i = iMin; i < iCount; i++) { bool bFoundNode = false; GraphNode graphNode = steering.listGraphNodePathPrev[i]; if (graphNode.TagOriginal == (uint)iTag) { //sDebug += iTag + " " + (Vector3)graphNode.position + " " + i + " " + steering.iCurrentNode + " "; bFoundNode = true; } /*else if (graphNode.Tag == AstarKofiExtension.iShore || graphNode.Tag == AstarKofiExtension.iShoreLava) { //sDebug += "#"; if (i == iCount - 1) { bFoundNode = true; } } else { //sDebug += "NO" + iTag + " " + (Vector3)graphNode.position + " " + i + " " + steering.iCurrentNode + " "; iJumpNodes = 0; graphNodeFound = null; break; }*/ if (bFoundNode) { iJumpNodes = i - steering.iCurrentNode; graphNodeFound = graphNode; break; } } if (graphNodeFound != null) { Vector3 v3GraphNodePos = (Vector3)graphNodeFound.position; //Vector3 v3LastNodeInPathPosition = v3GraphNodePos; float fLastNodeInPathDistance = (v3CurrentPos - v3GraphNodePos).sqrMagnitude; //Vector3.Distance(v3CurrentPos, v3GraphNodePos); //sDebug += " " + fLastNodeInPathDistance; if (fLastNodeInPathDistance <= fDistanceJump * fDistanceJump) // Astar tag (not layer) Ground or Water { // Hago sample position evitando todo lo que no sea el iTag objetivo de Ground o Water bool bSample = SamplePosition(v3GraphNodePos, out v3Retorn, 1f, false, ~(1 << iTag)); if (!bSample) { v3Retorn = default(Vector3); } //sDebug += " " + bSample; } } //Debug.Log(sDebug); // Por si acaso el ultimo nodo al que vamos es shore, pero esta hyper cerca de suelo // Vector3 v3Out = default(Vector3); // if (v3Retorn == default(Vector3) && v3LastNodeInPathPosition != default(Vector3) && fLastNodeInPathDistance <= fDistanceJump && // SamplePosition(v3LastNodeInPathPosition, out v3Out, 0.25f, false, ~(1 << iTag))) // TODO: Seguro que este iTag es la shore? // { // v3Retorn = v3Out; // } } return v3Retorn; } /*****************************************************/ /**************** Destroying Obstacles ***************/ /*****************************************************/ // This function tells you the cost of breaking an obstacle in distance using its life and your dps public float DistanceCostFromBreakingObstacle(Obstacle obstacle, float fMaxPSObst = 0) { UnitAI unitAIme = unitInfo.unitAI; // Here we will cleverly transform obstacle life into distance this costs by using // fDamage* 1 attack * fCoolDownUpgraded (to obtain ps*time) // ps*time * (obstacle.unitAI.GetMe().iPS / fDamage) = to obtain time to kill // fSpeedUpgraded * time to kill = distance it would cost to kill the obstacle // - This is just aproximate but is a good valoration float fDamage = 0; UnitRTSUnit unit = null; if (obstacle != null) { unit = obstacle.unitAI.GetMe(); fMaxPSObst = unit.fPS; } //bool bHits = true; UnitAttack attackTemp = new UnitAttack(null, null, new UnitAttackInfo(unitAIme.GetMe()), unit, default(Vector3)); if (obstacle != null) { fDamage = obstacle.unitAI.GetMe().CalculateDamage(attackTemp); } else { fDamage = UnitRTSUnit.CalculateDamageNeutral(1, 1, 1, attackTemp.attacker.fAttack, 1); } float psTime = fDamage / unitAIme.GetMe().fCooldown; float fDistanceCost = (fMaxPSObst / psTime) * (UnitRTSUnit.BaldosasPorSegundo(unitAIme.GetMe().fSpeed)); return fDistanceCost; } public Vector3 PathDirection() { Path pathCurrent = seeker.GetCurrentPath(); Vector3 v3Direction = (pathCurrent != null && pathCurrent.vectorPath.Count > 1) ? (pathCurrent.vectorPath[1] - pathCurrent.vectorPath[0]).normalized : unitInfo.unitAI.transformAI.forward.normalized; v3Direction += (Vector3.up * 0.2f); return v3Direction; } public void CheckAttackObstacle(GraphNode currentNode) { if (unitInfo.unitAI.unitActionControl.bBreakObstacles && GameControl.control.testsettings.bTraverseBreakableObstacles) { Vector3 v3Position = ((Vector3)currentNode.position); UnitAI unitAIObstacle = null; // Old method: Raycast // Ray ray = new Ray(v3Position, PathDirection()); // RaycastHit rayCastHit; // Debug.DrawRay(v3Position, ray.direction, new Color(1, 0.5f, 0), 10); // if (Physics.Raycast(ray, out rayCastHit, 2, GameControl.control.astarAsset.breakableObstacleMask)) // { // obstacle = rayCastHit.collider.GetComponent(); TestArriveToPosition(unitInfo.unitAI.GetMe(), v3Position, out unitAIObstacle, null, true, true, GameControl.control.astarAsset.breakableObstacleMask.value); if (unitAIObstacle != null && unitAIObstacle.unitInfo.obstacle.bBreakable) { bBreakingObstacle = true; moveToClassObstacle = new MoveToClass(unitInfo.unitAI.moveToClassCurrent); unitInfo.unitAI.ChangeRival(unitAIObstacle.GetMe()); } // } } } public void BackToPath() { if (unitInfo.unitAI.unitActionControl.bBreakObstacles && GameControl.control.testsettings.bTraverseBreakableObstacles) { bBreakingObstacle = false; if (moveToClassObstacle != null) MoveTo(moveToClassObstacle); else unitInfo.unitAI.StopPursuingRival(); } } /***************************************************************/ /*************** Restriccion de velocidad y masa ***************/ /***************************************************************/ ////peso weight heavy light (tags) //float fHighMass = 10; //float fRegularMass = 1; //float fLowMass = 0.5f; ///// ///// Cambia el peso (en físicas) de la unidad para que sea facil o dificil apartarlos al pasar otra unidad (no necesita ser cada frame para nada) ///// ////TODO Poner masa dependiendo de lo grande que sea una unidad tambien (que la diferencia entre la mas grande y la mas pequeña en masa no sea demasiado grande) //void UpdatePeso() //{ // float fCurrentMass = rbRigidBody.mass; // float fDesiredMass = fRegularMass; // if (unitInfo.unitAI.currentState.IsInAttackingState()) // { // if (unitInfo.unitAI.GetMe().fScope > 0) // { // fDesiredMass = fLowMass; // } // else if (unitInfo.unitAI.myRival != null && unitInfo.unitAI.myRival.unitAI.IsEnemy()) // Esto hace que no se le ponga la masa maxima si no esta luchando contra un enemigo. // { // fDesiredMass = fHighMass; // } // } // else if (unitInfo.unitAI.currentState.IsInStandbyState()) // { // fDesiredMass = fLowMass; // } // if (fDesiredMass != fCurrentMass) // { // rbRigidBody.mass = fDesiredMass; // } //} /// /// Updates the velocity restriction. /// /// If set to true b restrict vel only when falling. void UpdateVelocityRestriction(bool bRestrictVelOnlyWhenFalling = false) { if (!bGrounded) { bool bFalling = rbRigidBody.velocity.y < 0; //Si sólo quiero controlarlo cuando esté cayendo tendrá que estar cayendo para entrar aquí if (bFalling || !bRestrictVelOnlyWhenFalling) { // Regular Velocity restriction if (rbRigidBody.velocity.magnitude > velocityRestriction) { //print("Restricting "+gameObject.name+" velocity vector magnitude to "+ velocityRestriction); rbRigidBody.velocity = Vector3.ClampMagnitude(rbRigidBody.velocity, velocityRestriction); } // Fall Velocity restriction if (rbRigidBody.velocity.y < -velocityRestriction) { //print("Restricting "+gameObject.name+" fall velocity to "+ velocityRestriction); rbRigidBody.velocity = new Vector3(rbRigidBody.velocity.x, -velocityRestriction, rbRigidBody.velocity.z); } } } } float fAuxTimer = 0; void UpdateCheckLockedFalling() { if (!GameControl.control.bStillTransitioning && (IsInAir() && !bHanging && !bHasSopled) && !bIsThrownRecoveringFromGround && Mathf.Abs(rbRigidBody.velocity.y) < .25f && !unitInfo.unitAI.bIsNPC && (monturaManager == null || !monturaManager.IsRiding())) { fAuxTimer += TimeController.control.GetDeltaTime(0); //if (fAuxTimer > 1f) //if ((fAuxTimer > GameControl.control.testsettings.fTimeHanging && unitInfo.unitAI.GetMe() != SceneControl.control.rtsUnitMercebinary1) || fAuxTimer > 1f) if ((fAuxTimer > GameControl.control.testsettings.fTimeHanging && Mathf.Abs(unitInfo.unitAI.transformAI.position.y) < .01f) || fAuxTimer > 1) { Vector3 v3UnitPosition = unitInfo.unitAI.transformAI.position; Vector3 v3Aux; //int iTags = AstarKofiExtension.UnsafeTags; //if (unitInfo.unitAI.GetMe().rtsUnit.pPosition == RTSUnit.Position.Agua) //{ // iTags &= (~(1 << AstarKofiExtension.iWater)); //} //if (unitInfo.unitAI.GetMe().rtsUnit.pPosition == RTSUnit.Position.Levita) //{ // iTags &= (~(1 << AstarKofiExtension.iBarranco)); //} int iTags = (~ seeker.traversableTags) | (1 << AstarKofiExtension.iShore) | (1 << AstarKofiExtension.iShoreBarranco) | (1 << AstarKofiExtension.iShoreLava); SamplePosition(v3UnitPosition, out v3Aux, 4, false, iTags); if ((v3UnitPosition - v3Aux).sqrMagnitude > 0.01f) { SaltoCambiandoGravedad(1, true, v3Aux); bHanging = true; } else { Debug.Log("UnitMovement.UpdateCheckLockedFalling: No se ha podido hacer el salto porque el SamplePosition da la misma posicion."); } fAuxTimer = 0; } } else { fAuxTimer = 0; } } //// //// Get My Collider //// public Collider GetMyCollider() { Collider myCollider; if (capsCol != null) { myCollider = capsCol; } else if (boxCol != null) { myCollider = boxCol; } else { myCollider = unitInfo.unitAI.GetComponent(); } return myCollider; } /******************************************************************************************** ********************************** UNARMED FUNCTIONS ************************************** *******************************************************************************************/ // Sentarse, sienta, sit down public bool IsUnitInConsole() { return bUnitInConsole || bUnitInConsole2 || bUnitInConsole3 || bUnitInConsoleStand; } [HideInInspector] public bool bUnitInConsole = false; [HideInInspector] public bool bUnitInConsole2 = false; [HideInInspector] public bool bUnitInConsole3 = false; [HideInInspector] public bool bUnitInConsoleStand = false; public void ConsoleSit(bool bUseAnimation = true, bool bConsoleStand = false, bool bConsole2 = false, bool bConsole3 = false) { bUnitInConsole = true; bUnitInConsole2 = bConsole2; bUnitInConsole3 = bConsole3; bUnitInConsoleStand = bConsoleStand; if (bUseAnimation) unitInfo.unitAnimations.SetAnimation("ConsoleSit"); if (!bUseAnimation) unitInfo.unitAnimations.SetAnimation("ConsoleIdle", 0); } public void ConsoleGetUp(bool bUseAnimation = true) { if (bUseAnimation) unitInfo.unitAnimations.SetAnimation("ConsoleGetUp"); bUnitInConsole = false; bUnitInConsole2 = false; bUnitInConsole3 = false; bUnitInConsoleStand = false; unitInfo.unitAnimations.mouthController.bHoldEmotion = false; if (!bUseAnimation) unitInfo.unitAnimations.SetAnimation("Idle", 0); } /******************************************************************************************** ********************************** GROUNDED FUNCTIONS ************************************** *******************************************************************************************/ /// /// [AVISO] NO llamar a estas funciones, lo que hay que hacer es usar las variables publicas hitGround, bHitGround y bGrounded ya que el Update ya llama a IsGrounded() /// public bool UpdateIsGrounded() { Vector3 v3Pos; return UpdateIsGrounded(out v3Pos); } /// /// [AVISO] Esta funcion usa el bGrounded, hitGround que se calcula en el UpdateMovement /// public bool IsGroundedGround() { return bGrounded && hitGround.collider != null && hitGround.collider.gameObject.layer == ClickInfo.iLGround; } /// /// [AVISO] Esta funcion usa el bGrounded, hitGround que se calcula en el UpdateMovement /// public bool IsGroundedWater() { return bGrounded && hitGround.collider != null && hitGround.collider.gameObject.layer == ClickInfo.iLWater; } /// /// [AVISO] Esta funcion usa el bGrounded, hitGround que se calcula en el UpdateMovement /// public bool IsGroundedLava() { return bGrounded && hitGround.collider != null && hitGround.collider.gameObject.layer == ClickInfo.iLLava; } public int IsGroundedMoleDeath() { //Devolver algo para el tipo de efecto int iCauseDeath = -1; if (hitGround.collider == null) { iCauseDeath = ClickInfo.iLBarranco; } else if (hitGround.collider.gameObject.layer == ClickInfo.iLLava || hitGround.collider.gameObject.layer == ClickInfo.iLWater || hitGround.collider.gameObject.layer == ClickInfo.iLBarranco) { iCauseDeath = hitGround.collider.gameObject.layer; } else { Transform transformMaybePlatform = hitGround.collider.transform; bool bSonOfPlatform = false; for (int i = 0; i < 10; i++) { if (transformMaybePlatform == null) { break; } else if (transformMaybePlatform.gameObject.layer == ClickInfo.iLPlataformas) { bSonOfPlatform = true; break; } else { transformMaybePlatform = transformMaybePlatform.parent; } } if (bSonOfPlatform) { iCauseDeath = ClickInfo.iLPlataformas; } } return iCauseDeath; } private bool bPrevBarranco = false; public RaycastHit hitGround; public bool bHitGround; /*super mega*/ private bool UpdateIsGrounded(out Vector3 v3Pos) { bool bReturnGrounded = false; bool bLevita = (unitInfo.unitAI.GetMe().rtsPositionCurrent == RTSUnit.Position.Levita) && unitInfo.unitAI.GetMe().fStamina > 0; LayerMask mask = 1 << ClickInfo.iLGround | 1 << ClickInfo.iLWater | 1 << ClickInfo.iLLava; //8 Ground, 14 Obstacle, 16 Water2 if (bLevita) { mask |= 1 << ClickInfo.iLBarranco; } //if (GameControl.OptimizationFrames()) //{ bHitGround = Physics.Raycast(unitInfo.unitAI.transformAI.position + new Vector3(0, 2.0f, 0), Vector3.down, out hitGround, 100.0f, mask, QueryTriggerInteraction.Ignore); //} //bHitGround = Physics.SphereCast(unitInfo.unitAI.transformAI.position + new Vector3(0, 2.0f, 0), 0.08f, Vector3.down, out hitGround, 100.0f, mask, QueryTriggerInteraction.Ignore); v3Pos = v3GroundedHitPosition; //Debug.Log($"UpdateIsGrounded {Time.frameCount}"); bool bHideShadow = false; if (bHitGround) { bool bIsBarranco = (hitGround.collider.gameObject.layer == ClickInfo.iLBarranco); v3Pos = hitGround.point; v3GroundedHitPosition = v3Pos; fHeightToGrounded = unitInfo.unitAI.transformAI.position.y - v3Pos.y; // if (unitInfo.unitAI.name == "Kofi") // { // Debug.Log("Grounded Stuff"+bGrounded+" "+bPrevBarranco+" "+bIsBarranco+" "+(unitInfo.shadow != null)+" "+unitInfo.unitAI.isInAgujero+" "+unitInfo.shadow.IsHidden()); // //El error pasa porque no entra por no estar hidden (pese a no verse y luego por estar grounded...) // } if ((!bGrounded || bPrevBarranco && !bIsBarranco) && unitInfo.shadow != null && !unitInfo.unitAI.IsInAgujero()/*.isInAgujeroOLD*/ && unitInfo.shadow.IsHiding()) { // if (unitInfo.unitAI.name == "Kofi") // { // Debug.Log("POG!! "+bGrounded+" "+bPrevBarranco+" "+bIsBarranco+" "+(unitInfo.shadow != null)+" "+unitInfo.unitAI.isInAgujero+" "+unitInfo.shadow.IsHidden()); // } if (unitInfo.unitAI.GetMe().pPositionUnit == RTSUnit.Position.Agua) { bForceHeightAlphaWater = true; } else { unitInfo.shadow.Show(); } } // Si el suelo es barranco bPrevBarranco = bIsBarranco; if (bPrevBarranco) { bHideShadow = true; } } else { // Si no hay suelo bHideShadow = true; if (!bLevita) { // Mira si la unidad se va a caer a un barranco y la mata CheckFallingDead(v3Pos); } } if (bHideShadow) { if (unitInfo.shadow != null && !unitInfo.shadow.IsHidden()) { unitInfo.shadow.Hide(); } } CheckWater(); //TODO no estoy grounded si estoy entrando ni saliendo del agujero (isatravelerofagujero??) bReturnGrounded = (bHitGround && fHeightToGrounded <= 0.05f) && !isJumpingInAgujero && !isJumpingOutAgujero && !isExitingAgujero; // TODO: Antes estaba a 0.1f return bReturnGrounded; } void CheckFallingDead(Vector3 v3Pos) { bool bFallingDead = false; if (!bDyingFalling && !unitInfo.unitAI.IsInAgujero()/*.isInAgujeroOLD*/ && rbRigidBody.velocity.y < (-0.15f) && unitInfo.alphaSlow.alpha != 0 && !GameControl.control.bChangingScenes) { // Si caes por delante del ultimo sitio que has pisado o por detras bool bFallFront = (unitInfo.unitAI.transformAI.position.z + unitInfo.unitAI.transformAI.position.x) < (v3Pos.x + v3Pos.z); if (bFallFront) { if (unitInfo.unitAI.transformAI.position.y < v3Pos.y - 0.5f /*Poner aquí la altura a la que quieras empezar a desaparecer*/ ) { bFallingDead = true; } } else { //if (unitInfo.unitAI.transformAI.position.y < v3Pos.y + 0.2f /*Poner aquí la altura a la que quieras empezar a desaparecer*/ ) if (unitInfo.unitAI.transformAI.position.y < v3Pos.y - 0.1f /*Poner aquí la altura a la que quieras empezar a desaparecer*/ ) { bFallingDead = true; } } } if (bFallingDead) { DoFallingDead(); } } void DoFallingDead() { unitInfo.shadow.Hide(0.2f); //gameObject.SetActive(false); unitInfo.alphaSlow.Hide(-0.5f); if (unitInfo.unitAI.gameObject.activeSelf && !unitInfo.unitAI.unitActionControl.bAvoidExploteByCinematics) { //unitInfo.unitAI.StartCoroutine(unitInfo.unitAI.DestroyUnitInSeconds(0.25f, false)); unitInfo.unitAI.DestroyUnitInSeconds(0.25f, false); } if (!string.IsNullOrEmpty(GameControl.control.testsettings.sFallSound) && unitInfo.unitAI.unitActionControl.DeathAllowed()) { AudioControl.control.ActivarSonido(AudioControl.control.SonidoOverrided(GameControl.control.testsettings.sFallSound), false, Vector3.zero, null, unitInfo.unitAI.transformAI); //Si se encuentra estels/partículas para caer se puede poner aquí } bDyingFalling = true; } /*****************************************************/ /************* Water, Air, Fire, Earth ***************/ /*****************************************************/ bool bHeightAlphaWater = false; bool bForceHeightAlphaWater = false; float fStaminaRecoverTimeoutStart = 0; void CheckWater() { //public RaycastHit hitGround; //public bool bHitGround; bool bRestaStamina = false; if (bHitGround && hitGround.collider != null && !GameControl.control.bChangingScenes && !isATravelerOfAgujero) { RTSUnit.Position pPositionDefault = unitInfo.unitAI.GetMe().pPositionUnit; // RTSUnit.Position pPositionCurrent = unitInfo.unitAI.GetMe().rtsPositionCurrent; // RTSUnit.Position pPositionPrev = unitInfo.unitAI.GetMe().rtsPositionCurrent; // if(unitInfo.unitAI.name == "NinoEscama") // Debug.Log("CheckWater " + " " + fHeightToGrounded + " " + (hitGround.collider.gameObject.layer == ClickInfo.iLWater) + " " + pPositionDefault + " " + bHeightAlphaWater); SpriteRenderer[] spriteRendererOnlyWater = null; float fFunctionParabola = 0.7f; if (unitInfo.unitAnimations != null) { spriteRendererOnlyWater = unitInfo.unitAnimations.waterController.spriteRendererOnlyWater; fFunctionParabola = unitInfo.unitAnimations.waterController.fFunctionParabola; } GameObject goAux = hitGround.collider.gameObject; bool bGroundIsGround = (goAux.layer == ClickInfo.iLGround); bool bGroundIsWater = (goAux.layer == ClickInfo.iLWater); bool bGroundIsLava = (goAux.layer == ClickInfo.iLLava); bool bGroundIsBarranco = (goAux.layer == ClickInfo.iLBarranco); if ((bGroundIsGround || (bGroundIsWater && pPositionDefault == RTSUnit.Position.Agua)) && GameControl.OptimizationFrames(unitInfo.unitAI.iInstanceID)) { MobilePlatform mp = goAux.GetComponentInParent(); Bridge puente = goAux.GetComponentInParent(); if (mp == null && puente == null) { v3PrevPositionGroundedSafe = hitGround.point; } } if ((!bHeightAlphaWater || bForceHeightAlphaWater) && fHeightToGrounded < 0.2f && bGroundIsWater) // 16 Water2 { // Si levita, no ocultar la sombra //if(unitInfo.unitAI.name == "NinoEscama") Debug.Log("CheckWater " + "Water"); if (pPositionDefault != RTSUnit.Position.Levita && unitInfo.shadow != null) unitInfo.shadow.Hide(-1, pPositionDefault == RTSUnit.Position.Agua); unitInfo.changeColorChilds.ApplyHeightAlphaWater(true, hitGround.point + new Vector3(0, -0.05f, 0), new Color(0.54f, 0.8f, 0.84f), Color.white, spriteRendererOnlyWater, fFunctionParabola); bHeightAlphaWater = true; bForceHeightAlphaWater = false; } else if ((bHeightAlphaWater || bForceHeightAlphaWater) && (fHeightToGrounded >= 0.2f || bGroundIsGround)) // 8 Ground { // Si levita, no ocultar la sombra //if(unitInfo.unitAI.name == "NinoEscama") Debug.Log("CheckWater " + "Ground"); if (pPositionDefault != RTSUnit.Position.Levita && unitInfo.shadow != null) unitInfo.shadow.Show(-1); //, pPositionDefault == RTSUnit.Position.Agua); unitInfo.changeColorChilds.ApplyHeightAlphaWater(false, hitGround.point + new Vector3(0, -0.05f, 0), new Color(0.54f, 0.8f, 0.84f), Color.white, spriteRendererOnlyWater, fFunctionParabola); bHeightAlphaWater = false; bForceHeightAlphaWater = false; } else if (/*bHeightAlphaWater &&*/ fHeightToGrounded < 0.05f && // Lo he comentado porque la lava no entra por el if de mas arriba para ponerle la linea de agua a la unit (bGroundIsWater || bGroundIsLava)) // 16 Water2 { // if(unitInfo.unitAI.name == "Kofi") // Debug.Log("CheckWater2 " + " " + fHeightToGrounded + " " + (hitGround.collider.gameObject.layer == ClickInfo.iLLava) + " " + pPositionDefault); // Si las unidades son de suelo o Levitan pero ya no tienen stamina, matarlas if (pPositionDefault == RTSUnit.Position.Suelo || (pPositionDefault == RTSUnit.Position.Levita && (unitInfo.unitAI.GetMe().fStamina <= 0 || bHasSopled || bHasSopledHard)) || (pPositionDefault == RTSUnit.Position.Agua && bGroundIsLava)) { bool bUnitDestroyed = unitInfo.unitAI.DestroyUnit(); //Pool.pool.PoolInstantiate(GameControl.control.testsettings.prefabs.goPSWaterSplash, "WaterSplash", hitGround.point, Quaternion.identity); if (bUnitDestroyed) { // El water splash es una funcion que se le pasa el tamaño que se quiere de splash SceneControl.SpawnWaterSplash(hitGround.collider.gameObject.layer, hitGround.point, unitInfo.unitAI.GetMe().rtsUnit.fShadowRadius); } } else if (pPositionDefault == RTSUnit.Position.Levita) { // RESTA STAMINA bRestaStamina = true; } } else if (bGroundIsBarranco && pPositionDefault == RTSUnit.Position.Levita) { // RESTA STAMINA bRestaStamina = true; } } // else if(hitGround.collider == null) // { // // RESTA STAMINA // bRestaStamina = true; // } if (bRestaStamina /*|| InputControl.control.GetKey(KeyCode.O, InputControl.PermisosInput.Debug, 1, true)*/) { RestaStaminaUpdate(); } else { // La recuperacion de stamina if (unitInfo.unitAI.GetMe().fStamina < unitInfo.unitAI.GetMe().fStaminaMax) { fStaminaRecoverTimeoutStart -= TimeController.control.GetDeltaTime(unitInfo.unitAI.GetMe().iHackValue); if (fStaminaRecoverTimeoutStart <= 0) { fStaminaRecoverTimeoutStart = 0; unitInfo.unitAI.GetMe().fStamina += TimeController.control.GetDeltaTime(unitInfo.unitAI.GetMe().iHackValue) * GameControl.control.testsettings.fStaminaRecoverRate; // 0.5f is 2 seconds unitInfo.unitAI.GetMe().fStamina = Mathf.Min(unitInfo.unitAI.GetMe().fStamina, unitInfo.unitAI.GetMe().fStaminaMax); } } } } public float RestaStaminaUpdate(float fMultiplierAux = 1f) { unitInfo.unitAI.GetMe().fStamina -= TimeController.control.GetDeltaTime(unitInfo.unitAI.GetMe().iHackValue) * GameControl.control.testsettings.fStaminaDepletionRate * fMultiplierAux; // (1f / 2f) is 2 seconds if (unitInfo.unitAI.GetMe().fStamina <= 0) { unitInfo.unitAI.GetMe().fStamina = 0; } fStaminaRecoverTimeoutStart = GameControl.control.testsettings.fStaminaRecoverTimeoutStart; // 2 seconds // Esto es seconds // Crea la barra de stamina si existe // unitInfo.unitAI.StartHealthBar(UnitHealthBar.HealthBarType.Stamina); return unitInfo.unitAI.GetMe().fStamina; } // public void UpdateLevitatingShadow(GraphNode currentNode) // { // RTSUnit.Position pPositionUnit = unitInfo.unitAI.GetMe().rtsPositionCurrent; // if(pPositionUnit == RTSUnit.Position.Levita && unitInfo.shadow != null) // { // bool bIsHidden = unitInfo.shadow.IsHidden(); // bool bInBarranco = ((int)currentNode.Tag == AstarKofiExtension.iBarranco); // bool bInShoreBarranco = ((int)currentNode.Tag == AstarKofiExtension.iShoreBarranco); // if (bInShoreBarranco) // { // bInBarranco = false; // if (Physics.Raycast(unitInfo.unitAI.transform.position + Vector3.up*0.5f, -Vector3.up, 1.5f, (1 << ClickInfo.iLBarranco))) // { // bInBarranco = true; // } // } // if (bInBarranco && !bIsHidden) unitInfo.shadow.Hide(); // else if(!bInBarranco && bIsHidden) unitInfo.shadow.Show(); // } // } void UpdateCheckWater() { if (bHeightAlphaWater) { SpriteRenderer[] spriteRendererOnlyWater = null; if (unitInfo.unitAnimations != null) spriteRendererOnlyWater = unitInfo.unitAnimations.waterController.spriteRendererOnlyWater; unitInfo.changeColorChilds.UpdateHeightAlphaWaterVector(hitGround.point + new Vector3(0, -0.05f, 0), spriteRendererOnlyWater); } } private bool bIsKinematicStillTransitioning = false; /*****************************************************/ /*************** Salto, Soplo, Landing ***************/ /*****************************************************/ public void FixedUpdate() { UpdateCheckWater(); if (rbRigidBody != null) { /// /// Detectar en el aire sin haber hecho Salto / Soplo pero ---no caer--- por estar en cambio de zona /// if (GameControl.control.bStillTransitioning && !bHasSopled && !bHasSopledHard && !bHasJumped && !bGrounded && !isATravelerOfAgujero) { //Vector3 v3Velocity = rbRigidBody.velocity; //v3Velocity.y = 0; //rbRigidBody.velocity = v3Velocity; ////rbRigidBody.AddForce(new Vector3(0, fUnitGravityMultiplier * -1 * Physics.gravity.y, 0), ForceMode.Acceleration); //rbRigidBody.useGravity = false; rbRigidBody.isKinematic = true; bIsKinematicStillTransitioning = true; } if (!GameControl.control.bStillTransitioning && bIsKinematicStillTransitioning) { rbRigidBody.isKinematic = false; bIsKinematicStillTransitioning = false; } if (IsInAir()) //&& rbRigidBody.useGravity) { rbRigidBody.AddForce(new Vector3(0, (fUnitGravityMultiplier/* - 1*/) * Physics.gravity.y, 0), ForceMode.Acceleration); //if (!rbRigidBody.useGravity) //{ // rbRigidBody.useGravity = true; //} } //else //{ // if (rbRigidBody.useGravity) // { // rbRigidBody.useGravity = false; // } //} } } // Si la unidad esta controlada por las fisicas y llega al suelo void UpdateSaltoSoploLanding(bool bReset = false) { /// /// Salto para testear (salta a donde está el mouse) /// if (unitInfo.unitAI.selected && InputControl.control.GetKey(KeyCode.U, InputControl.PermisosInput.Debug, -1, true)) { ClickInfo clickinfo = GameControl.control.clickInfo; clickinfo.GetClickInfo(true, false, false, false, false, false, false, true); if (clickinfo.bGroundFound) { SaltoCambiandoGravedad(1, true, clickinfo.v3GroundHitPoint, "JumpAir", true, true, false); } else { SaltoCambiandoGravedad(1, true, clickinfo.v3MousePositionAt0, "JumpAir", true, true, false); } bJumpAndStop = false; } /// /// Soplo para testear /// if (unitInfo.unitAI.selected && InputControl.control.GetKey(KeyCode.O, InputControl.PermisosInput.Debug, -1, true)) { ClickInfo clickinfo = GameControl.control.clickInfo; clickinfo.GetClickInfo(true, false, false, false, false, false, false, true); SoploCambiandoGravedad(1, GameControl.control.testsettings.v2FuerzaSoplo, clickinfo.v3GroundHitPoint, false, default(Vector3), GameControl.control.testsettings.fExplosionRadiusTest); bJumpAndStop = false; } /// /// Sentarse para testear /// // if (unitInfo.unitAI.selected && InputControl.control.GetKey(KeyCode.O, InputControl.PermisosInput.Debug, -1, true)) // { // unitInfo.unitAnimations.SetAnimation("ConsoleSit"); // } /// /// Quedarse parado para testear /// //if (unitInfo.unitAI.selected && InputControl.control.GetKey(KeyCode.O, InputControl.PermisosInput.Debug, -1, true)) //{ // unitInfo.unitAI.StopPursuingRival(true); // unitInfo.unitAI.StopPursuingFollowee(); //} /// /// Detectar en el aire sin haber hecho Salto / Soplo /// if (!bIsThrown && !bGrounded && !isATravelerOfAgujero) { StartSaltoSoploMode(false); bHasJumped = true; } /// /// Detectar cualquier caida (SALTO y SOPLO) /// if (bIsThrown && fIsThrown + 0.2f < Time.time && !isATravelerOfAgujero && bGrounded) { if (bHasSopled || bHasSopledHard || bIsStunOrDown) { // La primera vez que cae, se da un golpe, la segunda ya realiza la caida. if ((bHasSopledHard || bIsStunOrDown) && !unitInfo.unitAI.currentState.IsInFallingState()) { // En el fallingState, se reproduce la animacion de darse el golpe al caer // Audio Grounded // if(unitInfo.unitAI.GetMe().pPositionUnit == RTSUnit.Position.Levita) // { // AudioControl.control.ActivarSonido(AudioControl.control.SonidoOverrided("whoosh_a"), false, Vector3.zero, unitInfo.unitAI); // } // else if (IsGroundedGround()) { AudioControl.control.ActivarSonido(AudioControl.control.SonidoOverrided("bodyfall_01"), false, Vector3.zero, unitInfo.unitAI); } else if (IsGroundedWater()) { AudioControl.control.ActivarSonido(AudioControl.control.SonidoOverrided("chof-1"), false, Vector3.zero, unitInfo.unitAI); } else if (IsGroundedLava()) { AudioControl.control.ActivarSonido(AudioControl.control.SonidoOverrided("chof-1"), false, Vector3.zero, unitInfo.unitAI); } unitInfo.unitAI.currentState.GoToFallingState(); bIsThrown = false; // Evitamos que entre mil veces por aqui y que no pare de ir al FallingState if (bJumpAndStop) { Stop(true); bJumpAndStop = false; } } else if (bHasSopled && !(bHasSopledHard || bIsStunOrDown) && rbRigidBody.velocity.magnitude < 0.01f) { ForceStandUp(); } } else if (bHasJumped && !unitInfo.unitAI.currentState.IsInLandingState()) { // Audio bodyfall_01 Para aterrizar con los pies if (GameControl.control.bStillTransitioning) { bReset = true; } if (!bReset) { if (fIsThrown + 0.3f >= Time.time) { // Si ha pasado menos de 0.2 segundos desde el salto, no hacer el sonido de caer porque puede pasar que estemos chocando con una subida y se quiera hacer todo el rato. } else if (unitInfo.unitAI.GetMe().pPositionUnit == RTSUnit.Position.Levita) { AudioControl.control.ActivarSonido(AudioControl.control.SonidoOverrided("whoosh_a"), false, Vector3.zero, unitInfo.unitAI); } else if (IsGroundedGround()) { AudioControl.control.ActivarSonido(AudioControl.control.SonidoOverrided("bodyfall_01"), false, Vector3.zero, unitInfo.unitAI); bHanging = false; } else if (IsGroundedWater()) { AudioControl.control.ActivarSonido(AudioControl.control.SonidoOverrided("chof-1"), false, Vector3.zero, unitInfo.unitAI); } else if (IsGroundedLava()) { AudioControl.control.ActivarSonido(AudioControl.control.SonidoOverrided("chof-1"), false, Vector3.zero, unitInfo.unitAI); } } // AudioControl.control.ActivarSonido(AudioControl.control.SonidoOverrided("footstep", unitInfo.unitAI), false, Vector3.zero, unitInfo.unitAI); ResetEndSaltoSoploLayer(); if (bJumpAndStop) { Stop(true); bJumpAndStop = false; } if (!unitInfo.unitAI.GetMe().IsDead()) { unitInfo.unitAttackManager.JumpAttackLanded(); } if (bReset) { unitInfo.unitAnimations.SetAnimation("Idle", 0.1f, 0, true); unitInfo.unitAnimations.SetCrossFadeMode(false); ResetEndSaltoSoploState(); } else { unitInfo.unitAI.currentState.GoToLandingState(); } } } /// /// FIX. Detectar posible error en el que no estemos en el FallingState o LandingState y que estemos aun recuperandonos /// Poner esto debajo de los cambios de estado o saldran errores /// UnitBaseState currentState = unitInfo.unitAI.currentState; if (IsInAir() && fIsThrown + 0.2f < Time.time && !isATravelerOfAgujero && bGrounded && !currentState.IsInAirState() && !currentState.IsInFallingState() && !currentState.IsInLandingState() && !currentState.IsInDeadState()) { unitInfo.unitAnimations.SetAnimation("Idle", 0.1f, 0, true); unitInfo.unitAnimations.SetCrossFadeMode(false); ForceStandUp(); } } private void StartSaltoSoploMode(bool bChangePhysicMaterial = true) { if (bChangePhysicMaterial) { GetMyCollider().sharedMaterial = GameControl.control.testsettings.physicMaterialUnitAir; } // Marcamos que estamos en el aire y el tiempo en el que salimos bIsThrown = true; fIsThrown = Time.time; bIsThrownRecoveringFromGround = false; // UnitAire unitInfo.unitAI.SetLayerUnit(ClickInfo.iLUnitAir); // Borramos todo el penalty if (unitInfo.unitPenalty != null) unitInfo.unitPenalty.DeletePenalty(); // Indica que estamos en el aire unitInfo.unitAI.FallLevitateUnit(true); // bForceHeightAlphaWater = true; // Vamos al AirState hasta que la unidad caiga al suelo if (unitInfo.unitAI.currentState != null) unitInfo.unitAI.currentState.GoToAirState(); } public void ResetEndSaltoSoploLayer() { // UnitInteractive // Debug.Log(unitInfo.unitAI.name + " SetLayerUnit(ClickInfo.iLInteractive"); unitInfo.unitAI.SetLayerUnit(ClickInfo.iLInteractive); rbRigidBody.velocity = Vector3.zero; Collider collider = GetMyCollider(); if (collider != null) { collider.isTrigger = false; } if (!isJumpingInAgujero) { rbRigidBody.isKinematic = false; } // Indica que estamos en el suelo unitInfo.unitAI.FallLevitateUnit(false); bIsThrownRecoveringFromGround = true; } public void ResetEndSaltoSoploState() { // Debug.Log("Supp "+unitInfo.unitAI.name); GetMyCollider().sharedMaterial = GameControl.control.testsettings.physicMaterialUnitGround; bIsStunOrDown = ReturnFalseIfCanRecoverFromStunOrDown(); if (bHasSopledHard && unitInfo.unitSpecialSkill != null) { unitInfo.unitSpecialSkill.UnitWasBlownAway(true); } bIsThrown = false; bHasJumped = false; bHasSopledHard = false; bHasSopled = false; bIsThrownRecoveringFromGround = false; if (steering != null) { steering.ResetPathFollowingV3Old(); } unitInfo.unitAI.currentState.GoToStandbyState(); //Le añado !isdead para que no se quede pillado en una muerte en landing // Debug.Log(" mov && (!dead || capslock)" + IsMoving() + " " + !unitInfo.unitAI.GetMe().IsDead() + " " + InputControl.control.bCapsLock); if (IsMoving() && (!unitInfo.unitAI.GetMe().IsDead() /*|| InputControl.control.bCapsLock*/)) { //unitInfo.unitAI.currentState.GoToMovingState(); MoveTo(unitInfo.unitAI.moveToClassCurrent); } //else //{ // unitInfo.unitAI.currentState.GoToStandbyState(); //} } /** ////////////////////////////////////////////////////////////// *** *** //////////////////////// STUN //////////////////////////////// *** *** ////////////////////////////////////////////////////////////// **/ // private float fStunTime = 0; // // public bool bIsStunned() // { // return fStunTime > 0; // } // // public void StunUnit(float fStunTime) // { // if (!bDyingAnimation && !unitNavMeshAgent.bIsDead && !unitNavMeshAgent.bIsThrown) // { // unitNavMeshAgent.bIsStunOrDown = true; // unitInfo.unitAnimations.SetCrossFadeMode(true); //// unitInfo.unitAnimations.SetCrossFadeAnimationByName("Stun"); // unitInfo.unitAnimations.SetAnimation("Stun", 0.1f, 0, true); // // this.fStunTime = fStunTime; // } // } // // void UpdateStun() // { // if(unitNavMeshAgent.bIsStunOrDown) // { // fStunTime -= Time.deltaTime; // if (fStunTime <= 0) // { // fStunTime = 0; // // unitNavMeshAgent.bIsStunOrDown = false; // unitInfo.unitAnimations.SetCrossFadeMode (false); //// unitInfo.unitAnimations.SetCrossFadeAnimationByName("Idle"); // unitInfo.unitAnimations.SetAnimation("Idle"); // } // } // } /** ////////////////////////////////////////////////////////////// *** *** /////////////////////// SALTO / SOPLO //////////////////////// *** *** ////////////////////////////////////////////////////////////// **/ public void SoploCambiandoGravedad(float fNewGravityMultiplier, Vector2 v2Soplo, Vector3 v3Origin, bool bDistanciaCilindrica = default(bool), Vector3 v3Destination = default(Vector3), float fExplosionRadius = 999, bool bIgnoreNoBlow = false) { if (fExplosionRadius == 999 && GameControl.control.testsettings.fExplosionRadiusOldSystem != 999) { fExplosionRadius = GameControl.control.testsettings.fExplosionRadiusOldSystem; } if (bManualMovement) return; SoploClass soploClass = new UnitMovement.SoploClass(v2Soplo, v3Origin, bDistanciaCilindrica, v3Destination, fExplosionRadius); fUnitGravityMultiplier = fNewGravityMultiplier; Soplo(soploClass, bIgnoreNoBlow); } public void SaltoCambiandoGravedad(float fNewGravityMultiplier, bool bBallistic, Vector3 position, string sJumpingAnimation = "JumpAir", bool bSaltoMirando = true, bool bHighShot = true, bool bEsUnSoploEnRealidad = false, bool bIgnoreNoBlow = false) { if (bManualMovement) return; fUnitGravityMultiplier = fNewGravityMultiplier; Salto(bBallistic, position, sJumpingAnimation, bSaltoMirando, bHighShot, bEsUnSoploEnRealidad, bIgnoreNoBlow: bIgnoreNoBlow); } public static float SignedAngle(Vector3 a, Vector3 b) { float angle = Vector3.Angle(a, b); float sign = Mathf.Sign(Vector3.Dot(Vector3.up, Vector3.Cross(a, b))); return angle * sign; } public static float AngleSigned(Vector3 v1, Vector3 v2, Vector3 n) { return Mathf.Atan2( Vector3.Dot(n, Vector3.Cross(v1, v2)), Vector3.Dot(v1, v2)) * Mathf.Rad2Deg; } public class SoploClass { public Vector2 v2Soplo; public float fExplosionRadius; public Vector3 v3Origin; public Vector3 v3Destination = default(Vector3); public bool bDistanciaCilindrica = default(bool); public bool bSalto = false; public SoploClass(Vector2 v2Soplo, Vector3 v3Origin, bool bDistanciaCilindrica = default(bool), Vector3 v3Destination = default(Vector3), float fExplosionRadius = 999, bool bSalto = false) { if (fExplosionRadius == 999 && GameControl.control.testsettings.fExplosionRadiusOldSystem != 999) { fExplosionRadius = GameControl.control.testsettings.fExplosionRadiusOldSystem; } this.v2Soplo = v2Soplo; this.v3Origin = v3Origin; this.v3Destination = v3Destination; this.bDistanciaCilindrica = bDistanciaCilindrica; this.fExplosionRadius = fExplosionRadius; this.bSalto = bSalto; } } void TirarAlPersonajeAlSuelo(Vector3 v3Fuerza) { if (unitInfo.unitAI.bAttackingAnimationBeforeEvent) { GameControl.control.saveAsset.scLogros.CharacterThrownBeforeAttack(unitInfo.unitAI.GetMe()); } unitInfo.unitAI.bAttackingAnimationBeforeEvent = false; // Esto ya lo hace el unitSpecialSkill.UnitWasBlownAway(false); /*if (SceneControl.control.bossMode != null) { SceneControl.control.bossMode.CancelAlphaDrops(); }*/ if (unitInfo.unitAttackManager != null) { unitInfo.unitAttackManager.UnitWasBlownAway(); } if (unitInfo.unitSpecialSkill != null) { unitInfo.unitSpecialSkill.UnitWasBlownAway(false); } // Curar varios estados alterados EstadoAlterado.RemoveAlteredStates(unitInfo.unitAI, EstadoAlterado.CUREBYBLOW); // Debug.Log("TirarAlPersonajeAlSuelo"); unitInfo.unitAttackManager.StopAttack(); bIsStunOrDown = true; bHasSopledHard = true; // CALCULO DE LAS ANIMACIONES DE CAERSE // El angulo en el que me lanzan Vector3 v3A = (new Vector3(v3Fuerza.x, 0, v3Fuerza.z)).normalized; Vector3 v3B = (new Vector3(1, 0, 1)).normalized; float fangle = SignedAngle(v3A, v3B); // Seteamos la variable de direccion (Back/Front) dependiendo del angulo en el que me lanzan bFrontBackSopled = !(unitInfo.interactiveRadius.bIsFacingLeft && fangle < 0 || !unitInfo.interactiveRadius.bIsFacingLeft && fangle > 0); string sAnimationFrontBackSopled; if (bFrontBackSopled) { sAnimationFrontBackSopled = "BlowFront"; } else { sAnimationFrontBackSopled = "BlowBack"; } //sFrontBackSopled = "Front"; //if (unitInfo.interactiveRadius.bIsFacingLeft && fangle < 0 || !unitInfo.interactiveRadius.bIsFacingLeft && fangle > 0) //{ // sFrontBackSopled = "Back"; //} Profiler.BeginSample("Anim Blow"); unitInfo.unitAnimations.SetCrossFadeMode(true); float fTransitionTime = 0.1f; if (unitInfo.unitAnimations.IsInState("BlowFrontDown") || unitInfo.unitAnimations.IsInState("BlowBackDown") || unitInfo.unitAnimations.IsInState("BlowFrontGetUp") || unitInfo.unitAnimations.IsInState("BlowBackGetUp")) { fTransitionTime = 0; } unitInfo.unitAnimations.SetAnimation(sAnimationFrontBackSopled, fTransitionTime, 0, true); Profiler.EndSample(); unitInfo.unitAI.currentState.GoToStandbyState(); } public void Soplo(SoploClass soploClass, bool bIgnoreNoBlow = false) { // if (moleScript != null && (moleScript.bTapandoAgujero || moleScript.bTapandoNoMolestar)) // { // return; // } if (!unitInfo.unitAttackManager.CanIBlow() || bManualMovement) { return; } UnitRTSUnit unitMe = unitInfo.unitAI.GetMe(); if ((unitMe.bNoBlowUnit && !bIgnoreNoBlow) || !unitInfo.unitAI.unitActionControl.bAllowBlow || (unitMe.rtsAttackDefault != null && unitMe.rtsAttackDefault.bKamikaze && unitMe.IsDead())) { return; } if (!EstadoAlterado.CanBlowAlteredStates(unitInfo.unitAI)) { return; } Vector2 v2Soplo = soploClass.v2Soplo; Vector3 v3Origin = soploClass.v3Origin; v3SoploOrigin = v3Origin; Vector3 v3Destination = soploClass.v3Destination; float fExplosionRadius = soploClass.fExplosionRadius; Vector3 v3SoploFinal = Vector3.zero; //bool bDerribadoPorSoplo = false; if (!unitInfo.unitAI.IsTower() && !unitInfo.unitAI.IsObstacle() && !unitInfo.unitAI.IsMuro() && v2Soplo.magnitude != 0) { // El float fDistance es la distancia de explosión hasta el objeto (a la mitad de su altura) Vector3 v3AlturaMitad = Vector3.up * (unitInfo.unitAI.me.rtsUnit.v2DimRadius.y / 2); float fDistance; if (soploClass.bDistanciaCilindrica) { fDistance = Vector3.Distance(new Vector3(unitInfo.unitAI.transformAI.position.x, 0, unitInfo.unitAI.transformAI.position.z), new Vector3(v3Origin.x, 0, v3Origin.z)); // Debug.Log("Soplo bDistanciaCilindrica: " + soploClass.bDistanciaCilindrica); } else { fDistance = Vector3.Distance(v3Origin, unitInfo.unitAI.transformAI.position + v3AlturaMitad); } // Esto está por si el soplo es negativo (agujero negro) poder hacer los clamp y luego devolverle su negatividad float fPosNegX = 1; if (v2Soplo.x < 0) { fPosNegX = -1; } float fPosNegY = 1; if (v2Soplo.y < 0) { fPosNegY = -1; } // El soplo pierde fuerza con la distancia y el peso de la victima, alcanzando su valor máximo en su origen. // La pérdida de fuerza del soplo es -2 puntos por baldosa y de 1 punto por unidad de peso float fWeightUnit = unitInfo.unitAI.me.fWeight; float fPeso = fWeightUnit; //if (fWeightUnit < 0) // fPeso = 0; float fForceFinalX; float fForceFinalY; // Debug.Log("v2Soplo en movement: "+v2Soplo+" "+GameControl.control.testsettings.fSoploPesoImportanceMult+" "+fDistance+" "+fExplosionRadius); if (fExplosionRadius == GameControl.control.testsettings.fExplosionRadiusOldSystem) { fForceFinalX = (v2Soplo.x - fPeso) - fDistance; fForceFinalX = Mathf.Clamp(fForceFinalX, 0, GameControl.control.testsettings.fSoploLimite) * fPosNegX; fForceFinalY = (v2Soplo.y - fPeso) - fDistance; fForceFinalY = Mathf.Clamp(fForceFinalY, 0, GameControl.control.testsettings.fSoploLimite) * fPosNegY; // Dejar comentado este log si no se usa porque genera allocations y boxing //Debug.Log("Soplo Viejo con fForceFinal: FX: " + fForceFinalX +" FY: "+ fForceFinalY+" P: "+ fPeso+" D: "+ fDistance); } else { // fForceFinalX = Mathf.Lerp(0, v2Soplo.x * 1 / (1 + fPeso), 1-(fDistance/fExplosionRadius)); float fLerp = 1 - (fDistance / (fExplosionRadius * (1 + GameControl.control.testsettings.fSoploRadiusMult))); fForceFinalX = Mathf.Lerp(0, v2Soplo.x - (fPeso * GameControl.control.testsettings.fSoploPesoImportanceMult), fLerp); fForceFinalX = Mathf.Clamp(fForceFinalX, 0, GameControl.control.testsettings.fSoploLimite) * fPosNegX; // fForceFinalY = Mathf.Lerp(0, v2Soplo.y * 1 / (1 + fPeso), 1-(fDistance/fExplosionRadius)); fForceFinalY = Mathf.Lerp(0, v2Soplo.y - (fPeso * GameControl.control.testsettings.fSoploPesoImportanceMult), fLerp); fForceFinalY = Mathf.Clamp(fForceFinalY, 0, GameControl.control.testsettings.fSoploLimite) * fPosNegY; // Debug.Log("Soplo Nuevo con Lerp"+fLerp +" y fForceFinal: " + fForceFinalX +" "+ fForceFinalY); } // Si la explosion es lo suficientemente fuerte el personaje queda derribado if ((fForceFinalX + fForceFinalY) >= 5) { //bDerribadoPorSoplo = true; } //bool bChorro = false; if (v3Destination != default(Vector3)) // Tipo chorro (no importa si es x o y, se suman en la dirección) { // bChorro = true; float fForceFinalDirectional = (Mathf.Abs(v2Soplo.x + v2Soplo.y) - fPeso - (fDistance) * 1); fForceFinalDirectional = Mathf.Clamp(fForceFinalDirectional, 0, GameControl.control.testsettings.fSoploLimite); v3SoploFinal = fForceFinalDirectional * v3Destination.normalized/* * TimeController.control.GetDeltaTime(unitInfo.unitAI.GetMe().iHackValue) * 10*/; //Para debug Gizmos v3SoploDestination = v3Destination; bSoploLength = true; bSoploRadius = false; fSoploLength = v2Soplo.magnitude; } else // Tipo explosión { Vector3 direccionX = new Vector3(unitInfo.unitAI.transformAI.position.x, 0, unitInfo.unitAI.transformAI.position.z) - new Vector3(v3Origin.x, 0, v3Origin.z); if (fForceFinalY == 0 && fForceFinalX != 0) { //Si la X vale algo y la Y no, le damos un pequeño valor a la Y para que se vuele un poco el personaje fForceFinalY = fForceFinalX / 2; } v3SoploFinal = fForceFinalX * (direccionX).normalized + fForceFinalY * Vector3.up; bSoploLength = false; bSoploRadius = true; fSoploRadius = v2Soplo.x; } if (float.IsNaN(v3SoploFinal.magnitude) || v3SoploFinal.magnitude == 0) { return; } // Realizamos las acciones en el personaje float fVelocityNeededToFall = 3;//TODO que dependa del peso? 1/unitMe.rtsUnit.v2Weight.y; Collider colliderCharacter = GetMyCollider(); if (colliderCharacter != null) { colliderCharacter.isTrigger = false; } //Lo quito de kinematic *si no es un rider* (//TODO cómo se comprueba eso?) porque si no ya puedes darle la velocity que quieras, que no se mueve if (rbRigidBody != null /* && !isARIDER kinematic*/) { rbRigidBody.isKinematic = false; } // Si la fuerza del soplo es más grande que mi velocidad (explosión fuerte cerca, etc.), // o si es suficiente para tirarme, se sobrescribe. // Si es más pequeña que mi velocidad (explosión débil, lejos, etc.) se suma. // Si es chorro hay que sumarla sí o sí, porque en un chorro se puede caminar mientras te empuja // Esto es para que no nos frene una explosión de espalda mientras huimos (que queda muy mal) Vector3 velocityGuardada = rbRigidBody.velocity; if (!unitMe.bUnmovable) { if ((v3SoploFinal.magnitude > velocityGuardada.magnitude || v3SoploFinal.magnitude > fVelocityNeededToFall)) //&& !bChorro) { rbRigidBody.velocity = v3SoploFinal; } else { rbRigidBody.velocity += v3SoploFinal; } } // IMPORTANTE: Settea las variables compartidas del salto/soplo StartSaltoSoploMode(); // Ponemos las variables especificas del tipo de salto/soplo bHasSopled = true; bHasJumped = false; //Evitamos que se meta en el agujero si no está yendo hacia abajo if (rbRigidBody.velocity.y >= 0) { isATravelerOfAgujero = false; isJumpingInAgujero = false; isEnteringAgujero = false; } // Tirar al personaje al suelo if (v3SoploFinal.magnitude > fVelocityNeededToFall) // TODO if(bDerribadoPorSoplo) { // Si el personaje es Wargo con Kofi o Limon, tenemos que bajarlos (si la explosion es suficientemente fuerte) // Cualquier otra unidad con riders (Kofi con Sokaris por ejemplo) tambien los baja if (monturaManager.currentRiders.Count > 0) { soploClass.v2Soplo += Vector2.one; // Con un extra monturaManager.DismountAndJump(soploClass); } TirarAlPersonajeAlSuelo(v3SoploFinal); } } } // TODO: Hacer funcion de presalto que reproduce la animación de preparar el salto private void Salto(bool bBallistic, Vector3 position, string sJumpingAnimation = "JumpAir", bool bSaltaMirando = true, bool bHighShot = true, bool bEsUnSoploEnRealidad = false, float fuerza = 1.50f, float fuerzaUp = 3.0f, bool bIgnoreNoBlow = false) { //Debug.Log ("Jumped "+ unitInfo.unitAI.name); if (rbRigidBody == null || unitInfo.unitAI.IsTower() || unitInfo.unitAI.IsMuro()) { // Dejar comentado este log si no se usa porque genera allocations y boxing //Debug.Log (unitInfo.unitAI.gameObject.name+" no puede saltar porque no tengo RigidBody o es Tower o Muro"); return; } if (float.IsNaN(position.y) || float.IsInfinity(position.y)) { // Dejar comentado este log si no se usa porque genera allocations y boxing //Debug.Log (unitInfo.unitAI.gameObject.name+" no puede saltar porque el objetivo es NAN o infinito: "+ position); return; } UnitRTSUnit unitMe = unitInfo.unitAI.GetMe(); if ((unitMe.rtsAttackDefault != null && unitMe.rtsAttackDefault.bKamikaze && unitMe.IsDead()) || bManualMovement || (bEsUnSoploEnRealidad && unitMe.bNoBlowUnit && !bIgnoreNoBlow)) { return; } //Lo quito de kinematic porque sinó ya puedes darle la velocity que quieras, que no se mueve if (rbRigidBody != null) { rbRigidBody.isKinematic = false; } if (bBallistic) { // Quaternion prevRotation = unitNavMeshAgent.transform.rotation; Vector3 v3BallisticVelocity1; Vector3 v3BallisticVelocity2; float fTime; float fDistance = (position - unitInfo.unitAI.transformAI.position).sqrMagnitude; float fVerticalOffset = position.y - unitInfo.unitAI.transformAI.position.y; float fFlatDistance = Vector3.Distance(new Vector3(position.x, 0, position.z), new Vector3(unitInfo.unitAI.transformAI.position.x, 0, unitInfo.unitAI.transformAI.position.z)); float fDistanceJump = Mathf.Max(fUnitGravityMultiplier * .4f, fFlatDistance + fVerticalOffset * 2f); /*Vector3.Distance(goCharacter.transform.position, position)*/ if (fDistance <= 0.01f) { Debug.Log(unitInfo.unitAI.gameObject.name + " no puede saltar porque el objetivo es su misma posición"); return; } // Debug.Log ("fDistanceJump "+ fDistanceJump); float fVelocity = ProjectileHelper.ComputeSpeedToReachMaxFlatRange(fDistanceJump, Physics.gravity.y * fUnitGravityMultiplier, out fTime); ProjectileHelper.ComputeDirectionToHitTargetWithSpeed(unitInfo.unitAI.transformAI.position, position, Physics.gravity.y * fUnitGravityMultiplier, fVelocity, out v3BallisticVelocity1, out v3BallisticVelocity2); if (bHighShot) { if (v3BallisticVelocity2.y > v3BallisticVelocity1.y) { v3BallisticVelocity1 = v3BallisticVelocity2; } } else { if (v3BallisticVelocity2.y < v3BallisticVelocity1.y) { v3BallisticVelocity1 = v3BallisticVelocity2; } } // Fix para el salto en el sitio if (v3BallisticVelocity1 == Vector3.zero) v3BallisticVelocity1 = Vector3.up; // Debug.Log("Saltando "+rbRigidBody.name+ " Ballistic"); rbRigidBody.isKinematic = false; if (!unitInfo.unitAI.GetMe().bUnmovable) rbRigidBody.velocity = v3BallisticVelocity1 * fVelocity; } else { Vector3 direccion = (position - unitInfo.unitAI.transformAI.position).normalized; direccion = new Vector3(direccion.x, 0, direccion.z); // unitNavMeshAgent.transform.LookAt(direccion); // unitInfo.billboard.DoBillboard(); // Debug.Log("Saltando "+rbRigidBody.name+ " NON Ballistic"); if (!unitInfo.unitAI.GetMe().bUnmovable) rbRigidBody.velocity = (fuerzaUp * Vector3.up + fuerza * direccion); } //Debug.DrawRay(rbRigidBody.gameObject.transform.position, rbRigidBody.velocity, Color.white, 1f); // Miramos hacia a donde vamos o no? if (bSaltaMirando) { bIsThrown = false; Vector3 direccion = (position - unitInfo.unitAI.transformAI.position).normalized; LookAt(position);//direccion); UpdateFacingRightOrLeft(); // Debug.Log("Hey"+direccion); } //Nos Caemos? if (bEsUnSoploEnRealidad) { TirarAlPersonajeAlSuelo(rbRigidBody.velocity); //Aún hay que tirar a los jinetes de las monturas } // IMPORTANTE: Setea las variables compartidas del salto/soplo StartSaltoSoploMode(); // Ponemos las variables especificas del tipo de salto/soplo bHasJumped = true; if (!bIsStunOrDown) { unitInfo.unitAnimations.SetAnimation(sJumpingAnimation); } } //////////////////////////////////////////////////////////////////////////////////// Vector3 GetAddedForcesUnit() { return v3AddedForces; } void DeleteAddedForcesUnit() { v3AddedForces = Vector3.zero; } public void AddAddedForceUnit(Vector3 v3, float fForce, bool bUseWeight = true) { float fForceAux = fForce; if (bUseWeight) { float fPeso = unitInfo.unitAI.GetMe().fWeight; if (fPeso < 0.5f) fPeso = 0.5f; fForceAux /= fPeso; // (fForce - fPeso); } fForceAux = Mathf.Clamp(fForceAux, 0, GameControl.control.testsettings.fSoploLimite); v3AddedForces += v3.normalized * fForceAux; } /////////////////////////////////////////////////////////////////////////////////////////////// Vector3 GetGamepadVelocity() { return v3GamepadForces; } void DeleteGamepadVelocity() { v3GamepadForces = Vector3.zero; } public void AddGamepadVelocity(Vector3 v3) { v3GamepadForces = v3; } /*****************************************************/ /**************** Stun, Falling, Down ****************/ /*****************************************************/ // Esta funcion devuelve FALSE solo si el personaje se recupera del Stun y no esta caido por soplo public bool ReturnFalseIfCanRecoverFromStunOrDown() { return false; // TODO: Pendiente de programar cuando hagamos el stun y el carse al suelo a la vez. } /*****************************************************/ /********************** Agujeros *********************/ /*****************************************************/ void UpdateAgujeroTravel() { // Si está creando un agujero // Para crear agujeros bool bIsTopo = false; if (unitInfo != null && unitInfo.unitSpecialSkill != null) { bIsTopo = unitInfo.unitSpecialSkill.IsMole(); } // FIX 2020-02-26: Los agujeros no funcionan en modo cinematica if (!bIsTopo && GameControl.control.cinematicsControl.bEnCinematica) { return; } // TODONEWMOLE bool bTopoNotBuildingAgujero = false; if (bIsTopo) { bTopoNotBuildingAgujero = unitInfo.unitSpecialSkill.SpecialSkillCanFallTrap((agujeroIn != null) ? agujeroIn.tTrap : null); for (int i = 0; i < unitInfo.unitSpecialSkill.specialSkillScript.Length; i++) { if (unitInfo.unitSpecialSkill.specialSkillScript[i] != null && unitInfo.unitSpecialSkill.specialSkillScript[i].IsMole()) { // if(unitInfo.unitSpecialSkill.specialSkillScript[i].GetType() == typeof(MoleScript)) moleScript = (MoleScript)unitInfo.unitSpecialSkill.specialSkillScript[i]; if (unitInfo.unitSpecialSkill.specialSkillScript[i].GetType() == typeof(NewMoleScript)) newMoleScript = (NewMoleScript)unitInfo.unitSpecialSkill.specialSkillScript[i]; } } // if (unitInfo.unitSpecialSkill.specialSkillScript != null && unitInfo.unitSpecialSkill.specialSkillScript.IsMole()) // { // if(unitInfo.unitSpecialSkill.specialSkillScript.GetType() == typeof(MoleScript)) moleScript = (MoleScript)unitInfo.unitSpecialSkill.specialSkillScript; // if(unitInfo.unitSpecialSkill.specialSkillScript.GetType() == typeof(NewMoleScript)) newMoleScript = (NewMoleScript)unitInfo.unitSpecialSkill.specialSkillScript; // } // else // { // if(unitInfo.unitSpecialSkill.specialSkillScript2.GetType() == typeof(MoleScript)) moleScript = (MoleScript)unitInfo.unitSpecialSkill.specialSkillScript2; // if(unitInfo.unitSpecialSkill.specialSkillScript2.GetType() == typeof(NewMoleScript)) newMoleScript = (NewMoleScript)unitInfo.unitSpecialSkill.specialSkillScript2; // } // if (moleScript != null) // { // moleScript.BuildingHole(); // bTopoNotBuildingAgujero = !moleScript.isBuildingAgujero; // } } // Si está entrando en un agujero y no lo está construyendo //Para entrar en el Agujero // if (bIsTopo) // { // Debug.Log("isEnteringAgujero && (!bIsTopo || (bIsTopo && !moleScript.isBuildingAgujero))" + isEnteringAgujero + " " + bIsTopo + " " + moleScript.isBuildingAgujero); // } // TODONEWMOLE if (isEnteringAgujero && (!bIsTopo || bTopoNotBuildingAgujero)) { isATravelerOfAgujero = true; bool bShouldEnter = true; if (bIsTopo) { // if (moleScript.bQuieroTaparAgujero) // { // moleScript.bQuieroTaparAgujero = false; // } //Empiezo a tapar el primer agujero // if (unitInfo.unitNavMeshAgent.agujeroIn != null && moleScript.bTapandoPrimerAgujero) // { // unitInfo.unitNavMeshAgent.agujeroIn.SetHoleParticles(); // } // if(moleScript != null) bShouldEnter = moleScript.JumpingHoleChecking(); // TOPO: Mole Method // else bShouldEnter = bTopoNotBuildingAgujero; } else { //Setear animacion blow if (true) { unitInfo.unitAnimations.SetCrossFadeMode(true); unitInfo.unitAnimations.SetAnimation("BlowBack", 0.75f, 0, true); } } // Si no está saltando hacia dentro y no se produce que esté tapando un agujero y ese agujero tiene tunel // y está en el suelo (rigidBody es kinemático) if (!isJumpingInAgujero && bShouldEnter) { //Saltar isJumpingInAgujero = true; if (rbRigidBody.velocity.y >= -0.1f) { if (newMoleScript != null) { newMoleScript.JumpIn(); } SaltoCambiandoGravedad(1.0f, true, agujeroIn.tTrap.transform.position - Vector3.up * 0.5f, "JumpIn"); // , 1.5f, 3, bIsTopo } // if (bIsTopo && moleScript != null && moleScript.bTapandoAgujero) // { // moleScript.MarkThisHole(agujeroIn); // } v3EnterJumpPos = unitInfo.unitAI.transformAI.position; } else // Si no está saltando hacia dentro o se produce que está tapando un agujero y tiene tunel o está saltando (rigidBody no kinematic) { isJumpingOutAgujero = false; isExitingAgujero = false; } // Si está cayendo y está por debajo del agujero // y iRef == iRefInicial (todavía no es invisible) //Volando hacia el agujero lo hacemos invisible if (rbRigidBody.velocity.y < 0 && !rbRigidBody.isKinematic) { if (unitInfo.unitAI.transformAI.position.y < agujeroIn.tTrap.transform.position.y + 0.3f) { BeInVisible(agujeroIn, agujeroIn.tTrap.transform.position, unitInfo.unitAI.transformAI.position); } // else // { // agujeroIn.tTrap.bParticlesIn = false; // } } if (!unitInfo.unitMovement.bParticlesTrap && (unitInfo.alphaSlow.bHiding || unitInfo.alphaSlow.bShowing)) { // Debug.Log("Puff in"); if (agujeroIn.tTrap.holeState == TrapHole.HoleState.Normal) Pool.pool.PoolInstantiate(GameControl.control.testsettings.prefabs.goParticulasCaerAgujeroPrefab, "ParticulasCaerAgujero", agujeroIn.tTrap.transform.position + new Vector3(-.1f, 0, -.1f), Quaternion.identity); else if (agujeroIn.tTrap.holeState == TrapHole.HoleState.Water) SceneControl.SpawnWaterSplash(ClickInfo.iLWater, agujeroIn.tTrap.transform.position + new Vector3(-.1f, 0, -.1f), unitInfo.unitAI.GetMe().rtsUnit.fShadowRadius); unitInfo.unitMovement.bParticlesTrap = true; } // TODONEWMOLE //Aquí le activo el cavar porque ya ha llegado // if (bIsTopo && moleScript != null && moleScript.bYendoATaparAgujero && moleScript.trapTarget == agujeroIn.tTrap) // { // // print("Aquí le activo el cavar porque ya ha llegado"); //// unitInfo.unitAnimations.SetAnimation(UnitAnimations.States.Run, false); // unitInfo.unitAnimations.SetAnimation("Idle"); //// agujeroOut.tTrap.animator.CrossFade("Close", 0, 0); // moleScript.bTapandoAgujero = true; // moleScript.bQuieroTaparAgujero = true; // moleScript.bYendoATaparAgujero = false; // } bool bTapando = false; //(bIsTopo && moleScript != null && moleScript.bTapandoAgujeroConTunel); // TOPO: Mole Method // Si tengo 2 agujeros y estoy viajando por ellos; o estoy tapando un agujero con tunel /*ya ha llegado al agujero*/ if ((agujeroIn != null && agujeroIn.DoIExist && agujeroIn.tTrap != null && unitInfo.unitAI.transformAI.position.y < agujeroIn.tTrap.transform.position.y - 1.0f && !rbRigidBody.isKinematic) || (bTapando)) { //Lo paro al llegar abajo rbRigidBody.isKinematic = true; // Si no soy el topo muero if (agujeroIn.DoIKill(unitInfo.unitAI)) { agujeroIn.KillByHole(unitInfo.unitAI); return; } //Esto lo ponía para que no chocara con los pinchos (creo) pero al quitar el characterController he visto que no hacía nada // this.GetComponent().enabled = false; //TODO Hago el personaje invisible isJumpingOutAgujero = false; isJumpingInAgujero = false; isEnteringAgujero = false; isInAgujero = true; // if (bIsTopo && agujeroIn != null && agujeroOut != null && moleScript != null) // { // //Entra 1 vez // moleScript.StartHoleTravel(agujeroIn, agujeroOut); // } } // Si no tengo 2 agujeros o no estoy viajando por ellos; o no estoy tapando un agujero con tunel // Si o bien uno de los dos agujeros es null y además estoy saltando, debajo del agujero de entrada // y todavía no estoy en el agujero else if ((agujeroIn == null || agujeroOut == null) && unitInfo.unitAI.transformAI.position.y < agujeroInPosition.y && !rbRigidBody.isKinematic && !isInAgujero) { Debug.Log("Salto in failed!"); isEnteringAgujero = false; isExitingAgujero = true; isInAgujero = false; GetMyCollider().enabled = true; Debug.Log("Aterrizado del failed Agujero"); } } // if(bIsTopo) // { // if(moleScript != null) moleScript.TravelingHoles(); // } // if (agujeroIn != null && agujeroIn.DoIExist && agujeroIn.tTrap != null) // { // if (moleScript != null && moleScript.bTapandoAgujero) // { // //Seguir tapando // } // else // { // BeVisible(agujeroIn.tTrap.transform.position, unitInfo.unitAI.transform.position); // } // } } public void BeVisible(Vector3 agujeroPosition, Vector3 positionUnit, float fYInicio = 0.2f) { // Si mi posición está por encima del agujero - 0.2f // Entonces dejo de ser invisible if (positionUnit.y > agujeroPosition.y - fYInicio && !unitInfo.alphaSlow.bShowing && unitInfo.alphaSlow.alpha < 1) { unitInfo.alphaSlow.Show(); if (unitInfo.shadow != null) unitInfo.shadow.Show(); } } public void BeInVisible(TrapHole agujero, Vector3 agujeroPosition, Vector3 positionUnit, float fYInicio = 0.2f, float fYFinal = -0.4f) { // Si mi posición está por debajo del agujero - 0.0f // Entonces soy invisible if (positionUnit.y < agujeroPosition.y + fYInicio && !unitInfo.alphaSlow.bHiding && unitInfo.alphaSlow.alpha > 0) { unitInfo.alphaSlow.Hide(fYFinal); if (unitInfo.shadow != null) unitInfo.shadow.Hide(); } } /*****************************************************/ /*********************** MoveTo **********************/ /*****************************************************/ public bool GivesForce() { UnitAI unitAIReceiveForce = unitInfo.unitAI; return !unitAIReceiveForce.IsTower() && unitAIReceiveForce.gameObject.layer != ClickInfo.iLFantasma; } public bool ReceivesForce() { UnitAI unitAIReceiveForce = unitInfo.unitAI; return CanMove() && !unitAIReceiveForce.IsTower() && unitAIReceiveForce.gameObject.layer != ClickInfo.iLFantasma && !unitAIReceiveForce.GetMe().bUnmovable; } private List arraySteeringManager = new List(); //Aquí se mueve, translate, velocity, move, ismoving, hasArrived void UpdateSteeringManager() { if (unitInfo.unitAI.IsTower() || !bGrounded || IsInAir() || unitInfo.unitAI.GetMe().bUnmovable) return; // Reset steering.ResetSteering(); // Follow path bool bIsFollowingPath = steering.IsFollowingPath(); bool bPushable = !unitInfo.unitAI.GetMe().rtsUnit.bNotPushable; // Separation (si el pathfollowing no es el ultimo nodo, para evitar que 2+ unidades quieran ir al mismo sitio a la vez y nunca lleguen por empujarse de forma infinita) if ((!bIsFollowingPath || !steering.bIsLastNode) && !IsUnitInConsole() && ReceivesForce() && bPushable) { if (GameControl.OptimizationFrames(unitInfo.unitAI.iInstanceID)) { // Los mercebinarios solo son empujados por otros mercebinarios bool bMercebinario = unitInfo.unitAI.GetMe().rtsUnit.bMercebinario; //(SceneControl.control.rtsUnitTalion == unitInfo.unitAI.GetMe() || SceneControl.control.rtsUnitMercebinary1 == unitInfo.unitAI.GetMe()); List listUnitAI = SceneControl.control.listUnits[unitInfo.unitAI.iCurrentSide]; //List arraySteeringManager = ListPool.Claim(); arraySteeringManager.Clear(); int iCount = listUnitAI.Count; for (int i = 0; i < iCount; i++) { if (listUnitAI[i] != null) { if (!listUnitAI[i].bSeparationForOthersCalculated) { listUnitAI[i].bSeparationForOthers = listUnitAI[i].unitInfo != null && listUnitAI[i].unitInfo.unitMovement != null && // IsActive es algo costoso con la cantidad de veces que se llama listUnitAI[i].GetMe() != null && listUnitAI[i].GetMe().IsActive() && listUnitAI[i].unitInfo.unitMovement.GivesForce(); } if (listUnitAI[i].bSeparationForOthers && (!bMercebinario || listUnitAI[i].GetMe().rtsUnit.bMercebinario)) { arraySteeringManager.Add(listUnitAI[i].unitInfo.unitMovement.steering); } } } } // [SEPARATION] steering.Separation(arraySteeringManager, 0.5f); //unitInfo.unitAI.GetMe().rtsUnit.v2DimRadius.x * 2f); // 0.25 //ListPool.Release(ref arraySteeringManager); } // Si el path es partial, pararse? /*if (bIsFollowingPath && steering.bIsLastNode && unitInfo.unitAI.moveToClassCurrent != null && unitInfo.unitAI.moveToClassCurrent.bInitialPathCalculatedIsPartial) { Debug.Log("v3Steering " + steering.v3Steering.sqrMagnitude); if(steering.v3Steering.sqrMagnitude > 1f*1f) { Stop(); return; } }*/ if (bIsFollowingPath) { // 8 -> 0.12 // 6 -> 0.09 // 4 -> 0.06 // 2 -> 0.03 // Regla de 3 (velocity * 0.03) / 2 float fSteeringChangeNodeStopDistance = (steering.fMaxVelocity * 0.05f) * 0.5f; // [PATHFOLLOWING] steering.PathFollowing(fSteeringChangeNodeStopDistance); } // Update steering.UpdateSteering(); // Ahora en steering.v3Velocity tenemos la velocidad de este tic // Added forces if (v3AddedForces.sqrMagnitude != 0) { steering.v3Velocity += GetAddedForcesUnit(); DeleteAddedForcesUnit(); } // Gamepad velocity if (v3GamepadForces.sqrMagnitude != 0) { steering.v3Velocity += GetGamepadVelocity(); DeleteGamepadVelocity(); } // TODO: Provisional. No borrar. Prueba para mover al personaje con las flechas // if (unitInfo.unitAI.bSelected) // { // if (Input.GetAxis("Horizontal") != 0 || Input.GetAxis("Vertical") != 0) // { // steering.v3Velocity = new Vector3(Input.GetAxis("Horizontal") + Input.GetAxis("Vertical") * 1.5f, 0, -Input.GetAxis("Horizontal") + Input.GetAxis("Vertical") * 1.5f).normalized * steering.fMaxVelocity; // unitInfo.unitAI.transform.LookAt(unitInfo.unitAI.transform.position + steering.v3Velocity); // // bIsMoving = true; // } // else // { // unitNavMeshAgent.GetLocalTransform(unitInfo.unitAI.transform.position); //v3Destination = unitInfo.unitAI.transform.position; // bIsMoving = false; // } // } //if(steering.v3Velocity != Vector3.zero) //{ Vector3 v3Velocity = Vector3.zero; if (steering != null) { v3Velocity = steering.v3Velocity; } //v3Velocity.y = rbRigidBody.velocity.y; // 2020-06-17 /*if (!isATravelerOfAgujero) // TODO: Vigiliar porque he quitado esto y no se que implica (30/11/2017) { rbRigidBody.isKinematic = false; }*/ //if (bGrounded && !IsInAir()) //{ // // Lo que mueve al personaje es esto // if (!unitInfo.unitAI.GetMe().bUnmovable) // { if (bManualMovement) { unitInfo.unitAI.transformAI.position += v3Velocity * Time.unscaledDeltaTime; } else { //rbRigidBody.velocity = v3Velocity; unitInfo.unitAI.transformAI.position += v3Velocity * TimeController.control.GetDeltaTime(unitInfo.unitAI.GetMe().iHackValue); } //} // if (!unitInfo.unitAI.GetMe().bUnmovable) // { // rbRigidBody.transform.LookAt(rbRigidBody.transform.position + new Vector3(v3.x, rbRigidBody.velocity.y, v3.z)); // rbRigidBody.isKinematic = true; // rbRigidBody.transform.Translate(new Vector3(v3.x, rbRigidBody.velocity.y, v3.z)*TimeController.control.GetDeltaTime(unitInfo.unitAI.GetMe().iHackValue)); // } //} //} //else if (bGrounded && !IsInAir()) //{ // // Restricting velocity on ground // if (!unitInfo.unitAI.GetMe().bUnmovable) rbRigidBody.velocity = new Vector3(0,rbRigidBody.velocity.y,0); //} //////////////////////////// // CONTROL POR MANDO TEST // //////////////////////////// /*if(unitInfo.unitAI.transformAI.name == "Kofi") // .name hace garbage { Rewired.Player player = ReInput.players.GetPlayer(0); float fHorizontal = player.GetAxis("Move Horizontal"); float fVertical = player.GetAxis("Move Vertical"); if (fHorizontal != 0 || fVertical != 0) { if (fHorizontal == 0) fHorizontal = 0.1f; if (fVertical == 0) fVertical = 0.1f; Vector3 v3OffsetMovement = Quaternion.AngleAxis(45, Vector3.up) * new Vector3(fHorizontal, 0, fVertical).normalized * 0.5f; Vector3 v3Movement = unitInfo.unitAI.transformAI.position + v3OffsetMovement; Debug.DrawLine(v3Movement, v3Movement + Vector3.up, Color.red); if (bIsMoving) { SetDestinationLocalTransform(v3Movement); fRecalculatePathCooldown = 0; // Se llama despues UpdateRecalculate(); } else { MoveTo(null, v3Movement); } } }*/ } // void UpdateJumpOutOfNoWalkable() // { // if (bIsMoving && !bCalculatingPath) // { // Vector3 v3hit = Vector3.zero; // bool bSamplePosition = UnitMovement.SamplePosition(transform.position, out v3hit, 2); // if (bSamplePosition && Vector3.Distance(v3hit, transform.position) > 0.1f) // { // print("Salto"); // // La unidad esta atrapada en una posicion de la que no puede salir // //TODO: Con raycast se puede comprobar que no haya obstaculo entre la unidad y la pos de destino // unitInfo.unitAI.GetMe().Salto(true, v3hit); // } // } // } public bool IsInAir() { //bool bAux = bIsThrown || bHasSopled || bHasSopledHard || bHasJumped; //if (bAux) //{ // return bAux; //} //return bAux; return bIsThrown || bHasSopled || bHasSopledHard || bHasJumped; } public bool IsMoving(bool bIgnoreIsInAir = false) { return (bIsMoving || bIsMovingGamepad) && (bIgnoreIsInAir || !IsInAir()); //&& bAlive && (rbRigidBody.velocity != Vector3.zero || bCalculatingPath); //sqrMagnitude para que sea mas barato el calculo } public bool IsMovingNotGamepad() { return bIsMoving; } public bool IsMovingGamepad() { return bIsMovingGamepad; } public bool CanMovePartial() { return CanMovePartialUnit() && CanMovePartialObstacle(); } public bool CanMovePartialUnit() { return !bIsDeadAnimation && !bDyingAnimation && !bDyingFalling && !IsInAir() && !bIsStunOrDown; } public bool CanMovePartialObstacle() { return unitInfo != null && !unitInfo.unitAI.IsTower() && !unitInfo.unitAI.IsMuro() && !unitInfo.unitAI.IsObstacle(); // && (unitInfo.unitAI.unitActionControl.bCineCanMove || !bWithPermitsCine) // !unitInfo.unitAI.bControllingUnit; } public bool CanMoveCine(bool bWithPermitsCine = true) { return (unitInfo.unitAI.unitActionControl.bCineCanMove || !bWithPermitsCine); } public bool CanMove(bool bWithPermitsCine = true, bool bWithPermitsSkill = true, bool bIgnoreSpecialSkills = false) { return CanMovePartial() && CanMoveCine(bWithPermitsCine) && unitInfo.unitAttackManager.CanMoveSkill(bWithPermitsSkill) && (bIgnoreSpecialSkills || unitInfo.unitSpecialSkill.CanMoveSpecialSkill()) && EstadoAlterado.CanMoveAlteredStates(unitInfo.unitAI) && TimeController.control.CanDoActions(unitInfo.unitAI.GetMe().iHackValue) && (!unitInfo.unitAI.bIsBoss || unitInfo.unitAI.bossInfoClass.AccionDisponible()) && unitInfo.unitAI.bCanMoveWhileDrawing; } public bool CanAttackCine(bool bWithPermitsCine = true) { return (unitInfo.unitAI.unitActionControl.bCineCanAttack || !bWithPermitsCine); } public bool CanAttack(bool bWithPermitsCine = true) { return CanHaveRival() && CanAttackCine(bWithPermitsCine) && unitInfo.unitAttackManager.CanIAttackSkillCurrent() && //unitInfo.unitSpecialSkill.CanAttackSpecialSkill() && // No existe EstadoAlterado.CanMoveAlteredStates(unitInfo.unitAI) && TimeController.control.CanDoActions(unitInfo.unitAI.GetMe().iHackValue) && (!unitInfo.unitAI.bIsBoss || unitInfo.unitAI.bossInfoClass.AccionDisponible()); } public bool CanHaveRival() { return !bIsDeadAnimation && !bDyingAnimation && !bDyingFalling && unitInfo != null && !unitInfo.unitAI.IsMuro() && !unitInfo.unitAI.IsObstacle() && !unitInfo.unitAI.IsInactiveTower() && !unitInfo.unitAI.bControllingUnit && EstadoAlterado.CanHaveRivalAlteredStates(unitInfo.unitAI); } // TODO FIXME: Puede que haya que hacer el MoveTo diferente, donde siempre haya un objetivo al que ir. // TODO FIXME: Y, si no has llegado, que vuelva a intentarlo. Hay que pensar esto para casos como el de Stun (o el de caerse al suelo con el soplo) // TODO FIXME: donde le puedes dar ordenes a una unit pero no se puede mover hasta al cabo de un rato. // GameObject goPrevObjectiveAI = null; // private GameObject goObjectiveAIMoveToDelayed; // private Vector3 v3posMoveToDelayed; public MoveToClass moveToClassDelayed = null; /// /// La funcion prueba de hacer un MoveTo si es posible, si no, se guarda la orden para que se haga Delayed /// /// true, if can move, false otherwise. /// GameObject objective /// Position objective public bool MoveTo(GameObject goObjectiveAI, Vector3 v3pos = default(Vector3), bool bWalkNotRun = false) { // Esta funcion solo es para llamar a la otra con los parametros viejos MoveToClass moveToClass = new MoveToClass(goObjectiveAI, v3pos); moveToClass.bWalkNotRun = bWalkNotRun; return MoveTo(moveToClass); } public bool MoveTo(MoveToClass moveToClass) { bool bRetorn = false; UnitAI myUnitAI = unitInfo.unitAI; // Si no se permite el movimiento, salir antes if (GameControl.control.bVictoryOrDefeatStop || GameControl.control.bSceneChangeOnlyTransitioning) { return bRetorn; } if (myUnitAI.IsTower()) { myUnitAI.towerScript.TowerMoveTo(moveToClass); return bRetorn; } // FIXME Esto no hace nada porque el if de arriba se come el IsTower y hace return //if (myUnitAI.bControllingUnit) //{ // if (myUnitAI.unitAIControlling.towerScript != null) myUnitAI.unitAIControlling.towerScript.GetUnitOutOfTheTower(); //} // Reset de las variables de delay ResetMoveToDelayed(); bRetorn = MoveToDelayed(moveToClass); if (!bRetorn) { // Guardamos las variables moveToClassDelayed = moveToClass; } return bRetorn; } public void SetFakeMoveToDelayed(MoveToClass moveToLocal) { moveToClassDelayed = moveToLocal; } // Update del delay // TODO: Hacer el contenido de esta funcion cada X sec? No se, queremos una respuesta bastante inmediata private void UpdateMoveToDelayed() { if (moveToClassDelayed != null) { MoveTo(moveToClassDelayed); } } // Reset de las variables de delay private void ResetMoveToDelayed() { moveToClassDelayed = null; } public void ResetMoveToClassAndScopeCurrent() { fScopeCurrent = 0; unitInfo.unitAI.MoveToClassCurrentToNull(); } public bool bTest = false; private bool MoveToDelayed(MoveToClass moveToClass) //GameObject goObjectiveAI, Vector3 v3pos = default(Vector3)) { //Debug.Log($"{unitInfo.unitAI.gameObject.name} MoveTo START"); bool bRetorn = false; if (moveToClass == null) { return bRetorn; } if (moveToClass.goObjectiveAI != null && !CanAttack()) { return bRetorn; } // Si no se permite el movimiento, salir antes (delay) if (GameControl.control.bVictoryOrDefeatStop || unitInfo.unitAI.unitActionControl.bBlowHornStop) { return bRetorn; } if (unitInfo.unitAttackManager != null && unitInfo.unitAttackManager.skillCurrent != null && unitInfo.unitAttackManager.skillCurrent.CanFinishWhenMoveTo()) { unitInfo.unitAttackManager.skillCurrent.EndSkill(true); } // Si está atacando se espera a que haya realizado el ataque para moverse (delay) if (!GameControl.control.testsettings.bCancelAttackAnimationMode && !InputControl.control.bCancelInputGet && unitInfo.unitAttackManager != null) { UnitAttack unitattack = unitInfo.unitAttackManager.unitAttackCurrent; if (unitattack != null && !unitattack.AttackFinished() && (unitattack.skillScript == null || !unitattack.skillScript.CanMoveSkill())) { // Dejar comentado este log si no se usa porque genera allocations y boxing //Debug.Log("Cancel MoveToDelayed " + unitattack.iNumberOfAttacks + " " + unitattack.iNumberOfAttacksDone + " " + unitInfo.unitAI.gameObject.name); // FIXME: Marcos: 2020-02-09: He quitado este arreglo no recuerdo porque, revisar lo de hacer ataques mientras pulsas sin parar la C para ver si sigue pasando // FIXME: Marcos: 2020-02-17: He vuelto a poner este arreglo porque lo de la C mientras atacas sigue dejando bloqueadas a las unidades if (unitInfo.unitAnimations.IsInState("Idle")) { unitInfo.unitAttackManager.StopAttack(); } else { return bRetorn; } } } // squad = moveToClass.squad; // if (squad != null) // { // // } unitInfo.unitAI.unitActionControl.bIsDirectlyControlledByThePlayer = moveToClass.bControlledDirectly; unitInfo.unitAI.unitActionControl.bSilvarCancelAggro = false; bRestoringPosition = true; // VARIABLES DE CONTROL bool bCanHaveRival = CanHaveRival(); bool bCanMove = CanMove(); bool bGotoObjectivePos = true; // Se puede mover (FIXME: Esto ya no lo hace:) y no pulsamos en el mismo objetivo otra vez mientras se esta moviendo hacia el if (bCanMove) { //Para poder moverse hacia mi rival si éste está detrás de una barrera de astar (rocas etc) // bCantGetThereAttacking = false; // Calcula la posicion dentro del objecto mas cercana, si no se hace asi la unidad puede decidir // que el punto mas cercano es al otro lado del objetivo cuando se hace el SamplePosition UnitAI unitAIToPass = null; if (moveToClass.interactiveRadiusObjective != null && moveToClass.interactiveRadiusObjective.unitInfo != null) { unitAIToPass = moveToClass.interactiveRadiusObjective.unitInfo.unitAI; } //bool bAttackHouse = !(moveToClass.mode == MoveToClass.MoveToMode.Default && unitAIToPass != null && unitAIToPass.unitInfo.obstacle != null && unitAIToPass.unitInfo.obstacle.bHasDoor); bGotoObjectivePos = CalculateMoveToObjectPositionWithOffset(unitAIToPass, ref moveToClass.v3Pos, moveToClass); } // Hacemos esto aqui para que calcule antes el bGotoObjectivePos? if (unitInfo.unitAI.GetMe().bUnmovable) { return false; } // Calculate correct distance // Si la distancia a la posicion objetivo es muy pequeña, no ir // Si tengo rival y esta en scope bool bCorrectDist = (moveToClass.v3Pos != default(Vector3) && (unitInfo.unitAI.transformAI.position - moveToClass.v3Pos).sqrMagnitude > UnitMovement.FMOVETODIST * UnitMovement.FMOVETODIST) || !HasArrivedLeader(unitInfo.unitAI.myRival); // Si se puede mover if (bCanMove && bCorrectDist) { unitInfo.unitAI.MoveToClassCurrentNewMoveTo(moveToClass); // Stop pursuing current rival, porque alguien le ha mandado moverse y despues alguien le pondra otro (o el mismo) rival unitInfo.unitAI.StopPursuingRival(true); // Dejar de seguir a la unidad que estemos siguiendo if (unitInfo.unitAI.unitActionControl.bIsDirectlyControlledByThePlayer) { unitInfo.unitAI.StopPursuingFollowee(true); } } // Le ponemos el rival y se decide si hay que ir al rival, a un dialogo, a una puerta de casa o a suelo // 0 [Suelo], 1 [Attack], 2 [Dialogue], 3 [Aliado] //unitInfo.unitAI.SetRivalAndMoveToClass(moveToClass, bCanMove); unitInfo.unitAI.SetRivalAndMoveToClass(moveToClass, CanMove(false)); // Si se va a hablar y no es Kofi (que solo vaya Kofi hasta hablar) if (moveToClass.iMovingToState == 2 && SceneControl.control.rtsUnitMercebinary1 != unitInfo.unitAI.GetMe()) { // Debug.Log("No puede hablar " + unitInfo.unitAI.gameObject.name); moveToClass.iMovingToState = 0; moveToClass.mode = MoveToClass.MoveToMode.OnlyWalk; moveToClass.fScopeOverride = 2f; // Que se queden a 3 de distancia } // Miramos si vamos a caminar para cambiar la speed if ((moveToClass.bWalkNotRun && !bWalkNotRun) || (!moveToClass.bWalkNotRun && bWalkNotRun)) { bWalkNotRun = moveToClass.bWalkNotRun; unitInfo.unitAI.GetMe().SetUnitSpeed(); } // Calculate scope // Caso especial en en que se queda cerca a pesar de haber goObjectiveAI bool bSpecialSkillFallTraps = false; if (moveToClass.interactiveRadiusObjective != null) { Trap trap = moveToClass.interactiveRadiusObjective.unitInfo.trap; if (trap != null && !trap.Attackable(unitInfo.unitAI, moveToClass)) bSpecialSkillFallTraps = unitInfo.unitSpecialSkill.SpecialSkillCanFallTrap(trap, true); } if (moveToClass.goObjectiveAI == null || bSpecialSkillFallTraps || moveToClass.mode == MoveToClass.MoveToMode.OnlyWalk || !bGotoObjectivePos) { // Scope de caminar a un suelo (no a una unidad!) fScopeCurrent = 0.01f; //Debug.Log(unitInfo.unitAI.name + " fScopeCurrent111 " + fScopeCurrent); if (moveToClass.fScopeOverride > 0) { // Se le pasa manualmente el scope (no se usa esto?) fScopeCurrent = moveToClass.fScopeOverride; //Debug.Log(unitInfo.unitAI.name + " fScopeCurrent222 " + fScopeCurrent); } } else { // Distancia a la que se queda del punto if (moveToClass.fScopeOverride >= 0) { // Se le pasa manualmente el scope (no se usa esto?) fScopeCurrent = moveToClass.fScopeOverride; //Debug.Log(unitInfo.unitAI.name + " fScopeCurrent333 " + fScopeCurrent); if (fScopeCurrent == 0) { fScopeCurrent = unitInfo.unitAI.GetUnitCurrentScope(moveToClass, true); //Debug.Log(unitInfo.unitAI.name + " fScopeCurrent444 " + fScopeCurrent); } } else { // Scope de caminar a una unidad bool bIgnoreScopeNoMelee = (moveToClass.iMovingToState == 2 || moveToClass.iMovingToState == 3); // El scope tiene que ser sin contar distancia (de arqueros o gente con fScope > 0) bool bGoingToInteract = unitInfo.unitAI.AmIGoingToInteractSpecial(moveToClass.interactiveRadiusObjective, bIgnoreScopeNoMelee); // bool bNoKamikaze = (moveToClass.iMovingToState == 1 && unitInfo.unitAI.GetMe().rtsAttackDefault.bKamikaze); if (!bGoingToInteract) // && bNoKamikaze) { fScopeCurrent = unitInfo.unitAI.GetUnitCurrentScope(moveToClass, bIgnoreScopeNoMelee); //Debug.Log(unitInfo.unitAI.name + " fScopeCurrent555 " + fScopeCurrent); } else { fScopeCurrent = unitInfo.interactiveRadius.fRadius; //Debug.Log(unitInfo.unitAI.name + " fScopeCurrent666 " + fScopeCurrent); } } } // if (unitInfo.unitAI.name == "Kofi") // { // Debug.Log("fScopeCurrent "+fScopeCurrent); // } // Rival onScope unitInfo.unitAI.isMyRivalOnScope = unitInfo.unitAI.IsMyRivalOnScopeOrSight(fScopeCurrent); // TODO: Esto FALLA seguro porque el fScopeCurrent no lo ha precalculado bien, puede valer 0.01f bool bCorrectDist2 = (unitInfo.unitAI.RivalIsNull() || !unitInfo.unitAI.isMyRivalOnScope); // Finalmente si se puede mover bool bIfConditionCalculatePath = bCanMove && (moveToClass.goObjectiveAI == null || bCanHaveRival); // Debug.Log(bIfConditionCalculatePath + " " + bCorrectDist + " " + bCorrectDist2 + " " + fScopeCurrent + " " + moveToClass.iMovingToState + " " + moveToClass.fScopeOverride + " " + moveToClass.v3Pos + " " + Vector3.Distance (unitInfo.unitAI.transform.position, moveToClass.v3Pos) ); bool bEntraDefaultMove = false; if (bIfConditionCalculatePath && bCorrectDist && bCorrectDist2 && !unitInfo.interactiveRadius.bHoldPosAttack && monturaManager != null && !monturaManager.IsRiding()) //&& (unitInfo.unitAI.bMovingToHoldPosition || !unitInfo.unitAI.bWantsToHoldPosition)) { // SetDestination SetDestinationLocalTransform(moveToClass.v3Pos, null, true); // El setmoving decide el state al que ir unitInfo.unitAI.SetMovingMoveTo(moveToClass, moveToClass.iMovingToState); bIsMoving = true; //Debug.Log($"{unitInfo.unitAI.gameObject.name} MoveTo DONE"); // Ahora todo se hace dentro de esta funcion y en el Delegate que llama al acabar de calcular el path CalculatePath(v3Destination); unitInfo.unitAI.hasArrived = unitInfo.unitMovement.HasArrived(); if (!unitInfo.unitAI.hasArrived) { // Debug.Log("MoveToDelayed hasArrived stop"); bEntraDefaultMove = true; } bRetorn = true; } if (!bEntraDefaultMove) { if (bIfConditionCalculatePath) { //si le has enviado super cerca de donde ya esta... SetDestinationLocalTransform(unitInfo.unitAI.transformAI.position, null, true); // El setmoving decide el state al que ir unitInfo.unitAI.SetMovingMoveTo(moveToClass, moveToClass.iMovingToState); // bIsMoving = false; //Debug.Log($"{unitInfo.unitAI.gameObject.name} MoveToStop 1"); bRetorn = true; } else { // El movimiento no se puede realizar (delay) bIsMoving = false; bRetorn = false; //Debug.Log($"{unitInfo.unitAI.gameObject.name} MoveToStop 2"); } } bool bHasToDoInteractionCantMove = !bCanMove && !moveToClass.bHasDoneInteractionCantMove; if (bRetorn || bHasToDoInteractionCantMove) { if (bHasToDoInteractionCantMove) moveToClass.bHasDoneInteractionCantMove = true; // Permitir el cambio de estado que el MovingToDialogue prohibe // unitInfo.unitAI.bAllowStateChange = true; // Callback de special skill al iniciar el moveTo unitInfo.unitSpecialSkill.MoveToAndCheckSpecialSkillMode(moveToClass.goObjectiveAI); if (moveToClass.goObjectiveAI != null) { InteractiveRadius interactiveRadiusObjectiveAI = moveToClass.interactiveRadiusObjective; if (interactiveRadiusObjectiveAI.IDCinematica != null) { bool bWillStop = false; List cccInteraccionList = GameControl.control.cinematicsControl.GetInteraccion(typeof(CCCondicionInteraccion), interactiveRadiusObjectiveAI.IDCinematica, unitInfo.interactiveRadius.IDCinematica, new Type[] { typeof(CCCondicionInteraccionAtaque), typeof(CCCondicionPuerta) }); int iCount = cccInteraccionList.Count; for (int i = 0; i < iCount; i++) { Type typeI = cccInteraccionList[i].GetType(); if (typeI == typeof(CCCondicionInteraccion)) { ((CCCondicionInteraccion)cccInteraccionList[i]).VienenHaciaMi(moveToClass.mode, unitInfo.unitAI); } else if (bCanMove && typeI == typeof(CCCondicionInteraccionAtaque) && moveToClass.AmIGoingToAttack()) { bWillStop = bWillStop || !((CCCondicionInteraccionAtaque)cccInteraccionList[i]).CanProceedWithAttack(true, unitInfo.unitAI.GetMe(), moveToClass.specialSkill); } else if (typeI == typeof(CCCondicionPuerta)) { ((CCCondicionPuerta)cccInteraccionList[i]).VienenHaciaMi(); } } if (bWillStop) { unitInfo.unitAI.StopPursuingRival(true); Stop(); } } } } //Debug.Log(unitInfo.unitAI.name + " MoveTo"); return bRetorn; } private void UpdateMoveTo() { UpdateMoveToDelayed(); //if (rbRigidBody != null && !unitInfo.unitAI.IsTower() && !unitInfo.unitAI.IsObstacle() && !unitInfo.unitAI.IsMuro()) if (!unitInfo.unitAI.IsTower() && !unitInfo.unitAI.IsObstacle() && !unitInfo.unitAI.IsMuro()) { // Run animation handler // float fVelocitySqrMagnitude = 0.2f * 0.2f; // Esto significa que comparamos la velocidad con 0.2f bool bIsMovingAux = (IsMoving() && steering.IsFollowingPath() || bIsMovingGamepad); bool bStill = false; // !bIsMovingPathComplete; //false; //rbRigidBody.velocity.sqrMagnitude <= fVelocitySqrMagnitude; // TODO: Poner la speed de la animacion de Run a un numero entre 2 valores (min, max) bool bAlive = !unitInfo.unitAI.GetMe().IsDead(); bool bIsInStateRun = unitInfo.unitAnimations.IsInState("Run") || unitInfo.unitAnimations.IsInState("Run2"); bool bIsInStateWalk = unitInfo.unitAnimations.IsInState("Walk"); bool bUseFirstAnimation = (rtsUnitEscapeFrom == null); // Definimos la variable Speed de las animaciones float fMinSpeed = 0.3f; if (bIsMovingAux && !bStill && bAlive) { Vector3 v3CurrentVelocity = steering.v3Velocity; //rbRigidBody.velocity; v3CurrentVelocity.y = 0; float fVelocityMagnitude = v3CurrentVelocity.magnitude; //Mathf.Max(v3CurrentVelocity.magnitude, v3JoystickMovementSpeedForAnimation.magnitude); // TODO: No se si es correcto: (currentVelocity) / (unit velocity + equipment - buffs - tiempo cracker) float fSpeedTanPerOne = fVelocityMagnitude / UnitRTSUnit.BaldosasPorSegundo(unitInfo.unitAI.GetMe().fSpeedWithoutTimeFactorWithoutBuffs); fSpeedTanPerOne = Mathf.Max(fSpeedTanPerOne, fMinSpeed); // Esto es para que en tiempo parado no caminen lento if (Time.timeScale < 0.001f) fSpeedTanPerOne = 1f; // Debug.Log("fVelocityMagnitude: " + fVelocityMagnitude); unitInfo.unitAnimations.SetSpeed(fSpeedTanPerOne); } else { // TODO: No se si es correcto: (unit velocity + equipment - tiempo cracker) / (unit velocity + equipment - buffs - tiempo cracker) float fSpeedTanPerOne = unitInfo.unitAI.GetMe().fSpeedWithoutTimeFactor / unitInfo.unitAI.GetMe().fSpeedWithoutTimeFactorWithoutBuffs; fSpeedTanPerOne = Mathf.Max(fSpeedTanPerOne, fMinSpeed); unitInfo.unitAnimations.SetSpeed(fSpeedTanPerOne); } // Indicamos el estado Run o el final del estado Run string sNameState = "Idle"; // TODO Puede molestar por setear cada vez el estado de animación if (bIsMovingAux && !bStill && (!(bIsInStateRun && !bWalkNotRun) || !(bIsInStateWalk && bWalkNotRun)) && bAlive) { sNameState = "Run"; if (!bUseFirstAnimation && unitInfo.unitAnimations.HasState("Run2", unitInfo.unitAnimations.animator)) { sNameState = "Run2"; } if (bWalkNotRun && unitInfo.unitAnimations.HasState("Walk", unitInfo.unitAnimations.animator)) { sNameState = "Walk"; } unitInfo.unitAnimations.SetAnimation(sNameState, 0.05f); } else if ((!bIsMovingAux || bStill) && (bIsInStateRun || bIsInStateWalk) && bAlive) { if (unitInfo.unitAI.myRival != null && unitInfo.unitAnimations.HasState("IdleAttack")) { sNameState = "IdleAttack"; } unitInfo.unitAnimations.SetAnimation(sNameState, 0.15f); } } } // Calcula la posicion dentro del carve mas cercana, no el centro del obstacle (torre/obstaculo) // Si no se hace asi, la posicion objetivo del Sample position puede dar al otro lado del obstacle public bool CalculateMoveToObjectPositionWithOffset(UnitAI unitAIObjective, ref Vector3 v3pos, MoveToClass moveToClass) { bool bGotoObjectivePos = true; if (unitAIObjective != null) { v3pos = unitAIObjective.transform.position; // TODO Esto mas adelante se deberia hacer con el ultimo corner del path calculado // TODO: Esto tiene en cuenta que siempre se hace el recalcular //float fRadius = unitAIObjective.unitInfo.interactiveRadius.fRadius; //v3pos += (unitInfo.unitAI.transformAI.position - v3pos).normalized * fRadius; // Calcula la posicion de la puerta de la casa bool bGoingToNPC = GoingToNPC(unitAIObjective); bool bGoingToDoor = GoingToHouse(unitAIObjective); bool bWillMoveToDialogue = (bGoingToNPC || bGoingToDoor) && (moveToClass == null || moveToClass.mode != MoveToClass.MoveToMode.Alternative); TrapHole.HoleState holeState = TrapHole.HoleState.Normal; if (unitInfo.unitAI.AmIGoingToInteractSpecial(unitAIObjective.unitInfo.interactiveRadius, bWillMoveToDialogue)) { Vector3 v3offsetWargo = Vector3.zero; //if (unitInfo.unitAI.me.bWargo) //{ // v3offsetWargo = Vector3.Normalize(unitAIObjective.unitInfo.interactiveRadius.v3ZonaInteraccionSelected); // Si es Wargo va a un punto 1 tile más alejado //} v3pos = unitAIObjective.transform.position + unitAIObjective.unitInfo.interactiveRadius.v3ZonaInteraccionSelected + v3offsetWargo; bool bReturnFalse = (moveToClass == null || moveToClass.mode != MoveToClass.MoveToMode.AlwaysAttack || unitInfo.unitAI.GetMe().fScope != 0); if (bReturnFalse) bGotoObjectivePos = false; //Debug.Log("v3ZonaInteraccion1 " + v3pos); //Debug.DrawLine(v3pos, v3pos + Vector3.up, Color.white, 5); } else if (unitAIObjective.unitInfo.trap != null && unitAIObjective.unitInfo.trap.trapScript.IsHole(ref holeState)) { v3pos = unitAIObjective.transform.position; } // else if (bGoingToNPC) // { // InteractiveRadius interactiveRadius = unitAIObjective.unitInfo.interactiveRadius; // if (interactiveRadius != null) // { // v3pos = unitAIObjective.transform.position + interactiveRadius.v3ZonaInteraccion; // bGotoObjectivePos = false; // } // } } Vector3 v3hit; if (SamplePosition(v3pos, out v3hit, 10, false, AstarKofiExtension.UnsafeWaypointGroundTags)) // TODO: Aqui hay que poner los tags de mi tipo de unidad, por ahora solo esta el Unsafe este de la shore { v3pos = v3hit; } return bGotoObjectivePos; } public bool GoingToNPC(UnitAI destinoUnitAi) { return (destinoUnitAi != null && destinoUnitAi.bIsNPC); // TODO: Cambiar esto para poder atacar a npcs, habrá que indicar con una nueva variable en el MoveTo si es acción principal o secundaria. } public bool GoingToHouse(UnitAI destinoUnitAi) { return (destinoUnitAi != null && destinoUnitAi.unitInfo.obstacle != null && destinoUnitAi.unitInfo.obstacle.bHasDoor); } public void LookAt(Vector3 v3pos) { if (!unitInfo.unitAI.unitActionControl.bCineCanLookAt) { return; } //Debug.Log("LookAt: "+unitInfo.unitAI.name + v3pos); unitInfo.unitAI.transformAI.LookAt(v3pos); unitInfo.unitAI.transformAI.rotation = Quaternion.Euler(0, unitInfo.unitAI.transformAI.rotation.eulerAngles.y, 0); //Quaternion.Euler(new Vector3(0, unitInfo.unitAI.transformAI.rotation.eulerAngles.y, 0)); unitInfo.billboard.DoBillboard(); // Debug.Log("LookAt"); } public void SetRotation(Quaternion newrotation) { if (!unitInfo.unitAI.unitActionControl.bCineCanLookAt) { return; } unitInfo.unitAI.transformAI.rotation = newrotation; unitInfo.billboard.DoBillboard(); } //public void SetRotation(Quaternion newrotation) //{ // unitInfo.unitAI.transformAI.rotation = newrotation; // //unitInfo.billboard.DoBillboard(); // Ya lo deberia hacer el UpdateFacingRightOrLeft // UpdateFacingRightOrLeft(); //} // Returns the unit to the correct position public void RestorePosition(GameObject goObjectiveAI = null) { if ((Vector3.Distance(v3Destination, unitInfo.unitAI.transformAI.position) > 0.5f && !IsMoving() && TestNobodyInPosition(v3Destination)) || !bGrounded) { MoveTo(goObjectiveAI, v3Destination); bRestoringPosition = true; } else if (!IsMoving()) { bRestoringPosition = false; } } public List GetListPositionsArroundGameObject(GameObject go, Vector3 v3Direction, float fDiv, float fDistance, float fExtraDistance = 0) { List v3Listi = new List(); float fAngle = 360 / fDiv; // Order i: 0, 45, 90, 135, 180, 225, 270, 315, 360 for (int i = 0; i < fDiv + 1; i++) { Vector3 v3CalculatedPosition; Vector3 v3CalculatedDirection; v3CalculatedPosition = RotatePointAroundPivot(go.transform.position + (v3Direction * fDistance), go.transform.position, Quaternion.Euler(0, i * fAngle, 0)); v3CalculatedDirection = (v3CalculatedPosition - go.transform.position).normalized; v3CalculatedPosition = go.transform.position + (v3CalculatedDirection * (fDistance + fExtraDistance)); v3Listi.Add(v3CalculatedPosition); } return v3Listi; } /** * Updates the animation after using MoveTo * It switches between the Run and Idle animations */ [Header("MoveTo")] public bool bCalculatingPath = false; // float fAvoidanceCounter; /** * Updates the destination after using MoveTo * If the destination is another unit, this function calculates the closest position and changes the current agent destination */ public enum RecalculateRouteToTarget { Error0 = 0, ErrorMeNotMovingOrMeRecalculatingPath1 = 1, Correct2 = 2, RivalMoving3 = 3, RivalNotBloqued4 = 4, NoRival5 = 5, RivalOverwhelmed6 = 6, IAmArcher7 = 7, DifferentRival8 = 8, CantReachRival9 = 9 } public RecalculateRouteToTarget UpdateRecalculateRouteToTarget(out Vector3 v3PosReturn) { RecalculateRouteToTarget recalculateRouteToTarget = RecalculateRouteToTarget.Error0; v3PosReturn = default(Vector3); bool bIgnoreThis = false; // TODO: Hacer este calculo cada X segundos, no todas las partes deben ser a X segundos, vigilar! // Recalcula el recorrido al atacar a un objetivo bool bRivalIsMoving = false; bool bCantReachRival = false; UnitAI unitAIMyRival = (unitInfo.unitAI.myRival != null) ? unitInfo.unitAI.myRival.unitAI : null; if (!unitInfo.unitAI.RivalIsNull() && unitAIMyRival.unitInfo.unitAttackManager != null) { bRivalIsMoving = unitAIMyRival.unitInfo.unitMovement.IsMoving() || unitAIMyRival.unitInfo.unitMovement.IsInAir(); float fDistanceCompare = fScopeCurrent + FMOVETODIST; bCantReachRival = ((unitAIMyRival.transformAI.position - v3Destination).sqrMagnitude > fDistanceCompare * fDistanceCompare); } // print("OK " + bIsMoving + " " + bRecalculatingPath + " " + unitInfo.unitAI.myRival + " " + bRivalIsMoving); if ((IsMoving() || bCalculatingPath) && !unitInfo.unitAI.RivalIsNull() && !bRivalIsMoving && !bCantReachRival) { Vector3 v3RivalPos = unitAIMyRival.transform.position; if (unitInfo.unitAI.AmIGoingToInteractSpecial(unitAIMyRival.unitInfo.interactiveRadius)) { // v3RivalPos += unitAIMyRival.unitInfo.interactiveRadius.v3ZonaInteraccionSelected; // v3PosReturn = v3RivalPos; // bIgnoreThis = true; // // Debug.Log("v3ZonaInteraccion1 " + v3PosReturn); // Debug.DrawLine(v3PosReturn, v3PosReturn + Vector3.up, Color.red); recalculateRouteToTarget = RecalculateRouteToTarget.RivalNotBloqued4; } else { // bool bLastCorner = steering.IsInTheLastCorner(); bool bMeArcher = unitInfo.unitAI.GetMe().fScope > 0; bool isMyRivalOnSight = unitInfo.unitAI.IsMyRivalOnScopeOrSight(unitInfo.unitAI.GetMe().fSight); UnitRTSUnit prevRival = unitInfo.unitAI.myRival; bool bTestArriveToTarget = isMyRivalOnSight && TestArriveToTarget(unitInfo.unitAI.myRival, true); bool bDifferentRival = prevRival != unitInfo.unitAI.myRival; //Debug.Log(!bTestArriveToTarget + " " + !bMeArcher + " " + !bDifferentRival); if (!bTestArriveToTarget && !bMeArcher && !bDifferentRival) { recalculateRouteToTarget = RecalculateRouteToTarget.RivalOverwhelmed6; float fCorrectedScope = unitInfo.unitAI.ScopeMelee(unitAIMyRival, unitAIMyRival.unitInfo.interactiveRadius); Vector3 v3RivalMeDir = (unitInfo.unitAI.transformAI.position - v3RivalPos).normalized; float fDivisions = Mathf.Clamp(unitInfo.unitAI.myRival.unitAI.unitInfo.interactiveRadius.fRadius * 30f, 10f, 30f); //30; // FIXME: Hacer esta var dependiente del diametro de la unit objetivo no? //////////////////////////////////////////////////////////////////////////////////////////////////////// // Cogemos el angulo mas cercano al de nuestra posicion inicial float fMin = 1000000; float fResultingAngle = 0; float fAngle = 360 / fDivisions; // Order i: 0, 45, 90, 135, 180, 225, 270, 315, 360 for (int i = 0; i < fDivisions + 1; i++) { float angle = AngleSigned(Vector3.forward, v3RivalMeDir, Vector3.up); if (angle < 0) angle = 360 + angle; float dist = Mathf.Abs((fAngle * i) - angle); if (dist < fMin) { fMin = dist; fResultingAngle = fAngle * i; } } if (fMin != 1000000) { // Result v3RivalMeDir = Quaternion.Euler(0, fResultingAngle, 0) * Vector3.forward; } else { Debug.LogError("ERRRRRROR"); } //////////////////////////////////////////////////////////////////////////////////////////////////////// // GetListPositionsArroundGameObject //List v3ListEnd = new List(); List v3Listi = GetListPositionsArroundGameObject(unitInfo.unitAI.myRival.goCharacter, v3RivalMeDir, fDivisions, fCorrectedScope); //unitInfo.unitAI.myRival.unitAI.unitInfo.interactiveRadius.fRadius); //, unitInfo.unitAI.GetMe().fInteractiveRadius * 1.5f); List v3Listj = new List(); // Reverse: v3Listj = v3Listi.Reverse(); int iListCount = v3Listi.Count; for (int i = iListCount - 1; i > -1; i--) { v3Listj.Add(v3Listi[i]); } iListCount = v3Listi.Count; // Order i: 0, 45, 90, 135, 180, 225, 270, 315, 360 for (int i = 0; i < iListCount; i++) { Vector3 v3CalculatedDirection; bool bTestArriveToPositioni, bTestArriveToPositionj; bool bSamplePositioni, bSamplePositionj; UnitAI unitAIout = null; Vector3 hit; v3CalculatedDirection = (v3Listi[i] - v3RivalPos).normalized; bTestArriveToPositioni = TestArriveToPosition(unitInfo.unitAI.myRival, v3RivalPos + (v3CalculatedDirection * fCorrectedScope), out unitAIout, unitInfo.unitAI.GetMe(), true); bSamplePositioni = SamplePosition(v3Listi[i], out hit, 0.1f);//, 0.4f, false, AstarKofiExtension.UnsafeWaypointGroundTags); if (bSamplePositioni) v3Listi[i] = hit; else v3Listi[i] = default(Vector3); bTestArriveToPositioni = bTestArriveToPositioni && bSamplePositioni; v3CalculatedDirection = (v3Listj[i] - v3RivalPos).normalized; bTestArriveToPositionj = TestArriveToPosition(unitInfo.unitAI.myRival, v3RivalPos + (v3CalculatedDirection * fCorrectedScope), out unitAIout, unitInfo.unitAI.GetMe(), true); bSamplePositionj = SamplePosition(v3Listj[i], out hit, 0.1f);//, false, AstarKofiExtension.UnsafeWaypointGroundTags); if (bSamplePositionj) v3Listj[i] = hit; else v3Listj[i] = default(Vector3); bTestArriveToPositionj = bTestArriveToPositionj && bSamplePositionj; /// Select between v3Listi (right?) or v3Listj (left?) /// if ((bTestArriveToPositioni || bTestArriveToPositionj)) { bool bClockWise = false; if (bTestArriveToPositioni && bTestArriveToPositionj) { float random = UnityEngine.Random.Range(0, 2); if (random == 0) bClockWise = true; else bClockWise = false; } else if (bTestArriveToPositioni) { bClockWise = true; } else if (bTestArriveToPositionj) { bClockWise = false; } if (bIgnoreThis) break; if (bClockWise) { v3PosReturn = v3Listi[i]; } else { v3PosReturn = v3Listj[i]; } //Debug.DrawLine(v3PosReturn, v3PosReturn + new Vector3(0, 2, 0), Color.cyan, 0.7f); recalculateRouteToTarget = RecalculateRouteToTarget.Correct2; break; } } } else if (bDifferentRival) { recalculateRouteToTarget = RecalculateRouteToTarget.DifferentRival8; } else if (bMeArcher) { recalculateRouteToTarget = RecalculateRouteToTarget.IAmArcher7; } else { recalculateRouteToTarget = RecalculateRouteToTarget.RivalNotBloqued4; } } } else if (!unitInfo.unitAI.RivalIsNull()) { if (bRivalIsMoving) { recalculateRouteToTarget = RecalculateRouteToTarget.RivalMoving3; } else if (bCantReachRival) { recalculateRouteToTarget = RecalculateRouteToTarget.CantReachRival9; } else { recalculateRouteToTarget = RecalculateRouteToTarget.ErrorMeNotMovingOrMeRecalculatingPath1; } } else if (unitInfo.unitAI.RivalIsNull()) { recalculateRouteToTarget = RecalculateRouteToTarget.NoRival5; } return recalculateRouteToTarget; } /******************************************************************************************** ****************************** A* PATHFINDING FUNCTIONS ************************************ *******************************************************************************************/ static NNConstraint nnConstraint = new NNConstraint(); public static bool SamplePosition(Vector3 v3pos, out Vector3 v3posOut, float maxDistance, bool bPenalty = false, int iMaskForbid = 0) { bool bReturn = false; v3posOut = v3pos; if (AstarPath.active) { //TODO ver si la pos es roja y cambiarla por la verde más cercana: esto está en unitpenalty.cs o nnConstraint.constrainTags = true; nnConstraint.tags = ~iMaskForbid; nnConstraint.bPenalty = bPenalty; NNInfo nearest = AstarPath.active.GetNearest(v3pos, nnConstraint, bPenalty); v3posOut = nearest.position; if (v3posOut != Vector3.zero) { bReturn = ((v3pos - v3posOut).sqrMagnitude <= maxDistance * maxDistance); } else { bReturn = false; } int iTag = 0; if (nearest.node != null) { iTag = ((int)nearest.node.Tag); } if (iMaskForbid != 0 && (iMaskForbid & (1 << iTag)) != 0) { bReturn = false; } } return bReturn; } // float fTestTime = 0; // Recaulculate path in seconds public void ReCalculatePathDelay(bool bRecalculate = true) { if (bRecalculate && !GameControl.control.bSceneChangeOnlyTransitioning) { // Start timer Recalculate float rand = UnityEngine.Random.Range(UnitMovement.fRECALCULATEFREQ - (UnitMovement.fRECALCULATEFREQ * 0.5f), UnitMovement.fRECALCULATEFREQ + (UnitMovement.fRECALCULATEFREQ * 0.5f)); // fReCalculateFreq; fRecalculatePathCooldown = rand; // Debug.Log("TimeStart " + fRecalculatePathCooldown); // fTestTime = Time.time; } else { bCalculatingPath = false; fRecalculatePathCooldown = -1; } } private float fRecalculatePathCooldown = -1; public void UpdateRecalculate() { if (fRecalculatePathCooldown != -1) { fRecalculatePathCooldown -= TimeController.control.GetDeltaTime(unitInfo.unitAI.GetMe().iHackValue); if (fRecalculatePathCooldown <= 0) { fRecalculatePathCooldown = -1; // Debug.Log("TimeElapsed " + (Time.time - fTestTime)); // Recalculate the path ReCalculatePath(); } } if (squad != null) { if (!HasArrived(unitInfo.unitAI.myRival)) { // TODO: Esto es bastante costoso, no hacerlo cada update unitInfo.unitAI.GetMe().SetUnitSpeed(); } // else // { // squad.RecalculateFollowPoints(unitInfo.unitAI.GetMe()); // Vector3 v3Dest = squad.GetPositionUnit(unitInfo.unitAI.GetMe()); // steering.SetPathFollowingVector3(new List(){ v3Dest }); // } } } public void ReCalculatePath() { Profiler.BeginSample("Recalculate path"); if (unitInfo.unitAI.GetMe().IsDead() || GameControl.control.bSceneChangeOnlyTransitioning) { return; } UnitAI unitAI = unitInfo.unitAI; bool bRecalculatePathStateSpecific = unitAI.currentState.ReCalculatePath(); if (bRecalculatePathStateSpecific) { return; } //Codigo Rival aquí abajo //bool bHasArrived = HasArrived(unitAI.myRival); //bool bHasArrivedLeader = HasArrivedLeader(unitAI.myRival); //bool bHasArrivedDestination = HasArrivedDestinationPartial(); // (v3Destination - unitAI.transform.position).sqrMagnitude >= FMOVETODIST; //bool bIsMyRivalOnScope = unitAI.IsMyRivalOnScopeOrSight(unitAI.GetMe().fScope); //if ((!bHasArrived || !bHasArrivedLeader) && (unitAI.RivalIsNull() || (!bIsMyRivalOnScope && !bHasArrivedDestination))) //{ Vector3 v3Objective = v3Destination; if (squad != null) { // if (!bHasArrived) // { // unitAI.GetMe().SetUnitSpeed(); // } squad.RecalculateFollowPoints(unitAI); v3Objective = squad.GetPositionUnit(unitAI); // Debug.DrawLine(v3Objective, v3Objective + Vector3.up, Color.red, 2); } if (!unitAI.RivalIsNull()) { if (!unitAI.unitActionControl.bIsDirectlyControlledByThePlayer) { // Hay un rival mejor que el que tengo? UnitRTSUnit unitPossibleRival = unitAI.IsThereEnemyCloseby(unitAI.GetMe().fSight, unitAI.myRival); int iWhichIsBetterRival = unitAI.CriteriaForEnemies(unitAI.myRival, unitPossibleRival); // print(unitAI.myRival.rtsUnit.sName + " " + unitPossibleRival.rtsUnit.sName + " => " + iWhichIsBetterRival); if (iWhichIsBetterRival == 1) { unitAI.ChangeRival(unitPossibleRival, unitAI.myRival); } } // Calcula la posicion dentro del objecto mas cercana, si no se hace asi la unidad puede decidir // que el punto mas cercano es al otro lado del objetivo cuando se hace el SamplePosition CalculateMoveToObjectPositionWithOffset(unitAI.myRival.unitAI, ref v3Objective, unitAI.moveToClassCurrent); } CalculatePath(v3Objective); //} //else //{ // Debug.Log("ERROR"); //} Profiler.EndSample(); } public bool IsPathPossibleSlow(Vector3 v3Finish, Vector3 v3Start = default, int iLayerIgnore = 0) { if (v3Start == default) { v3Start = steering.GetPathFollowingNextPosition(); // unitInfo.unitAI.transform.position } return AstarKofiExtension.IsPathPossibleSlow(v3Finish, v3Start, seeker.traversableTags, iLayerIgnore); // Obtenemos la siguiente posicion en vez de la actual del personaje para tener un poco de margen al recalcular /*if (v3SelectOrigin == default) { v3SelectOrigin = steering.GetPathFollowingNextPosition(); // unitInfo.unitAI.transform.position } List listGraphNode = new List(); NNConstraint nnConstraint = new NNConstraint(); nnConstraint.constrainTags = true; nnConstraint.tags = seeker.traversableTags; GraphNode graphNodeStart = AstarPath.active.GetNearest(v3SelectOrigin, nnConstraint).node; GraphNode graphNodeFinish = AstarPath.active.GetNearest(v3pos, nnConstraint).node; listGraphNode.Add(graphNodeStart); listGraphNode.Add(graphNodeFinish); int iMask = seeker.traversableTags; if (iLayerIgnore != 0) { iMask = iMask | iLayerIgnore; } bool bPathPossibleSlow = PathUtilities.IsPathPossible(listGraphNode, iMask); // Do not delete the local variable, a print in this line with the result and info on the nodes gives useful information return bPathPossibleSlow;*/ } public bool IsPathPossible(Vector3 v3Finish) { Vector3 v3CurrentPos = steering.GetPathFollowingNextPosition(); return AstarKofiExtension.IsPathPossible(v3Finish, v3CurrentPos); /*GraphNode node1 = AstarPath.active.GetNearest(v3CurrentPos, NNConstraint.Default).node; GraphNode node2 = AstarPath.active.GetNearest(v3pos, NNConstraint.Default).node; return Pathfinding.PathUtilities.IsPathPossible(node1, node2);*/ } public static bool IsPathPossible(Vector3 v3Pos1, Vector3 v3Pos2, int iWalkableTags) { GraphNode node1 = AstarPath.active.GetNearest(v3Pos1, NNConstraint.Default).node; GraphNode node2 = AstarPath.active.GetNearest(v3Pos2, NNConstraint.Default).node; return IsPathPossible(node1, node2, iWalkableTags); } public static bool IsPathPossible(GraphNode node1, GraphNode node2, int iWalkableTags) { List myList = Pathfinding.Util.ListPool.Claim(); myList.Add(node1); myList.Add(node2); bool bIsPathPossible = PathUtilities.IsPathPossible(myList, iWalkableTags); Pathfinding.Util.ListPool.Release(ref myList); return bIsPathPossible; } public bool CalculatePath(Vector3 v3pos, bool bJustTestingPath = false) { if (GameControl.control.bSceneChangeOnlyTransitioning) { ReCalculatePathDelay(false); // Cancel Invoke return false; } // Obtenemos la siguiente posicion en vez de la actual del personaje para tener un poco de margen al recalcular Vector3 v3CurrentPos = steering.GetPathFollowingNextPosition(); // unitInfo.unitAI.transform.position if (bJustTestingPath) { if (!UnitMovement.IsPathPossible(v3CurrentPos, v3pos, seeker.traversableTags)) { //Debug.Log("Entra en CalculatePath false"); return false; } } else { ReCalculatePathDelay(false); // Cancel Invoke Vector3 v3posRecalculated; RecalculateRouteToTarget recalculateRouteToTarget = UpdateRecalculateRouteToTarget(out v3posRecalculated); // Debug.Log(recalculateRouteToTarget); // Como la funcion comprueba si esta overwhelmed, lo ponemos a false y si lo esta, lo ponemos a true if (!unitInfo.unitAI.RivalIsNull()) unitInfo.unitAI.myRival.unitAI.bIsOverwhelmedSurrounded = false; //print(recalculateRouteToTarget); bool bStartPath = true; switch (recalculateRouteToTarget) { case RecalculateRouteToTarget.Error0: bStartPath = false; break; case RecalculateRouteToTarget.ErrorMeNotMovingOrMeRecalculatingPath1: bStartPath = false; break; case RecalculateRouteToTarget.Correct2: // IMPORTANT //Debug.Log("CalculatePath " + v3posRecalculated); SetDestinationLocalTransform(v3posRecalculated, transformDestination, true); v3pos = v3Destination; break; case RecalculateRouteToTarget.RivalMoving3: break; case RecalculateRouteToTarget.RivalNotBloqued4: break; case RecalculateRouteToTarget.NoRival5: // IMPORTANT Default Walking without enemy break; case RecalculateRouteToTarget.RivalOverwhelmed6: // IMPORTANT unitInfo.unitAI.myRival.unitAI.bIsOverwhelmedSurrounded = true; bool isMyRivalOnSight = unitInfo.unitAI.IsMyRivalOnScopeOrSight(unitInfo.unitAI.GetMe().fSight); if (isMyRivalOnSight) { LookAt(unitInfo.unitAI.myRival.unitAI.transformAI.position); bStartPath = false; } break; case RecalculateRouteToTarget.IAmArcher7: // IMPORTANT fScope > 0 break; case RecalculateRouteToTarget.DifferentRival8: bStartPath = false; break; case RecalculateRouteToTarget.CantReachRival9: break; } if (bStartPath) { // Usamos esta variable para que no haga otro StartPath mientras se está calculando el primero, // o al menos para saber que estamos calculando uno ya if (seeker.pathCallback == null) { seeker.pathCallback = OnPathComplete; // Comentario: Esto está hardcodeado para evitar tener que tocar el prefab de las Units seeker.startEndModifier.exactStartPoint = StartEndModifier.Exactness.ClosestOnNode; seeker.startEndModifier.exactEndPoint = StartEndModifier.Exactness.ClosestOnNode; } // v3CurrentPos = unitInfo.unitAI.transform.position + new Vector3(2, 0, 3); // v3pos = new Vector3(6.092f, 0, 0.52f); // Debug.DrawLine(v3CurrentPos, v3CurrentPos + Vector3.up, Color.white, 0.5f); // Debug.DrawLine(v3pos, v3pos + Vector3.up, Color.white, 0.5f); unitInfo.unitAI.unitActionControl.bBreakObstacles = true; StartPath(v3CurrentPos, v3pos, unitInfo.unitAI.unitActionControl.bBreakObstacles); //, OnPathComplete); } else { Stop(true); } } return true; } void StartPath(Vector3 v3CurrentPos, Vector3 v3posEnd, bool bWithBreakableObstacles = false) { if (!GameControl.control.testsettings.bTraverseBreakableObstacles) bWithBreakableObstacles = false; int iTraversableTagsPrev = seeker.traversableTags; int iTraverseObstacle = seeker.traversableTags | (1 << AstarKofiExtension.iObstacleBreakable) | (1 << AstarKofiExtension.iObstacleBreakableFantasma); //GameControl.control.astarAsset.iObstacleBreakableTag; // For ghosts // if (iTraversableTagsPrev == iTraverseObstacle) bGhostsThroughObstacles = true; // if (bGhostsThroughObstacles) Debug.Log("bGhostsThroughObstacles: To be used later, this log is to avoid warnings and compile faster"); if (bWithBreakableObstacles) seeker.traversableTags = iTraverseObstacle; // seeker.startEndModifier.mask = seeker.traversableTags; Profiler.BeginSample("Xpath construction"); XPath myXPath = XPath.Construct(v3CurrentPos, v3posEnd); myXPath.calculatePartial = true; //Debug.DrawLine(v3posEnd, v3posEnd + Vector3.up * 4, Color.blue, 1f); PathEndingCondition pathEndingCondition = null; //if (unitInfo.unitAI.myRival != null && unitInfo.unitAI.GetMe().fScope > 0 && fScopeCurrent > 1f) if (fScopeCurrent > 0.1f) { //Debug.Log("fScopeCurrent " + (fScopeCurrent - UnitMovement.FMOVETODIST)); Vector3 v3Aux = v3posEnd; if (unitInfo.unitAI.myRival != null) { v3Aux = unitInfo.unitAI.myRival.unitAI.transformAI.position; } pathEndingCondition = new XPathEndingCondition(myXPath, v3Aux, fScopeCurrent - UnitMovement.FMOVETODIST); // Le pongo un pequeno offset } else { pathEndingCondition = new ABPathEndingCondition(myXPath); } myXPath.endingCondition = pathEndingCondition; //Debug.Log("fMaxDistance = " + xpathEndingCondition.fMaxDistance); Path path = seeker.StartPath(myXPath); // El OnPathComplete ya se le pasa al seeker // Path path = seeker.StartPath(v3CurrentPos, v3posEnd); Profiler.EndSample(); // Le definimos el traversalProvider para quitarle el Penalty del suelo a los fantasmas if (unitInfo.unitSpecialSkill != null) unitInfo.unitSpecialSkill.SetSpecialSkillPathCriteria(path); seeker.traversableTags = iTraversableTagsPrev; bCalculatingPath = true; } void OnPathComplete(Path p) { // Si a medio StartPath decidimos parar, esto valdrá false y el personaje se quedará quieto if (bCalculatingPath) { bCalculatingPath = false; p.Claim(this); PathCompleteState pathCompleteState = p.CompleteState; if (p.path.Count == 0 || ((p.error || pathCompleteState == PathCompleteState.Error || pathCompleteState == PathCompleteState.NotCalculated) && (unitInfo.unitAI.moveToClassCurrent == null || !unitInfo.unitAI.moveToClassCurrent.bSkipObstacleBody))) { if (!steering.IsFollowingPath()) { Stop(); // Puede que si hay error se quede caminando quieto } else { ReCalculatePathDelay(true); } p.Release(this); return; } // Control de error por si el path es correcto pero vacio if (p.vectorPath.Count == 0 && (pathCompleteState == PathCompleteState.Partial || pathCompleteState == PathCompleteState.Complete)) { p.vectorPath.Add(unitInfo.unitAI.transformAI.position); } bool bCallSteering = true; bool bCallRecalculate = true; // 2020-03-11: Metodo de seguridad por si el path final es un count de 2 con las 2 posiciones muy cerca int iCount = p.vectorPath.Count; if (pathCompleteState == PathCompleteState.Partial && iCount <= 2) { bool bEqual = true; Vector3 v3UnitPos = unitInfo.unitAI.transformAI.position; for (int i = 0; i < iCount && bEqual; i++) { bEqual = bEqual && ((v3UnitPos - p.vectorPath[i]).sqrMagnitude <= UnitMovement.FMOVETODIST * UnitMovement.FMOVETODIST); } if (bEqual) { bCallSteering = false; bCallRecalculate = false; } } // Esto es para el ingeniero y las torres if (unitInfo.unitAI.moveToClassCurrent != null && unitInfo.unitAI.moveToClassCurrent.bSkipObstacleBody) { p.vectorPath.Add(unitInfo.unitAI.moveToClassCurrent.v3PosPrevious); if (p.vectorPath.Count == 1) { p.vectorPath.Insert(0, unitInfo.unitAI.transformAI.position); } bCallRecalculate = false; } // Las unidades no controladas no van al objetivo si no pueden llegar. Los enemigos no se acercan a las vallas. unitInfo.unitAI.moveToClassCurrent.bPathPartialNotControlledByPlayer = (!unitInfo.unitAI.unitActionControl.bIsDirectlyControlledByThePlayer && pathCompleteState == PathCompleteState.Partial); if (unitInfo.unitAI.moveToClassCurrent.bPathPartialNotControlledByPlayer) { bCallSteering = false; bCallRecalculate = false; } //////////////////////////// // Set path and destination if (bCallSteering) { steering.SetPathFollowing(p); } else { steering.SetPathFollowing(null); } //PATH COMPLETE bIsMovingPathComplete = false; //bIsMovingPathComplete = !(HasArrived(unitInfo.unitAI.myRival) || HasArrivedDestinationPartialWithSteering(unitInfo.unitAI.myRival)); //true; bIsMovingPathComplete = !(HasArrived(unitInfo.unitAI.myRival) || HasArrivedDestinationPartial(unitInfo.unitAI.myRival)); //true; Vector3 v3LookAt = steering.GetCurrentPathPoint(); if (!float.IsInfinity(v3LookAt.x)) { LookAt(v3LookAt); } ////////////////////////////////////// // Recalculamos el path en X segundos //if (bCallRecalculate) //{ ReCalculatePathDelay(bCallRecalculate); // } p.Release(this); } } /******************************************************************************************** ******************************* SET DESTINATION BLOCK ************************************** *******************************************************************************************/ private Vector3 v3LocalDestination; private Transform transformDestination; public Vector3 v3Destination { get { Vector3 v3Vec = v3LocalDestination; if (transformDestination != null) v3Vec += transformDestination.position; return v3Vec; } } private Vector3 v3LocalDestinationPrev; private Transform transformDestinationPrev; public Vector3 v3DestinationPrev { get { Vector3 v3Vec = v3LocalDestinationPrev; if (transformDestinationPrev != null) v3Vec += transformDestinationPrev.position; return v3Vec; } } public void SetDestinationLocalTransform(Vector3 v3Position, Transform transformGotten = null, bool bSetPositionAfterAronPathfinding = false) { if (transformGotten == null) { Vector3 v3Origin = v3Position + (Vector3.up * 0.05f); RaycastHit rayCastHit; LayerMask layerMaskGrounds = (1 << ClickInfo.iLGround | 1 << ClickInfo.iLWater); if (Physics.Raycast(new Ray(v3Origin, (v3Position - v3Origin).normalized), out rayCastHit, 0.1f, layerMaskGrounds)) // Ground { transformGotten = rayCastHit.transform; // v3Position = transformDestination.position; } } transformDestination = transformGotten; if (transformDestination == null) { // v3LocalDestination = unitInfo.unitAI.transformAI.position; v3LocalDestination = v3Position; } else { v3LocalDestination = v3Position - transformDestination.position; } if (bSetPositionAfterAronPathfinding) { //Debug.Log("SetDestinationLocalTransform " + v3LocalDestination); v3LocalDestinationPrev = v3LocalDestination; transformDestinationPrev = transformGotten; } } ////////////////////////////////////////// /////////// Auxiliar functions /////////// ////////////////////////////////////////// private Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Quaternion angle) { float distance = Vector3.Distance(point, pivot); return pivot + (angle * (point - pivot).normalized * distance); } ///////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////// public bool TestNobodyInPosition(Vector3 v3TargetPos, UnitRTSUnit unitRival = null) { bool bFound = true; RaycastHit hit; // TODO Use these variables, if not, erase them //RaycastHit hit1, hit2; // "5" represents the number of the "UI" layer // "8" represents the number of the "ground" layer // "9" represents the number of the "interactive" layer // "10" represents the number of the "interactiveObjective" layer // "14" represents the number of the "Obstacle" layer // Por si se me olvida, el signo ~ es el de negar LayerMask mask = 1 << ClickInfo.iLInteractive /*| 1 << 10*/ | 1 << 14; //bool bRaycast = Physics.Raycast(v3TargetPos - Vector3.up, Vector3.up, out hit, 2, mask); bool bRaycast = Physics.BoxCast(v3TargetPos - Vector3.up, new Vector3(unitInfo.unitAI.GetMe().rtsUnit.v2DimRadius.x, 1f, unitInfo.unitAI.GetMe().rtsUnit.v2DimRadius.x) * 0.7f, Vector3.up, out hit, Quaternion.identity, 2, mask); if (bRaycast) { UnitAI unitAIhit = hit.transform.GetComponent(); if (unitAIhit != null && unitAIhit != unitInfo.unitAI && unitAIhit.GetMe() != unitRival) { // Hay alguien bFound = false; } } return bFound; } public bool TestArriveToPositionOneRaycast(UnitRTSUnit me, ref UnitAI unitAIOut, Vector3 v3Pos, Vector3 v3dirToTarget, float fdisEnemy, UnitRTSUnit unitRival, int iLayerMask = -1) { float fDebugTime = 0.7f; bool bFound = true; UnitAI unitAIhit; // ObstacleClickCollider obstacleClickCollider; RaycastHit hit; // "5" represents the number of the "UI" layer // "8" represents the number of the "ground" layer // "9" represents the number of the "interactive" layer // "10" represents the number of the "interactiveObjective" layer // "12" represents the number of the "Agujeros" layer // "14" represents the number of the "Obstacle" layer // Por si se me olvida, el signo ~ es el de negar LayerMask mask = 1 << ClickInfo.iLInteractive | 1 << ClickInfo.iLObstacle | 1 << ClickInfo.iLObstacleFantasma | 1 << ClickInfo.iLWall; if (iLayerMask != -1) mask.value = iLayerMask; bool bRaycast = Physics.Raycast(v3Pos, v3dirToTarget, out hit, fdisEnemy, mask); if (bRaycast) { unitAIhit = hit.transform.GetComponent(); // obstacleClickCollider = hit.transform.GetComponent(); if (unitAIhit != null && unitAIhit != me.unitAI && unitAIhit.GetMe() != unitRival && // obstacleClickCollider == null && (unitAIhit.myRival == me || unitAIhit.myRival == unitRival)) //(!unitAIhit.IsObstacle() || (obstacle == null || obstacle.isActiveAndEnabled || !hit.collider.isTrigger))) { // Hay alguien bFound = false; if (unitAIOut == null) unitAIOut = unitAIhit; } } //Debug.DrawLine(v3Pos, v3Pos + (v3dirToTarget * fdisEnemy), bFound ? Color.white : Color.red, fDebugTime); //Debug.DrawLine(v3Pos, v3Pos + v3dirToTarget * fdisEnemy, Color.white, fDebugTime); /*if (!bFound) { Debug.DrawLine(v3Pos + v3dirToTarget * fdisEnemy, v3Pos + v3dirToTarget * fdisEnemy + Vector3.up * 2f, Color.red, fDebugTime); }*/ return bFound; } //public void OnDrawGizmos() //{ // Gizmos.color = new Color(1F, 0.5F, 0.1F, 0.7F); // Gizmos.DrawSphere((Vector3)v3Destination, 0.1f); // if (bSoploRadius) // { // Gizmos.DrawWireSphere((Vector3)v3SoploOrigin, fSoploRadius); // } // if (bSoploLength) // { // Gizmos.DrawLine(v3SoploOrigin, v3SoploOrigin + (v3SoploDestination - v3SoploOrigin).normalized * fSoploLength); // } //} /// /// Test if a unit can arrive to a position (true = not found) /// /// true if arrive to position doesnt found anybody, false if someone is found. /// RTSUnit me. /// V3 target position. /// UnitAI found. /// RTSUnit rival. /// If set to true three raycasts, false one raycast. public bool TestArriveToPosition(UnitRTSUnit me, Vector3 v3TargetPos, out UnitAI unitAIOut, UnitRTSUnit unitRival = null, bool bThreeRaycasts = true, bool bRivalRadius = true, int iLayerMask = -1) { bool bFound = true; unitAIOut = null; if (v3TargetPos != default(Vector3) && me != null && me.unitAI) { Vector3 v3posTarget = v3TargetPos; Vector3 myPosition = me.unitAI.transformAI.position; // TODO Use this variable, if not, erase it //float fMyRadius = me.fInteractiveRadius; Vector3 v3dirToTarget = (v3posTarget - myPosition).normalized; float fdisEnemy = Vector3.Distance(myPosition, v3posTarget); float raycastupdist = 0.4f; float disthorizontal = 0.3f; if (unitRival == null) { bRivalRadius = false; } else { disthorizontal = unitRival.rtsUnit.v2DimRadius.x; //0.2f; } if (!bRivalRadius) { disthorizontal = me.rtsUnit.v2DimRadius.x; } Vector3 v3Pos = myPosition + Vector3.up * raycastupdist; bFound = TestArriveToPositionOneRaycast(me, ref unitAIOut, v3Pos, v3dirToTarget, fdisEnemy, unitRival, iLayerMask); if (bThreeRaycasts && bFound) { Vector3 v3Pos1 = myPosition + (Vector3.Cross(v3dirToTarget, Vector3.up) * (disthorizontal)) + Vector3.up * raycastupdist; bool bFound1 = TestArriveToPositionOneRaycast(me, ref unitAIOut, v3Pos1, v3dirToTarget, fdisEnemy, unitRival, iLayerMask); Vector3 v3Pos2 = myPosition + (Vector3.Cross(v3dirToTarget, Vector3.up) * -(disthorizontal)) + Vector3.up * raycastupdist; bool bFound2 = TestArriveToPositionOneRaycast(me, ref unitAIOut, v3Pos2, v3dirToTarget, fdisEnemy, unitRival, iLayerMask); bFound = bFound1 && bFound2; } } return bFound; } public bool TestArriveToTarget(UnitRTSUnit unitRival, bool bThreeRaycasts = true) { bool bFound = true; if (unitRival != null && unitRival.goCharacter != null) { Vector3 v3posEnemy = unitRival.unitAI.transformAI.position; UnitAI unitAIposibleRival = null; // Si no es un rival fijo se puede cambiar por uno mejor if (unitInfo.unitAI.moveToClassCurrent == null || !unitInfo.unitAI.moveToClassCurrent.bFixedRival) { bFound = TestArriveToPosition(unitInfo.unitAI.GetMe(), v3posEnemy, out unitAIposibleRival, unitRival, bThreeRaycasts, false); } if (unitAIposibleRival != null)// && !unitInfo.unitAI.bIsDirectlyControlledByThePlayer) { unitInfo.unitAI.ChangeRival(unitAIposibleRival.GetMe(), unitRival); bFound = false; } } return bFound; } ///////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////// public bool HasArrivedTarget(UnitRTSUnit target = null, float fOffsetExtraDistance = 0) { Vector3 v3DestAux = v3Destination; if (target != null && target.goCharacter != null) { v3DestAux = target.unitAI.transformAI.position; bool bGoingToInteract = unitInfo.unitAI.AmIGoingToInteractSpecial(target.unitAI.unitInfo.interactiveRadius); if (bGoingToInteract) { v3DestAux += target.unitAI.unitInfo.interactiveRadius.v3ZonaInteraccionSelected; } } float dist = 1; if (unitInfo.unitAI.transformAI != null) { Vector3 v3OriginAux = unitInfo.unitAI.transformAI.position; v3OriginAux.y = 0; v3DestAux.y = 0; dist = (v3OriginAux - v3DestAux).sqrMagnitude; } // Debug.Log(dist + " " + fScopeCurrent + " " + fOffsetExtraDistance + " " + v3DestAux); float fDistanceCompare = UnitMovement.FMOVETODIST + fOffsetExtraDistance + fScopeCurrent; return (dist != Mathf.Infinity && dist <= fDistanceCompare * fDistanceCompare); } public bool HasArrivedSteering() { bool bRetorn = false; if (!bCalculatingPath) { if (steering.arrayV3Path != null) { Vector3 v3FinalPositionSteering = steering.arrayV3Path[steering.arrayV3Path.Count - 1]; bRetorn = (v3FinalPositionSteering - unitInfo.unitAI.transformAI.position).sqrMagnitude < UnitMovement.FMOVETODIST * UnitMovement.FMOVETODIST; } else { bRetorn = true; } } return bRetorn; } ///////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////// //si ha llegado a su v3Destination public bool HasArrived(UnitRTSUnit target = null, float fOffsetExtraDistance = 0) { if (!bCalculatingPath && steering.arrayV3Path == null && bIsMovingPathComplete) return true; Vector3 v3DestAux = v3Destination; if (target != null && target.goCharacter != null) { v3DestAux = target.unitAI.transformAI.position; bool bGoingToInteract = unitInfo.unitAI.AmIGoingToInteractSpecial(target.unitAI.unitInfo.interactiveRadius); if (bGoingToInteract) { v3DestAux += target.unitAI.unitInfo.interactiveRadius.v3ZonaInteraccionSelected; } } float dist = 1; if (unitInfo.unitAI.transformAI != null) { dist = (unitInfo.unitAI.transformAI.position - v3DestAux).sqrMagnitude; } // Debug.Log(dist + " " + fScopeCurrent + " " + fOffsetExtraDistance + " " + v3DestAux); float fDistanceCompare = (UnitMovement.FMOVETODIST * 0.5f) + fOffsetExtraDistance + fScopeCurrent; bool bReturn = false; if (dist != Mathf.Infinity && dist <= fDistanceCompare * fDistanceCompare) { bReturn = true; } //return (dist != Mathf.Infinity && dist <= fDistanceCompare * fDistanceCompare); return bReturn; } public bool HasArrived2(UnitRTSUnit target = null, float fOffsetExtraDistance = 0) { //bool bPathPermit = false; //if (!bCalculatingPath && steering.arrayV3Path == null && bIsMovingPathComplete) //{ // bPathPermit = true; //}; Vector3 v3DestAux = v3Destination; if (target != null && target.goCharacter != null) { v3DestAux = target.unitAI.transformAI.position; bool bGoingToInteract = unitInfo.unitAI.AmIGoingToInteractSpecial(target.unitAI.unitInfo.interactiveRadius); if (bGoingToInteract) { v3DestAux += target.unitAI.unitInfo.interactiveRadius.v3ZonaInteraccionSelected; //if (unitInfo.unitAI.me.bWargo) //{ // v3offsetWargo = Vector3.Normalize(unitAIObjective.unitInfo.interactiveRadius.v3ZonaInteraccionSelected); // Si es Wargo va a un punto 1 tile más alejado //} } } float dist = 1; if (unitInfo.unitAI.transformAI != null) { dist = (unitInfo.unitAI.transformAI.position - v3DestAux).sqrMagnitude; } // Debug.Log(dist + " " + fScopeCurrent + " " + fOffsetExtraDistance + " " + v3DestAux); float fDistanceCompare = (UnitMovement.FMOVETODIST * 0.5f) + fOffsetExtraDistance + fScopeCurrent; //bool bReturn = false; //if ((dist != Mathf.Infinity && dist <= fDistanceCompare * fDistanceCompare)) //{ // bReturn = true; //} //return bPathPermit && (dist != Mathf.Infinity && dist <= fDistanceCompare * fDistanceCompare); return (dist != Mathf.Infinity && dist <= fDistanceCompare * fDistanceCompare); } //si El leader del squad ha llegado a su v3Destination public bool HasArrivedLeader(UnitRTSUnit target = null, float fOffsetExtraDistance = 0) { return squad == null || squad.unitLeader.unitInfo.unitMovement.HasArrived(target, fOffsetExtraDistance); } //si ha llegado a la posicion que se le pasa public bool HasArrivedFollowing(Vector3 pos) { float possibleErrorSQR = 0.04f * 0.04f;// TODO: antes era .2f; float dist = (unitInfo.unitAI.transformAI.position - pos).sqrMagnitude; return (dist != Mathf.Infinity && dist <= possibleErrorSQR); } // Esta funcion siempre se tiene que usar junto a un "is myRival in scope", sino da resultados indeseados public bool HasArrivedDestinationPartial(UnitRTSUnit target = null) { // Debug.Log(v3Destination + " " + v3DestinationPrev + " " + unitInfo.unitAI.transform.position + " \n" + // (v3Destination - unitInfo.unitAI.transform.position).sqrMagnitude + " " + (v3Destination - v3DestinationPrev).sqrMagnitude + " " + fScopeCurrent); Vector3 v3DestAux = v3Destination; if (target != null && target.goCharacter != null) { bool bGoingToInteract = unitInfo.unitAI.AmIGoingToInteractSpecial(target.unitAI.unitInfo.interactiveRadius); if (bGoingToInteract) { v3DestAux = target.unitAI.transformAI.position + target.unitAI.unitInfo.interactiveRadius.v3ZonaInteraccionSelected; } } return (v3DestAux - unitInfo.unitAI.transformAI.position).sqrMagnitude < FMOVETODIST * FMOVETODIST && (v3DestAux - v3DestinationPrev).sqrMagnitude > FMOVETODIST * FMOVETODIST && (v3DestAux - v3DestinationPrev).sqrMagnitude > fScopeCurrent * fScopeCurrent; } public bool HasArrivedDestinationPartialWithSteering(UnitRTSUnit target = null) { // Debug.Log(v3Destination + " " + v3DestinationPrev + " " + unitInfo.unitAI.transform.position + " \n" + // (v3Destination - unitInfo.unitAI.transform.position).sqrMagnitude + " " + (v3Destination - v3DestinationPrev).sqrMagnitude + " " + fScopeCurrent); bool bRetorn = false; if (steering.arrayV3Path != null) { Vector3 v3FinalPositionSteering = steering.arrayV3Path[steering.arrayV3Path.Count - 1]; bRetorn = (v3FinalPositionSteering - unitInfo.unitAI.transformAI.position).sqrMagnitude < FMOVETODIST * FMOVETODIST && (v3FinalPositionSteering - v3Destination).sqrMagnitude > FMOVETODIST * FMOVETODIST && (v3FinalPositionSteering - v3Destination).sqrMagnitude > fScopeCurrent * fScopeCurrent; } return bRetorn; } /******************************************************************************************** ****************************** IS FACING RIGHT OR LEFT ************************************* *******************************************************************************************/ // Is facing right or Left public void UpdateFacingRightOrLeft() { if (unitInfo.unitAI.bIgnoreRotateUnit) return; Vector3 v3DirectionFacing = unitInfo.unitAI.transformAI.forward; if (IsMoving() && steering.v3PathFollowingDirection != Vector3.zero) { v3DirectionFacing = steering.v3PathFollowingDirection; //steering.v3Velocity; } else if (bIsThrown && rbRigidBody != null && rbRigidBody.velocity != Vector3.zero) { v3DirectionFacing = rbRigidBody.velocity; } Profiler.BeginSample("Facing left"); unitInfo.interactiveRadius.IsFacingLeft(v3DirectionFacing); Profiler.EndSample(); if (unitInfo.unitAnimations != null) unitInfo.unitAnimations.bOncePerFrame = false; } /******************************************************************************************** ****************************** STOP si no puede pasar ******************************** *******************************************************************************************/ // [Header("Stop si no puede pasar")] // public bool bHasWalkedSinceStopping; // public bool bCantGetThere = false; // public bool bCantGetThereAttacking = false; // // // TODO: Esto se usa? // public void UpdateCantGetThere() // { // bCantGetThere = false; // // if (IsMoving() || unitInfo.unitAI.currentState.IsInMovingToAttackState()) // { // if (!bHasWalkedSinceStopping && IsMoving()) // { // bHasWalkedSinceStopping = rbRigidBody.velocity.magnitude > steering.fMaxVelocity * 0.1; // } // //Frena si está en el último paso del path y a menos de su radio de la llegada (para compensar colliders) // bool bStopedForPushing = false; // bool bSuperClose = false; // bool bLastStepOfPath = false; // bool bPathFail = false; // if (steering.arrayV3Path != null) // { // float radiusUnit = fCapsColRadius; // int iCount = steering.arrayV3Path.Count; // Vector3 v3LastPosPathfinding = steering.arrayV3Path[iCount - 1]; // //bPathFail = si el ultimo path acaba en el waypoint // bPathFail = !bCalculatingPath && Vector3.Distance(v3Destination, v3LastPosPathfinding) > 0.1f; // //bSuperClose = si estoy cerca del ultimo path // bSuperClose = Vector3.Distance(unitInfo.unitAI.transform.position, v3LastPosPathfinding) < radiusUnit; // //bStopedForPushing = si me estaba moviendo pero he frenado a un tercio de mi vel max // bStopedForPushing = bHasWalkedSinceStopping && rbRigidBody.velocity.magnitude < steering.fMaxVelocity / 3; // //bLastStepOfPath = si estoy en el último path // // bLastStepOfPath = (steering.arrayV3Path.Count > 0) && (steering.iCurrentNode < steering.arrayV3Path.Count); // //bCantGetThere = calculo final para que solo se pare si (ha chocado o ha llegado) Y el path no llega al waypoint // bCantGetThere = /*bLastStepOfPath &&*/ bPathFail && (bStopedForPushing || bSuperClose); // // Debug.Log("bLastStepOfPath && bPathFail && (bCollidingWithSomeone || bSuperClose)"+bLastStepOfPath+bPathFail+bStopedForPushing+bSuperClose); // // if (bCantGetThere || bPathFail || unitInfo.unitAI.myRival == null) // // { // //if (gameObject.name == "Niño orco") // //print(unitInfo.unitAI.currentState.IsInMovingToAttackState() + " " + (bPathFail && bSuperClose) + " " + bStopedForPushing); // // bCantGetThereAttacking = unitInfo.unitAI.currentState.IsInMovingToAttackState() && ((bPathFail && bSuperClose) || bStopedForPushing); // // } // } // } // } /******************************************************************************************** ***************************************** STOP ********************************************* *******************************************************************************************/ /// /// Stops the Unit. /// /// If set to true force stop. public void Stop(bool bforceStop = false) { //Debug.Log($"{unitInfo.unitAI.gameObject.name} STOP"); InitializeStart(); ResetMoveToDelayed(); if (seeker != null) { seeker.CancelCurrentPathRequest(false); } if (IsMoving() || bforceStop) { if (unitInfo.unitAI.IsInAgujero()) { Debug.Log($"Can't stop {unitInfo.unitAI.name}"); unitInfo.unitAI.bWantsToStop = true; return; } // If the agent is in attack range and visible Vector3 v3PosStop = (unitInfo.unitAI.transformAI != null) ? unitInfo.unitAI.transformAI.position : unitInfo.unitAI.transform.position; SetDestinationLocalTransform(v3PosStop, null, true); steering.SetPathFollowing(null); rbRigidBody.velocity = Vector3.zero; ReCalculatePathDelay(false); // Cancel Invoke bRestoringPosition = false; bIsMoving = false; bIsMovingPathComplete = false; // bHasWalkedSinceStopping = false; //Esto es correcto en caso de follow?? // if (unitInfo.unitAI.unitActionControl.bIsDirectlyControlledByThePlayer) // { // unitInfo.unitAI.unitActionControl.bIsDirectlyControlledByThePlayer = false /*|| !unitInfo.unitAI.FolloweeIsNull()*/; // Debug.Log("Fak yu player"); // } } } /******************************************************************************************* ********************************* ANIMATION FUNCTIONS ************************************** *******************************************************************************************/ // [System.NonSerialized] public SkillScriptBase skillScriptBaseNextAttack; // [System.NonSerialized] public string sSkillScriptBaseNextAnimation; public void SetAttackAnimation(Vector3 v3RivalPosOverride = default(Vector3)) { UnitRTSUnit myRival = unitInfo.unitAI.myRival; UnitRTSUnit me = unitInfo.unitAI.GetMe(); if (me.bDoesntAttack) return; // Has default attack? RTSAttack rtsAttack = me.rtsAttackDefault; if (rtsAttack == null && !unitInfo.unitAI.IsTower()) { Debug.Log(unitInfo.unitAI.gameObject.name + " No tiene rtsAttack configurado."); return; // TODO: Warning } Vector3 v3RivalPos = Vector3.zero; if (myRival != null) { unitInfo.interactiveRadius.IsFacingLeft(myRival.unitAI.transformAI.position - unitInfo.unitAI.transformAI.position); // TODO: Esto no deberia ser un lookat? v3RivalPos = myRival.unitAI.transformAI.position; } if (v3RivalPosOverride != default(Vector3)) { v3RivalPos = v3RivalPosOverride; } // Cooldown AttackSpeed float fMinSpeedCooldown = 10f; float fMaxSpeedCooldown = .1f; float fSpeedTanPerOne = unitInfo.unitAI.GetMe().fCooldownUnit / unitInfo.unitAI.GetMe().fCooldown; //fSpeedTanPerOne = Mathf.Min(fSpeedTanPerOne, fMinSpeedCooldown); fSpeedTanPerOne = Mathf.Clamp(fSpeedTanPerOne, fMaxSpeedCooldown, fMinSpeedCooldown); //Debug.Log("SetSpeedCooldown " + fSpeedTanPerOne + " = " + unitInfo.unitAI.GetMe().fCooldownUnit + " / " + unitInfo.unitAI.GetMe().fCooldown); unitInfo.unitAnimations.SetSpeedCooldown(fSpeedTanPerOne); // La skill de Ninato de SkillNinatoTajoSombra puede sobreescribir el siguiente ataque // if (skillScriptBaseNextAttack != null) // { // unitInfo.unitAttackManager.unitAttackCurrent = new UnitAttack(null, skillScriptBaseNextAttack, unitInfo.unitAI.GetMe(), myRival, unitInfo.unitAI.transformAI.position, v3RivalPos); // skillScriptBaseNextAttack = null; // Se lo quitamos para que no haga otro ataque con esto // } // else // { // Default UnitAttack unitInfo.unitAttackManager.unitAttackCurrent = new UnitAttack(rtsAttack, null, unitInfo.unitAI.GetMe(), myRival, unitInfo.unitAI.transformAI.position, v3RivalPos); // } if (unitInfo.unitSpecialSkill != null && unitInfo.unitAttackManager.unitAttackCurrent.skillScript == null && unitInfo.unitAttackManager.unitAttackCurrent.specialSkillScript == null) { SpecialSkillBaseClass specialSkillOverride = unitInfo.unitSpecialSkill.OverridesStats(); if (specialSkillOverride != null) { specialSkillOverride.OverrideAttackStats(unitInfo.unitAttackManager.unitAttackCurrent); } } if (unitInfo.unitAnimations != null) { float fAttackAngle = 0; //bool bHit = true; UnitAttack unitAttackCurrent = unitInfo.unitAttackManager.unitAttackCurrent; if (unitInfo.unitAI.GetMe().rtsUnit.bHasAttackAngleAnimations) { fAttackAngle = unitAttackCurrent.fAttackAngle; } unitAttackCurrent.CalculateHitOrMiss(); bool bHitAttackAnimation = unitAttackCurrent.bHits; // Debug.Log((bHitAttackAnimation) ? "HIT!" : "Miss..."); if (unitAttackCurrent.rtsAttack != null && unitAttackCurrent.rtsAttack.bProjectileNotAura) { bHitAttackAnimation = true; } unitInfo.unitAnimations.SetAnimationAttackAngle(bHitAttackAnimation, fAttackAngle, 90); //, sSkillScriptBaseNextAnimation); // sSkillScriptBaseNextAnimation = ""; // Skill Ninato Animation solo para el siguiente ataque que hace } } // public void Teleport(Vector3 v3PositionToTeleport) // { // unitInfo.unitAI.transform.position = v3PositionToTeleport; // // v3Destination = v3PositionToTeleport; // } /*public void UpdateAnimationSpeedRunning(bool bSkipThis = false) { if (bSkipThis) { return; } float fAnimSpeed = 1; if (unitInfo.unitAnimations.IsInState(UnitAnimations.States.Run)) { if (rbRigidBody.velocity.magnitude > .05f) { fAnimSpeed = Mathf.Max(0.1f, ((rbRigidBody.velocity.magnitude * (100 / 31)) / unitInfo.unitAI.GetMe().fSpeedUnitUpgraded)); // print("fanimSpeed"+fAnimSpeed); } else { //TODO Deberia parar la animación de correr y pasar a una de empujar o algo pk está quieto pero corriendo } } unitInfo.unitAnimations.animator.speed = fAnimSpeed; }*/ /****************** * * * * * Path Utils * * * * * ********************/ //#if UNITY_EDITOR // [System.NonSerialized] private Color c = Color.white; //#endif // public void UpdatePathUtils() // { //#if UNITY_EDITOR // // DrawPath (agent.path); // if (steering.IsFollowingPath()) // { // DrawSteeringPath(steering.arrayV3Path, Color.white); // DrawSteeringPath(steering.listV3corners, Color.blue); // } // if (!unitInfo.unitAI.RivalIsNull() && unitInfo.unitAI != null && unitInfo.unitAI.gameObject != null) // { // //Debug.DrawLine(transform.position, GetComponent().myRival.goCharacter.transform.position, Color.red); // } //#endif // } //#if UNITY_EDITOR // void DrawPath(UnityEngine.AI.NavMeshPath path) // { // if (path.corners.Length < 2) // return; // switch (path.status) // { // case UnityEngine.AI.NavMeshPathStatus.PathComplete: // c = Color.white; // break; // case UnityEngine.AI.NavMeshPathStatus.PathInvalid: // c = Color.red; // break; // case UnityEngine.AI.NavMeshPathStatus.PathPartial: // c = Color.yellow; // break; // } // Vector3 previousCorner = path.corners[0]; // int i = 1; // while (i < path.corners.Length) // { // Vector3 currentCorner = path.corners[i]; // Debug.DrawLine(previousCorner, currentCorner, c); // previousCorner = currentCorner; // i++; // } // } // void DrawSteeringPath(List listV3, Color color) // { // if (listV3 == null || listV3.Count < 2) // return; // Vector3 previousCorner = listV3[0]; // int i = 1; // while (i < listV3.Count) // { // Vector3 currentCorner = listV3[i]; // Debug.DrawLine(previousCorner, currentCorner, color); // previousCorner = currentCorner; // i++; // } // } //#endif }