public override bool PreAI(Projectile projectile) { if (goButFaster && projectile.aiStyle == 19) { goButFaster = false; projectile.AI(); } return(true); }
//private void onProjectileSetDefaults(SetDefaultsEventArgs<Projectile,int> args) //{ // var projectile = args.Object; // var customProjectile = GetCustomProjectile(projectile); // if(customProjectile!=null) // { // Debug.Print("onProjectileSetDefaults!"); // var definition = customProjectile.Definition; // definition.ApplyTo(projectile); // lock( locker ) // { // Utils.TryExecuteLua(() => definition.OnSpawn?.Call(customProjectile), definition.Name); // } // TSPlayer.All.SendData(PacketTypes.ProjectileNew, "", projectile.whoAmI); // args.Handled = true; // } //} private HookResult OnProjectilePreUpdate(Projectile projectile, ref int index) { var result = HookResult.Continue; var customProjectile = GetCustomProjectile(projectile); if (customProjectile == null) { return(HookResult.Continue); } var definition = customProjectile.Definition; var lastTimeLeft = projectile.timeLeft; //we capture this to help determine whether we need to decrement timeLeft at the end of this method. //terraria has many points where it sets timeLeft internally, but our custom proj modifies whether/when those points run. //by the end of this method we hopefully have enough information to tell if terraria modified it, or if we need to do it ourselves. //game updates var onGameUpdate = definition.OnGameUpdate; if (customProjectile.Active && onGameUpdate != null) { try { CustomIDFunctions.CurrentID = definition.Name; var handled = onGameUpdate(customProjectile); result = handled == true ? HookResult.Cancel : HookResult.Continue; if (result == HookResult.Cancel) { //if we dont pass execution onto Terraria's Projectile.Update(), AI() will never get run, so we better run it ourselves. projectile.AI(); } customProjectile.SendNetUpdate = true; } catch (Exception ex) { Utils.LogScriptRuntimeError(ex); definition.OnGameUpdate = null; } } //collision tests //players if (customProjectile.Active && definition.OnCollision != null) { foreach (var player in TShock.Players) { if (player?.Active == true) { var onCollision = definition.OnCollision; if (onCollision != null) { var tplayer = player.TPlayer; var playerHitbox = tplayer.Hitbox; if (!tplayer.immune && projectile.Hitbox.Intersects(playerHitbox)) { try { CustomIDFunctions.CurrentID = definition.Name; onCollision(customProjectile, player); customProjectile.SendNetUpdate = true; } catch (Exception ex) { Utils.LogScriptRuntimeError(ex); definition.OnCollision = null; } } } } } } //tiles if (customProjectile.Active && projectile.tileCollide) { // this is a bit convoluted, because of the 2 conditions-- player wants to run custom code on tile collisions and/or player isn't allowing terraria // to run Update(), thus the projectile wont be killed in a timely manner. See condition below for result == HookResult.Cancel if (definition.OnTileCollision != null || result == HookResult.Cancel) { var tileCollisions = TileFunctions.GetOverlappedTiles(projectile.Hitbox); if (tileCollisions.Count > 0) { var killProjectile = false; //if terrarias code won't be running Update(and thus AI() ), we should kill the projectile ourselves if we hit any applicable tile. if (result != HookResult.Continue) { //...we have to scan the list before the player does, to ensure they dont modify anything(we shouldn't have switched from ReadOnlyCollection. ) foreach (var hit in tileCollisions) { if (TileFunctions.IsSolidOrSlopedTile(hit.X, hit.Y) || (!(definition.BaseOverride.IgnoreWater == true) && TileFunctions.IsLiquidTile(hit.X, hit.Y))) { killProjectile = true; break; } } } try { CustomIDFunctions.CurrentID = definition.Name; definition.OnTileCollision?.Invoke(customProjectile, tileCollisions); //customProjectile.SendNetUpdate = true; } catch (Exception ex) { Utils.LogScriptRuntimeError(ex); definition.OnTileCollision = null; } //script hasnt killed projectile, but we did hit a foreground tile, so lets kill it ourselves if (customProjectile.Active && killProjectile == true) { customProjectile.Kill(); } } } } //We need to decrement timeLeft ourselves if no other code has, and no code run after this point will do so if (customProjectile.Active && result == HookResult.Cancel && customProjectile.TimeLeft == lastTimeLeft) { customProjectile.TimeLeft--; if (customProjectile.TimeLeft < 1) { customProjectile.Kill(); } } if (customProjectile.Active && customProjectile.SendNetUpdate) { SendProjectileUpdate(customProjectile.Index); } return(result); }
internal static void CalculateCatches(Vector2 spawnPosition, int bobberType, out int poolSize, out bool lava, out bool honey) { poolSize = 0; lava = false; honey = false; Player player = Main.LocalPlayer; Vector2 originalCenter = player.Center; bool originalWet = player.wet; bool originalHoneyWet = player.honeyWet; bool originalLavaWet = player.lavaWet; Projectile bobber = null; try { // Spawn bobber with downward starting velocity int index = Projectile.NewProjectile(spawnPosition, Vector2.UnitY * 8, bobberType, 0, 0f, Main.myPlayer); if (index >= Main.maxProjectiles) { return; } bobber = Main.projectile[index]; // Debug: Visualize bobber spawn location //for (int i = 0; i < 10; i++) //{ // Dust.NewDust(bobber.position, 16, 16, DustID.Fire); //} catches.Clear(); currentlyTesting = true; int failureCount = 0; player.Center = spawnPosition - new Vector2(0f, bobber.height * 2); //Temporarily teleport player above the bobber + twice the bobbers height player.wet = false; player.honeyWet = false; player.lavaWet = false; for (int i = 0; i < Tries; i++) { failureCount++; if (!bobber.wet) { // If not wet, call Update so it moves into liquid first (handles everything including AI) bobber.Update(index); i--; // Make sure we actually test Tries times, the bobber does not catch if not wet } else { // Once wet, just call AI. Calling Update will reel the bobber back to the player otherwise, which is not intended bobber.AI(); if (poolSize == 0) { poolSize = GetPoolStats(bobber.Center, out lava, out honey); } } if (failureCount >= 2 * Tries) { break; } } } finally { player.Center = originalCenter; player.wet = originalWet; player.honeyWet = originalHoneyWet; player.lavaWet = originalLavaWet; currentlyTesting = false; bobber?.Kill(); } // Debug: Visualize bobber final location //for (int i = 0; i < 10; i++) //{ // Dust.NewDust(bobber.position, 16, 16, DustID.Fire); //} }