コード例 #1
0
        /// <summary>
        /// This method controls the AI for the grappling hook. Note that there is a separate AI run
        /// for each active hook. There are three states that a hook can be in:
        /// 1(0f) -- Extending
        /// 2(1f) -- Retreating
        /// 3(2f) -- Grappled
        /// The AI starts at Extending when shot and updates the state of the hook based on certain
        /// conditions. If the hook has been shot, but has not reached the max grapple distance, the
        /// ai state stays at Extending. If the hook has reached the max grapple distance, the ai state
        /// is updated to Retreating. If the hook ever reaches a solid tile that it can grapple to, the
        /// ai state is updated to Grappled. The AI then continues to check that the block that the
        /// hook grappled to is still active. If the block is ever removed (e.g. broken), the ai state
        /// of the hook is updated to Retreating.
        /// </summary>
        public override void AI()
        {
            Player hookPlayer = Main.player[projectile.owner];

            if (hookPlayer.dead || hookPlayer.stoned || hookPlayer.webbed || hookPlayer.frozen)
            {
                projectile.Kill();
            }
            else
            {
                // Determines distance from player to grapple
                int     newPosition;
                Vector2 playerLocation     = hookPlayer.MountedCenter;
                Vector2 projectileLocation = new Vector2(projectile.position.X + (float)projectile.width * 0.5f, projectile.position.Y + (float)projectile.height * 0.5f);
                float   xDistance          = playerLocation.X - projectileLocation.X;
                float   yDistance          = playerLocation.Y - projectileLocation.Y;
                float   grappleDistance    = (float)Math.Sqrt((double)(xDistance * xDistance + yDistance * yDistance));
                projectile.rotation = (float)Math.Atan2((double)yDistance, (double)xDistance) - 1.57f;

                // ai state for when grapple is extending
                if (projectile.ai[0] == 0f)
                {
                    // Sets grappling hook ai to retreat (1) if the hook is outside of the max grappling range
                    if (grappleDistance > maxRange)
                    {
                        projectile.ai[0] = 1f;
                    }

                    /* If the hook is still within the max grappling range, it checks to see if the grapple has collided
                     * with a tile and, if it has, sets the grappling hook's ai to grappled (2)*/

                    // Sets position of hook's hitbox
                    Vector2 bottomLeftVector = projectile.Center - new Vector2(5f);
                    Vector2 topRightVector   = projectile.Center + new Vector2(5f);
                    Point   bottomLeftPoint  = (bottomLeftVector - new Vector2(16f)).ToTileCoordinates();
                    Point   topRightPoint    = (topRightVector + new Vector2(32f)).ToTileCoordinates();
                    int     grappleLeft2     = bottomLeftPoint.X;
                    int     grappleRight2    = topRightPoint.X;
                    int     grappleBottom2   = bottomLeftPoint.Y;
                    int     grappleTop2      = topRightPoint.Y;

                    // Verifies hook's hitbox position is within map bounds. If not, sets  hitbox to map bounds.
                    if (grappleLeft2 < 0)
                    {
                        grappleLeft2 = 0;
                    }
                    if (grappleRight2 > Main.maxTilesX)
                    {
                        grappleRight2 = Main.maxTilesX;
                    }
                    if (grappleBottom2 < 0)
                    {
                        grappleBottom2 = 0;
                    }
                    if (grappleTop2 > Main.maxTilesY)
                    {
                        grappleTop2 = Main.maxTilesY;
                    }

                    /* Iterates through all tiles within hook's hitbox position to check for a solid tile the hook
                     * can grapple to. If there is, the grapple's ai is set to grappled (2)*/
                    for (int xPos = grappleLeft2; xPos < grappleRight2; xPos = newPosition + 1)
                    {
                        for (int yPos = grappleBottom2; yPos < grappleTop2; yPos = newPosition + 1)
                        {
                            Vector2 tilePosition = default;
                            tilePosition.X = (float)(xPos * 16);
                            tilePosition.Y = (float)(yPos * 16);
                            if (bottomLeftVector.X + 10f > tilePosition.X &&
                                bottomLeftVector.X < tilePosition.X + 16f &&
                                bottomLeftVector.Y + 10f > tilePosition.Y &&
                                bottomLeftVector.Y < tilePosition.Y + 16f &&
                                Main.tile[xPos, yPos].nactive() &&
                                (Main.tileSolid[Main.tile[xPos, yPos].type] || Main.tile[xPos, yPos].type == 314))
                            {
                                // Updates player's grapple count
                                if (hookPlayer.grapCount < 10)
                                {
                                    hookPlayer.grappling[hookPlayer.grapCount] = projectile.whoAmI;
                                    hookPlayer.grapCount += 1;
                                }
                                ShouldKillOldestHook();                                 // Kills the oldest hook if player currently has max number of hooks grappled
                                WorldGen.KillTile(xPos, yPos, true, true, false);
                                Main.PlaySound(0, xPos * 16, yPos * 16, 1, 1f, 0f);
                                projectile.velocity.X = 0f;
                                projectile.velocity.Y = 0f;
                                projectile.ai[0]      = 2f;
                                projectile.position.X = (float)(xPos * 16 + 8 - projectile.width / 2);
                                projectile.position.Y = (float)(yPos * 16 + 8 - projectile.height / 2);
                                projectile.damage     = 0;
                                projectile.netUpdate  = true;
                                if (Main.myPlayer == projectile.owner)
                                {
                                    NetMessage.SendData(13, -1, -1, null, projectile.owner, 0f, 0f, 0f, 0, 0, 0);
                                }
                                break;
                            }
                            newPosition = yPos;
                        }
                        if (projectile.ai[0] == 2f)
                        {
                            break;
                        }
                        newPosition = xPos;
                    }
                    return;
                }

                // ai state for when grapple is retreating
                if (projectile.ai[0] == 1f)
                {
                    ProjectileLoader.GrappleRetreatSpeed(projectile, hookPlayer, ref retreatSpeed);
                    // Kills the hook once close enough to the player on retreat
                    if (grappleDistance < 24f)
                    {
                        projectile.Kill();
                    }
                    grappleDistance       = retreatSpeed / grappleDistance;
                    xDistance            *= grappleDistance;
                    yDistance            *= grappleDistance;
                    projectile.velocity.X = xDistance;
                    projectile.velocity.Y = yDistance;
                }

                // ai state for when grapple is hooked
                else if (projectile.ai[0] == 2f)
                {
                    // Sets position of hook's hitbox
                    int grappleLeft   = (int)(projectile.position.X / 16f) - 1;
                    int grappleRight  = (int)((projectile.position.X + (float)projectile.width) / 16f) + 2;
                    int grappleBottom = (int)(projectile.position.Y / 16f) - 1;
                    int grappleTop    = (int)((projectile.position.Y + (float)projectile.height) / 16f) + 2;

                    // Verifies hook's hitbox position is within map bounds. If not, sets  hitbox to map bounds.
                    if (grappleLeft < 0)
                    {
                        grappleLeft = 0;
                    }
                    if (grappleRight > Main.maxTilesX)
                    {
                        grappleRight = Main.maxTilesX;
                    }
                    if (grappleBottom < 0)
                    {
                        grappleBottom = 0;
                    }
                    if (grappleTop > Main.maxTilesY)
                    {
                        grappleTop = Main.maxTilesY;
                    }

                    /* Iterates through all tiles within hook's hitbox position to check for a solid tile the hook
                     * is grappled to. If the hook is no longer grappled to a tile (e.g. if the tile is broken with
                     * a pickaxe), the grapple ai is set to retreat (1)*/
                    bool notGrappled = true;
                    for (int xPos = grappleLeft; xPos < grappleRight; xPos = newPosition + 1)
                    {
                        for (int yPos = grappleBottom; yPos < grappleTop; yPos = newPosition + 1)
                        {
                            Vector2 tilePosition = default;
                            tilePosition.X = (float)(xPos * 16);
                            tilePosition.Y = (float)(yPos * 16);
                            if (projectile.position.X + (float)(projectile.width / 2) > tilePosition.X &&
                                projectile.position.X + (float)(projectile.width / 2) < tilePosition.X + 16f &&
                                projectile.position.Y + (float)(projectile.height / 2) > tilePosition.Y &&
                                projectile.position.Y + (float)(projectile.height / 2) < tilePosition.Y + 16f &&
                                Main.tile[xPos, yPos].nactive() &&
                                (Main.tileSolid[Main.tile[xPos, yPos].type] || Main.tile[xPos, yPos].type == 314 || Main.tile[xPos, yPos].type == 5))
                            {
                                notGrappled = false;
                            }
                            newPosition = yPos;
                        }
                        newPosition = xPos;
                    }

                    // Sets the grapple's ai to retreat (1) if the tile that he hook was grappled to is no longer there
                    if (notGrappled)
                    {
                        projectile.ai[0] = 1f;
                    }
                    else if (hookPlayer.grapCount < 10)
                    {
                        hookPlayer.grappling[hookPlayer.grapCount] = projectile.whoAmI;
                        hookPlayer.grapCount += 1;
                    }
                    KillHookOnJump(hookPlayer);                     // checks if the player has jumped. On a jump, all hooks are killed (including actively extending hooks)
                }
                return;
            }
        }