Nuclear Invaders para CP/M-86

Foro dedicado a la programación en todo tipo de sistemas clásicos.
dancresp
Amiga 1200
Amiga 1200
Mensajes: 1393
Registrado: 23 Dic 2008, 17:53
Sistema Favorito: MSX
primer_sistema: ZX81
Primera consola: Atari 2600
Gracias dadas: 3 veces
Gracias recibidas: 20 veces

Nuclear Invaders para CP/M-86

Mensajepor dancresp » 15 Feb 2014, 12:24

Game_3.jpg
Game_3.jpg (49.28 KiB) Visto 7584 veces


EL JUEGO
Vuelven los invasores del espacio!!!

En el año 1977 intentaron invadir las ciudades de la tierra, y fracasaron.
Ahora vuelven con un malvado plan: destruir las centrales nucleares para dejar el planeta inhabitable.
Con la ayuda de nuestro tanque debemos evitar que cumplan con su objetivo.

Cada nivel lo componen 30 invasores, y al superarlo aparecerá una central más grande y los invasores deberán recorrer menos distancia para llegar a ella.

Destruye el UFO que aparece por la parte superior para conseguir puntos extras.

Pulsa “RETURN” en la pantalla de presentación para empezar la partida.

Controles:
Pulsa “X” para mover el tanque a la derecha y “Z” para mover el tanque a la izquierda.
Pulsa RETURN para disparar.

Descargar código fuente (PAS) y ejecutable (CMD):
El adjunto Nuclear Invaders - CPM86.rar ya no está disponible


BLOQUES
En PASCAL los programas se suelen construir a base de PROCEDURES, que son procedimientos encargados de hacer una tarea concreta.

Para el programa se han definido 13 PROCEDURES:
Pause: Hace una pausa de un tiempo dado. Un valor “100” hace una pausa de 1 segundo aproximadamente.
BorrarPantalla: Borra la pantalla hasta la fila indicada. Respeta el marcador superior.
PulsarTecla: Queda en espera hasta que se pulsa una tecla.
Marcador: Muestra un valor numérico de 4 cifras en la zona de los marcadores. Indicar posición horizontal y el valor.
Presentacion: Muestra la pantalla de presentación y la pantalla de inicio de partida.
InicioPrograma: Procedimiento que se ejecuta la primera vez que se ejecuta el programa. Define ciertas matrices y el récod.
ResetVariables: Inicializa las variables antes de iniciar un nivel o perder una vida.
ZonaDeJuego: Al iniciar un nivel muestra el tanque, los invasores y la central nuclear.
MoverTanque: Controla el movimiento de nuestro tanque.
ControlDisparo: Controla todo lo relacionado con el disparo: movimiento e impacto.
MoverInvasor: Controla el movimiento de los invasores.
MoverUFO: Controla el UFO.
PerderVida: Decrementa el contador de vidas y controla el final de la partida.

Al final hay el nudo principal de desarrollo formado por 3 bucles REPEAT:

1) Se va repitiendo partida tras partida, mostrando la presentación primero de todo, y engloba los otros dos. Es un bucle infinito, del que solo se puede salir pulsando CTRL+C o ESC. Este último lo controla el programa y fuerza un HALT.

2) Controla la repetición cada vez que superamos un nivel o perdemos una vida. Este bucle finaliza al perder las tres vidas.

3) Controla todo el desarrollo a lo largo de la partida. Engloba el movimiento del tanque, enemigos, UFO, etc. Este bucle finaliza cuando perdemos una vida o pasamos de nivel.


EL LISTADO

Código: Seleccionar todo

PROGRAM NuclearInvaders;

{ *** Definición de Variables y Constantes Globales *** }
CONST
    invader_graf : ARRAY [1..10] OF STRING[3] = ('w8w','"8"','/Y\','\Y/','/Y\','\Y/','qXp','dXb','qXp','dXb');
    base1 : STRING[27] = '+-+-+-+-+-+-+-+-+-+-+-+-+-+';
    base2 : STRING[27] = '|*|*|*|*|*|*|*|*|*|*|*|*|*|';
    valor : ARRAY [1..10] OF INTEGER = (30,20,20,10,10,30,20,20,10,10);

VAR
    tecla : CHAR;
    muerto, disparo : BOOLEAN;
    f, i, pos_ini, pos_fin : INTEGER;
    puntos, puntos_max, nivel, vidas : INTEGER;
    tanque_x, laser_x, laser_y, indice : BYTE;
    ufo_cont, ufo_x, animar, flota : BYTE;
    invader_x, invader_y : ARRAY [1..10] OF INTEGER;
    invader_vel, invader_num : ARRAY [1..10] OF INTEGER;
    invader_mov : ARRAY [1..10,1..2] OF STRING[4];

{ *** Rutina de Pausa *** }
PROCEDURE Pausa(ciclos:INTEGER);
BEGIN
    FOR f:=1 TO ciclos DO Delay(15);
END;

{ *** Borrado parcial de la pantalla *** }
PROCEDURE BorrarPantalla(linea:INTEGER);
BEGIN
    GotoXY(1,3);
    FOR f := 3 TO linea DO
        Writeln('                                                                              ');
END;

{ *** Pausar hasta que se pulsa una tecla *** }
PROCEDURE PulsarTecla;
BEGIN
    REPEAT
    UNTIL KeyPressed;

    IF KeyPressed THEN read(KBD,tecla);
END;

{ *** Mostrar las Puntuaciones del Marcador *** }
PROCEDURE Marcador(x,y,numero:INTEGER);
VAR
    result : STRING[8];
BEGIN
    Str(numero,result); result:=ConCat('0000',result);
    GotoXY(x,y); Writeln(Copy(result,Length(result)-3,4));
END;

{ *** Pantalla de Presentacion del juego *** }
PROCEDURE Presentacion;
BEGIN
    ClrScr;
    Writeln('     PLAYER<1>                      HI-SCORE                      PLAYER<2>');
    Marcador(8,2,puntos); Marcador(39,2,puntos_max);
    GotoXY(39,5);  Write('PLAY');
    GotoXY(33,7);  Write('NUCLEAR INVADERS');
    GotoXY(29,10); Write('* SCORE ADVANCE TABLE *');
    GotoXY(34,12); Write('<*> =? MYSTERY');
    GotoXY(34,14); Write('w8w =30 POINTS');
    GotoXY(34,16); Write('/Y\ =20 POINTS');
    GotoXY(34,18); Write('qXp =10 POINTS');
    GotoXY(3,21); Write('(C) SCAINET SOFT, 2014');
    GotoXY(55,21); Write('<= Z - X => : RETURN = |');
    GotoXY(6,24);  Write('oxIxo oxIxo');
    GotoXY(69,24); Write('CREDIT 00');

    PulsarTecla;
    BorrarPantalla(23);

    GotoXY(8,2); Write('0000');
    GotoXY(34,12); Write('PLAY PLAYER<1>');
    Pausa(300);

    GotoXY(3,23); Writeln('____________________________________________________________________________');
    Write('   3');
END;

{ *** Inicio del Programa *** }
PROCEDURE InicioPrograma;
BEGIN
    puntos_max:=0;

    FOR f:=1 TO 5 DO BEGIN
        invader_mov[f,1]:=invader_graf[f*2-1];
        invader_mov[f,2]:=invader_graf[f*2];
        invader_mov[f+5,1]:=invader_mov[f,1];
        invader_mov[f+5,2]:=invader_mov[f,2];
    END;
END;

{ *** Reset de las Variables *** }
PROCEDURE ResetVariables;
BEGIN
    tanque_x:=5; disparo:=FALSE;
    muerto:=FALSE; indice:=1;
    ufo_x:=0; ufo_cont:=200;
    animar:=1; flota:=30;

    FOR f:=1 TO 5 DO BEGIN
        invader_y[f]:=f*2+5; invader_y[f+5]:=f*2+5;
        invader_x[f]:=4; invader_x[f+5]:=75;
        invader_vel[f]:=0; invader_vel[f+5]:=0;
        invader_num[f]:=3; invader_num[f+5]:=3;
    END;
END;

{ *** Mostrar la Zona de Juego *** }
PROCEDURE ZonaDeJuego;
BEGIN
    i:=nivel*4+5; pos_ini:=42-Round(i/2); pos_fin:=pos_ini+i-1;
    GotoXY(pos_ini,6); Write(Copy(base1,1,i));

    FOR f:=7 TO 11 DO BEGIN
        GotoXY(pos_ini,f); Write(Copy(base2,1,i));
        GotoXY(pos_ini,f+1); Write(Copy(base1,1,i));
        f:=f+1
    END;

    FOR f:=1 TO 10 DO BEGIN
        GotoXY(invader_x[f],invader_y[f]); Write(invader_mov[f,1]);
    END;

    GotoXY(tanque_x,22); Write('oxIxo');
END;

{ *** Mover el Tanque *** }
PROCEDURE MoverTanque;
BEGIN
    read(KBD,tecla); tecla:=UpCase(tecla);

    IF (tecla='Z') AND (tanque_x>5) THEN BEGIN
        tanque_x:=tanque_x-1;
        GotoXY(tanque_x,22); Write('oxIxo ');
    END;
    IF (tecla='X') AND (tanque_x<72) THEN BEGIN
        GotoXY(tanque_x,22); Write(' oxIxo');
        tanque_x:=tanque_x+1;
    END;
    IF (tecla=chr(13)) AND (disparo=FALSE) THEN BEGIN
        disparo:=TRUE;
        laser_x:=tanque_x+2; laser_y:=22;
    END;
    IF (tecla=chr(27)) THEN Halt;
END;

{ *** Controlar el Disparo del Tanque *** }
PROCEDURE ControlDisparo;
BEGIN
    IF (laser_y<22) THEN BEGIN
        GotoXY(laser_x,laser_y); Write(' ');
    END;
    laser_y:=laser_y-1;

    IF (laser_y=2) OR ((laser_x>=pos_ini) AND (laser_x<=pos_fin) AND (laser_y<17)) THEN
        disparo:=FALSE
    ELSE BEGIN
        GotoXY(laser_x,laser_y); Write('|');

        FOR f:=1 TO 10 DO
            IF (laser_y=invader_y[f]) AND (laser_x>=invader_x[f]) AND (laser_x<=invader_x[f]+2) THEN BEGIN
                GotoXY(invader_x[f],invader_y[f]); Write('   ');
                invader_vel[f]:=0;
                IF (f>5) THEN invader_x[f]:=75 ELSE invader_x[f]:=4;
                invader_num[f]:=invader_num[f]-1;
                If (invader_num[f]>0) THEN BEGIN
                    GotoXY(invader_x[f],invader_y[f]); Write(invader_mov[f,1]);
                END;
                disparo:=FALSE; flota:=flota-1;
                puntos:=puntos+valor[f]; Marcador(8,2,puntos);
            END;

        IF (disparo=TRUE) THEN
            IF (ufo_x<>0) AND (laser_y=4) THEN
                IF (laser_x>=ufo_x+1) AND (laser_x<=ufo_x+3) THEN BEGIN
                    f:=(Random(3)+1)*50;
                    GotoXY(ufo_x+1,4); Write(f,' ');
                    Pausa(30);
                    GotoXY(ufo_x+1,4); Write('   ');
                    puntos:=puntos+f; Marcador(8,2,puntos);
                    disparo:=FALSE; ufo_x:=0; ufo_cont:=200;
                END;
    END;
END;

{ *** Mover a los Invasores *** }
PROCEDURE MoverInvasor;
BEGIN
    IF (invader_num[indice]>0) THEN BEGIN
        IF (invader_vel[indice]=0) THEN BEGIN
            IF (Random(10)>7) THEN BEGIN
                invader_vel[indice]:=1;
                IF (indice>5) THEN invader_vel[indice]:=-invader_vel[indice];
            END;
        END
        ELSE BEGIN
            GotoXY(invader_x[indice],invader_y [indice]); Write('   ');
            invader_x[indice]:=invader_x[indice]+invader_vel[indice];
            GotoXY(invader_x[indice],invader_y[indice]); Write(invader_mov[indice,animar]);
            IF (indice<6) AND (invader_x[indice]>pos_ini-2) THEN muerto:=TRUE;
            IF (indice>5) AND (invader_x[indice]<pos_fin) THEN muerto:=TRUE;
        END;
    END;

    indice:=indice+1;
    IF (indice>10) THEN BEGIN
       indice:=1;
       IF (animar=1) THEN animar:=2 ELSE animar:=1;
    END;
END;

{ *** Mover el Platillo Volante *** }
PROCEDURE MoverUFO;
BEGIN
    IF (ufo_x=0) THEN BEGIN
        ufo_cont:=ufo_cont-1;
        IF (ufo_cont=0) THEN ufo_x:=3;
    END
    ELSE BEGIN
        ufo_x:=ufo_x+1;
        GotoXY(ufo_x,4);
        IF (ufo_x<75) THEN Write(' <*>')
        ELSE BEGIN
            Writeln('    ');
            ufo_x:=0; ufo_cont:=200;
        END;
    END;
END;

PROCEDURE PerderVida;
BEGIN
    vidas:=vidas-1;
    IF (vidas>0) THEN BEGIN
        GotoXY(4,24); Write(vidas);
        GotoXY(vidas*6,24); Write('     ');
    END
    ELSE BEGIN
        GotoXY(35,19); Write(' GAME  OVER ');
        IF (puntos_max<puntos) THEN puntos_max:=puntos;
        Pausa(300);
    END
END;

{ *** Nudo Principal de Desarrollo *** }
BEGIN
    InicioPrograma;

    { *** Bucle Inicio Partida *** }
    REPEAT
        Presentacion;

        nivel:=1; vidas:=3; puntos:=0;

        { *** Bucle Iniciar Nivel *** }
        REPEAT
            ResetVariables;
            BorrarPantalla(22);
            ZonaDeJuego;

            { *** Bucle Principal *** }
            REPEAT
                IF (KeyPressed) THEN MoverTanque;
                IF (disparo=TRUE) THEN ControlDisparo;
                MoverInvasor;
                MoverUFO;
                Pausa(5);
            UNTIL (flota=0) OR (muerto=TRUE);

            IF (muerto=FALSE) THEN BEGIN
                IF (nivel<4) THEN nivel:=nivel+1;
            END
            ELSE PerderVida;

            Pausa(300);
        UNTIL (vidas=0);
    UNTIL FALSE;
END.


APUNTES FINALES
No tocaba el lenguaje PASCAL desde el año 1993, en que conseguí una copia “ilegal” del programa TURBO Pascal de Borland para MS-DOS. Es un lenguaje que me gustó porque es potente, estructurado y bastante fácil de aprender. Pero la verdad es que hice un par de proyectos de los míos y no lo toqué más. También en su día conseguí una copia del HiSoft PASCAL para el ZX-Spectrum pero me limité a hacer alguna sencilla prueba.

Para CP/M-80 tengo el PASCAL MT+, pero no me acaba de gustar. Hace un tiempo conseguí el Turbo PASCAL para CP/M-86 y una vez bien configurado descubrí todo su potencial. Lleva un completo editor incorporado y puedes ejecutar el programa o generar un ejecutable con extensión CMD.

Poco después conseguí un manual de instrucciones en formato PDF y saltando algunas cosas, acabó impreso en
láser a doble cara y metido en un archivador para poder ser consultado cómodamente.

Y el descubrir que incorpora el comando “GotoXY”, que permite escribir en cualquier posición de la pantalla, me animó a desarrollar mi primer juego de acción. Y que mejor que una versión del “Nuclear Invaders”... en modo texto !!!


Pongámonos en ambiente
La verdad es que esta ha sido una versión muy especial para mi.

Este es el escenario:
Un AMSTRAD PC-1515-SD, con un 8086 a 8MHz y 512 KB de RAM, monitor monocromo de fósforo blanco, una disquetera de 5.25” DD de 360 KB y un teclado de 83 teclas.

Un disquete con una copia del sistema operativo CP/M-86, otro disquete con una copia del Turbo PASCAL y un manual de este compilador.

Y para finalizar, un proyecto: Nuclear Invaders.

Con este cóctel era imposible que no acabara bien.


El desarrollo
En un principio he estructurado el programa basándome un poco en la versión del Jupiter ACE, aunque está más simplificado.
Aquí, en lugar de palabras he definido procedimientos.

Como siempre he empezado por lo más fácil, para poco a poco irme sumergiendo en el desarrollo de las partes más complejas.

Así, he empezado con la pantalla de presentación, el movimiento de nuestro tanque y del disparo, el movimiento de los invasores y del UFO. He dejado para el final el control de lo que toca el disparo.

EL CP/M no permite, de una forma estándar, consultar lo que hay en una posición determinada de la pantalla con lo que esta parte se ha modificado respecto al resto de versiones. Así, al avanzar el disparo va comprobando si coincide con la posición de alguno de los 10 invasores, del UFO e incluso de la central nuclear. Como va compilado, el rendimiento es perfecto.

Para el tema de los gráficos, me he basado en el juego “ALIENS” del CP/M-80. El uso de letras no da para mucho, pero las animaciones han quedado más que bien.


Y los problemillas…
El programa se ha desarrollado en un equipo a 8 MHz, y la velocidad es perfecta en él, pero con el programa 22DSK he pasado el fichero con el código fuente al PC moderno, y al ejecutarlo allí, en mi simulador de CP/M-86 me encuentro que va a una velocidad brutal. En resumen, es injugable. Si el simulador lo ejecuto desde el DOS-Box, la velocidad baja, pero sigue siendo muy alta.

Para hacer la pausa uso la intrucción “Delay(n)” donde “n” indica los milisegundos que ha de esperar. El manual ya indica que no es perfecto, y doy fe que no lo es.

He intentado ver si es posible acceder al reloj del sistema, pero no he sido capaz de descubrir si es posible. Esto iría bien para montar una rutina que calculara la velocidad del equipo y la ajustara automáticamente. Seguiremos investigando.

Por otro lado, el control de la nave no es muy bueno porque al pulsar una tecla espera a que entre en modo auto-repetición para mover nuestro tanque continuamente. A pesar de ello, el juego es jugable y he adaptado la dificultad a ello.

En el juego “ALIENS” lo que hacen es que al mover la nave en una dirección no para hasta que pulsas una determinada tecla. No me ha gustado la solución y lo he dejado como estaba.

Con todo, me ha hecho mucha gracia programar esta versión. Programar el juego entero me ha llevado 3 noches, aprovechando los ratitos de tren para documentarme y planificar cosas.

Para terminar, decir que este programa ha sido desarrollado en un AMSTRAD PC-1512-SD bajo CP/M-86, el listado lo he impreso con una matricial y he usado el 22DSK para pasar el programa a un disquete de 3.5" y de allí a un PC moderno con disquetera USB.

Os invito a probarlo.

Game_1.jpg
Game_1.jpg (42.54 KiB) Visto 7584 veces

Game_2.jpg
Game_2.jpg (44.73 KiB) Visto 7584 veces

Game_3.jpg
Game_3.jpg (49.28 KiB) Visto 7584 veces

Game_4.jpg
Game_4.jpg (64.9 KiB) Visto 7584 veces

Game_5.jpg
Game_5.jpg (59.29 KiB) Visto 7584 veces

list_2.jpg
list_2.jpg (81.45 KiB) Visto 7584 veces

Equipo_1.jpg
Equipo_1.jpg (82.67 KiB) Visto 7584 veces
Buscando la IP de la W.O.P.R.

Avatar de Usuario
UFO
Atari 1040 STf
Atari 1040 STf
Mensajes: 803
Registrado: 19 Feb 2010, 15:16
Sistema Favorito: PC
primer_sistema: MSX
consola_favorita: Sega Genesis/Megadrive
Primera consola: TV Games/Pong Clone
Gracias dadas: 43 veces
Gracias recibidas: 4 veces

Re: Nuclear Invaders para CP/M-86

Mensajepor UFO » 15 Feb 2014, 16:21

Enhorabuena dancresp, cómo lo curras, macho!
-

TwitterZDP
ZX Spectrum 16
ZX Spectrum 16
Mensajes: 2
Registrado: 03 Nov 2013, 19:45

Re: Nuclear Invaders para CP/M-86

Mensajepor TwitterZDP » 16 Feb 2014, 17:19

Esto merece un twitt =D>

Avatar de Usuario
na_th_an
Amiga 1200
Amiga 1200
Mensajes: 1273
Registrado: 10 Oct 2012, 11:17
Sistema Favorito: (Otro)
primer_sistema: Spectrum +2
consola_favorita: Sony PlayStation 1
Primera consola: Sega Master System
Gracias dadas: 18 veces
Gracias recibidas: 15 veces

Re: Nuclear Invaders para CP/M-86

Mensajepor na_th_an » 17 Feb 2014, 00:24

¿Qué tarjeta gráfica monta el ordenador? En casi todos los PC con tarjetas que soporten un modo de texto se puede consultar el bit de retrazo y usar vsync para temporizar y calcular los retardos. En casi todas las tarjetas que conozco, además, el modo de texto mapea al segmento $B000 o $B800 dependiendo del modo, con lo que podría leerse el contenido de la pantalla aunque el sistema operativo no implemente ninguna función para hacerlo.

Edito: ahora caigo en la cuenta de que si se ha hecho para CP/M se busca portabilidad, así que no he dicho nada :)

dancresp
Amiga 1200
Amiga 1200
Mensajes: 1393
Registrado: 23 Dic 2008, 17:53
Sistema Favorito: MSX
primer_sistema: ZX81
Primera consola: Atari 2600
Gracias dadas: 3 veces
Gracias recibidas: 20 veces

Re: Nuclear Invaders para CP/M-86

Mensajepor dancresp » 17 Feb 2014, 18:05

na_th_an escribió:Edito: ahora caigo en la cuenta de que si se ha hecho para CP/M se busca portabilidad, así que no he dicho nada :)

Pues portable lo es.

Lo he compilado con el Turbo Pascal de CP/M-80 y excepto por la velocidad (es más lento) ha compilado a la primera.
Solo hay que modificar el "Delay(15)" del procedimiento "Pausa" a un valor más pequeño.

Y lo he compilado con el Turbo Pascal de MS-DOS y con unos pequeños cambios:
- "uses crt,dos;" justo después de la primera línea del programa (el PROGRAM).
- Ajustando el valor del "Delay(15)" del procedimiento "Pausa" por "Delay(3000)" en mi PC.
- Y sustituyendo el "read(KBD,tecla);" por "tecla:=ReadKey;"

El programa también ha funcionado perfectamente.

Estoy por hacer que se le pueda pasar el valor del Delay como parámetro (opcional) para que funcione en todos bien.
Buscando la IP de la W.O.P.R.

Avatar de Usuario
zup
Amiga 2500
Amiga 2500
Mensajes: 3012
Registrado: 04 Sep 2009, 20:07
Sistema Favorito: Spectrum 16Kb/48Kb
primer_sistema: Spectrum 16Kb/48Kb
consola_favorita: Nintendo DS/3DS
Primera consola: Nintendo GameBoy
Ubicación: Navarra
Gracias dadas: 86 veces
Gracias recibidas: 356 veces
Contactar:

Re: Nuclear Invaders para CP/M-86

Mensajepor zup » 17 Feb 2014, 19:01

Cuidado al recompilar en Turbo Pascal de MS-DOS.

Si usas la función Delay para sincronizar, cascará en todo lo que se más rápido que un Pentium II/200 (creo, pero seguro que en un Pentium III ya no funciona) con el Runtime Error 200. Esto se debe a un ¿bug? en la librería crt... el bucle que utiliza para determinar la velocidad del equipo provoca una división por cero si el micro es muy rápido. Hay un programa (tppatch) que sirve para solucionar estos problemas (o eso, o te buscas otro método de sincronizar que no use delay).

En otro orden de cosas, en su día Borland hizo freeware las versiones 1.0, 3.02 y 5.5 de Turbo Pascal... se pueden descargar desde la web de Embarcadero (la marca Borland acabó por aquí).
I have traveled across the universe and through the years to find Her. Sometimes going all the way is just a start.
Además vendo cosas!

dancresp
Amiga 1200
Amiga 1200
Mensajes: 1393
Registrado: 23 Dic 2008, 17:53
Sistema Favorito: MSX
primer_sistema: ZX81
Primera consola: Atari 2600
Gracias dadas: 3 veces
Gracias recibidas: 20 veces

Re: Nuclear Invaders para CP/M-86

Mensajepor dancresp » 18 Feb 2014, 15:16

zup escribió:Cuidado al recompilar en Turbo Pascal de MS-DOS.

Si usas la función Delay para sincronizar, cascará en todo lo que se más rápido que un Pentium II/200...

Hombre, bueno es saberlo.

De todas formas lo he probado en mi notebook y ha funcionado...

:jumper:
Buscando la IP de la W.O.P.R.

Avatar de Usuario
Joss
Atari 1040 STf
Atari 1040 STf
Mensajes: 930
Registrado: 17 Jul 2012, 20:07
Gracias dadas: 14 veces
Gracias recibidas: 2 veces

Re: Nuclear Invaders para CP/M-86

Mensajepor Joss » 18 Feb 2014, 18:33

Enhorabuena por el trabajo =D> Tiene una pinta genial :D y gracias por comentar el desarrollo y tus experiencias, que con estos temas son tan importantes como el resultado final.


Volver a “Programación”

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 4 invitados