public static bool Prefix(BasicProjectile __instance, GameLocation location, Farmer player)
        {
            bool damagesMonsters = ModEntry.BRGame.ModHelper.Reflection.GetField <NetBool>(__instance, "damagesMonsters").GetValue().Value;

            Console.WriteLine($"damage, m = {damagesMonsters}");
            if (!damagesMonsters || true && SlingshotPatch5.GetFarmerBounds(player).Intersects(__instance.getBoundingBox()))            //TODO: remove?
            {
                //TODO: modify slingshot damage here?
                int damage = __instance.damageToFarmer.Value;
                damage = Math.Min(damage, 10);

                if (player == Game1.player)
                {
                    Console.WriteLine("sending slingshot damage to self");
                    ModEntry.BRGame.TakeDamage(damage);
                }
                else
                {
                    Console.WriteLine("sending slingshot damage to other player");
                    NetworkUtility.SendDamageToPlayer(damage, player, Game1.player.UniqueMultiplayerID);
                }

                try
                {
                    if (!HitShaker.IsPlayerFlashing(player.UniqueMultiplayerID))
                    {
                        var r = ModEntry.BRGame.ModHelper.Reflection;
                        r.GetMethod(__instance, "explosionAnimation", false)?.Invoke(location);
                    }
                }
                catch (Exception) { }
            }
            return(false);
        }
Example #2
0
        public override void Entry(IModHelper helper)
        {
            Events = helper.Events;

            if (AntiCheat.HasIllegalMods())
            {
                Game1.quit = true;

                Console.Clear();
                Monitor.Log("You have an illegal mod installed. Please uninstall it before using this mod.", LogLevel.Warn);
                if (AntiCheat.IllegalMod != null)
                {
                    Monitor.Log($"- '{AntiCheat.IllegalMod}'", LogLevel.Warn);
                }
                Monitor.Log("Press any key to continue...", LogLevel.Warn);
                Console.ReadKey();
                return;
            }

            var config = helper.ReadConfig <ModConfig>();

            if (config == null)
            {
                config = new ModConfig();
                helper.WriteConfig(config);
            }

            Config = config;

            BRGame = new Game(helper, config);

            Patch.PatchAll("ilyaki.battleroyale");

            try
            {
                //Remove player limit
                var multiplayer = helper.Reflection.GetField <Multiplayer>(typeof(Game1), "multiplayer").GetValue();
                multiplayer.playerLimit = config.PlayerLimit;//250 is the Galaxy limit (it's multiplied by two for some reason, so set 125)
            }catch (Exception)
            {
                Monitor.Log("Error setting player limit. The max is 125", LogLevel.Error);
            }


            helper.ConsoleCommands.Add("br_start", "Start the game. Alternative to pressing Right control", (c, a) => {
                if (Game1.IsServer)
                {
                    BRGame.ServerStartGame();
                }
            });

            Events.Input.ButtonPressed += (o, e) =>
            {
                if (Game1.IsServer && e.Button.TryGetKeyboard(out Keys key) && key == Keys.RightControl)
                {
                    BRGame.ServerStartGame();
                }

                if (e.Button.TryGetKeyboard(out Keys key2) && key2 == Keys.RightAlt)
                {
                    var loc = Game1.player.currentLocation;
                    int x   = Game1.player.getTileX();
                    int y   = Game1.player.getTileY();


                    Monitor.Log($"tile location={loc.Name}, pos=({x},{y})", LogLevel.Info);

                    Monitor.Log($"my id : {Game1.player.UniqueMultiplayerID}", LogLevel.Info);

                    Monitor.Log($"precise position = {Game1.player.Position}", LogLevel.Info);

                    /*Game1.player.addItemToInventory(new StardewValley.Tools.Slingshot());
                     * Game1.player.addItemToInventory(new StardewValley.Tools.Slingshot(33));
                     *
                     * var xd = new int[] { 388, 390, 378, 380, 384, 382, 386, 441 };
                     * foreach(int asdf in xd)
                     * {
                     *      Game1.player.addItemToInventory(new StardewValley.Object(asdf, 100));
                     * }*/
                }
            };

            Events.GameLoop.UpdateTicked += (o, e) => BRGame?.Update(Game1.currentGameTime);
            Events.GameLoop.UpdateTicked += (o, e) => HitShaker.Update(Game1.currentGameTime);
            Events.GameLoop.UpdateTicked += (o, e) => SpectatorMode.Update();

            //https://github.com/funny-snek/Always-On-Server-for-Multiplayer/blob/master/Always%20On%20Server/ModEntry.cs
            string currentSavedInviteCode = "";

            Events.GameLoop.UpdateTicked += (o, e) =>
            {
                if (e.IsMultipleOf(60 * 5) && Game1.server != null && Game1.options.enableServer == true && !string.Equals(currentSavedInviteCode, Game1.server.getInviteCode()))
                {
                    currentSavedInviteCode = Game1.server.getInviteCode();

                    try
                    {
                        //helper.DirectoryPath
                        StreamWriter sw = new StreamWriter("InviteCode.txt");                        //Should be in the same folder as StardewModdingAPI.exe
                        sw.WriteLine(currentSavedInviteCode);
                        sw.Close();
                    }
                    catch (Exception b)
                    {
                        Console.WriteLine("Exception writing InviteCode: " + b.Message);
                    }
                }
            };

            Events.Player.Warped += (o, e) =>
            {
                if (e.NewLocation != null && (e.NewLocation is StardewValley.Locations.Woods || e.NewLocation.Name == "BugLand"))
                {
                    e.NewLocation.characters.Clear();
                }
            };

            Events.Display.RenderingHud += (o, e) => {
                if (!SpectatorMode.InSpectatorMode)
                {
                    Storm.Draw(Game1.spriteBatch);
                }
            };


            Events.Display.Rendered += (o, e) =>
            {
                if (SpectatorMode.InSpectatorMode)                //Spectator mode can only see the storm when it is drawn above everything
                {
                    Storm.Draw(Game1.spriteBatch);

                    if (Game1.activeClickableMenu == null)
                    {
                        string message = "Spectating";
                        SpriteText.drawStringWithScrollBackground(Game1.spriteBatch, message, Game1.viewport.Width / 2 - SpriteText.getWidthOfString(message) / 2, 16, "", 1f, -1);
                    }
                }
            };

            helper.ConsoleCommands.Add("br_kick", "Kick a player. Usage: br_kick <player ID>", (a, b) =>
            {
                if (!Game1.IsServer)
                {
                    Monitor.Log("Need to be the server host", LogLevel.Info);
                }
                else if (b.Length != 1)
                {
                    Monitor.Log("Need 1 argument", LogLevel.Info);
                }
                else if (!long.TryParse(b[0], out long x))
                {
                    Monitor.Log("Not a valid number", LogLevel.Info);
                }
                else
                {
                    try
                    {
                        var f = Game1.getOnlineFarmers().First(p => p != Game1.player && p.UniqueMultiplayerID == x);
                        NetworkUtility.KickPlayer(f, "You have been kicked by the host.");
                    }
                    catch (Exception)
                    {
                        Monitor.Log($"Could not find player with id {x}", LogLevel.Info);
                    }
                }
            });

            helper.ConsoleCommands.Add("br_setNumberOfPlayerSlots", "Sets the number of player slots. Usage: br_setNumberOfPlayerSlots <number of slots>", (a, b) =>
            {
                if (!Game1.IsServer)
                {
                    Monitor.Log("Need to be the server host", LogLevel.Info);
                }
                else if (b.Length != 1)
                {
                    Monitor.Log("Need 1 argument", LogLevel.Info);
                }
                else if (!int.TryParse(b[0], out int n))
                {
                    Monitor.Log("Not a valid number", LogLevel.Info);
                }
                else
                {
                    n = Math.Abs(n);
                    var emptyCabins = Game1.getFarm().buildings.Where(z => z.daysOfConstructionLeft.Value <= 0 && z.indoors.Value is Cabin).ToArray();

                    if (n > emptyCabins.Length)
                    {
                        for (int i = 0; i < n - emptyCabins.Length; i++)
                        {
                            var blueprint = new BluePrint("Log Cabin");
                            var building  = new Building(blueprint, new Vector2(-10000, 0));
                            Game1.getFarm().buildings.Add(building);

                            try
                            {
                                foreach (var warp in building.indoors.Value.warps)
                                {
                                    //warp.TargetName = "Forest";
                                    Helper.Reflection.GetField <NetString>(warp, "targetName", true).SetValue(new NetString("Forest"));
                                    warp.TargetX = 100;
                                    warp.TargetY = 20;
                                }
                            }
                            catch (Exception) { }
                        }

                        Monitor.Log($"Added {n - emptyCabins.Length} player slots", LogLevel.Info);
                    }
                    else if (n < emptyCabins.Length)
                    {
                        for (int i = 0; i < emptyCabins.Length - n; i++)
                        {
                            Game1.getFarm().buildings.Remove(emptyCabins[i]);
                        }

                        Monitor.Log($"Removed {emptyCabins.Length - n} player slots", LogLevel.Info);
                    }
                    else
                    {
                        Monitor.Log($"There are already {n} player slots", LogLevel.Info);
                    }
                }
            });
        }
        private static bool ProcessMessage(byte[] msgData, Farmer sourceFarmer)
        {
            var subMessageType = (NetworkUtility.MessageTypes)BitConverter.ToInt32(msgData, 0);

            Console.WriteLine($"Receiving special message, type = {subMessageType}. Message length = {msgData.Length}");

            switch (subMessageType)
            {
            case (NetworkUtility.MessageTypes.TAKE_DAMAGE):
                int damage = BitConverter.ToInt32(msgData, 4);

                long?damagerID = null;
                if (msgData.Length > 8)
                {
                    damagerID = BitConverter.ToInt64(msgData, 8);
                }

                Console.WriteLine($"Taking {damage} damage from another player");

                ModEntry.BRGame.TakeDamage(damage, damagerID);
                return(false);

            case (NetworkUtility.MessageTypes.SERVER_BROADCAST_GAME_START):
                if (!Game1.IsServer)
                {
                    int  numberOfPlayers     = BitConverter.ToInt32(msgData, 4);
                    int  stormIndex          = BitConverter.ToInt32(msgData, 8);
                    bool isHostParticipating = BitConverter.ToBoolean(msgData, 12);

                    ModEntry.BRGame.ClientStartGame(numberOfPlayers, isHostParticipating, stormIndex);
                }
                return(false);

            case NetworkUtility.MessageTypes.WARP:
                int tileX = BitConverter.ToInt32(msgData, 4);
                int tileY = BitConverter.ToInt32(msgData, 8);

                StringBuilder sb = new StringBuilder();
                for (int i = 12; i < msgData.Length; i++)
                {
                    sb.Append((char)msgData[i]);
                }

                string locationName = sb.ToString().Substring(1);

                Console.WriteLine($"I was told by the server to move to {locationName} @ ({tileX},{tileY})");

                //Face downwards & Warp
                Game1.player.FacingDirection = 2;
                Game1.player.warpFarmer(new TileLocation(locationName, tileX, tileY).CreateWarp());
                return(false);

            case (NetworkUtility.MessageTypes.SERVER_BROADCAST_GAME_END):
                if (!Game1.IsServer)
                {
                    ModEntry.BRGame.ClientEndGame();
                }
                return(false);

            case (NetworkUtility.MessageTypes.RELAY_MESSAGE_TO_ANOTHER_PLAYER):
                if (Game1.IsServer)
                {
                    long targetUniqueID = BitConverter.ToInt64(msgData, 4);

                    byte[] messageData = new byte[msgData.Length - 12];
                    Array.Copy(msgData, 12, messageData, 0, msgData.Length - 12);

                    if (targetUniqueID == Game1.player.UniqueMultiplayerID)
                    {
                        Console.WriteLine("Received relay instruction addressed to myself");
                        ProcessMessage(messageData, sourceFarmer);
                        return(false);
                    }
                    else
                    {
                        Console.WriteLine($"Relaying a message to {targetUniqueID}...");
                        Game1.server.sendMessage(targetUniqueID, new OutgoingMessage(NetworkUtility.uniqueMessageType, Game1.player, (object)messageData));
                        return(false);
                    }
                }
                else
                {
                    Console.WriteLine("Accidentally received relay instruction as a client");
                    return(false);
                }

            case (NetworkUtility.MessageTypes.TELL_SERVER_I_DIED):
                if (msgData.Length > 12)
                {
                    ModEntry.BRGame.HandleDeath(sourceFarmer, BitConverter.ToInt64(msgData, 12));
                }
                else
                {
                    ModEntry.BRGame.HandleDeath(sourceFarmer);
                }
                return(false);

            case NetworkUtility.MessageTypes.SERVER_BROADCAST_CHAT_MESSAGE:
                string message   = Encoding.UTF8.GetString(msgData.Skip(4).ToArray()).Substring(1);
                string colorName = "white";
                try
                {
                    colorName = message.Substring(1, message.Substring(1).IndexOf(']'));
                    Console.WriteLine($"color = {colorName}");
                }
                catch (Exception) { }

                var color = StardewValley.Menus.ChatMessage.getColorFromName(colorName);
                if (color == Microsoft.Xna.Framework.Color.White)
                {
                    color = Microsoft.Xna.Framework.Color.Gold;
                }

                Game1.chatBox.addMessage(message, color);
                return(false);

            case (NetworkUtility.MessageTypes.TELL_PLAYER_HIT_SHAKE_TIMER):
                int howLongMilliseconds = BitConverter.ToInt32(msgData, 4);
                HitShaker.SetHitShakeTimer(sourceFarmer.UniqueMultiplayerID, howLongMilliseconds);
                return(false);

            case NetworkUtility.MessageTypes.BROADCAST_ALIVE_COUNT:
                int howManyPlayersAlive = BitConverter.ToInt32(msgData, 4);

                if (ModEntry.BRGame.OverlayUI != null)
                {
                    ModEntry.BRGame.OverlayUI.AlivePlayers = howManyPlayersAlive;
                }
                return(false);

            case NetworkUtility.MessageTypes.SEND_PHASE_DATA:
                try
                {
                    var b = new BinaryFormatter();

                    var phases = (List <Phase>[])b.Deserialize(new MemoryStream(msgData.Skip(4).ToArray()));

                    Console.WriteLine($"Received phases data from server, info: Length={phases.Length}");

                    Storm.Phases = phases;
                }catch (Exception)
                {
                    Console.WriteLine("Unable to process phases data. Kicking to prevent glitches...");
                    long id = sourceFarmer.UniqueMultiplayerID;
                    NetworkUtility.SendChatMessageToPlayerWithoutMod(id, "Unable to process phases data. Kicking to prevent glitches...");
                    Game1.server.sendMessage(id, new OutgoingMessage((byte)19, id, new object[0]));
                    Game1.server.playerDisconnected(id);
                    Game1.otherFarmers.Remove(id);
                }
                return(false);

            case NetworkUtility.MessageTypes.KICK_PLAYER:
                StringBuilder sb2 = new StringBuilder();
                for (int i = 4; i < msgData.Length; i++)
                {
                    sb2.Append((char)msgData[i]);
                }

                string kickMsg = sb2.ToString().Substring(1);
                Console.WriteLine($"Kicked. Reason: \"{kickMsg}\"");

                Game1.client.disconnect();

                System.Threading.Tasks.Task.Factory.StartNew(() =>
                {
                    System.Threading.Thread.Sleep(300);
                    Game1.activeClickableMenu = new StardewValley.Menus.DialogueBox($"Kicked. Reason: \"{kickMsg}\"");
                });

                return(false);

            case NetworkUtility.MessageTypes.SEND_MY_VERSION_TO_SERVER:
                if (Game1.IsServer)
                {
                    int    major = BitConverter.ToInt32(msgData, 4);
                    int    minor = BitConverter.ToInt32(msgData, 8);
                    byte[] sha   = msgData.Skip(12).ToArray();

                    Console.WriteLine($"Received version from client {sourceFarmer.Name}/{sourceFarmer.UniqueMultiplayerID}: {major}.{minor}");
                    new AutoKicker().AcknowledgeClientVersion(sourceFarmer.UniqueMultiplayerID, major, minor, sha);

                    if (ModEntry.BRGame.IsGameInProgress)
                    {
                        System.Threading.Tasks.Task.Factory.StartNew(() =>
                        {
                            System.Threading.Thread.Sleep(1000);
                            Game1.server.sendMessage(sourceFarmer.UniqueMultiplayerID,
                                                     new OutgoingMessage(NetworkUtility.uniqueMessageType, Game1.player, (int)NetworkUtility.MessageTypes.TELL_NEW_PLAYER_TO_SPECTATE));
                        });
                    }
                }
                return(false);

            case NetworkUtility.MessageTypes.TELL_NEW_PLAYER_TO_SPECTATE:
                if (!Game1.IsServer)
                {
                    System.Threading.Tasks.Task.Factory.StartNew(() =>
                    {
                        System.Threading.Thread.Sleep(1000);

                        if (!ModEntry.BRGame.alivePlayers.Contains(Game1.player.UniqueMultiplayerID))
                        {
                            var oldLocation = Game1.player.currentLocation;
                            var oldPosition = new xTile.Dimensions.Location(
                                (int)Game1.player.Position.X - Game1.viewport.Width / 2,
                                (int)Game1.player.Position.Y - Game1.viewport.Height / 2);

                            SpectatorMode.EnterSpectatorMode(oldLocation, oldPosition);
                        }
                    });
                }
                return(false);

            case NetworkUtility.MessageTypes.GIVE_HAT:
                int hatID = 10;                        //Chicken hat
                Game1.player?.addItemToInventory(new StardewValley.Objects.Hat(hatID));
                return(false);

            default:
                return(true);
            }
        }