Skip to content

PROCEDURES

Introdução

Stored Procedures são blocos de código SQL armazenados no banco de dados, permitindo a execução de várias instruções em uma única unidade. Elas melhoram o desempenho ao reduzir a comunicação entre a aplicação e o banco, além de oferecerem mais segurança ao restringir o acesso direto a tabelas. Outra vantagem é a manutenção simplificada, já que a lógica pode ser centralizada no banco de dados, facilitando atualizações sem a necessidade de alterar a aplicação.

Procedures

-- PostGreSQL: create `civil` linking to `npc`
CREATE OR REPLACE FUNCTION criar_civil(
    IN nome VARCHAR(100),
    IN sub_regiao_id INT,
    IN descricao TEXT,
    IN tipo TIPO_CIVIL
) RETURNS INT AS $$
DECLARE 
    npc_id INT;
BEGIN

    INSERT INTO npc (tipo)
    VALUES ('Civil')
    RETURNING id INTO npc_id;

    INSERT INTO civil (
        id,
        sub_regiao_id,
        tipo,
        nome,
        descricao
    )
    VALUES (
        npc_id,
        sub_regiao_id,
        tipo::TIPO_CIVIL,
        nome,
        descricao
    );

    RETURN npc_id;
END;
$$ LANGUAGE plpgsql;

-- PostGreSQL: create `quester` linking to `civil` using `criar_civil`.
CREATE OR REPLACE FUNCTION criar_quester(
    IN nome VARCHAR(100),
    IN sub_regiao_id INT,
    IN descricao TEXT,
    IN dialogo TEXT
) RETURNS INT AS $$
DECLARE
    npc_id INT;
BEGIN
    npc_id = criar_civil(nome, sub_regiao_id, descricao, 'Quester');

    -- create line in table `quester` linking to `npc_id`.
    INSERT INTO quester (
        id,
        num_quests,
        dialogo
    )
    VALUES (
        npc_id,
        0,
        dialogo
    );

    RETURN npc_id;
END;
$$ LANGUAGE plpgsql;

-- PostGreSQL: create `mercador` linking to `npc`
CREATE OR REPLACE FUNCTION criar_mercador(
    IN nome VARCHAR(100),
    IN sub_regiao_id INT,
    IN descricao TEXT,
    IN dialogo TEXT
) RETURNS INT AS $$
DECLARE
    npc_id INT;
BEGIN
    npc_id = criar_civil(nome, sub_regiao_id, descricao, 'Mercador');

    INSERT INTO mercador (
        id,
        dialogo
    )
    VALUES (
        npc_id,
        dialogo
    );

    RETURN npc_id;
END;
$$ LANGUAGE plpgsql;


-- PostGreSQL: create enemy linking to npc.
CREATE OR REPLACE FUNCTION criar_inimigo(
    IN emoji TEXT,
    IN nome VARCHAR(100),
    IN descricao TEXT,
    IN elemento TEXT,
    IN vida_maxima INT,
    IN xp_obtido INT,
    IN inteligencia INT,
    IN moedas_obtidas INT,
    IN conhecimento_arcano INT,
    IN energia_arcana_maxima INT,
    IN dialogo TEXT
) RETURNS INT AS $$
DECLARE 
    npc_id INT;
BEGIN
    INSERT INTO npc (tipo)
    VALUES ('Inimigo')
    RETURNING id INTO npc_id;

    INSERT INTO inimigo (
        id,
        emoji,
        nome,
        descricao,
        elemento,
        vida_maxima,
        xp_obtido,
        inteligencia,
        moedas_obtidas,
        conhecimento_arcano,
        energia_arcana_maxima,
        dialogo
    )
    VALUES (
        npc_id,
        emoji,
        nome,
        descricao,
        elemento::TIPO_ELEMENTO,
        vida_maxima,
        xp_obtido,
        inteligencia,
        moedas_obtidas,
        conhecimento_arcano,
        energia_arcana_maxima,
        dialogo
    );
    RETURN npc_id;
END;
$$ LANGUAGE plpgsql;

-- PostGreSQL: create `personagem`
CREATE OR REPLACE FUNCTION criar_personagem(
    IN p_nome VARCHAR(20),
    IN p_elemento TEXT
) RETURNS INT AS $$
DECLARE
    v_personagem_id INT;
    v_inventario_mochila_id INT;
    v_inventario_grimorio_id INT;
BEGIN
    INSERT INTO personagem (
        sub_regiao_id,
        nome,
        elemento,
        conhecimento_arcano,
        vida,
        vida_maxima,
        xp,
        xp_total,
        energia_arcana,
        energia_arcana_maxima,
        inteligencia,
        moedas,
        nivel
    )
    VALUES (
        1,
        p_nome,
        p_elemento::TIPO_ELEMENTO,
        10,
        100,
        100,
        0,
        10,
        50,
        50,
        1,
        15,
        1
    )
    RETURNING id INTO v_personagem_id;
    -- Criar o inventário do tipo Mochila
    INSERT INTO inventario (personagem_id, tipo)
    VALUES (v_personagem_id, 'Mochila')
    RETURNING id INTO v_inventario_mochila_id;

    INSERT INTO mochila (id, peso, peso_total)
    VALUES (v_inventario_mochila_id, 0, 20);

    --  Criar o inventário do tipo Grimório
    INSERT INTO inventario (personagem_id, tipo)
    VALUES (v_personagem_id, 'Grimório')
    RETURNING id INTO v_inventario_grimorio_id;

    INSERT INTO grimorio (id, num_pag, num_pag_maximo)
    VALUES (v_inventario_grimorio_id, 0, 5);

    RETURN v_personagem_id;
END;
$$ LANGUAGE plpgsql;


-- PostGreSQL: create `acessorio`
CREATE OR REPLACE FUNCTION criar_acessorio(
  IN tipo TEXT,
  IN descricao TEXT,
  IN drop_inimigos_media INT,
  IN nome VARCHAR(200),
  IN peso INT,
  IN preco INT  
)
RETURNS INT AS $$
DECLARE
     v_item_id INT;
BEGIN
     INSERT INTO item (tipo)
     VALUES ('Acessório')
     RETURNING id INTO v_item_id;

     INSERT INTO acessorio (id, tipo, descricao, drop_inimigos_media, nome, peso, preco)
     VALUES (v_item_id, tipo::TIPO_ACESSORIO, descricao, drop_inimigos_media, nome, peso, preco);

     RETURN v_item_id;
END;
$$ LANGUAGE plpgsql;

-- PostGreSQL: create `pergaminho`
CREATE OR REPLACE FUNCTION criar_pergaminho(
    IN p_descricao TEXT,
    IN p_drop_inimigos_media INT,
    IN p_nome VARCHAR(20),
    IN p_peso INT,
    IN p_preco INT,
    IN p_cor TEXT
) RETURNS INT AS $$
DECLARE
    v_item_id INT;
BEGIN
    -- create item.
    INSERT INTO item (tipo)
    VALUES ('Pergaminho')
    RETURNING id INTO v_item_id;

    -- create scroll.
    INSERT INTO pergaminho (id, cor, descricao, drop_inimigos_media, nome, peso, preco)
    VALUES (v_item_id, p_cor::TIPO_COR, p_descricao, p_drop_inimigos_media, p_nome, p_peso, p_preco);

    RETURN v_item_id;
END;
$$ LANGUAGE plpgsql;

-- PostGreSQL: create `pocao`
CREATE OR REPLACE FUNCTION criar_pocao(
    IN p_descricao TEXT,
    IN p_drop_inimigos_media INT,
    IN p_nome VARCHAR(20),
    IN p_peso INT,
    IN p_preco INT
) RETURNS INT AS $$
DECLARE
    v_item_id INT;
BEGIN
    -- Criar o item da poção
    INSERT INTO item (tipo)
    VALUES ('Poção')
    RETURNING id INTO v_item_id;

    -- Criar a poção
    INSERT INTO pocao (id, descricao, drop_inimigos_media, nome, peso, preco)
    VALUES (v_item_id, p_descricao, p_drop_inimigos_media, p_nome, p_peso, p_preco);

    RETURN v_item_id;
END;
$$ LANGUAGE plpgsql;

-- PostGreSQL: create `feitico_dano`
CREATE OR REPLACE FUNCTION criar_feitico_dano(
    IN nome TEXT,
    IN descricao TEXT ,
    IN elemento TEXT, -- ::TIPO_ELEMENTO,
    IN countdown INT,
    IN conhecimento_arcano_necessario INT,
    IN energia_arcana INT,
    IN dano_total INT
) RETURNS INT AS $$
DECLARE
    v_feitico_id INT;
BEGIN
    -- Criar o feitico
    INSERT INTO feitico (tipo)
    VALUES ('Dano')
    RETURNING id INTO v_feitico_id;

    -- Criar a feitico_dano
    INSERT INTO feitico_dano (
        id,
        dano_total,
        descricao,
        elemento,
        countdown,
        conhecimento_arcano_necessario,
        energia_arcana,
        nome
    )
    VALUES (
        v_feitico_id,
        dano_total,
        descricao,
        elemento::TIPO_ELEMENTO,
        countdown,
        conhecimento_arcano_necessario,
        energia_arcana,
        nome
    );

    RETURN v_feitico_id;
END;
$$ LANGUAGE plpgsql;


-- PostGreSQL: create `feitico_dano_area`
CREATE OR REPLACE FUNCTION criar_feitico_dano_area(
    IN nome TEXT,
    IN descricao TEXT,
    IN elemento TEXT, -- ::TIPO_ELEMENTO,
    IN countdown INT,
    IN conhecimento_arcano_necessario int,
    IN energia_arcana INT,
    IN dano INT,
    IN qtd_inimigos_afetados INT
) RETURNS INT AS $$
DECLARE
    v_feitico_id INT;
BEGIN
    -- Criar o feitico
    INSERT INTO feitico (tipo)
    VALUES ('Dano de área')
    RETURNING id INTO v_feitico_id;

    -- Criar a feitico_dano_area
    INSERT INTO feitico_dano_area(id, 
        dano, qtd_inimigos_afetados, 
        descricao, 
        elemento, 
        countdown, 
        conhecimento_arcano_necessario, 
        energia_arcana,
        nome
    )
    VALUES (v_feitico_id, 
        dano, 
        qtd_inimigos_afetados, 
        descricao, 
        elemento::TIPO_ELEMENTO, 
        countdown, 
        conhecimento_arcano_necessario, 
        energia_arcana,
        nome);

    RETURN v_feitico_id;
END;
$$ LANGUAGE plpgsql;

-- PostGreSQL: create `feitico_cura`
CREATE OR REPLACE FUNCTION criar_feitico_cura(
    IN nome TEXT,
    IN descricao TEXT,
    IN elemento TIPO_ELEMENTO,
    IN countdown INT,
    IN conhecimento_arcano_necessario INT,
    IN energia_arcana INT,
    IN qtd_cura INT
) RETURNS INT AS $$
DECLARE
    v_feitico_id INT;
BEGIN
    -- Criar o feitico
    INSERT INTO feitico (tipo)
    VALUES ('Cura')
    RETURNING id INTO v_feitico_id;

    -- Criar a feitico_cura
    INSERT INTO feitico_cura (
        id,
        qtd_cura,
        descricao,
        elemento,
        countdown,
        conhecimento_arcano_necessario,
        energia_arcana,
        nome
    )
    VALUES (
        v_feitico_id,
        qtd_cura,
        descricao,
        elemento::TIPO_ELEMENTO,
        countdown,
        conhecimento_arcano_necessario,
        energia_arcana,
        nome
    );

    RETURN v_feitico_id;
END;
$$ LANGUAGE plpgsql;

-- PosGreSQL: create `atualizar_combate`
CREATE OR REPLACE FUNCTION atualizar_combate(
    IN p_personagem_id INT,
    IN p_inimigo_id INT,
    IN p_vida_personagem INT,
    IN p_vida_inimigo INT
) RETURNS INT AS $$
DECLARE
    v_personagem_id INT;
BEGIN
    -- Update character fields.
    UPDATE personagem
    SET vida = GREATEST(p_vida_personagem, 0)
    WHERE id = p_personagem_id;

    -- Update enemy fields.
    UPDATE inimigo_instancia
    SET vida = GREATEST(p_vida_inimigo, 0)
    WHERE id = p_inimigo_id;

    RETURN p_personagem_id;
END;
$$ LANGUAGE plpgsql;

-- PostGreSQL: create `combate`
CREATE OR REPLACE FUNCTION finalizar_combate(
    IN p_id INT,
    IN p_xp INT,
    IN p_energia_arcana INT,
    IN p_dano_recebido INT, 
    IN p_dano_causado INT,
    IN ei_id INT
) RETURNS INT[] AS $$
DECLARE
    v_armazenamentos RECORD;
    list_items INT[];
BEGIN
    -- Update character fields.
    UPDATE personagem
    SET xp = LEAST(xp + p_xp, xp_total), 
        energia_arcana = LEAST(energia_arcana + p_energia_arcana, energia_arcana_maxima)
    WHERE id = p_id;

    -- To-do: if `xp` is equal to `xp_total`, increase `nivel` by 1 and set `xp` to 0, and update attributes.

    -- Get items that can be dropped from this enemy.
    SELECT
        a.item_id,
        a.quantidade,
        i.drop_inimigos_media
    INTO v_armazenamentos
    FROM armazenamento_inimigo AS ai
    JOIN armazenamento AS a ON ai.armazenamento_id = a.id
    JOIN item AS i ON a.item_id = i.id
    WHERE ai.inimigo_id = ei_id;

    -- If the record variable `v_armazenamentos` is null, return the variable `list_items`.
    IF v_armazenamentos IS NULL THEN
        RETURN list_items;
    END IF;

    -- Generate items that will be dropped.
    FOR i IN 1..v_armazenamentos.quantidade LOOP
        IF random() < 1 / v_armazenamentos.drop_inimigos_media THEN
            list_items := list_items || v_armazenamentos.item_id;
        END IF;
    END LOOP;

    -- Add to the table `combate`: inimigo_instancia_id, personagem_id, dano_cauisad, dano_recebido.
    INSERT INTO combate (inimigo_instancia_id, personagem_id, dano_causado, dano_recebido)
    VALUES (ei_id, p_id, p_dano_causado, p_dano_recebido);

    RETURN list_items;
END;
$$ LANGUAGE plpgsql;

-- PostGreSQL: create `aprender_feitico`
CREATE OR REPLACE FUNCTION aprender_feitico(
    IN p_personagem_id INT,
    IN p_feitico_id INT
) RETURNS INT AS $$
DECLARE
    v_inventario_id INT;
    v_requisito_id INT;
    v_pre_requisito_aprendido BOOLEAN;
    v_possui_feitico_atual BOOLEAN;
    v_conhecimento_arcano_suficiente BOOLEAN;
BEGIN
    SELECT id
    INTO v_inventario_id
    FROM inventario
    WHERE personagem_id = p_personagem_id;

    SELECT de_id
    INTO v_requisito_id
    FROM feitico_requerimento
    WHERE para_id = p_feitico_id;

    -- Verifica se já possui o feitiço pré-requisito
    SELECT EXISTS (
        SELECT 1
        FROM feitico_aprendido
        WHERE inventario_id = v_inventario_id
          AND feitico_id = v_requisito_id
    ) INTO v_pre_requisito_aprendido;

    -- Verifica se já possui o feitiço atual
    SELECT EXISTS (
        SELECT 1
        FROM feitico_aprendido
        WHERE inventario_id = v_inventario_id
          AND feitico_id = p_feitico_id
    ) INTO v_possui_feitico_atual;

    -- Verifica se possui conhecimento arcano suficiente para o feitiço
    SELECT (p.conhecimento_arcano >= f.conhecimento_arcano_necessario)
    INTO v_conhecimento_arcano_suficiente
    FROM personagem p
    JOIN feitico f ON f.id = p_feitico_id
    WHERE p.id = p_personagem_id;

    -- Só aprende o feitiço se ainda não tiver, já possuir o pré-requisito e tiver conhecimento arcano
    IF NOT v_possui_feitico_atual
       AND v_pre_requisito_aprendido
       AND v_conhecimento_arcano_suficiente THEN
        INSERT INTO feitico_aprendido(inventario_id, feitico_id)
        VALUES (v_inventario_id, p_feitico_id);
    END IF;

    RETURN p_personagem_id;
END;
$$ LANGUAGE plpgsql;

-- PostGreSQL: create `criar_transacao`
CREATE OR REPLACE FUNCTION criar_transacao(
    IN p_mercador_id INT,
    IN p_personagem_id INT,
    IN p_item_id INT
) RETURNS INT AS $$
DECLARE
    v_inventario_id INT;
    v_transacao_id INT;
BEGIN
    -- Select inventario personagem
    SELECT id
    INTO v_inventario_id
    FROM inventario
    WHERE personagem_id = p_personagem_id;

    -- Criar instancia de Item
    INSERT INTO item_instancia (item_id, inventario_id)
    VALUES (p_item_id, v_inventario_id);

    -- Cria transacao
    INSERT INTO transacao (mercador_id, personagem_id, item_id)
    VALUES (p_mercador_id, p_personagem_id, p_item_id)
    RETURNING id INTO v_transacao_id;

    RETURN v_transacao_id;
END;
$$ LANGUAGE plpgsql;

-- PostGreSQL: create `create_instance_quest`
CREATE OR REPLACE FUNCTION create_new_instance_quest(
    IN p_quest_id INT,
    IN p_personagem_id INT,
    IN p_regiao_nome VARCHAR(50)
) RETURNS INT AS $$
DECLARE
    v_quest_instancia_id INT;
BEGIN

    -- Check if a quest with the same id and personagem_id already exists.
    IF EXISTS (
        SELECT 1
        FROM quest_instancia
        WHERE quest_id = p_quest_id AND personagem_id = p_personagem_id
    ) THEN
        RAISE EXCEPTION 'Quest instance already exists for this character and quest.';
    END IF;

    INSERT INTO quest_instancia (quest_id, personagem_id, completed)
    VALUES (p_quest_id, p_personagem_id, FALSE)
    RETURNING id INTO v_quest_instancia_id;

    -- Atualiza a situação das sub-regiões da região passada como argumento para "Passável"
    UPDATE sub_regiao_conexao
    SET situacao = 'Passável'
    WHERE sub_regiao_2 IN (
        SELECT id FROM sub_regiao WHERE regiao_id = (
            SELECT id FROM regiao WHERE nome = p_regiao_nome
        )
    );

    RETURN v_quest_instancia_id;
END;
$$ LANGUAGE plpgsql;

-- Remove potion effects and return potion IDs.
CREATE OR REPLACE FUNCTION end_combat(
    IN p_personagem_id INT,
    IN enemies_id INT[]
) RETURNS INT[] AS $$
DECLARE
    v_potions_id INT[];
    item_rec INT[];
    v_dropped_items_id INT[];
    enemy_id INT;
    potion_id INT;
BEGIN
    -- Get potion IDs that were marked as used.
    SELECT ARRAY(
        SELECT item_id
        FROM item_instancia
        WHERE usado = TRUE
          AND mochila_id IN (
            SELECT id FROM inventario WHERE personagem_id = p_personagem_id
        )
    ) INTO v_potions_id;

    -- If v_potions_id is empty, return.
    IF v_potions_id IS NULL THEN
        RETURN v_potions_id;
    END IF;

    -- Loop through each potion's id in v_potions_id and remove their effect from personagem.
    FOREACH potion_id IN ARRAY v_potions_id LOOP
        UPDATE personagem
        SET
            inteligencia = inteligencia / (
                SELECT inteligencia 
                FROM efeito 
                WHERE id IN (
                    SELECT efeito_id 
                    FROM pocao_efeito 
                    WHERE pocao_id = potion_id
                )
            ),
            vida_maxima = vida_maxima / (
                SELECT vida 
                FROM efeito 
                WHERE id IN (
                    SELECT efeito_id 
                    FROM pocao_efeito 
                    WHERE pocao_id = potion_id
                )
            ),
            energia_arcana_maxima = energia_arcana_maxima / (
                SELECT energia_arcana 
                FROM efeito 
                WHERE id IN (
                    SELECT efeito_id 
                    FROM pocao_efeito 
                    WHERE pocao_id = potion_id
                )
            )
        WHERE id = p_personagem_id;
    END LOOP;

    DELETE FROM item_instancia
    WHERE usado = TRUE
      AND mochila_id IN (
        SELECT id FROM inventario WHERE personagem_id = p_personagem_id
    );

    v_dropped_items_id := '{}';

    -- For each enemy, append all of its drop items to v_dropped_items_id.
    FOREACH enemy_id IN ARRAY enemies_id LOOP
        FOR item_rec IN
            SELECT
                a.item_id,
                a.quantidade,
                COALESCE(p.drop_inimigos_media, ac.drop_inimigos_media, po.drop_inimigos_media)
            FROM armazenamento_inimigo ai
            JOIN armazenamento a ON ai.armazenamento_id = a.id
            JOIN item i ON a.item_id = i.id
            JOIN acessorio ac ON i.id = ac.id
            JOIN pergaminho p ON i.id = p.id
            JOIN pocao po ON i.id = po.id
            WHERE ai.inimigo_id = enemy_id
        LOOP
            FOR counter IN 1..item_rec.quantidade LOOP
                IF random() < 1.0 / item_rec.drop_inimigos_media THEN
                    v_dropped_items_id := array_append(v_dropped_items_id, item_rec.item_id);
                END IF;
            END LOOP;
        END LOOP;
    END LOOP;

    RETURN v_dropped_items_id;
END;
$$ LANGUAGE plpgsql;

Histórico de Versão

Versão Data Descrição Autor
1.0 03/02/2025 Criação Grupo
2.0 10/02/2025 Atualização Grupo