________ ._______ ____ _________ \______ \ | \ \ / / \_ ___ \ ____ _____ ______ ____ | | \| |\ Y / / \ \/ / _ \ / \\____ \ / _ \ | ` \ | \ / \ \___( <_> ) Y Y \ |_> > <_> ) /_______ /___| \___/ \______ /\____/|__|_| / __/ \____/ \/ \/ \/|__|


Trucos y Consejos

Rutas relativas

DIV1 y DIV2 tienen una curiosa forma de trabajar con las rutas; por defecto mientras estés en el entorno de DIV2 todos los archivos se cargan con respecto a la carpeta de DIV2, y la forma de resolver las rutas es la siguiente (muchas gracias a @vii1 por la información):

Así que para poder compartir los juegos con comodidad y que todos podamos ejecutarlos a la primera, sin tener que andar reemplazando rutas, lo suyo sería utilizar el siguiente sistema:

C:\DIV2\USUARIO\JUEGO

De esta forma si hago un juego llamado JANDER y mi usuario es AZAZEL28, la carpeta de mi juego sería:

C:\DIV2\AZAZEL28\JANDER

NOTA: Las carpetas deberían ceñirse siempre al sistema de archivos de MS-DOS, es decir, máximo 8 carácteres para los nombres y máximo 3 carácteres para las extensiones. De esta forma no tenemos que pegarnos con nombres raros.

Y a la hora de cargar los archivos en mi .PRG el código quedaría de la siguiente manera:

load_fpg("azazel28\jander\graficos.fpg");
load_fnt("azazel28\jander\fuente.fnt");
load("azazel28\jander\ranking.dat");

Si no queréis tener que teclear siempre lo mismo podéis copiar el siguiente codigo y así sólo tendréis que cambiar las rutas una vez:

program paths_test;
const
  PATH_USER="AZAZEL28";
  PATH_PROG="TEST";
begin

  load_fnt(path_resolve("MIFUENTE.FNT"));

end

function path_resolve(file)
begin
  return (PATH_USER+"\"+PATH_PROG+"\"+file);
end

strncpy

Algo muy habitual al trabajar con cadenas en DIV es que ocurran errores de acceso fuera de rango (error #140). Para poder copiar cadenas con cadenas y garantizar que se respeta el ancho máximo lo mejor sería utilizar strncpy pero DIV no provee esta función, así que aquí tenéis una implementación sencillota:

function strncpy(string pointer dest, string src, max_length)
private
  length, src_length, index;
begin
  src_length = strlen(src);
  if (src_length > max_length)
    length = max_length;
  else
    length = src_length;
  end
  for (index = 0; index < max_length; index++)
    dest[index] = src[index];
  end
  return (length);
end

Dividir y multiplicar por 2

Esto son métodos clásicos para multiplicar y dividir por dos utilizando enteros (además no le afectan las divisiones entre 0).

width >> 1; // División entre 2
width << 1; // Multiplicación por 2
width >> 2; // División entre 4
width << 2; // Multiplicación por 4
width >> x;

Interpolación

Estas son algunas sencillas funciones de interpolación para poder hacer transiciones y animaciones.

// p es un valor que va de 0 a 100 (0% y 100% de interpolación)
// a es el valor inicial
// b es el valor final
function linear(p, a, b)
begin
  return ((((100 - p) * a) + (p * b)) / 100);
end

// p es un valor que va de 0 a 100 (0% y 100% de interpolación)
// a es el valor inicial
// b es el valor de control
// c es el valor final
function quadratic(p, a, b, c)
private
  d, e;
begin
  d = linear(p, a, b);
  e = linear(p, b, c);
  return (linear(p, d, e));
end

// p es un valor que va de 0 a 100 (0% y 100% de interpolación)
// a es el valor inicial
// b es el valor de control 1
// c es el valor de control 2
// d es el valor final
function cubic(p, a, b, c, d)
private
  e, f;
begin
  e = quadratic(p, a, b, c);
  f = quadratic(p, b, c, d);
  return (linear(p, e, f));
end

Por ejemplo, si queremos hacer una pequeña animación con rebote de un texto podemos utilizar estas funciones de la siguiente manera:

program interpolation_test;
private
  text_id;
begin
  text_id = write(0, 0, 0, 0, "Hola, Mundo!");
  loop
    move_text(text_id, 0, quadratic(clamp(timer, 0, 100), 0, 120, 100));
    frame;
  end
end

Rotar la paleta

DIV 2 salió a la venta con un error que hacía que no se rotase la paleta:

program roll_pal_fix_prg;
global
  // Es MUY importante definir este valor
  // como global.
  roll_pal_fix = 0;
begin

end

function roll_pal(start, count, inc)
begin
  // Esta función pone activa_paleta=1 pero como
  // la velocidad es 1 y la variación es muy poca
  // no le da tiempo a cambiar la paleta.
  fade(99 + roll_pal_fix, 100, 100, 1);
  // Vamos rotando el valor de destino porque
  // fade comprueba si el valor de destino es igual
  // al actual y si es así, no llama a activa_paleta=1
  roll_pall_fix = (roll_pall_fix + 1) % 3;
  // Llamamos al roll_palette estropeado.
  roll_palette(start, count, inc);
end

Funciones útiles

Estas son funciones útiles que no tengo muy claro en qué categoría poner:

Clamping

// Realiza el clamping de un valor
function clamp(value, min, max)
begin
  if (value < min) return (min); end
  if (value > max) return (max); end
  return (value);
end

Scroll con tiles

program tiled_scroll_test;
private
  tiles[31] =
    1, 1, 1, 1, 1, 1, 1, 1,
    1, 2, 2, 2, 1, 2, 2, 1,
    1, 1, 2, 1, 1, 1, 2, 1,
    1, 2, 2, 2, 2, 2, 2, 1,
    1, 1, 1, 1, 1, 1, 2, 1,
    1, 2, 2, 2, 2, 1, 2, 1,
    1, 1, 1, 2, 1, 1, 2, 1,
    1, 2, 2, 2, 2, 2, 2, 1,
    1, 1, 1, 1, 1, 1, 1, 1;

  tiled_map;

begin
  c_type = c_scroll;
  scroll.camera = id;
  tiled_map = draw_tiles(tiles, 8, 8, 32, 32);
  start_scroll(0, 0, tiled_map, 0, 0, 3);
  loop
    // si se quisiesen actualizar algunos de los tiles del mapa, se podría usar
    // map_put o alguna otra función para actualizar los gráficos del scroll
    // y entonces llamar a refresh_scroll(0) para actualizar el mapa.
    frame;
  end
end

function draw_tiles(pointer tiles_ptr, map_width, map_height, tile_width, tile_height)
private
  buffer_width, buffer_height, buffer, tile_index;
begin
  buffer_width = tile_width * map_width;
  buffer_height = tile_height * map_height;
  buffer = new_map(buffer_width, buffer_height, buffer_width >> 1, buffer_height >> 1, 0);
  for (y = 0; y < map_height; y++)
    for (x = 0; x < map_width; x++)
      tile_index = width * y + x;
      map_put(0, buffer, tiles_ptr[tile_index], x * tile_width, y * tile_height);
    end
  end
  return (buffer);
end

Configuración de las teclas

Una manera sencilla para permitir que los usuarios configuren sus teclas es utilizar el siguiente código:

program control_test;
const
  CONTROLS_FILE="controls.cfg";

global
  struct controls
    byte move_left, move_right, move_forward, move_backward, fire, use;
  end = _left, _right, _up, _down, _control, _space;
begin
  controls_init();

  loop
    if (key(controls.move_left))
      // movemos a la izquierda
    end
    if (key(controls.move_right))
      // movemos a la derecha
    end
    if (key(controls.move_forward))
      // movemos a la arriba
    end
    if (key(controls.move_backward))
      // movemos a la abajo
    end
    frame;
  end
end

function controls_save()
begin
  save(path_resolve(CONTROLS_FILE), offset controls, sizeof(controls));
end

function controls_load()
begin
  load(path_resolve(CONTROLS_FILE), offset controls);
end

function controls_init()
begin
  // Si no existe el archivo guardamos
  // la configuración por defecto.
  if (!get_fileinfo(path_resolve(CONTROLS_FILE))
    controls_save();
  end

  // Cargamos la configuración de los controles.
  controls_load();
end