domingo, 29 de novembro de 2009

Text Adventure Engine

Clique em um dos links abaixo para baixar:
Clique aqui para baixar a Text Adventure Engine (VERSÃO COMPLETA 1.0)

O que Text Adventure Engine NÃO É:

- Text Adventure Engine NÃO É um jogo.
- Text Adventure Engine NÃO É uma biblioteca de classes.
- Text Adventure Engine NÃO É uma biblioteca de funções.
- Text Adventure Engine NÃO É um vírus.

...Então o que é?

Resumo do que É Text Adventure Engine:

- Text Adventure Engine é um conjunto relativamente pequeno de arquivos com programas em C/C++ que se você souber programar em C/C++, você pode modificar (melhorar, aumentar, diminuir, alterar, amaldiçoar, excluir) para criar um jogo (ou não) no estilo "text-adventure" onde toda a interação é através de uma linha de comando, e não existem gráficos, apenas texto puro numa tela de terminal (tipo o DOS).

O que fazer com Text Adventure Engine?

- Você é quem sabe. Algumas sugestões: transformá-lo numa biblioteca de funções/classes, fazer um jogo, estudá-lo para aprender PROGRAMAÇÃO DESORIENTADA A OBJETOS, esculhambar com o código todo, enviar para o seu amigo nerd, enviar o pacote para a lixeira ou excluí-lo definitivamente do seu HD.

Como fazer alguma coisa útil com Text Adventure Engine?

- Em primeiro lugar, você deve saber programar em C/C++ e ter a biblioteca PDCurses instalada e configurada para o seu compilador (de preferência Dev-C++, pois o projeto é do Dev-C++);
- Em segundo lugar, você deve ler muito atentamente todos os arquivos, eu tentei escrever O MÁXIMO de comentários para que qualquer pessoa que saiba ler em português seja capaz de entender;
- Em terceiro lugar, você deve saber onde colocar "cada coisa no seu lugar". Leia os próximos tópicos a seguir para saber mais.

Onde colocar "cada coisa no seu lugar"?

Infelizmente, eu não ando muito bem de programação orientada a objetos, e tive diversos problemas para analisar o sistema do jogo. Então, como eu queria MUITO criar esta "biblioteca", digamos assim, eu resolvi fazer tudo misturado: umas classes aqui, umas funções soltas alí, e assim nasceu esta bagunça, que por incrível que pareça, FUNCIONA PERFEITAMENTE :)

O arquivo MAIN.CPP já vem com um jogo de exemplo: um pequeno conjunto de 9 salas de exemplo e 1 objeto de exemplo que você deve pegar numa certa sala e usar em uma certa sala diferente. Jogue esse pequeno exemplo e veja os códigos-fonte para entender como tudo funciona.

*** Arquivo MAIN.CPP ***

No arquivo 'main.cpp' é onde você provavelmente vai adicionar (ou alterar) a maior quantidade de código.

Logo no início do arquivo, na seção "ALGUMAS DEFINICOES" você pode ver três #defines importantíssimos: MAX_MAP_WIDTH, MAX_MAP_HEIGHT e MAX_OBJECTS. MAX_MAP_WIDTH e MAX_MAP_HEIGHT referem-se às dimensões do array bidimensional de ponteiros para objetos Room. Multiplicando esses dois valores, você obtém o número máximo de salas que o jogo poderá instanciar. MAX_OBJECTS refere-se à dimensão do array unidimensional de ponteiros para objetos Object. Esse valor indica o máximo de objetos que o jogo poderá instanciar.

Depois, na seção "OBJETOS GLOBAIS" existe a declaração dos objetos mais importantes: char command[] que representa o string digitado na linha de comando; Object* objects[] que representa o array de objetos Object que serão instanciados na função init_objects(); Room* map[][] que representa o mapa do jogo, isto é nada mais do que uma matriz de ponteiros para objetos Room. Os objetos Room, isto é, as salas do mapa, são instanciadas na função init_rooms(); Player player, que representa o personagem do jogador com um nome específico dentro de uma sala específica: os dois argumentos 'int' referem se à posição do jogador no mapa de salas, isto é, aos índices da matriz de objetos Room, linha e coluna respectivamente.

Logo em seguida, vem a seção "PROTOTIPOS DAS FUNCOES". Aqui o próprio nome já diz: estão os protótipos de todas as funções implementadas logo após o main(). Muito provavelmente você aumentará (muito) esta lista se for fazer um jogo que preste (fazendo do 'main.cpp' um dos maiores arquivos de implementação em C++ que você já viu).

Depois da declaração dos protótipos, está o todo-poderoso main(). Nem preciso dizer que sem essa função nada funcionará nem hoje, nem amanhã e nem nunca. A chamada à função init_game() inicializa tudo! Então não tire ela do lugar! Você não deveria mexer no laço infinito for(;;) por que é ele que é responsável por atualizar a sala que está aparecendo na tela, por aguardar os comandos do jogador e interpretá-los. E na verdade, ele não é infinito. Digitando "sair" no prompt de comando, o jogo termina (desde que você não retire o comando "sair" do interpretador!). Entre a init_game() e o laço infinito for(;;) você pode incluir funções que mostram "telas estáticas" antes do jogo iniciar realmente (tela de introdução, tela de ajuda, instruções, papo-furado antes do jogo, etc). Declare as funções no arquivo 'static_screens.h' e as defina em 'static_screens.cpp'.

Finalmente, logo abaixo do main() está a implementação de TODAS as funções declaradas na seção "PROTOTIPOS DAS FUNCOES". As funcoes que voce cria deveriam ser implementadas lá no final do arquivo, para não confundir com as funções que já vieram no o arquivo original.

Você pode perceber que as funções básicas já estão implementadas. E os comandos básicos já são interpretados, como 'ir norte', 'ir sul', 'sair', etc. A maioria das funções novas que você provavelmente criará são as funções chamadas ao usar objetos que você criou.

Na função init_objects() você vai instanciar os objetos do seu jogo no array de ponteiros para Object. É bom usar um #define para cada objeto novo, pois assim você define o nome do objeto como um número, que corresponde ao índice daquele objeto no array; assim fica mais fácil de passar um objeto do array como referência para uma função, e evita os números mágicos:

Ao invés de:
use_object(objects[24]); // Número mágico: o que é aquele 24?

É melhor:
#define OBJETO_ESPECIAL 24 // Agora eu sei :)
// ...
objects[OBJETO_ESPECIAL] = new Object(
"Objeto Especial",
"Um objeto especial.",
"Peguei um objeto especial"
);
//...
use_object(objects[OBJETO_ESPECIAL]).

É mais fácil lembrar do "nome" de um objeto do que do índice do array onde ele está, não acha?

Em init_rooms() você vai instanciar as salas no array multidimensional de ponteiros para Room. O bom é você seguir o exemplo pronto: instancie a sala, e logo em seguida defina o texto que aparece naquela sala com o método set_desc().

Finalmente, em interpret_command() você pode (e deveria) adicionar novos comandos para serem interpretados durante o jogo. Por causa da função lowercase() invocada toda vez que o sistema interpreta um comando, não há necessidade de exigir que o jogador digite tudo só em minúsculas, porque a função lowercase() já transofrma todos os strings digitados na linha de comando em letras minúsculas. Que moleza, não acha? Portanto, independente de o jogador digitar em maiúsculas ou em minúsculas, todos os comandos são interpretados se existirem.

Você não deveria mexer nas demais funções, afim de não arruinar o sistema todo!

*** Arquivos SCREEN.H/SCREEN.CPP ***

O que as funções em 'screen.cpp´ fazem é inicializar a tela do terminal, alterar as cores da letra na tela e definir algumas cores usadas por frases específicas durante todo o jogo. Muito provavelmente as únicas alterações que você fará aqui, serão os #defines das cores das frases, já que por padrão tudo é escrito em branco. Leia esses arquivos para saber mais.

*** Arquivos SYSTEM.H/SYSTEM.CPP ***

Aqui não há necessidade de alterar praticamente nada. Só existem duas funções, halt() para mostrar uma mensagem na tela e aguardar o pressionamento de qualquer tecla, e get_command() que mostra o prompt de comando e recebe uma string do teclado, armazenando no string 'command' declarado em 'main.cpp'.

*** Arquivo MESSAGES.H ***

Aqui existem apenas alguns #defines que definem as strings que aparecem por todo o jogo. Dê uma olhada nesse arquivo para saber mais.

*** Arquivos OBJECT.H/OBJECT.CPP ***

Aqui está definida a classe Object que representa um objeto numa sala que o jogador pode pegar, usar, etc. Não creio que seja necessário mexer nesses arquivos, mas se for alterar alguma coisa, cuidado para não arruinar o sistema todo!

*** Arquivos ROOM.H/ROOM.CPP ***

Aqui vai a classe Room que representa uma sala do jogo. Você não deveria mexer aqui, a não ser que saiba o que está fazendo, ou pode arruinar o sistema todo!

*** Arquivos PLAYER.H/PLAYER.CPP ***

Aqui vai a classe Player que representa o personagem do jogador. Assim como na classe Room e na classe Object, você não deveria mexer aqui, para não correr o risco de arruinar o sistema todo!

*** Arquivos STATIC_SCREENS.H/STATIC_SCREENS.CPP ***

Estes arquivos contém as declarações/implementações das funções que mostram as "telas estáticas". Inclua aqui telas de introdução que aparecem antes do jogo, telas de interlúdio (que aparecem durante o jogo), telas de ajuda, instruções, papo-furado, etc. Repare que todas as funções aqui deveriam iniciar por ss_ (abreviação de static screen) por convenção. Geralmente, essas "telas estáticas" iniciam com uma chamada às funções da PDCurses clear() para limpar a tela, em seguida um conjunto de printw() para escrever na tela, e lá no final, geralmente vem um halt() do arquivo 'system.cpp' ou um napms() para aguardar alguns segundos antes de retornar.