//Toto je hlavni soubor specialnich procedur pro hru BRANY SKELDALU
#include <platform.h>
#include <stdio.h>
#include <stdlib.h>
#include <event.h>
#include <strlite.h>
#include "bgraph.h"
#include "globals.h"
#include "specproc.h"
#include "bmouse.h"
#include <math.h>
//#include "i86.h"

#define MOB_GO(m) if (m->dir & 1)m->headx=mob_go_x[m->dir];else m->heady=mob_go_y[m->dir]

int cur_event_number;

static void event_error(char *text,int number)
  {
  char buff[256];
  closemode();
  sprintf(buff,"%s\n Specproc num: %d\n",text,number);
  display_error(buff);
  exit(1);
  }


//Item procs---------------------------------------
ITEM_PROC(item_test)
  {
  putchar('\x7');event,ptr,p;
  putchar('\n');
  return 1;
  }

//Map procs---------------------------------------
MAP_PROC(map_test)
  {
  putchar('\x7');sector,side,event,value;
  putchar('\n');
  return 1;
  }


MAP_PROC(map_teleport)
  {
  side=0;event;
  teleport_target=value;
  if (mob_map[sector]!=0)
     {
     int i;
     spell_teleport(-mob_map[sector],-1,-1);
     if ((i=mobs[mob_map[sector]-1].next)!=0)
        spell_teleport(-i,-1,-1);
     side=1;
     }
  if (map_coord[sector].flags & MC_DPLAYER)
     {
     int i,j=-1;THUMAN *h;
     int bit=0;

     for(i=0,h=postavy;i<POCET_POSTAV;i++,h++) if (h->used && h->sektor==sector && h->sektor==viewsector)
        {
        j=i;break;
        }
     for(i=0,h=postavy;i<POCET_POSTAV;i++,h++) if (h->used && h->sektor==sector) bit|=1<<i;
     postavy_teleport_effect(teleport_target,h->direction,bit,j!=-1);
     side=1;
     auto_group();
     }
  return side;
  }

/*
static void otoc_obraz1(word *source,word *target)
  {
  word *p,*q;
  word *sp,*sq;
  int x,y;

  sp=source+320-180;
  sq=target+scr_linelen2*359+320-180;
  y=360;
  while (y--)
     {
     p=sp;
     q=sq;
     x=360;
     while (x--)
        {
        *q=*p;
        q-=scr_linelen2;
        p++;
        }
     sp+=scr_linelen2;
     sq++;
     }
  }

void swap_screen(word *_p,word *_q)
  {
  __asm
    {
    mov esi,_p
    mov edi,_q

    std
    mov  ecx,115200
    lp1:
    mov  ax,[edi]
    xchg ax,[esi]
    stosw
    add  esi,2
    dec  ecx
    jnz  lp1
    cld
    }
  }
//  #pragma aux swap_screen parm [esi][edi]=\ modify [ecx eax]

static void otoc_obraz2(word *source)
  {
  swap_screen(source,source+scr_linelen2*360-2);
  }

static void otoc_obraz3(word *source,word *target,short smer)
  {
  word *tt,*ss;
  int x,y,xs,ys;
  int xp,yp;

  tt=target;
  if (smer>0)x=320+126-224;else x=320+126+224;
  if (smer>0)y=180-126-224;else y=180+126-224;
  yp=360;
  ys=0;
  while (yp--)
     {
     xs=0;
     ss=source+scr_linelen2*y+x;
     xp=640;
     while (xp--)
        {
        if (ss>=source && ss<source+scr_linelen2*360) *tt++=*ss; else *tt++=0;
        xs+=7;
        if (xs>10)
           {
           xs-=10;
           ss+=scr_linelen2+smer;
           }
        }
     ys+=7;
     if (ys>10)
        if (smer>0)
        {
        ys-=10;
        x--;
        y++;
        }
        else
        {
        ys-=10;
        x--;
        y--;
        }
     }
  }

static void show_liane(THE_TIMER *t)
  {
  word *bt,*bs;
  static int counter=0;

  schovej_mysku();
  if (counter<5 && counter)
     {
     bt=GetScreenAdr()+SCREEN_OFFSET;
     bs=GetBuffer2nd()+SCREEN_OFFSET;
     }
     else
     {
     redraw_scene();
     bs=GetScreenAdr()+SCREEN_OFFSET;
     }

  switch(counter)
     {
     case 0:break;
     case 1:otoc_obraz3(bs,bt,1);break;
     case 2:curcolor=0;
            bar(0,SCREEN_OFFLINE,139,SCREEN_OFFLINE+359);
            bar(640-140,SCREEN_OFFLINE,639,SCREEN_OFFLINE+359);
            otoc_obraz1(bs,bt);break;
     case 3:otoc_obraz3(bs,bt,-1);break;

     default:otoc_obraz2(bs);if (counter==4)OutBuffer2nd();break;
     }
  ukaz_mysku();
  showview(0,0,0,0);
  if (!counter)
     {
     schovej_mysku();
     OutBuffer2nd();
     ukaz_mysku();
     }
  if (t->calls==1)
     {
     THUMAN *h;
     int i;

     save_load_trigger((word)(t->userdata[0]));
     for(i=0,h=postavy;i<POCET_POSTAV;i++,h++) if (get_player_triggered(i))
        {
        player_hit(h,h->lives,0);
        h->sektor=0;
        }
     wire_proc();
     counter=-1;
     }
  counter++;
  }
*/



static const float Snapper=3<<22;

static __inline int toInt(float fval)
  {
	fval += Snapper;
	return ( (*(int *)&fval)&0x007fffff ) - 0x00400000;
  }

static void OtocObrazPodleMatice(float mx[3][2], word *picture)
  {
    int32_t scr_linelen2 = GetScreenPitch();

  word *trg=GetScreenAdr()+17*scr_linelen2;
  int x,y;
  picture+=6;
  for (y=0;y<360;y++,trg+=scr_linelen2)
	for (x=0;x<640;x++)
	  {
	  int oldx=x-320;
	  int oldy=y-180;
	  int newx=toInt(oldx*mx[0][0]+oldy*mx[1][0]+320);
	  int newy=toInt(oldx*mx[0][1]+oldy*mx[1][1]+180);
	  if (newx>=0 && newx<640 && newy>=0 && newy<360)
		trg[x]=picture[newx+640*newy];
	  else
		trg[x]=0;
	  }
  }

static void OtaceniObrazu()
  {
  word *picture=(word *)malloc(640*360*2+16);
  float mx[3][2];

  int maxtime=500;
  int lasttime=get_game_tick_count();
  int curtime;
  get_picture(0,17,640,360,picture);
  do
	{
	float phase;
	float uhel;
	float cosuhel;
	float sinuhel;
	curtime=get_game_tick_count()-lasttime;
	phase=curtime/(float)maxtime;
	if (phase>1.0f) phase=1.0f;
	uhel=phase*3.14159265;
	cosuhel=cos(uhel);
	sinuhel=sin(uhel);
	mx[0][0]=cosuhel;
	mx[0][1]=sinuhel;
	mx[1][0]=-sinuhel;
	mx[1][1]=cosuhel;
	OtocObrazPodleMatice(mx,picture);
	showview(0,0,0,0);
	do_events();
	}
  while (curtime<maxtime);
  sleep_ms(1000);
  free(picture);
  }

MAP_PROC(map_liana)
  {
  int i;
  THUMAN *h;

  value;
  sector,side,event;
  bott_draw(1);
  other_draw();
  unwire_proc();
//  add_to_timer(TM_SCENE,gamespeed,15,show_liane)->userdata[0]=save_load_trigger(-1);
  schovej_mysku();
  OtaceniObrazu();
  ukaz_mysku();
     for(i=0,h=postavy;i<POCET_POSTAV;i++,h++) if (get_player_triggered(i))
        {
        player_hit(h,h->lives,0);
        h->sektor=0;
        }
  redraw_scene();
  cancel_pass=1;
  cancel_render=1;
  showview(0,0,0,0);
  wire_proc();
  return 1;
  }
#define ID_XS 400
#define ID_YS 380

MAP_PROC(map_identify)
  {
  int x,y,yp,xp,ys;
  int i,cnt;char s[100];
  TITEM *it;
  TSTR_LIST ls;

  sector;side;event;value;
  if (picked_item==NULL) return 0;
  it=glob_items+*picked_item-1;
  ls=create_list(256);
  unwire_proc();
  sprintf(s,texty[210],it->jmeno);str_add(&ls,s);
  sprintf(s,texty[211],it->hmotnost*2,it->hmotnost>0 && it->hmotnost<3?texty[236]:texty[237]);str_add(&ls,s);
  if (it->nosnost)
     {
     sprintf(s,texty[212],it->nosnost);str_add(&ls,s);
     }
  for(i=0;i<21;i++)
     if (it->zmeny[i] && texty[213+i]!=NULL)
        {
        if (i==VLS_HPREG || i==VLS_MPREG || i==VLS_VPREG)
           sprintf(s,texty[213+i],it->zmeny[i]>0?texty[234]:texty[235]);
        else
           sprintf(s,texty[213+i],it->zmeny[i],it->zmeny[i+1]);
        str_add(&ls,s);
        }
  if (it->zmeny[VLS_MGSIL_H])
     {
     sprintf(s,texty[233],texty[22+it->zmeny[VLS_MGZIVEL]]);str_add(&ls,s);
     }
  for(i=0;i<16;i++)
     if (it->zmeny[VLS_KOUZLA] & (1<<i) && texty[i+240]!=NULL) str_add(&ls,texty[i+240]);
  for(i=str_count(ls);i>0;i--) if (ls[i-1]!=NULL) break;
  cnt=i;i=0;
  ys=cnt*10+10;
  do
     {
     x=320-ID_XS/2;
     y=240-ys/2;
     create_frame(x,y,ID_XS,ys,1);
     xp=x+5;yp=y+5;ys=ID_YS-10;
     set_font(H_FBOLD,NOSHADOW(0));
     while(i<cnt)
        {
        position(xp,yp);outtext(ls[i]);
        yp+=10;
        i++;
        }
     showview(0,0,0,0);
     wire_proc();
     }
  while (i<cnt);
  return 1;
  }

//Mob Procs ---------------------------------------

static int get_dangerous_place(int sector)
  {
  int i;

  for(i=0;i<4;i++)
     {
     int s=map_sectors[sector].step_next[i];
     if (s && map_coord[s].flags & MC_PLAYER) return 1;
     }
  return 0;
  }

MOB_PROC(mob_open_door)
  {
  if (event==SMPR_WALK) {
     if (m->user_data<128 || m->user_data>192)
        {
        int sector=m->sector;
        int i;
        TSTENA *side;

        for(i=0,side=map_sides+(sector<<2);i<4;side++,i++)
           if (side->flags & SD_MONST_IMPS && side->sector_tag!=0 && (~side->flags & (SD_PASS_ACTION | SD_SECRET))==(SD_PASS_ACTION|SD_SECRET))
              break;
        if (i!=4)
           {
           m->dir=i;stop_mob(m);
           if (flag_map[(sector<<2)+i] & SD_MONST_IMPS) a_touch(sector,m->dir);
           m->user_data=128;
           return 1;
           }
        side=map_sides+(sector<<2)+((m->dir+2)&3);
        if (~side->flags & SD_MONST_IMPS && side->sector_tag!=0 && (~side->flags & (SD_PASS_ACTION | SD_SECRET))==(SD_PASS_ACTION|SD_SECRET))
           {
           int ss=map_sectors[sector].step_next[(m->dir+2)&3];
           int j=mob_map[ss];
           while (j) if (mobs[j-1].dir==m->dir) return 0;
              else j=mobs[j-1].next;
           a_touch(sector,(m->dir+2)&3);
           return 0;
           }
        }
     else
        {
        if (m->user_data>=128 && m->user_data<192) m->user_data++;
        if (~map_sides[(m->sector<<2)+m->dir].flags & SD_MONST_IMPS)
           {
           if (m->dir & 1) m->headx=mob_go_x[m->dir];else  m->heady=mob_go_y[m->dir];
           m->user_data=map_coord[map_sectors[m->sector].step_next[m->dir]].flags & MC_PLAYER?255:127;
           }
        return m->user_data<144;
        }
  }
  return 0;
  }

MOB_PROC(mob_open_door_battle)
  {
  if (event==SMPR_ATTACK)
     {
     m->specproc=5;
     }
  return 0;
  }

static char spec_proc_test_mob(int event_type,TMOB *m)
  {
  m,event_type;
  putchar('\x7');
  putchar('\n');
  return 0;
  }


static char mob_dokola(int event_type,TMOB *m)
  {
  if (event_type==SMPR_WALK)
     {
     m->dir++;
     m->dir &=3;
     MOB_GO(m);
     return 1;
     }
  return 0;
  }

static char mob_carodej(int event_type,TMOB *m)
  {
static char kouzla[5]={3,8,13,18,80};

  if (event_type==SMPR_ATTACK)
     {
     m->casting=kouzla[rnd(5)];
     }
  return 0;
  }

static char mob_strelec(int event_type,TMOB *m)
  {
  if (event_type==SMPR_ATTACK)
     {
     int i,l;

     if (m->dostal==0 && ~m->user_data & 128) return 0;
     for (i=0;i<4;i++)
        {
        l=map_sectors[m->sector].step_next[i];
        if (l!=0 && map_coord[l].flags & MC_PLAYER) break;
        }
     if (i==4)
        {
        if (m->user_data & 128)
           {
           int s=m->sector;
           m->user_data&=~128;
           i=(m->dir+2)&3;
           while (s && !(map_coord[s].flags & MC_PLAYER)) if (map_sides[(s<<2)+i].flags & SD_MONST_IMPS) s=0;else s=map_sectors[s].step_next[i];
           if (s) m->dir=i;else return 1;
           }
        return 0; //strilej
        }
     i=(i+2)&3;
     if (mob_check_next_sector(m->sector,i,m->stay_strategy & MOB_BIG,0))
        {
        int l=4,z,max=RAND_MAX;
        for(i=0;i<4;i++)
           if (!mob_check_next_sector(m->sector,i,m->stay_strategy & MOB_BIG,0))
              {
              int s=map_sectors[m->sector].step_next[i];
              if (!get_dangerous_place(s) && (z=rand())<max) max=z,l=i;
              }
        if (l==4) return 0;
        i=l;
        }
     m->dir=i;
     MOB_GO(m);
     m->user_data|=128;
     return 1;
     }
  else
     {
     /*
     int i,l;
        i=m->dir;
        l=m->sector;
        if (map_sides[(l<<2)+i].flags & SD_MONST_IMPS) return 0;
        l=map_sectors[l].step_next[i];
        for(i=0;i<4;i++)
           {
           int s=map_sectors[l].step_next[i];
           if (isplayer(s,NULL,0)!=NULL)
              {
              m->dir=i+2&3;
              m->headx=mob_go_x[i];
              m->heady=mob_go_y[i];
              return 1;
              }
           }
      */
      if (m->user_data & 128)
        {
        THUMAN *h;
        int i;

        for(i=0,h=postavy;i<POCET_POSTAV;i++,h++)
           if (h->lives && h->used &&
               abs(map_coord[h->sektor].x-map_coord[m->sector].x)<2 &&
               abs(map_coord[h->sektor].y-map_coord[m->sector].y)<2)
                 {
                 stop_mob(m);
                 return 1;
                 }
        }
      return 0; //delej si co chces
      }
  }




MOB_PROC(mob_krikloun)
  {
  if (event==SMPR_ATTACK)
     {
     sirit_zvuk(m->sector);
     }
  return 0;
  }

static char mob_taktik_recurse(int recall,int sector,int *min_obr,int big,int *csect,int *cdir)
  {
     int i;
     char nasel=0;

     //nejprve zjistime kdo je okolo nas
     for(i=0;i<4;i++)
        {
        int s=map_sectors[sector].step_next[i];
        THUMAN *h=NULL;

        if (!s) continue; //pokud tam je stena tak pokracuj jinym smerem
        if (mob_check_next_sector(sector,i,big,0)==1) continue;
        if ((h=isplayer(s,h,0))!=NULL) //nekdo tam je - zjisti kdo
           while(h!=NULL)
           {
           if (h->vlastnosti[VLS_OBRAN_H]<*min_obr) // pokud ma nizsi obranu
               {
               *csect=sector;
               *cdir=i;
               *min_obr=h->vlastnosti[VLS_OBRAN_H];
               nasel=1;
               }
           h=isplayer(s,h,0);
           }
        else if (recall) //pokud tam nikdo neni koukni se vedle
           {
           if (mob_taktik_recurse(0,s,min_obr,big,csect,cdir)) *cdir=i;
           }
        }
   return nasel;
  }


MOB_PROC(mob_taktik)
  {
  int min_obr=1000;
  int csect=m->sector;
  int cdir=-1;

  if (event==SMPR_ATTACK)
     {
     mob_taktik_recurse(1,m->sector,&min_obr,m->stay_strategy & MOB_BIG,&csect,&cdir);
     if (cdir==-1) return 0;
     m->dir=cdir;
     if (m->sector!=csect)
        {
        MOB_GO(m);
        return 1;
        }

     }
  return 0;
  }

MOB_PROC(mob_stoji)
  {
  if (event==SMPR_ATTACK)
     {
     int i;
     int s;

     for(i=0;i<4;i++)
        {
        s=map_sectors[m->sector].step_next[i];
        if (s && map_coord[s].flags & MC_PLAYER) return 0;
        }
     m->headx=m->locx;
     m->heady=m->locy;
     m->stay_strategy&=~(MOB_WALK | MOB_LISTEN);
     m->vlajky&=~MOB_IN_BATTLE;
     return 1;
     }
  else if (event==SMPR_WALK)
     {
     m->headx=m->locx;
     m->heady=m->locy;
     }
  else if (event==SMPR_KNOCK) return 1;
  return 0;
  }

static t_mob_proc sp_mob_table[]=
  {
  NULL,                   //0
  spec_proc_test_mob,     //1
  mob_dokola,             //2
  mob_carodej,            //3
  mob_strelec,            //4
  mob_open_door,          //5
  mob_open_door_battle,   //6
  mob_krikloun,           //7
  mob_taktik,             //8
  mob_stoji,              //9
  };

#define SP_MOB_TAB_SIZE (sizeof(sp_mob_table)/sizeof(t_mob_proc))

static t_item_proc sp_item_table[]=
  {
  NULL,                   //0
  item_test,              //1
  };

#define SP_ITEM_TAB_SIZE (sizeof(sp_item_table)/sizeof(t_item_proc))

static t_map_proc sp_map_table[]=
  {
  NULL,                   //0
  map_test,               //1
  map_teleport,           //2
  map_liana,              //3
  map_identify,           //4
  };

#define SP_MAP_TAB_SIZE (sizeof(sp_map_table)/sizeof(t_map_proc))

char call_mob_event(int event_number,int event_type,TMOB *m)
  {
  if (!event_number) return 0;
  if (event_number>=(int)SP_MOB_TAB_SIZE)
     event_error("Nestv�ra pou��va neplatnou specproc.",event_number);
  cur_event_number=event_number;
  return sp_mob_table[event_number](event_type,m);
  }

char call_item_event(int event_number,int event_type,short *ptr,THUMAN *p)
  {
  if (!event_number) return 0;
  if (event_number>=(int)SP_ITEM_TAB_SIZE)
     event_error("��slo ud�losti u v�ci je neplatn�. Specproc nen� definov�na.",event_number);
  cur_event_number=event_number;
  return sp_item_table[event_number](event_type,ptr,p);
  }

char call_map_event(int event_number,int sector,int side,int value,int event)
  {
  if (!event_number) return 0;
  if (event_number>=(int)SP_MAP_TAB_SIZE)
     event_error("Neplatn� ��slo ud�losti na st�n�. Specproc s t�mto ��slem nen� definov�na.",event_number);
  cur_event_number=event_number;
  return sp_map_table[event_number](sector,side,value,event);
  }

