-- Reserva permanente de nomes (23/08).
--
-- Nome usado uma vez fica reservado para sempre, mesmo depois do personagem ser apagado.
-- E a regra padrao de MMO: impede que alguem assuma o nome de outro que saiu, e mantem
-- historico de chat e log fazendo sentido.
--
-- A comparacao e feita pelo collation do banco, que por padrao IGNORA maiusculas -- ou seja
-- "Maga" e "maga" contam como o mesmo nome. E o que se quer aqui: senao a reserva seria
-- burlada so trocando a caixa de uma letra.

USE Rosh;
GO

IF OBJECT_ID('dbo.nome_reservado', 'U') IS NULL
CREATE TABLE dbo.nome_reservado (
    nome            NVARCHAR(64) NOT NULL PRIMARY KEY,
    conta_id        INT          NULL REFERENCES dbo.conta(conta_id),
    entity_id       INT          NULL,   -- id do personagem que usou o nome (informativo)
    reservado_em    DATETIME2    NOT NULL DEFAULT SYSUTCDATETIME(),
    personagem_vivo BIT          NOT NULL DEFAULT 1   -- 0 = apagado, mas o nome segue reservado
);
GO

-- nomes que ja existem passam a constar como reservados
INSERT INTO dbo.nome_reservado (nome, conta_id, entity_id, personagem_vivo)
SELECT p.nome, p.conta_id, p.entity_id, 1
  FROM dbo.personagem p
 WHERE NOT EXISTS (SELECT 1 FROM dbo.nome_reservado r WHERE r.nome = p.nome);
GO

CREATE OR ALTER PROCEDURE dbo.criar_personagem
    @usuario    NVARCHAR(64),
    @nome       NVARCHAR(64),
    @classe     INT,
    @aparencia  INT,
    @primeiro_id INT = 20000,
    @resultado  NVARCHAR(32) OUTPUT,   -- ok | conta_inexistente | nome_em_uso |
                                       -- nome_reservado | classe_invalida | limite_de_personagens
    @entity_id  INT          OUTPUT
AS
BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON;
    SET @entity_id = NULL;

    DECLARE @conta_id INT, @hp INT, @mp INT,
            @fo INT, @de INT, @vi INT, @it INT, @sa INT;

    BEGIN TRAN;

    SELECT @conta_id = conta_id FROM dbo.conta WITH (UPDLOCK, HOLDLOCK)
     WHERE usuario = @usuario AND bloqueada = 0;
    IF @conta_id IS NULL
    BEGIN
        SET @resultado = N'conta_inexistente';
        ROLLBACK TRAN; RETURN;
    END

    IF (SELECT COUNT(*) FROM dbo.personagem WHERE conta_id = @conta_id) >= 4
    BEGIN
        SET @resultado = N'limite_de_personagens';
        ROLLBACK TRAN; RETURN;
    END

    SELECT @hp = hp_base, @mp = mp_base, @fo = forca, @de = destreza,
           @vi = vitalidade, @it = inteligencia, @sa = sabedoria
      FROM dbo.classe WHERE classe_id = @classe;
    IF @hp IS NULL
    BEGIN
        SET @resultado = N'classe_invalida';
        ROLLBACK TRAN; RETURN;
    END

    -- a reserva e a verificacao que importa: cobre tanto personagem vivo quanto ja apagado
    IF EXISTS (SELECT 1 FROM dbo.nome_reservado WITH (UPDLOCK, HOLDLOCK) WHERE nome = @nome)
    BEGIN
        SET @resultado = CASE
            WHEN EXISTS (SELECT 1 FROM dbo.personagem WHERE nome = @nome)
            THEN N'nome_em_uso' ELSE N'nome_reservado' END;
        ROLLBACK TRAN; RETURN;
    END

    SELECT @entity_id = ISNULL(MAX(entity_id), @primeiro_id - 1) + 1
      FROM dbo.personagem WITH (UPDLOCK, HOLDLOCK);

    INSERT INTO dbo.personagem
        (conta_id, entity_id, nome, classe, aparencia, hp, hp_max, mp, mp_max,
         forca, destreza, vitalidade, inteligencia, sabedoria)
    VALUES
        (@conta_id, @entity_id, @nome, @classe, @aparencia, @hp, @hp, @mp, @mp,
         @fo, @de, @vi, @it, @sa);

    INSERT INTO dbo.nome_reservado (nome, conta_id, entity_id, personagem_vivo)
    VALUES (@nome, @conta_id, @entity_id, 1);

    SET @resultado = N'ok';
    COMMIT TRAN;
END
GO

CREATE OR ALTER PROCEDURE dbo.apagar_personagem
    @entity_id INT,
    @usuario   NVARCHAR(64) = NULL,
    @apagados  INT OUTPUT
AS
BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON;
    DECLARE @nome NVARCHAR(64);

    BEGIN TRAN;
    SELECT @nome = p.nome
      FROM dbo.personagem p LEFT JOIN dbo.conta c ON c.conta_id = p.conta_id
     WHERE p.entity_id = @entity_id AND (@usuario IS NULL OR c.usuario = @usuario);

    DELETE p FROM dbo.personagem p
      LEFT JOIN dbo.conta c ON c.conta_id = p.conta_id
     WHERE p.entity_id = @entity_id AND (@usuario IS NULL OR c.usuario = @usuario);
    SET @apagados = @@ROWCOUNT;

    -- o personagem some, a reserva do nome NAO
    IF @apagados > 0 AND @nome IS NOT NULL
        UPDATE dbo.nome_reservado SET personagem_vivo = 0 WHERE nome = @nome;

    COMMIT TRAN;
END
GO
