private void OnGameUpdate(EventArgs args) { Utils.TrySpawnForEachPlayer(TrySpawnCustomNpc); foreach (var npc in Main.npc.Where(n => n?.active == true)) { var customNpc = GetCustomNpc(npc); if (customNpc?.Definition.ShouldAggressivelyUpdate == true) { npc.netUpdate = true; } if (customNpc?.HasChildren == true) { customNpc?.UpdateChildren(); } if (customNpc?.HasTransformed == true) { var id = customNpc.Npc.whoAmI; customNpc.HasTransformed = false; var definition = customNpc.Definition; if (definition.OnTransformed != null) { try { CustomIDFunctions.CurrentID = definition.Name; definition.OnTransformed(customNpc); } catch (Exception ex) { Utils.LogScriptRuntimeError(ex); definition.OnTransformed = null; } TSPlayer.All.SendData(PacketTypes.NpcUpdate, "", id); TSPlayer.All.SendData(PacketTypes.UpdateNPCName, "", id); } } var npcId = npc.whoAmI; if (_checkNpcForReplacement[npcId]) { TryReplaceNpc(npc); _checkNpcForReplacement[npcId] = false; } } //player - npc collisions foreach (var player in TShock.Players.Where(p => p?.Active == true)) { var tplayer = player.TPlayer; var playerHitbox = tplayer.Hitbox; if (!tplayer.immune) { player.SetData(IgnoreCollisionKey, false); } foreach (var npc in Main.npc.Where(n => n?.active == true)) { var customNpc = GetCustomNpc(npc); if (customNpc != null) { var definition = customNpc.Definition; if (definition.OnCollision != null) { if (npc.Hitbox.Intersects(playerHitbox) && !player.GetData <bool>(IgnoreCollisionKey)) { try { CustomIDFunctions.CurrentID = definition.Name; definition.OnCollision(customNpc, player); } catch (Exception ex) { Utils.LogScriptRuntimeError(ex); definition.OnCollision = null; } //player.SetData(IgnoreCollisionKey, true); //break;//should this be a continue instead?? } } } } if (!tplayer.immune) { player.SetData(IgnoreCollisionKey, true); } } //npc - tile collisions foreach (var npc in Main.npc.Where(n => n?.active == true)) { var customNpc = GetCustomNpc(npc); if (customNpc != null) { var definition = customNpc.Definition; if (definition.OnTileCollision != null) { var tileCollisions = TileFunctions.GetOverlappedTiles(npc.Hitbox); if (tileCollisions.Count > 0) { try { CustomIDFunctions.CurrentID = definition.Name; definition.OnTileCollision(customNpc, tileCollisions); } catch (Exception ex) { Utils.LogScriptRuntimeError(ex); definition.OnTileCollision = null; } } } } } }
//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); }