Ems/EmsRules.cs

Static rules table for the UCFD EMS job: grades, fleet, locker kit, billable tariffs, heal timings and hospital point ids. Server and client read the same values.

namespace HorizonRP;

/// <summary>
/// Les règles de l'Union City Fire Department — Emergency Medical Services (UCFD EMS), lues à
/// l'identique par le serveur et par les clients (Julien, 29/10/2026). Sur le modèle exact de la
/// police (Police/PoliceRules.cs) et des systèmes de secours FiveM les plus repris
/// (qb-ambulancejob, esx_ambulancejob) :
///
/// - Métier sur liste blanche : seuls les secouristes inscrits par l'administration (panneau web,
///   page EMS : hz_ems_staff) prennent le métier « EMS » (id « medecin », gardé pour les profils).
/// - On est EMS « hors service » tant qu'on n'a pas pris son service au vestiaire de l'hôpital :
///   la paie de grade, le matériel, le garage, les actions de secours (F6), la réanimation, le 911
///   et les gyrophares demandent d'être en service.
/// - Les soins se facturent (tarifs ci-dessous, recalculés par l'API), payés dans l'appli Factures (débit HZ BANK).
///
/// GRADES, FLEET, les droits et TARIFFS sont déclarés en double dans api.php (EMS_GRADES,
/// EMS_FLEET, EMS_RIGHTS, EMS_TARIFS) : les changer ensemble. Des propriétés, pas des champs : un
/// tableau statique garderait son ancienne valeur après un rechargement à chaud.
/// </summary>
public static class EmsRules
{
	/// <summary>L'id du métier (Jobs.cs) : « medecin », enregistré dans les profils avant l'UCFD.</summary>
	public const string JobId = "medecin";
	public const string Department = "Union City Fire Department";
	public const string Short = "UCFD";
	public const string Service = "EMS";

	/// <summary>Les grades, à l'américaine (même forme que ceux de la police).</summary>
	public static PoliceRules.Grade[] Grades => new PoliceRules.Grade[]
	{
		new( 0, "Stagiaire", "EMT Trainee", 80, "school" ),
		new( 1, "Ambulancier", "EMT-Basic", 105, "medical_services" ),
		new( 2, "Ambulancier avancé", "Advanced EMT", 125, "medical_information" ),
		new( 3, "Paramédic", "Paramedic", 150, "monitor_heart" ),
		new( 4, "Paramédic chef", "Senior Paramedic", 175, "ecg_heart" ),
		new( 5, "Lieutenant", "Lieutenant", 205, "star" ),
		new( 6, "Capitaine", "Captain", 240, "stars" ),
		new( 7, "Chef de bataillon", "Battalion Chief", 280, "workspace_premium" ),
		new( 8, "Chef des secours", "EMS Chief", 330, "local_hospital" ),
	};

	public static PoliceRules.Grade GradeOf( int rank ) => Grades.FirstOrDefault( x => x.Rank == rank ) ?? Grades[0];

	/// <summary>Le plus haut grade (le chef des secours).</summary>
	public static int MaxRank => Grades.Max( x => x.Rank );

	// Droits : le grade minimum pour chaque chose (EMS_RIGHTS d'api.php).
	public const int RankCare = 0;      // diagnostiquer, soigner, stabiliser, escorter, mettre en véhicule
	public const int RankRevive = 0;    // réanimer (tout secouriste en service : un stagiaire seul ne laisse pas mourir)
	public const int RankBill = 1;      // établir une facture de soins
	public const int RankRoster = 6;    // recruter, promouvoir, rétrograder, renvoyer (en jeu)

	/// <summary>La flotte (même forme que celle de la police). La livrée n'est pas teintée : la couleur ne s'y applique pas.</summary>
	public static PoliceRules.FleetVehicle[] Fleet => new PoliceRules.FleetVehicle[]
	{
		// Le G 63 aux couleurs de l'UCFD (outils/ems/livree_g63.py, fiche « g63_ems » hors vente).
		new( "ambulance", "Ambulance (G 63, livrée UCFD)", "g63_ems", "#f2f2ee", 0, true ),
	};

	public static PoliceRules.FleetVehicle FleetOf( string id ) => Fleet.FirstOrDefault( x => x.Id == id );

	/// <summary>
	/// Le matériel du vestiaire : un objet d'inventaire (Items.cs) et combien on en prend. Ce qui a
	/// été donné et pas utilisé est repris en fin de service (comme les munitions de l'armurerie).
	/// </summary>
	public sealed record KitItem( string ItemId, string Label, int Count, int MinRank );

	public static KitItem[] Kit => new KitItem[]
	{
		new( "bandage", "Bandages", 10, 0 ),
		new( "trousse", "Trousses de soin", 3, 0 ),
	};

	/// <summary>Un soin facturable : son id (EMS_TARIFS), son intitulé et son prix.</summary>
	public sealed record Tariff( string Id, string Label, int Price, string Icon );

	public static Tariff[] Tariffs => new Tariff[]
	{
		new( "consultation", "Consultation et examen", 150, "stethoscope" ),
		new( "pansement", "Soins légers (pansement)", 250, "healing" ),
		new( "soins", "Soins (trousse de soin)", 500, "medical_services" ),
		new( "stabilisation", "Stabilisation d'un blessé grave", 750, "monitor_heart" ),
		new( "reanimation", "Réanimation", 1500, "ecg_heart" ),
		new( "transport", "Transport en ambulance", 400, "airport_shuttle" ),
		new( "accueil", "Soins à l'accueil de l'hôpital", 800, "local_hospital" ),
	};

	public static Tariff TariffOf( string id ) => Tariffs.FirstOrDefault( x => x.Id == id );

	/// <summary>Le forfait de l'accueil de l'hôpital (soins sans secouriste en service).</summary>
	public const string DeskTariff = "accueil";

	/// <summary>Lignes d'une facture au plus (EMS_BILL_LINES_MAX).</summary>
	public const int BillLinesMax = 10;

	// Les soins d'un secouriste (EmsActions) : ce que rend un objet, en combien de temps.

	/// <summary>Points de vie rendus par une trousse (soin long) et par un bandage.</summary>
	public const float HealTrousse = 60f, HealBandage = 25f;

	/// <summary>La durée d'un soin : la vie remonte peu à peu pendant ce temps (s).</summary>
	public const float HealSeconds = 6f;

	/// <summary>Stabiliser un inconscient ajoute ce temps avant la mort (s), deux fois au plus par chute.</summary>
	public const int StabilizeSeconds = 120;
	public const int StabilizeMax = 2;
	public const float StabilizeDuration = 4f;

	/// <summary>Les points de l'hôpital, posés en jeu par le staff (hz_settings, type « point »).</summary>
	public const string PointLocker = "ems_vestiaire";             // prise de service, matériel
	public const string PointGarage = "ems_garage";                // menu du garage
	public const string PointsGarageSpawn = "ems_garage_sorties";  // où les ambulances apparaissent (plusieurs)
	public const string PointDesk = "ems_accueil";                 // l'accueil : se faire soigner, payer ses factures

	/// <summary>Distance pour utiliser un point de l'hôpital (pouces, ~2,5 m), comme la police.</summary>
	public const float PointRange = PoliceRules.PointRange;
}