### Install SampSharp Template Source: https://sampsharp.net/docs/getting-started/quick-start.html Installs the SampSharp project template using the .NET CLI. This is a one-time setup command. ```bash dotnet new install SampSharp.Templates ``` -------------------------------- ### Handle Menu Events with a System Source: https://sampsharp.net/docs/features/dialog-menus.html An example system that creates a menu and handles player interactions via `OnPlayerSelectedMenuRow` and `OnPlayerExitedMenu` events. ```csharp public class MenuEventSystem : ISystem { private readonly Menu _actionsMenu; public MenuEventSystem(IWorldService worldService) { _actionsMenu = worldService.CreateMenu("Actions", new Vector2(200, 100), 200); _actionsMenu.AddItem("Buy weapon"); _actionsMenu.AddItem("Heal"); _actionsMenu.AddItem("Quit"); } [Event] public void OnPlayerSelectedMenuRow(Player player, byte row) { switch (row) { case 0: player.SendClientMessage("You chose: Buy weapon"); break; case 1: player.Health = 100; player.SendClientMessage("You have been healed."); break; case 2: player.SendClientMessage("Goodbye!"); player.Kick(); break; } _actionsMenu.Hide(player); } [Event] public void OnPlayerExitedMenu(Player player) { // Player pressed Escape to close the menu player.SendClientMessage("Menu closed."); } } ``` -------------------------------- ### Example: A Simple System Implementation Source: https://sampsharp.net/docs/entity-component-system/systems.html Demonstrates a basic system implementing ISystem with timer, event, and command handlers. Includes dependency injection for event handlers. ```csharp using SampSharp.Entities; using SampSharp.Entities.SAMP; using SampSharp.Entities.SAMP.Commands; public class MyFirstSystem : ISystem { [Timer(1000)] public void OnTimer() { // This method runs every second. } [Event] public void OnGameModeInit(IWorldService world, IEntityManager entityManager) { // Called when the gamemode starts. var vehicle = world.CreateVehicle(VehicleModelType.Landstalker, new Vector3(0, 6, 15), 45, 4, 4); vehicle.SetNumberPlate("SampSharp"); } [PlayerCommand(Name = "kill")] public void KillPlayer(Player player) { player.Health = 0; player.SendClientMessage("You have been killed!"); } } ``` -------------------------------- ### Player Command with Multiple Parameters Source: https://sampsharp.net/docs/features/command-system.html Example of a player command with multiple parameters, including a Player, a VehicleModelType, and an IWorldService. ```csharp [PlayerCommand(Name = "spawn")] public void SpawnCommand(Player player, VehicleModelType model, IWorldService worldService) { player.SendClientMessage($"Spawned a {model}!"); var vehicle = worldService.CreateVehicle(model, player.Position, player.Angle, -1, -1); player.PutInVehicle(vehicle); } ``` -------------------------------- ### Extend Host Builder with Command Modules Source: https://sampsharp.net/docs/host-configuration/startup.html Use `UseEntities()` to get the host builder and chain `UsePlayerCommands()`, `UseConsoleCommands()`, or `UseCommands()` to enable command systems. ```csharp context.UseEntities() .UsePlayerCommands() // player /commands only .UseConsoleCommands() // server console commands only .UseCommands(); // shortcut for both ``` -------------------------------- ### Injecting IConfiguration in a Service Source: https://sampsharp.net/docs/host-configuration/configuration.html Demonstrates how to inject `IConfiguration` into a service constructor to access configuration values. The example shows retrieving a database connection string. ```csharp public class MyService(IConfiguration configuration) { private readonly string? _connectionString = configuration["Database:ConnectionString"]; } ``` -------------------------------- ### Create a Global Pickup Source: https://sampsharp.net/docs/features/pickups.html Use CreatePickup to spawn a global pickup visible to all players. This example creates a health pack that respawns when the player dies. ```csharp [Event] public void OnGameModeInit(IWorldService worldService) { var pickup = worldService.CreatePickup( model: 1240, // health pack type: PickupType.ShowAndRespawnWhenDeath, position: new Vector3(2000, -1500, 13)); } ``` -------------------------------- ### Configure Services Before Gamemode Init Source: https://sampsharp.net/docs/host-configuration/startup.html Use the Configure method to access the service provider and perform startup tasks like database migrations or pre-loading data before the gamemode initializes. This ensures services are ready when event handlers and systems start receiving callbacks. ```csharp public void Configure(IEcsBuilder builder) { // Run a database migration before the gamemode starts accepting events. var db = builder.Services.GetRequiredService(); db.Database.Migrate(); // Pre-load reference data into a cache so the first OnPlayerConnect doesn't pay the cost. var cache = builder.Services.GetRequiredService(); cache.Preload(); } ``` -------------------------------- ### Create a Single-Column Menu Source: https://sampsharp.net/docs/features/dialog-menus.html Creates a menu with a title and adds three selectable items. This is a basic example of menu creation and item population. ```csharp [Event] public void OnGameModeInit(IWorldService worldService) { var menu = worldService.CreateMenu( title: "Actions", position: new Vector2(200, 100), col0Width: 200 ); menu.AddItem("Buy weapon"); menu.AddItem("Heal"); menu.AddItem("Quit"); } ``` -------------------------------- ### Adding Custom Player State with Components Source: https://sampsharp.net/docs/features/players.html This example shows how to define a custom component 'Account' to store player-specific data like user ID, level, and login status. It demonstrates adding this component to a player upon connection. ```csharp public class Account : Component { public int UserId { get; set; } public int Level { get; set; } public bool IsLoggedIn { get; set; } } public class AccountSystem : ISystem { [Event] public void OnPlayerConnect(Player player) { var account = player.AddComponent(); account.Level = 1; } [Event] public void OnPlayerSpawn(Player player) { var account = player.GetComponent(); if (account is { IsLoggedIn: false }) player.SendClientMessage(Color.Red, "Please /login first."); } } ``` -------------------------------- ### Create Actor Example Source: https://sampsharp.net/docs/features/npcs.html Create a static Actor with a specific model, position, and rotation. Actors can have animations applied to them. This is useful for non-movable characters like shop clerks. ```csharp [Event] public void OnGameModeInit(IWorldService worldService) { var clerk = worldService.CreateActor( modelId: 156, // skin position: new Vector3(1352, -1758, 13), rotation: 0f); clerk.IsInvulnerable = true; clerk.ApplyAnimation( library: "SHOP", name: "SHP_Rob_React", fDelta: 4.1f, loop: true, lockX: false, lockY: false, freeze: false, time: TimeSpan.Zero); } ``` -------------------------------- ### Create New SampSharp Project Source: https://sampsharp.net/docs/getting-started/quick-start.html Creates a new SampSharp gamemode project with the specified name and navigates into the project directory. This command uses the installed SampSharp template. ```bash dotnet new sampsharp -n MyFirstGameMode cd MyFirstGameMode ``` -------------------------------- ### Creating a Plain Tablist Dialog Source: https://sampsharp.net/docs/features/dialog-menus.html Example of creating a tablist dialog without explicit column headers, specified by column count. ```csharp var dialog = new TablistDialog("Online Players", "View", "Close", columnCount: 2); dialog.Add("Johnny", "100"); dialog.Add("Sarah", "250"); ``` -------------------------------- ### Create Player Gang Zone with Parent Source: https://sampsharp.net/docs/features/gang-zones.html Example of creating a player-specific gang zone that is automatically destroyed when its parent player entity is destroyed. ```csharp worldService.CreatePlayerGangZone(owner: player, min: a, max: b, parent: player); ``` -------------------------------- ### Create a Player Pickup with Parent Source: https://sampsharp.net/docs/features/pickups.html To ensure a player-specific pickup is destroyed when the owner disconnects, pass the player as the 'parent' argument during creation. This example creates a money bag pickup parented to the player. ```csharp worldService.CreatePlayerPickup( owner: player, model: 1254, type: PickupType.ShowTillPickedUp, position: player.Position, parent: player); ``` -------------------------------- ### Handle Pickup Events Source: https://sampsharp.net/docs/features/pickups.html Implement OnPlayerPickUpPickup and OnPlayerPickUpPlayerPickup events to react when players collect global or player-specific pickups, respectively. The first example heals the player, and the second grants money and destroys the pickup. ```csharp public class PickupSystem : ISystem { [Event] public void OnPlayerPickUpPickup(Player player, Pickup pickup) { if (pickup.Model == 1240) player.Health = Math.Min(100f, player.Health + 25f); } [Event] public void OnPlayerPickUpPlayerPickup(Player player, PlayerPickup pickup) { player.GiveMoney(500); pickup.Destroy(); } } ``` -------------------------------- ### Create a Static Pickup Source: https://sampsharp.net/docs/features/pickups.html Use CreateStaticPickup for server-handled pickups like weapons, health, or armor that provide an effect on contact without event handling. This example creates an armor pickup. ```csharp worldService.CreateStaticPickup( model: 1242, // armor type: PickupType.ShowAndRespawnWhenDeath, position: new Vector3(2010, -1500, 13)); ``` -------------------------------- ### Advanced Timer Control with ITimerService Source: https://sampsharp.net/docs/features/timers.html Utilize ITimerService for manual control over timer creation and lifecycle. Start returns a repeating timer, and Delay returns a one-time timer. Both return a TimerReference for cancellation. ```csharp [Event] public void OnGameModeInit(ITimerService timerService) { // Repeating timer: runs every 1 second var repeatTimer = timerService.Start(serviceProvider => { Console.WriteLine("Timer tick!"); }, TimeSpan.FromSeconds(1)); // One-time timer: runs once after 5 seconds var delayTimer = timerService.Delay(serviceProvider => { Console.WriteLine("Delayed action executed"); }, TimeSpan.FromSeconds(5)); } ``` -------------------------------- ### Custom Text Formatter Implementation Source: https://sampsharp.net/docs/features/command-system.html Implement the ICommandTextFormatter interface to customize how command usage and error messages are formatted. This example formats command usage strings. ```csharp public class MyCommandTextFormatter : ICommandTextFormatter { public string FormatCommandUsage(string commandName, string? group, CommandParameterInfo[] parameters, bool includeSlash = true) { var prefix = includeSlash ? "/" : ""; var groupPrefix = group != null ? $"{group} "" : ""; var paramStr = string.Join(" ", parameters.Select(p => p.IsOptional ? $"[{p.Name}]" : $"<{p.Name}>" )); return $"{prefix}{groupPrefix}{commandName} {paramStr}".Trim(); } } ``` -------------------------------- ### Create a Player-Specific Pickup Source: https://sampsharp.net/docs/features/pickups.html Use CreatePlayerPickup to create a pickup visible and collectible only by a specific player. This example creates a money bag pickup near the player. ```csharp [Event] public void OnPlayerSpawn(Player player, IWorldService worldService) { worldService.CreatePlayerPickup( owner: player, model: 1254, // money bag type: PickupType.ShowTillPickedUp, position: player.Position + new Vector3(2, 0, 0)); } ``` -------------------------------- ### Custom Permission Checker Implementation Source: https://sampsharp.net/docs/features/command-system.html Implement the IPermissionChecker interface to define custom permission logic for player commands. This example checks for a 'permission' tag on the command. ```csharp public class MyPermissionChecker : IPermissionChecker { public bool HasPermission(Player player, CommandDefinition command) { // Check if the command has a "permission" tag var permission = command.GetTag("permission"); if (permission == null) return true; // No permission requirement // Check your permission system return player.IsAdmin || HasPlayerPermission(player, permission); } private bool HasPlayerPermission(Player player, string permission) { // Implement your game's permission system here return false; } } ``` -------------------------------- ### NPC Path Following System Source: https://sampsharp.net/docs/features/npcs.html Defines a system for creating and managing NPC patrols along a defined path. Includes starting a patrol and restarting it upon completion. ```csharp public class PatrolSystem : ISystem { public PatrolSystem(INpcService npc, IWorldService world) { var pathId = npc.CreatePath(); npc.AddPointToPath(pathId, new Vector3(2000, -1500, 13), stopRange: 1f); npc.AddPointToPath(pathId, new Vector3(2050, -1500, 13), stopRange: 1f); npc.AddPointToPath(pathId, new Vector3(2050, -1450, 13), stopRange: 1f); var bandit = world.CreateNpc("Patrol_01"); bandit.Spawn(); bandit.MoveByPath(pathId, NPCMoveType.Jog); } [Event] public void OnNPCFinishMovePath(Npc npc, int pathId) { // Restart the patrol npc.MoveByPath(pathId, NPCMoveType.Jog, reverse: true); } } ``` -------------------------------- ### Simple Repeating Timers with [Timer] Attribute Source: https://sampsharp.net/docs/features/timers.html Use the [Timer] attribute on a method within an ISystem to create a repeating timer. Specify the interval in milliseconds. The timer starts automatically upon system initialization. ```csharp public class GameSystem : ISystem { [Timer(1000)] // Interval in milliseconds (1 second) public void OnGameTick() { Console.WriteLine("Game tick!"); } [Timer(100)] // 10 times per second public void OnFastUpdate() { // High-frequency updates } } ``` -------------------------------- ### Custom Permission Checking with Command Tags Source: https://sampsharp.net/docs/features/command-system.html Provides an example of a custom IPermissionChecker that uses command tags to determine if a player has permission to execute a command. It checks for a 'permission' tag and verifies the player's authorization. ```csharp public class MyPermissionChecker : IPermissionChecker { public bool HasPermission(Player player, CommandDefinition command) { var permission = command.GetTag("permission"); if (permission == null) return true; // No permission requirement return player.HasPermission(permission); } } ``` -------------------------------- ### Enable Command System in Startup Source: https://sampsharp.net/docs/features/command-system.html Call UseCommands() in your IEcsStartup.Initialize() method to enable the command system. ```csharp public class Startup : IEcsStartup { public void Initialize(IStartupContext context) { context.UseEntities() .UseCommands(); } } ``` -------------------------------- ### Registering Systems Manually with Dependency Injection Source: https://sampsharp.net/docs/entity-component-system/systems.html Shows how to disable default system loading and manually register a system using AddSystem() within an IEcsStartup implementation. ```csharp using Microsoft.Extensions.DependencyInjection; public class Startup : IEcsStartup { public void Initialize(IStartupContext context) { context.UseEntities() .DisableDefaultSystemsLoading(); } public void ConfigureServices(IServiceCollection services) { services.AddSystem(); } public void Configure(IEcsBuilder builder) { } } ``` -------------------------------- ### Creating and Showing a List Dialog Source: https://sampsharp.net/docs/features/dialog-menus.html Demonstrates how to create a list dialog, add items with tags, and process the response to spawn a vehicle. ```csharp var dialog = new ListDialog("Select a vehicle", button1: "Spawn", button2: "Cancel"); dialog.Add("Infernus", tag: VehicleModelType.Infernus); dialog.Add("Turismo", tag: VehicleModelType.Turismo); dialog.Add("Cheetah", tag: VehicleModelType.Cheetah); var response = await dialogs.ShowAsync(player, dialog); if (response.Response == DialogResponse.LeftButton && response.Item != null) { var model = (VehicleModelType)response.Item.Tag!; worldService.CreateVehicle(model, player.Position, player.Angle, -1, -1); player.SendClientMessage($"Spawned a {model}."); } ``` -------------------------------- ### Initialize ECS Host Builder Source: https://sampsharp.net/docs/host-configuration/startup.html Call `UseEntities()` on the startup context to opt into the ECS framework and chain further configurations. ```csharp public void Initialize(IStartupContext context) { context.UseEntities() .UseCommands() .ConfigureUnhandledExceptionHandler((sp, where, ex) => { var log = sp.GetRequiredService>(); log.LogError(ex, "Unhandled exception in {Where}", where); }); } ``` -------------------------------- ### Configuring Event Middleware in Startup Source: https://sampsharp.net/docs/entity-component-system/events.html Shows how to register a custom middleware component for a specific event ('OnPlayerText') within the ECS configuration in the Startup class. ```csharp public class Startup : IEcsStartup { public void Configure(IEcsBuilder builder) { builder.UseMiddleware("OnPlayerText"); } // ... other methods } ``` -------------------------------- ### Adding Additional Logging Providers Source: https://sampsharp.net/docs/host-configuration/logging.html Use ConfigureLogging on the host builder to add more logging providers, such as file logging, alongside the default open.mp provider. This provides flexibility for richer output. ```csharp public void Initialize(IStartupContext context) { context.UseEntities() .ConfigureLogging(logging => { logging.SetMinimumLevel(LogLevel.Information); logging.AddFile("logs/gamemode-{Date}.log"); // e.g. Serilog.Extensions.Logging.File }); } ``` -------------------------------- ### Show and Hide a Menu for a Player Source: https://sampsharp.net/docs/features/dialog-menus.html Demonstrates how to display a menu to a specific player using `Show(player)` and how to dismiss it later using `Hide(player)`. ```csharp menu.Show(player); // later... menu.Hide(player); ``` -------------------------------- ### Register Services with Dependency Injection Source: https://sampsharp.net/docs/host-configuration/startup.html Implement `IEcsStartup.ConfigureServices` to register application services using `IServiceCollection` and access configuration. ```csharp public void ConfigureServices(IServiceCollection services, IConfiguration configuration) { services.AddSingleton(); services.AddSingleton(); services.AddDbContext(o => o.UseSqlite(configuration["Database:ConnectionString"])); } ``` -------------------------------- ### Filtering Messages for the open.mp Provider Source: https://sampsharp.net/docs/host-configuration/logging.html Filter messages routed specifically through the open.mp provider, for example, to send 'Trace' level messages to open.mp while maintaining a different default filter for other providers. ```json { "Logging": { "Omp": { "LogLevel": { "Default": "Trace" } } } } ``` -------------------------------- ### Chaining Dialogs for Multi-Step Flows Source: https://sampsharp.net/docs/features/dialog-menus.html Illustrates how to chain multiple dialogs asynchronously to create a simple multi-step user interaction flow. ```csharp [Event] public async Task OnPlayerConnect(Player player, IDialogService dialogs) { var rules = new MessageDialog("Rules", "Do you accept the rules?", "Yes", "No"); var rulesResponse = await dialogs.ShowAsync(player, rules); if (rulesResponse.Response != DialogResponse.LeftButton) { player.Kick(); return; } var name = new InputDialog("Setup", "Enter your display name:", "OK"); var nameResponse = await dialogs.ShowAsync(player, name); if (nameResponse.Response == DialogResponse.LeftButton && !string.IsNullOrWhiteSpace(nameResponse.InputText)) { player.SetName(nameResponse.InputText); } } ``` -------------------------------- ### Minimal SampSharp Startup Class Source: https://sampsharp.net/docs/host-configuration/startup.html This is the basic structure of a SampSharp startup class generated by the project template. It implements the IEcsStartup interface to configure the ECS framework. ```csharp using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using SampSharp.OpenMp.Core; public class Startup : IEcsStartup { public void Initialize(IStartupContext context) { context.UseEntities().UseCommands(); } public void ConfigureServices(IServiceCollection services, IConfiguration configuration) { } public void Configure(IEcsBuilder builder) { } } ``` -------------------------------- ### Create and Configure a 3D Model Preview TextDraw Source: https://sampsharp.net/docs/features/text-draws.html This snippet demonstrates how to create a TextDraw that displays a 3D model preview. It sets the font to PreviewModel, configures its size and appearance, specifies the model to preview (Infernus), and sets its rotation and zoom. It also shows how to set vehicle colors for previews and finally displays the text draw. ```csharp var preview = worldService.CreateTextDraw(new Vector2(500, 200), "_"); preview.Font = TextDrawFont.PreviewModel; preview.TextSize = new Vector2(80, 80); preview.UseBox = true; preview.PreviewModel = 411; // Infernus preview.SetPreviewRotation(new Vector3(0, 0, 45), zoom: 1.0f); preview.SetPreviewVehicleColor(1, 1); preview.Show(); __ ``` -------------------------------- ### Create and Style a Global TextDraw Source: https://sampsharp.net/docs/features/text-draws.html Demonstrates creating a global TextDraw, setting its font, letter size, color, and box properties, and then showing it to all players. Ensure styling is configured before showing. ```csharp [Event] public void OnGameModeInit(IWorldService worldService) { var hud = worldService.CreateTextDraw( position: new Vector2(10, 400), text: "My Server"); hud.Font = TextDrawFont.Pricedown; hud.LetterSize = new Vector2(0.5f, 1.6f); hud.ForeColor = new Color(255, 255, 0, 255); hud.UseBox = false; hud.Show(); } ``` -------------------------------- ### Manipulate Pickup Properties Source: https://sampsharp.net/docs/features/pickups.html Modify pickup appearance or behavior at runtime using methods like SetModel and SetType. This example changes a pickup's model to armor and its type to respawn near the player. ```csharp // Change the appearance or behaviour at runtime pickup.SetModel(1242); pickup.SetType(PickupType.ShowNearAndRespawnWhenPickup); // Per-player visibility pickup.SetHiddenForPlayer(player, hidden: true); if (pickup.IsHiddenForPlayer(player)) { /* ... */ } // Force streaming pickup.StreamOutForPlayer(player); pickup.StreamInForPlayer(player); // Move silently (no visual update) pickup.SetPositionNoUpdate(new Vector3(2020, -1500, 13)); ``` -------------------------------- ### Creating a Tablist Dialog with Headers Source: https://sampsharp.net/docs/features/dialog-menus.html Demonstrates creating a tablist dialog with custom column headers and attaching player data as tags to rows. ```csharp var dialog = new TablistDialog( caption: "Online Players", button1: "View", button2: "Close", columnHeader1: "Name", columnHeader2: "Score" ); foreach (var onlinePlayer in onlinePlayers) { dialog.Add(new TablistDialogRow(onlinePlayer.Name, onlinePlayer.Score.ToString()) { Tag = onlinePlayer }); } var response = await dialogs.ShowAsync(player, dialog); if (response.Response == DialogResponse.LeftButton && response.Item != null) { var selected = (Player)response.Item.Tag!; player.SendClientMessage($"You selected {selected.Name}."); } ``` -------------------------------- ### Create and Configure Selectable Text Draw Source: https://sampsharp.net/docs/features/text-draws.html Demonstrates creating a clickable text draw, setting its appearance, and enabling selection mode for player interaction. Requires explicit alignment and text size for hitbox registration. ```csharp public class MenuSystem : ISystem { private readonly TextDraw _playButton; public MenuSystem(IWorldService world) { _playButton = world.CreateTextDraw(new Vector2(50, 25), "PLAY"); _playButton.Font = TextDrawFont.Pricedown; _playButton.LetterSize = new Vector2(0.8f, 2.2f); _playButton.ForeColor = new Color(0, 255, 0, 255); _playButton.UseBox = true; _playButton.Alignment = TextDrawAlignment.Left; _playButton.TextSize = new Vector2(140, 20); _playButton.Selectable = true; } [Event] public void OnPlayerConnect(Player player) { _playButton.Show(player); // Enter selection mode so the player can click; hoverColor highlights the // currently-hovered text draw. player.SelectTextDraw(new Color(255, 255, 0, 255)); } [Event] public void OnPlayerClickTextDraw(Player player, TextDraw draw) { if (draw != _playButton) return; _playButton.Hide(player); player.CancelSelectTextDraw(); player.Spawn(); } } ``` -------------------------------- ### Add Custom Components to Player Classes Source: https://sampsharp.net/docs/features/players.html This system demonstrates attaching custom components like 'Faction' to player classes to store metadata. It shows how to add components in `OnGameModeInit` and retrieve them in `OnPlayerRequestClass`. ```csharp public class Faction : Component { public string Name { get; set; } = ""; public string Description { get; set; } = ""; } public class ClassSetupSystem : ISystem { [Event] public void OnGameModeInit(IServerService server) { var copClass = server.AddPlayerClass(modelId: 280, spawnPosition: new Vector3(1552, -1675, 16), angle: 90f); copClass.AddComponent().Name = "LSPD"; var medicClass = server.AddPlayerClass(modelId: 274, spawnPosition: new Vector3(1172, -1323, 15), angle: 0f); var medicFaction = medicClass.AddComponent(); medicFaction.Name = "Paramedic"; medicFaction.Description = "Revive downed players for a reward."; } [Event] public void OnPlayerRequestClass(Player player, Class klass) { var faction = klass.GetComponent(); if (faction != null) player.GameText($"~y~{faction.Name}", TimeSpan.FromSeconds(2), GameTextStyle.Style3); } } ``` -------------------------------- ### Style PlayerTextDraw Properties Source: https://sampsharp.net/docs/features/text-draws.html This example shows various styling options available for PlayerTextDraws, including font face, letter size, foreground and background colors, shadow, outline, box usage, box color, text size (box dimensions), alignment, and proportional character spacing. These properties allow for detailed customization of the text draw's appearance. ```csharp // Font face (Diploma, Normal, Slim, Pricedown, DrawSprite, PreviewModel) draw.Font = TextDrawFont.Pricedown; // Letter width and height draw.LetterSize = new Vector2(0.5f, 1.6f); // Colors draw.ForeColor = new Color(255, 255, 255, 255); // letter color draw.BackColor = new Color(0, 0, 0, 255); // background color draw.Shadow = 1; // shadow depth draw.Outline = 0; // outline thickness // Box around the text draw.UseBox = true; draw.BoxColor = new Color(0, 0, 0, 150); draw.TextSize = new Vector2(200, 50); // box size + clickable area (see warning below) // Alignment (Default, Left, Center, Right) draw.Alignment = TextDrawAlignment.Center; // Variable-width vs fixed-width characters draw.Proportional = true; ``` -------------------------------- ### Injecting Dependencies into Commands Source: https://sampsharp.net/docs/features/command-system.html Demonstrates injecting services like IWorldService into command methods. Injected services are automatically provided, while other parameters can be optional user inputs. ```csharp [PlayerCommand(Name = "money")] public void MoneyCommand(Player player, IWorldService worldService, int? amount = null) { if (amount.HasValue) { player.Money = amount.Value; player.SendClientMessage($"Money set to ${amount.Value}"); } else { player.SendClientMessage($"Current money: ${player.Money}"); } } ``` -------------------------------- ### Configure Case-Sensitive Commands Source: https://sampsharp.net/docs/features/command-system.html Configure command case sensitivity in your startup by setting StringComparison to StringComparison.Ordinal for player and console commands. ```csharp public class Startup : IEcsStartup { public void Initialize(IStartupContext context) { context.UseEntities() .UsePlayerCommands(cfg => cfg.StringComparison = StringComparison.Ordinal) .UseConsoleCommands(cfg => cfg.StringComparison = StringComparison.Ordinal); } } ``` -------------------------------- ### Create and Initialize an NPC Source: https://sampsharp.net/docs/features/npcs.html Spawns an NPC with a specified model, sets its skin, position, and spawns it. This is typically done during game mode initialization. ```csharp [Event] public void OnGameModeInit(IWorldService worldService) { var npc = worldService.CreateNpc("Bandit"); npc.Skin = 109; npc.Position = new Vector3(2000, -1500, 13); npc.Spawn(); } ``` -------------------------------- ### Create a Two-Column Menu with Headers Source: https://sampsharp.net/docs/features/dialog-menus.html Creates a two-column menu with specified column widths and headers. Items are added with corresponding values in the second column. ```csharp var menu = worldService.CreateMenu( title: "Shop", position: new Vector2(200, 100), col0Width: 150, col1Width: 80 ); menu.Col0Header = "Item"; menu.Col1Header = "Price"; menu.AddItem("AK-47", "$500"); menu.AddItem("Shotgun", "$300"); menu.AddItem("Health", "$100"); ``` -------------------------------- ### Displaying a Message Dialog with Two Buttons Source: https://sampsharp.net/docs/features/dialog-menus.html Shows how to present a message dialog with two distinct buttons and handle the user's choice, including kicking the player if they decline. ```csharp var dialog = new MessageDialog( caption: "Server Rules", content: "No cheating. No griefing.\n\nDo you agree?", button1: "I Agree", button2: "Decline" ); dialogs.Show(player, dialog, response => { if (response.Response == DialogResponse.LeftButton) { player.SendClientMessage("Welcome aboard!"); } else { player.Kick(); } }); ``` -------------------------------- ### Add Player Classes on Game Mode Initialization Source: https://sampsharp.net/docs/features/players.html This snippet shows how to define player classes available during class selection by using `AddPlayerClass` in `OnGameModeInit`. ```csharp [Event] public void OnGameModeInit(IServerService server) { server.AddPlayerClass( modelId: 0, // CJ skin spawnPosition: new Vector3(1958, -2184, 13), angle: 0f, weapon1: Weapon.Colt45, weapon1Ammo: 100); } ``` -------------------------------- ### Accessing Registered Commands Source: https://sampsharp.net/docs/features/command-system.html Shows how to use IPlayerCommandService to retrieve and list all registered commands, including their aliases. This is useful for implementing help or command listing features. ```csharp [PlayerCommand(Name = "help")] public void HelpCommand(Player player, IPlayerCommandService commands) { player.SendClientMessage("--- Available Commands ---"); var commandList = commands.Registry.GetAll() .OrderBy(c => c.Name) .ToList(); foreach (var cmd in commandList) { var aliases = cmd.Aliases.Count > 0 ? $(" ({string.Join(", ", cmd.Aliases.Select(a => $"/{a.Name}"))})") : ""; player.SendClientMessage($"/{cmd.Name}{aliases}"); } } ``` -------------------------------- ### Extending Configuration Sources Source: https://sampsharp.net/docs/host-configuration/configuration.html Shows how to add custom configuration sources like JSON files and environment variables using `ConfigureAppConfiguration`. These custom sources will override SampSharp's built-in sources. ```csharp public void Initialize(IStartupContext context) { context.UseEntities() .ConfigureAppConfiguration(builder => { builder.AddJsonFile("secrets.json", optional: true, reloadOnChange: true); builder.AddEnvironmentVariables(prefix: "MYAPP_"); }); } ``` -------------------------------- ### Handle Vehicle Events Source: https://sampsharp.net/docs/features/vehicles.html Implement ISystem to respond to vehicle spawn, player enter, and player exit events. ```csharp public class VehicleEventSystem : ISystem { [Event] public void OnVehicleSpawn(Vehicle vehicle) { Console.WriteLine($"Vehicle spawned: {vehicle.Model}"); } [Event] public void OnPlayerEnterVehicle(Player player, Vehicle vehicle, bool isPassenger) { Console.WriteLine($"{player} entered vehicle {vehicle.Model}"); } [Event] public void OnPlayerExitVehicle(Player player, Vehicle vehicle) { Console.WriteLine($"{player} exited vehicle {vehicle.Model}"); } } ``` -------------------------------- ### Create and Show a Global Gang Zone Source: https://sampsharp.net/docs/features/gang-zones.html Demonstrates how to create a global gang zone with specified coordinates and color, and make it visible to all players. ```csharp [Event] public void OnGameModeInit(IWorldService worldService) { var zone = worldService.CreateGangZone( min: new Vector2(2000, -1700), max: new Vector2(2100, -1600)); zone.Color = new Color(255, 0, 0, 128); // semi-transparent red zone.Show(); // make it visible to all players } ``` -------------------------------- ### Handling InputDialog Response Source: https://sampsharp.net/docs/features/dialog-menus.html Demonstrates how to display an input dialog, await the response, and process the user's text input, including validation for empty or whitespace input. ```csharp // Plain text input var nameDialog = new InputDialog( caption: "Change Name", content: "Enter your new name:", button1: "OK", button2: "Cancel" ); var response = await dialogs.ShowAsync(player, nameDialog); if (response.Response == DialogResponse.LeftButton && !string.IsNullOrWhiteSpace(response.InputText)) { player.SetName(response.InputText); player.SendClientMessage($"Name changed to {player.Name}."); } ``` -------------------------------- ### Registering Custom Text Formatter Source: https://sampsharp.net/docs/features/command-system.html Register your custom text formatter in your startup class to replace the default implementation. ```csharp services.RemoveAll(typeof(ICommandTextFormatter)); services.AddSingleton(); ``` -------------------------------- ### Registering Custom Parameter Parser Factory Source: https://sampsharp.net/docs/features/command-system.html Register your custom parser factory in your startup class to enable parsing of custom parameter types. ```csharp services.RemoveAll(typeof(ICommandParameterParserFactory)); services.AddSingleton(); ``` -------------------------------- ### Spawn a Vehicle Source: https://sampsharp.net/docs/features/vehicles.html Use IWorldService.CreateVehicle to spawn a dynamic vehicle with specified model, position, rotation, colors, and respawn delay. ```csharp public void OnGameModeInit(IWorldService worldService) { var vehicle = worldService.CreateVehicle( VehicleModelType.Infernus, new Vector3(1500, -1500, 14), // position 90f, // rotation (degrees) color1: -1, // -1 = random primary color color2: -1, // -1 = random secondary color respawnDelay: 60 // seconds without a driver before respawn; -1 disables respawn ); } ``` -------------------------------- ### Connect Legacy NPC in C# Source: https://sampsharp.net/docs/features/npcs.html Connects a legacy NPC bot to the server using its name and the associated Pawn script. The script is expected to be located in the `npcmodes/` folder. ```csharp [Event] public void OnGameModeInit(IServerService server) { server.ConnectNpc(name: "Bot_01", script: "idle"); // npcmodes/idle.amx } ``` -------------------------------- ### Handle Common Player Events Source: https://sampsharp.net/docs/features/players.html This system demonstrates how to handle player connection, disconnection, spawning, and death events. It also shows how to intercept and filter player text messages. ```csharp public class PlayerEventSystem : ISystem { [Event] public void OnPlayerConnect(Player player) { player.SendClientMessage($"Hello, {player.Name}."); } [Event] public void OnPlayerDisconnect(Player player, DisconnectReason reason) { Console.WriteLine($"{player.Name} left ({reason})."); } [Event] public void OnPlayerSpawn(Player player) { player.GiveWeapon(Weapon.Colt45, 50); } [Event] public void OnPlayerDeath(Player player, Player killer, Weapon reason) { if (killer != null) killer.Score++; } [Event] public bool OnPlayerText(Player player, string message) { // Return false to suppress the message; return true to let it propagate to chat. return !message.Contains("badword", StringComparison.OrdinalIgnoreCase); } } ``` -------------------------------- ### Create a Static Vehicle Source: https://sampsharp.net/docs/features/vehicles.html Use CreateStaticVehicle for efficient, permanent spawn points, especially for train models, during OnGameModeInit. ```csharp worldService.CreateStaticVehicle( VehicleModelType.FreightTrain, // model 537 new Vector3(1750, -1950, 14), 0f, color1: -1, color2: -1); ``` -------------------------------- ### Create a Per-Player Text Label Source: https://sampsharp.net/docs/features/text-labels.html Use `CreatePlayerTextLabel` to create a text label visible only to a specific player. The label's position can be relative to the player. ```csharp [Event] public void OnPlayerSpawn(Player player, IWorldService worldService) { worldService.CreatePlayerTextLabel( player: player, text: "Welcome back!", color: new Color(0, 255, 0, 255), position: player.Position + new Vector3(0, 0, 2), drawDistance: 10f); } ``` -------------------------------- ### Register Player and Console Commands Source: https://sampsharp.net/docs/features/command-system.html Command methods must be part of an ISystem implementation. The system automatically discovers and registers methods marked with PlayerCommand or ConsoleCommand attributes. ```csharp public class MyCommandsSystem(IEntityManager entityManager) : ISystem { [PlayerCommand(Name = "hello")] public void HelloCommand(Player player) { player.SendClientMessage("Hello!"); } [ConsoleCommand(Name = "server_status")] public void ServerStatus() { Console.WriteLine("Server is running."); } } ``` -------------------------------- ### Create and Show a Player-Specific Gang Zone Source: https://sampsharp.net/docs/features/gang-zones.html Illustrates creating a gang zone tied to a specific player, setting its appearance, and showing it only to that player. ```csharp [Event] public void OnPlayerSpawn(Player player, IWorldService worldService) { var personal = worldService.CreatePlayerGangZone( owner: player, min: new Vector2(2000, -1500), max: new Vector2(2020, -1480)); personal.Color = new Color(0, 255, 0, 128); personal.Show(player); } ``` -------------------------------- ### Manually Constructing List Dialog Rows Source: https://sampsharp.net/docs/features/dialog-menus.html Demonstrates creating a ListDialogRow manually for fine-grained control over row properties like the Tag. ```csharp dialog.Add(new ListDialogRow("Infernus") { Tag = VehicleModelType.Infernus }); ``` -------------------------------- ### Player Command with Optional Parameter Source: https://sampsharp.net/docs/features/command-system.html Commands support optional parameters, which can have default values, making command syntax more flexible. ```csharp [PlayerCommand(Name = "money")] public void MoneyCommand(Player player, int? amount = null) { if (amount.HasValue) { player.Money = amount.Value; player.SendClientMessage($"Money set to ${amount.Value}"); } else { player.SendClientMessage($"Current money: ${player.Money}"); } } ``` -------------------------------- ### Registering Custom Permission Checker Source: https://sampsharp.net/docs/features/command-system.html Register your custom permission checker in your startup class to replace the default implementation. ```csharp services.RemoveAll(typeof(IPermissionChecker)); services.AddSingleton(); ``` -------------------------------- ### Create a Global Text Label Source: https://sampsharp.net/docs/features/text-labels.html Use `CreateTextLabel` to create a text label visible to all players. The `testLos` parameter controls whether the label is hidden by geometry. ```csharp [Event] public void OnGameModeInit(IWorldService worldService) { var label = worldService.CreateTextLabel( text: "General Store", color: new Color(255, 255, 255, 255), position: new Vector3(1352, -1758, 16), drawDistance: 20f, virtualWorld: 0, testLos: true); } ``` -------------------------------- ### Create and Show a PlayerTextDraw Source: https://sampsharp.net/docs/features/text-draws.html This snippet demonstrates how to create a PlayerTextDraw for a specific player upon spawning. It sets basic properties like position, text, font, letter size, and color before making it visible. The PlayerTextDraw is associated with the player using the 'player' parameter. ```csharp [Event] public void OnPlayerSpawn(Player player, IWorldService worldService) { var hud = worldService.CreatePlayerTextDraw( player: player, position: new Vector2(10, 400), text: "Score: 0"); hud.Font = TextDrawFont.Normal; hud.LetterSize = new Vector2(0.4f, 1.2f); hud.ForeColor = new Color(255, 255, 255, 255); hud.Show(); } ``` -------------------------------- ### Create a Custom Component by Inheriting from Component Source: https://sampsharp.net/docs/entity-component-system/entities-components.html Define a new component by inheriting from the base 'Component' class. This allows you to add custom data and behavior to entities. ```csharp public class BankAccount : Component { public decimal Balance { get; set; } public void Deposit(decimal amount) { Balance += amount; } public void Withdraw(decimal amount) { if (amount <= Balance) Balance -= amount; } } ``` -------------------------------- ### Configuring Log Levels in appsettings.json Source: https://sampsharp.net/docs/host-configuration/logging.html Set log levels for different categories in appsettings.json using the standard Logging:LogLevel schema. Categories are matched by prefix. ```json { "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "MyGameMode.Combat": "Debug" } } } ``` -------------------------------- ### Command Overloading with Different Signatures Source: https://sampsharp.net/docs/features/command-system.html Supports multiple command handlers with the same name but different parameter signatures, allowing for different use cases. ```csharp [CommandGroup("teleport")] [PlayerCommand(Name = "player")] public void TeleportCommand(Player player, Player target) { // /teleport player player.Position = target.Position; player.SendClientMessage($"Teleported to {target.Name}"); } ``` ```csharp [CommandGroup("teleport")] [PlayerCommand(Name = "player")] public void TeleportCommand(Player player, float x, float y, float z) { // /teleport player player.Position = new Vector3(x, y, z); player.SendClientMessage($"Teleported to ({x}, {y}, {z})"); } ``` ```csharp [CommandGroup("teleport")] [PlayerCommand(Name = "player")] [Alias("tp")] public void TeleportCommand(Player player, Player target, float x, float y, float z) { // /teleport player OR /tp // Admin command to teleport another player target.Position = new Vector3(x, y, z); player.SendClientMessage($"Teleported {target.Name} to ({x}, {y}, {z})"); } ``` -------------------------------- ### Define a Player Command Source: https://sampsharp.net/docs/features/command-system.html Marks a method as a player command. The first parameter determines execution context. ```csharp [PlayerCommand(Name = "kill")] public void KillCommand(Player player) { player.Health = 0; } ``` -------------------------------- ### Show Message Dialog with Callback Source: https://sampsharp.net/docs/features/dialog-menus.html Displays a simple message dialog and handles the user's response using a callback function. Useful when an immediate result is not required. ```csharp public class MySystem : ISystem { [Event] public void OnPlayerConnect(Player player, IDialogService dialogs) { var dialog = new MessageDialog( caption: "Welcome", content: "Press OK to continue.", button1: "OK" ); dialogs.Show(player, dialog, response => { // response.Response is DialogResponse.LeftButton or RightButtonOrCancel player.SendClientMessage($"You clicked: {response.Response}"); }); } } ``` -------------------------------- ### Accessing open.mp Config with IConfigService Source: https://sampsharp.net/docs/host-configuration/configuration.html Use IConfigService to retrieve typed configuration values from open.mp's config.json. If a value is missing or of the wrong type, GetString returns null and GetInt returns null. ```csharp public class WelcomeSystem(IConfigService config) : ISystem { [Event] public void OnGameModeInit() { var hostname = config.GetString("name") ?? "SA-MP Server"; var maxPlayers = config.GetInt("max_players") ?? 50; } } ``` -------------------------------- ### Access Typed Options Source: https://sampsharp.net/docs/host-configuration/configuration.html Inject `IOptions` or `IOptionsMonitor` into your systems or services to read the bound configuration values. `IOptionsMonitor` is useful for hot-reloaded configurations. ```csharp public class GameSystem(IOptions options) : ISystem { private readonly DatabaseOptions _db = options.Value; [Event] public void OnGameModeInit() { // use _db.ConnectionString } } ``` -------------------------------- ### Create a Per-Player Text Label with Parent Source: https://sampsharp.net/docs/features/text-labels.html To ensure a `PlayerTextLabel` is destroyed when the owner disconnects, pass the `player` as the `parent` argument. ```csharp worldService.CreatePlayerTextLabel(player, "Hi", Color.White, pos, 10f, parent: player); ``` -------------------------------- ### NPC Node Navigation Source: https://sampsharp.net/docs/features/npcs.html Initiates NPC navigation using the game's built-in node network. Requires opening a node file and then playing the node. ```csharp npcService.OpenNode(0); // pedestrian nodes for the first node file pc.PlayNode(0, NPCMoveType.Jog); ``` -------------------------------- ### Organize Commands with Command Groups Source: https://sampsharp.net/docs/features/command-system.html Use the [CommandGroup] attribute to organize commands into hierarchies. This can be applied to system classes or individual methods. ```csharp [CommandGroup("admin")] public class AdminCommandsSystem : ISystem { [PlayerCommand(Name = "kick")] public void KickCommand(Player player, Player target) { } [PlayerCommand(Name = "ban")] public void BanCommand(Player player, Player target) { } } ``` -------------------------------- ### Configuring Password InputDialog Source: https://sampsharp.net/docs/features/dialog-menus.html Shows how to configure an InputDialog to mask the characters entered by the player, suitable for sensitive information like PINs. ```csharp // Password input (characters are masked) var pinDialog = new InputDialog { Caption = "Enter PIN", Content = "Enter your security PIN:", Button1 = "Submit", Button2 = "Cancel", IsPassword = true }; ```