Static helper that generates fictional ID and bank card details for a wallet from a player's SteamID: citizen ID, Luhn-valid card number, CVV, expiry dates and job badges. Values are derived by hashing so they stay the same everywhere without storage.
namespace HorizonRP;
/// <summary>
/// Les numéros des papiers du portefeuille, tirés du SteamID comme le numéro de
/// téléphone : toujours les mêmes, sans rien stocker, et lisibles sur toutes les
/// machines (on peut montrer sa carte d'identité à un autre joueur).
/// </summary>
public static class Documents
{
/// <summary>Le nom de l'État fictif d'Union City, imprimé sur les papiers.</summary>
public const string State = "ÉTAT DE NEW HORIZON";
public const string BankName = "HZ BANK";
/// <summary>Durées de validité, en années, à partir de la première venue en ville.</summary>
public const int IdValidYears = 8;
public const int CardValidYears = 4;
static ulong Mix( ulong steamId, ulong salt )
{
var x = steamId + salt * 0x9E3779B97F4A7C15UL;
x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9UL;
x = (x ^ (x >> 27)) * 0x94D049BB133111EBUL;
return x ^ (x >> 31);
}
/// <summary>« UC4K7Z2QX » : UC (Union City) et sept caractères.</summary>
public static string CitizenId( ulong steamId )
{
const string alphabet = "ABCDEFGHJKLMNPRSTUVWXYZ23456789";
var x = Mix( steamId, 1 );
var id = new System.Text.StringBuilder( "UC" );
for ( var i = 0; i < 7; i++ )
{
id.Append( alphabet[(int)(x % (ulong)alphabet.Length)] );
x /= (ulong)alphabet.Length;
}
return id.ToString();
}
/// <summary>Seize chiffres commençant par 4, avec une clé de Luhn juste, comme une vraie carte.</summary>
public static string CardNumber( ulong steamId )
{
var digits = "4" + (Mix( steamId, 2 ) % 100_000_000_000_000UL).ToString( "00000000000000" );
var sum = 0;
for ( var i = 0; i < 15; i++ )
{
var d = digits[14 - i] - '0';
if ( i % 2 == 0 )
{
d *= 2;
if ( d > 9 ) d -= 9;
}
sum += d;
}
digits += ((10 - sum % 10) % 10).ToString();
return $"{digits[..4]} {digits[4..8]} {digits[8..12]} {digits[12..]}";
}
public static string Cvv( ulong steamId ) => (Mix( steamId, 3 ) % 1000).ToString( "000" );
/// <summary>« AAAA-MM-JJ » + n ans, en « JJ/MM/AAAA ».</summary>
public static string PlusYears( string isoDate, int years )
{
if ( !DateTime.TryParse( isoDate, out var date ) )
return "—";
return date.AddYears( years ).ToString( "dd/MM/yyyy" );
}
/// <summary>« MM/YY » : la fin de validité d'une carte bancaire.</summary>
public static string ValidThru( string isoDate )
{
if ( !DateTime.TryParse( isoDate, out var date ) )
return "—";
return date.AddYears( CardValidYears ).ToString( "MM/yy" );
}
/// <summary>Le badge de métier, pour ceux qui en ont un.</summary>
public static (string Agency, string Title)? BadgeOf( string jobId ) => jobId switch
{
"policier" => ("POLICE D'UNION CITY", "INSIGNE D'AGENT"),
"medecin" => ("HÔPITAL GÉNÉRAL D'UNION CITY", "PERSONNEL MÉDICAL"),
"maire" => ("MAIRIE D'UNION CITY", "MAIRE"),
"mecanicien" => ("UNION CITY MOTORS", "BADGE D'EMPLOYÉ"),
"staff" => ("HORIZON RP", "STAFF"),
_ => null,
};
}