Ativa o menu
Alternar menu de preferências
Alternar menu pessoal
Não autenticado(a)
Your IP address will be publicly visible if you make any edits.

A quick beginner's guide to ACS

De Brdoom wiki
Revisão de 21h55min de 4 de fevereiro de 2026 por MegaManx3 (discussão | contribs) (Criou página com 'Este tutorial vai guiar você na criação dos seus primeiros scripts ACS, e espera-se que seja útil se você é novo em ACS básico. Presume-se que você já conhece alguns fundamentos de mapeamento e que esteja usando o formato de mapa Hexen ou UDMF. ==Criando um script== Abra o Doom Builder e crie um novo mapa (chame-o de MAP01) no formato ZDoom (Doom in Hexen). Crie um setor e coloque um Player 1 Start. ====Para GZDoom Builder:==== Procure por...')
(dif) ← Edição anterior | Revisão atual (dif) | Versão posterior → (dif)

Este tutorial vai guiar você na criação dos seus primeiros scripts ACS, e espera-se que seja útil se você é novo em ACS básico. Presume-se que você já conhece alguns fundamentos de mapeamento e que esteja usando o formato de mapa Hexen ou UDMF.

Criando um script

Abra o Doom Builder e crie um novo mapa (chame-o de MAP01) no formato ZDoom (Doom in Hexen). Crie um setor e coloque um Player 1 Start.

Para GZDoom Builder:

Procure por "Script Editor", encontrado na barra de ferramentas principal/no menu "View", ou pressionando F10.

Para versões mais antigas do Doom Builder:

Vá ao menu "Scripts" e escolha "Edit BEHAVIOR Lump". Você deve ver um botão chamado "Make New Script". Clique nele.

A estrutura de um script

Aqui está a estrutura de um script básico:

#include "zcommon.acs"

script <identificador do script> <tipo de script> (<argumentos do script>)
{
    <instrução>;
}

Vamos quebrar os componentes textuais do script, ou seja, sua sintaxe:

  • #include "zcommon.acs"
    • Coloque isso como a primeira linha do script, copiado exatamente. Isso importa dados ACS para o script compilar e executar com sucesso.
  • <identificador do script>
    • Cada script tem um identificador, que será um número ou um "nome entre aspas" (chamado de string). Nota: todos os scripts numerados devem estar entre 1 - 32767.
  • <tipo de script>
    • Cada script tem um tipo, que em geral determina quando ele será executado automaticamente. Veja Script types para a lista de tipos disponíveis.
  • (<argumentos do script>)
    • Scripts podem aceitar valores chamados argumentos quando executados. Isso é como passar valores para uma função. Nem todos os tipos de script precisam de argumentos. Eles devem ficar entre parênteses.
  • {
    • Todo o código do script deve ficar {entre chaves}.
  • <instrução>;
    • Isso seria o código que o script executa. Cada linha de código precisa terminar com ponto-e-vírgula; caso contrário, o compilador vai gerar erro. Ele não “lê” quebras de linha como um humano.
  • }
    • Lembre-se de colocar a chave de fechamento para marcar o fim do script.

Primeiro script de exemplo

Aqui está o primeiro exemplo de script que vamos criar, que exibirá a mensagem "Hello World!" na tela do jogador quando ele entrar no mapa pela primeira vez:

#include "zcommon.acs"

script 1 ENTER 
{
    print(s:"Hello World!");
}

Este script será identificado como script 1. Ele tem o tipo ENTER, que executa quando um jogador entra no mapa pela primeira vez. Scripts ENTER não precisam de argumentos.

A instrução print()

No exemplo acima, é usada a instrução/função print(). A instrução print permite exibir uma mensagem na tela do jogador.

A sintaxe do print é assim:

print(<tipo de cast>:<expressão>);

Quebra da sintaxe:

  • print(
    • print é o nome da função a ser executada (chamada) nesta linha, e toda chamada de função deve ser seguida por parênteses. O print exige argumentos dentro dos parênteses.
  • <tipo de cast>:
    • O conteúdo da mensagem precisa ser interpretado com um código de letra. Por enquanto, você pode usar s: para texto (strings) ou d: para exibir números de variáveis (explicado mais adiante). Veja print para todas as possibilidades.
  • <expressão>
    • Contém o conteúdo da mensagem a exibir. Texto deve ficar entre "aspas".
  • );
    • Certifique-se de colocar o parêntese final e o ponto-e-vírgula.

Novas linhas

Para colocar uma nova linha dentro de uma string, adicione o texto \n dentro das aspas. Exemplo:

print(s:"Hello\nWorld!");

Isso coloca a palavra "World!" na linha abaixo de "Hello". O compilador não vai pegar a quebra de linha do seu editor e imprimir como você talvez espere; você precisa usar o código especial \n.

Múltiplas expressões

Você também pode fornecer múltiplas expressões para o print, separadas por vírgulas. Exemplo:

print(s:"Hello", s:" World!");

Essa é uma forma alternativa de imprimir "Hello World!" na tela. Garanta que o espaço esteja incluído nas aspas antes da palavra "World!".

Segundo script de exemplo

Aqui está outro script que você pode adicionar depois do script 1:

script 2 (void)
{
    print(s:"Bye World!");
}

Diferente do script ENTER do exemplo 1, este script não executa automaticamente quando o jogador entra no mapa e precisa ser executado explicitamente. void dentro dos parênteses indica ao compilador que não há argumentos para este script; essa palavra-chave é obrigatória para qualquer script que não seja de um tipo especial e que não receba argumentos.

Um script do tipo (void) provavelmente é o tipo mais comum. Você também pode usar scripts com argumentos, mas isso será discutido mais adiante.

Para ativar um script (void), você precisa ter um thing com o special do thing configurado para 80: ACS_Execute (monstro morre, item/arma/powerup/chave é pego), ou um linedef cujo special esteja configurado para 80: ACS_Execute (ao cruzar a linha, ao usar, ou quando um tiro acerta ou passa), ou ainda ativar a partir de outro script (você vai aprender isso depois).

O primeiro argumento do special 80: ACS_Execute deve ser 2, que executará o script 2 acima. Você pode deixar os outros argumentos como 0.

É bem comum entre mappers colocar linedefs que você não percebe e que não bloqueiam o caminho. Essas linhas são usadas para scripting.

Variáveis

Esta seção vai ensinar variáveis e uso básico. Variáveis servem para armazenar dados (como um número) para uso posterior no script, semelhante à representação simbólica de números em álgebra.

Exemplo:

script 3 (void) 
{
    int a = 9;
    print(s:"a is ", d:a);
}

Este script faz duas coisas:

  1. Declara a como inteiro e define seu valor como 9.
  2. Imprime a string "a is " seguida do valor de a. Neste caso, o resultado seria "a is 9".

Quebra de sintaxe para declaração de variável:

<tipo de dado> <nome da variável> = <valor>;
  • <tipo de dado>
    • O tipo de dado da variável. int é o mais comum, e representa um número inteiro. Existem outros tipos, listados em Data Types.
  • <nome da variável>
    • O nome que a variável declarada vai usar. É melhor respeitar a capitalização do nome em cada lugar em que for usado.
  • =
    • Um valor inicial pode ser atribuído com o sinal de igual, embora não seja obrigatório.
      • Exemplo sem valor padrão: "int a;"
  • <valor>
    • O valor inicial da variável. Para int, um número.
  • ;
    • Não esqueça o ponto-e-vírgula ao final de todas as instruções!

Com inteiros, você pode fazer matemática básica (e também matemática mais avançada se programar corretamente).

script 3 (void)
{
    int a = 9;
    int b = 17;
    print(d:a + b);
}

Este script é um exemplo de soma básica. Ele declara a com valor 9, declara b com valor 17 e imprime o resultado de a + b. O cast d: é usado para exibir um valor numérico no print. Como a é 9 e b é 17, ele imprime 26. Se você ativar o script durante o jogo, verá o número 26.

Se você quisesse imprimir explicitamente o texto "9 + 17", e não "26", você poderia fazer assim:

script 3 (void) 
{
    print(s:"9 + 17");
}

O cast s: é usado aqui porque ele imprime uma string em vez do valor de uma variável.

Operadores úteis de inteiro

(Nos exemplos abaixo, a é 9 e b é 17)

  • Atribuição: a = b
    • Define a variável a com o valor de b, então a vira 17. Um único sinal de igual NÃO representa verificação de igualdade.
  • Igualdade: a == b
    • Verifica se o valor de a é igual ao valor de b. Resulta em 1 se for verdadeiro, ou 0 se for falso. Neste caso, 0. Lembre-se: é necessário usar dois sinais de igual.
  • Diferença: a != b
    • Igual ao de igualdade, mas resulta em 1 se os valores forem diferentes e 0 se forem iguais.
  • Adição: a + b == 26
  • Subtração: a - b == -8
  • Multiplicação: a * b == 153
  • Divisão: a / b == 0
    • O resultado não é 0,5294, porque divisão de inteiros “arredonda para baixo” e retorna outro inteiro.
    • Dividir por 0 resulta em erro de script.
  • Módulo: a % b == 9
    • Dá o resto inteiro ao tentar dividir 9 por 17.
  • Incremento unário: a++ == 10
    • Soma 1 à variável. a + 1 == 10.
  • Decremento unário: a-- == 8
  • Parênteses: (a + 1) * b == 170
    • Valores podem ser agrupados entre parênteses. O que estiver entre parênteses é avaliado primeiro.

Veja Operators para todos os operadores e mais informações.

Ações e parâmetros de script

Claro que existem outras possibilidades além de imprimir texto e números.

Suponha que colocamos uma chave vermelha no mapa. No Doom vanilla, pegá-la só te dá a chave vermelha. Com ACS do ZDoom, você pode fazer a chave vermelha fazer muito mais! Você pode fazer a chave matar o jogador, dar a BFG9000, não fazer nada, ou levantar monstros mortos! Se você tiver duas chaves vermelhas, pode fazer cada uma fazer algo totalmente diferente!

Crie um setor e coloque um Player Start e uma chave vermelha. Agora, dê à chave a ação especial 80: ACS_Execute com o número do script 1.

Digite o código abaixo (note que ele não compilará porque faltam parâmetros de propósito):

#include "zcommon.acs"

script 1 (void) 
{
    Thing_Damage();
}

Agora, para a instrução Thing_Damage, precisamos de parâmetros:

  • tid: o TID do thing que você quer causar dano.
  • amount: a quantidade de dano que o thing vai receber.
  • mod: meio de morte. Determina a mensagem de óbito se um jogador morrer. Tipos de dano relevantes estão em Damage_types.

O primeiro parâmetro é quem queremos danificar. Como queremos danificar o jogador, precisamos de um jeito de identificar o jogador para o script: um Thing ID (aka TID). Muitas funções ACS não têm como referenciar um jogador sem TID definido, então vamos definir um TID no jogador com Thing_ChangeTID.

Use este script:

script 2 ENTER
{
    Thing_ChangeTID(0, 1000);
}

Quando o jogador entrar no mapa, nós vamos atribuir a ele um TID de 1000 (o segundo parâmetro). O primeiro parâmetro de Thing_ChangeTID é de quem mudar o TID; usamos 0 para referir ao ativador do script, que num script ENTER é o jogador que entrou. Usar 0 como TID do ativador é comum em várias funções ACS, então vale guardar isso.

  • Nota: TIDs de jogador começando em 1000 é um padrão bem comum, mas nem todos os mods seguem essa convenção (infelizmente)...

Agora que o jogador tem um TID, podemos completar a chamada de Thing_Damage:

#include "zcommon.acs"

script 1 (void) 
{
    Thing_Damage(1000, 2, 0);
}

Estamos causando dano ao jogador com TID 1000, com 2 de dano, e o meio de morte padrão 0 (gera um óbito genérico "Player died.").

Teste seu nível, pegue a chave vermelha e observe a vida. Ela vai diminuir ao pegar a chave.

Veja esta página para mais ações utilizáveis: Action specials

Comentando seu código

Você pode adicionar notas ao lado do código do script (chamadas comentários). Você pode usá-las como quiser, mas elas são especialmente recomendadas para anotar comportamentos complexos ou o motivo de certas decisões.

Você pode adicionar um comentário após uma linha de código com um espaço e duas barras, seguido por texto. Tudo depois das barras é ignorado pelo compilador e não é tratado como código até a próxima linha.

Adicionando comentários a um exemplo anterior:

script 3 (void)
{
    // Sou um comentário. Aqui é onde inicializamos as variáveis:
    int a = 9;
    int b = 17;
    print(d:a + b); // Imprimir a soma de a e b
}

Usando funções para fazer mudanças simples no mapa

Você pode usar funções ACS e ações de linedef para alterar um mapa, não só things.

Água simples e nadável

Para começar, abra o Doom Builder e crie um novo mapa no formato ZDoom (Doom in Hexen). Crie um setor quadrado de 512x512. Vamos chamar esse de setor "grande". Coloque um Player 1 Start em algum lugar dentro dele.

Crie um segundo setor "pequeno" de 64x64 dentro do primeiro:

  1. Abaixe o piso para -64 para parecer uma piscina vazia.
  2. Não esqueça de texturizar as bordas da piscina.
  3. Atribua a tag 1 a esse setor.
  4. Defina a textura do piso como FWATER1.

Depois disso, crie outro setor separado fora do setor grande, sem encostar nele. Esse será o setor "de controle". O tamanho não importa (você pode mantê-lo pequeno para ficar mais fácil de mexer).

  1. Garanta que o teto desse setor tenha a mesma altura dos outros.
  2. Defina o piso para -8
  3. Defina a textura do teto como FWATER1.

Edite qualquer linedef no setor de controle e atribua a ela a ação 209:Transfer_Heights. Transfer_Heights aceita dois parâmetros (tag do setor, efeito). Defina o primeiro parâmetro como 1 (tag 1) e o segundo como 8 (a parte submersa é nadável).

Salve e rode o mapa. Se tudo estiver certo, você verá que a piscina está "cheia"... ou melhor, que a textura FWATER1 agora fica apenas 8 unidades abaixo do piso em vez de 64. Isso acontece porque a *altura* do setor de controle foi *transferida* para a piscina. Se você pular na piscina, deve conseguir nadar (ande para frente e mova o mouse para cima e para baixo. Você vai flutuar na água em vez de só olhar para cima e para baixo). Saia do mapa e volte ao Doom Builder.

Agora queremos criar um script que adicione cor e neblina no setor de controle. Para o script funcionar, o setor de controle precisa de uma tag, então vamos dar a ele a tag 2. No script ACS, precisamos de duas funções: Sector_SetColor() e Sector_SetFade(). Ambas aceitam os mesmos 4 argumentos (tag do setor, vermelho, verde, azul). Cada cor vai de 0 a 255, dependendo da intensidade. Exemplo: azul = 0 significa sem azul; azul = 255 significa azul no máximo.

#include "zcommon.acs"
// O tipo de script OPEN executa uma vez quando o mapa carrega
script 1 OPEN
{
    // vamos usar a tag do setor de controle, 2
    Sector_SetColor(2, 0, 0, 205); // isso tinge o setor com azul
    Sector_SetFade(2, 0, 0, 205); // isso cria um efeito de neblina azul
}

Agora, quando você nadar na piscina, a parte submersa ficará azul escura e turva.

Porta abrindo sob condição

O próximo tutorial vai permitir criar uma porta que abre quando você mata um inimigo.

Para isso, crie um novo mapa e faça duas salas ligadas por uma porta. A primeira sala deve ter um monstro, por exemplo um zombieman, e o Player 1 Start. Na outra sala pode haver um item como recompensa. A porta que conecta as duas salas não deve ter ações de linha (para você não conseguir abrir manualmente), mas precisa ter uma tag de setor. Defina a tag do setor da porta como 1.

Vamos usar a função ACS Door_Open() para abrir a porta. Door_Open() aceita três argumentos:

  • tag: tag do setor afetado; no nosso caso, 1
  • speed: a velocidade de abertura; use 64 para abrir rápido
  • lighttag: tag do setor para efeito gradual de luz; pode deixar 0
script 1 (void)
{
    print(s:"You killed a zombieman!");
    Door_Open(1, 64, 0); // Abrir o setor tag 1 como porta, velocidade 64
}

Agora, para isso funcionar, precisamos dar ao zombieman um thing special. Neste caso, use 80:ACS_Execute(). Defina o primeiro argumento como 1 (script 1) e deixe os demais como 0.

Teste o mapa: a porta deve abrir ao matar o zombieman.

Controle de fluxo

Execução condicional (if / else)

Podemos fazer uma verificação lógica num script para decidir entre diferentes trechos de código usando if e else.

Exemplo simples de if:

script 1 ENTER
{
    int condition = 1;
    if(condition)
    {
        print(s:"The condition was true.");
    }
}

O if usa seu próprio par de chaves para delimitar o bloco de código que ele controla. Garanta que cada chave de abertura tenha uma de fechamento e não confunda com as chaves do script.

Isso vai executar o print porque definimos condition como 1, que é “verdadeiro”. Qualquer valor numérico diferente de 0 conta como verdadeiro; 0 conta como falso.

Se você mudar condition para 0, o print não executa.

Exemplo de else encadeado com if:

script 1 ENTER
{
    int condition = 0;
    if(condition == 0)
    {
        print(s:"The condition was 0.");
    }
    else if(condition == 1)
    {
        print(s:"The condition was 1.");
    }
    else
    {
        print(s:"The condition was something else entirely...");
    }
}

Tente mudar condition para 0, depois 1, depois 123, e veja qual ramo executa.

Loops condicionais de repetição (while)

Um loop "while" faz a mesma coisa que um if, exceto que ele repete quando termina. O script para de repetir quando a(s) condição(ões) deixam de ser satisfeitas.

NOTA SOBRE LOOPS INFINITOS: Um loop que deve rodar para sempre PRECISA conter pelo menos um Delay() com parâmetro 1 ou maior. Caso contrário, o ZDoom vai encerrar o script (um loop eterno sem pausas impede que o resto do mapa rode). Se isso acontecer, você verá uma mensagem como "Runaway script # terminated" no topo da tela.

script 1 OPEN
{
    int i = 5;
    while(i > 0)
    {
        comando ou série de comandos que mudam i;
    }
}

Esse while roda enquanto i for maior que 0.

Se você quiser um while que nunca termine (por exemplo, para criar um efeito contínuo), pode usar uma expressão que nunca vira falsa:

script 1 OPEN
{
    while(Predefinição:Const) // Ou while(1), ou while(4 == 4), etc.
    {
        comandos
        ; // esperar 1 tic entre execuções do loop
    }
}

Nota: o operador de negação ! pode ser usado para checar a falsidade de uma condição, exemplo: !(true) (não verdadeiro), ou seja, falso.

Loops controlados por contagem (for)

Um loop "for" repete um trecho do script um número definido de vezes:

script 1 (void)
{
    for(int i = 0; i < 10; i++)
    {
        log(d:i);
    }
}

Esse script imprime 0 a 9 no log do console.

ACS usa o chamado for de três expressões, por causa das três partes:

  1. Inicializador int i = 0
  2. Teste do loop i < 10
  3. Contador i++

Cada expressão é separada por ponto-e-vírgula.

Você não precisa declarar uma variável no inicializador, e dá para variar as expressões, mas esse exemplo é o uso típico.

Na primeira iteração, i == 0, o teste i < 10 é verdadeiro e o código dentro do loop executa. Depois i é incrementado em 1 (por causa do i++) e a próxima iteração começa. Isso continua até i == 10, quando o teste i < 10 falha e o bloco interno não executa. A execução então segue após o for.

Referência adicional

  • Tutoriais de ACS nesta wiki: ACS
  • Outros tutoriais: Tutorials