/* map.c */

/* Handles map stuff */

#include <stdio.h>

#include "DeskLib:Error.h"

#include "clock.h"
#include "graphics.h"
#include "man.h"
#include "map.h"
#include "scrn.h"
#include "bsound.h"

/* Overall map of level */
map_array map;

/* For creating chain reactions of explosions */
map_array map_of_bombs[2];
int map_bank;

/* Bombs remaining to be blown up */
int map_bombs_left;

/* Table of what graphic corresponds to what map item */
graphic *map_to_sprite[map_NUMTYPES];

/* Number of bombs detonated and whether there is a chain reaction
   of explosions in progress
 */
int map_bombs_pending;
BOOL map_chain_in_progress;

void map_init_table()
{
  int i;

  map_to_sprite[map_BLANK] = &graphics_pool[graphic_BLANK];
  for (i = 0; i <= 11; i++)
    map_to_sprite[i + map_EXPLO1] = &graphics_pool[graphic_EXPLO1 + i/2];
  /* This makes detonated bombs flash */
  for (i = 0; i <= 39;i ++)
    if (i & 4)
      map_to_sprite[i + map_TIMER0] = &graphics_pool[graphic_BLANK];
    else
      map_to_sprite[i + map_TIMER0] = &graphics_pool[graphic_BOMBON];
  map_to_sprite[map_BOMBOFF] = &graphics_pool[graphic_BOMBOFF];
  map_to_sprite[map_DETONATOR] = &graphics_pool[graphic_DETONATOR];
  map_to_sprite[map_BRICK] = &graphics_pool[graphic_BRICK];
  map_to_sprite[map_STEEL] = &graphics_pool[graphic_STEEL];
  map_to_sprite[map_EARTH] = &graphics_pool[graphic_EARTH];
}

BOOL map_load_level(char *path,int level,
     		    int *time_limit,char *title,char *password)
{
  char filename[64];
  FILE *fp;
  int i;
  char ch;

  map_bombs_pending = 0;

  /* Levels are files ('1' to '50') stored in numbered directories
     of 50 at a time, RISC OS doesn't allow many more files in a directory */
  level--;
  sprintf(filename,"%s%d.%02d",path,level / 50 + 1,level % 50 + 1);

  fp = fopen(filename,"r");
  if (!fp)
    return FALSE;

  map_bombs_left = 0;
  for (i = 0; i < MAPSIZEX * MAPSIZEY; i++)
  {
    ch = getc(fp);
    switch (ch)
    {
      case ' ' :
        map.quickref[i] = map_BLANK;
        break;
      case 'O' :
        map.quickref[i] = map_BOMBOFF;
        map_bombs_left++;
        break;
      case '0' :
        map.quickref[i] = map_TIMER39;
        map_bombs_left++;
        map_bombs_pending++;
        break;
      case 'X' :
        map.quickref[i] = map_STEEL;
        break;
      case 'B' :
        map.quickref[i] = map_BRICK;
        break;
      case '.' :
        map.quickref[i] = map_EARTH;
        break;
      case 'D' :
        map.quickref[i] = map_DETONATOR;
        break;
      case 'S' :
        map.quickref[i] = map_BLANK;
        man_init_pos(i % MAPSIZEX,i / MAPSIZEX);
        break;
      default :
        /* Ignore */
        i--;
        break;
    }
    map_of_bombs[0].quickref[i] = map_of_bombs[1].quickref[i] = 0;
  }
  /* Read time limit */
  *time_limit = 0;
  fscanf(fp,"%d",time_limit);
  if (!*time_limit)
    *time_limit = clock_DEFAULT_WIDTH;

  /* Read password */
  ch = 0;
  while (ch <32 && ch != EOF)
    ch = getc(fp);
  if (ch == EOF)
  {
    password[0] = title[0] = 0;
    return TRUE;
  }
  ungetc(ch,fp);
  for (i=0; i < 8; i++)
  {
    password[i] = getc(fp) ^ (i + 0x0c);
    if (password[i] < 'A')
      password[i] = 0;
  }
  password[8] = 0;

  /* Read title */
  ch = 0;
  while (ch < 32 && ch != EOF)
    ch = getc(fp);
  if (ch == EOF)
  {
    title[0]=0;
    return TRUE;
  }
  ungetc(ch,fp);
  for (i=0; ch >= 32; i++)
  {
    ch = getc(fp);
    if (ch >= 32)
      title[i] = ch;
    else
      title[i] = 0;
  }
  fclose(fp);

  map_chain_in_progress = FALSE;
  map_bank = 0;

  return TRUE;
}

void map_draw(int vga)
{
  register int x;
  register int y;
  register int i;

  i = 0;
  for (y = 0; y < MAPSIZEY; y++)
    for (x = 0; x < MAPSIZEX; x++)
      (*scrn_fast_plotter)(map_to_sprite[map.quickref[i++]],
      		     	   x << MAPPIX, y << MAPPIX,
      		     	   vga);
}

void map_place_item(int x,int y,map_items item)
{
  map.coords[y][x] = item;
}

BOOL map_loc_free(int x,int y,int dx,int dy,BOOL man,
     		  man_status *status,BOOL *pushing,BOOL *detonator)
{
  int dummy;
  if (x < 0 || x >= MAPSIZEX || y < 0 || y >= MAPSIZEY)
  {
    if (man && map_bombs_left == 0)
      *status = status_COMPLETED;
    return FALSE;
  }
  switch (map.coords[y][x])
  {
    case map_BLANK:
      return TRUE;
    case map_EARTH:
      if (man)
      {
        if (SoundChannels)
        {
          xsound_stereo(sound_channel%SoundChannels+1,
          	bsound_stereotab[x], &dummy);
          xsound_attach_named_voice(sound_channel%SoundChannels+1,
          	"BombzVoc1");
          xsound_control((sound_channel++)%SoundChannels+1, SoundVolume-0x20,
          	DigPitch, 1);
        }
        return TRUE;
      }
      else
        return FALSE;
    case map_BOMBOFF:
      if (man)
      {
        if (*detonator)
        {
          if (SoundChannels)
          {
            xsound_stereo(sound_channel%SoundChannels+1,
            	bsound_stereotab[x], &dummy);
            xsound_attach_named_voice(sound_channel%SoundChannels+1,
            	"BombzVoc2");
            xsound_control((sound_channel++)%SoundChannels+1, SoundVolume,
            	BeepPitch, 100);
          }
          map.coords[y][x] = map_TIMER39;
          map_bombs_pending++;
          *detonator = FALSE;
          return FALSE;
        }
        else if (map_loc_free(x + dx,y + dy,dx,dy,
             	 	      FALSE,status,pushing,detonator))
        {
          *pushing = TRUE;
          return TRUE;
        }
        else return FALSE;
      }
      else
        return FALSE;
    case map_DETONATOR:
      if (man && !*detonator)
      {
        if (SoundChannels)
        {
          xsound_stereo(sound_channel%SoundChannels+1,
          	bsound_stereotab[x], &dummy);
          xsound_attach_named_voice(sound_channel%SoundChannels+1, "BombzVoc3");
          xsound_control((sound_channel++)%SoundChannels+1, SoundVolume,
          	GongPitch, 100);
        }
        *detonator = TRUE;
        return TRUE;
      }
      else
        return FALSE;
    default:
      if (map.coords[y][x] >= map_EXPLO1 && map.coords[y][x] <= map_EXPLO12)
        *status = status_BLOWNUP;
      return FALSE;
  }
  return FALSE;
}

BOOL map_scan()
{
  register int x,y;
  BOOL result = FALSE;
  BOOL nobang = FALSE;
  int dummy;

  if (map_bombs_pending)
  {
    register int i,a;

    i = 0;
    for (y = 0; y < MAPSIZEY; y++)
      for (x = 0; x < MAPSIZEX; x++)
      {
        a = map.quickref[i];
        if (a >= map_EXPLO1 && a <= map_TIMER39)
          map.quickref[i] = a - 1;
        if (a == map_TIMER0)
        {
          if (SoundChannels)
          {
            xsound_stereo(sound_channel%SoundChannels+1,
            	bsound_stereotab[x], &dummy);
            xsound_attach_named_voice(sound_channel%SoundChannels+1,
            	"BombzVoc1");
            xsound_control((sound_channel++)%SoundChannels+1, SoundVolume,
            	BangPitch, 100);
            nobang = TRUE;
          }
          map_of_bombs[map_bank].quickref[i] = map_chain_in_progress = TRUE;
        }
        else if (a == map_EXPLO1)
          map_bombs_pending--;
        if (a >= map_EXPLO1 && a <= map_EXPLO12 &&
            x == man_mapx && y == man_mapy)
          result = TRUE;
        i++;
      }
  }
  if (map_chain_in_progress)
  {
    map_chain_in_progress = FALSE;
    for (y = 0; y < MAPSIZEY; y++)
      for (x = 0; x < MAPSIZEX; x++)
        if (map_of_bombs[map_bank].coords[y][x])
        {
          register int dx,dy;

          map_bombs_left--;
          map_of_bombs[map_bank].coords[y][x] = FALSE;
          for (dy = -1; dy < 2; dy++)
            for (dx = -1; dx < 2;dx ++)
              if (x + dx >= 0 && x + dx < MAPSIZEX &&
              	  y + dy >= 0 && y + dy < MAPSIZEY &&
              	  !(dx == 0 && dy == 0))
              {
                register map_items whats_here = map.coords[y + dy][x + dx];

                if (whats_here >= map_TIMER0 && whats_here <= map_BOMBOFF)
                {
                  if (SoundChannels && !nobang)
                  {
                    xsound_stereo(sound_channel%SoundChannels+1,
                    	bsound_stereotab[x], &dummy);
                    xsound_attach_named_voice(sound_channel%SoundChannels+1,
                    	"BombzVoc1");
                    xsound_control((sound_channel++)%SoundChannels+1,
                    	SoundVolume, BangPitch, 100);
                    nobang = TRUE;
                  }
                  map_of_bombs[map_bank ^ 1].coords[y + dy][x + dx] =
                    map_chain_in_progress = TRUE;
                }
                if (whats_here != map_STEEL)
                {
                  map.coords[y + dy][x + dx] = map_EXPLO12;
                  map_bombs_pending++;
                }
              }
        }
  }
  map_bank ^= 1;
  return result;
}
