#include <platform.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <bios.h>
#include <mem.h>
#include <types.h>
#include <event.h>
#include <memman.h>
#include <bgraph.h>
#include <zvuk.h>
#include "engine1.h"
#include "globals.h"
#include "specproc.h"

#define MOB_ZNAKY "FLBLCH"
#define MOB_START 1


#define MOB_DIST 24
#define MBS_WALK 0
#define MBS_ATTACK 1
#define MBS_HIT 2


#define MBA_ATTACK 3 //potvora utoci
#define MBA_SPELL  1 //potvora caruje
#define MBA_FLEE   2 //potvora utika
#define MBA_NONE   0

#define PK_QUERY 0  //dotaz zda je v mrtvole predmet
#define PK_PICK 1 //prvni predmet v mrtvole

#define mob_walk_sound(p) if (p->locx!=p->headx || p->locy!=p->heady) mob_sound_event(p,MBS_WALK)

TMOB mobs[MAX_MOBS];
char *mob_map;
char hex_chars[]="0123456789ABCDEF";
int mob_dostal=0;
int mob_dostal_pocet;

char folow_mode=0;
char folow_mob;

char mob_go_x[]={128,255,128,0};
char mob_go_y[]={0,128,255,128};
char mob_batt_x[]={128,128+MOB_DIST,128,128-MOB_DIST};
char mob_batt_y[]={128-MOB_DIST,128,128+MOB_DIST,128};
short konv_x[]={1,1,-1,-1};
short konv_y[]={-1,1,1,-1};
char going[]={0,0,1,0,1,1};

static word *mob_paths[MAX_MOBS];
static word *mob_path_ptr[MAX_MOBS];
static int monster_block;

void *sound_template=NULL;

short att_sect;
char battle=0;
char neco_v_pohybu=1;
char nohassle=0;

typedef struct tmobsavedata
{
  short anim_counter;        //citac animaci
  char anim_phase;            //cinnost kterou mob dela
  char dir;
}TMOBSAVEDATA;

static TMOBSAVEDATA **mobsavedata=0;

static void register_mob_path(int mob,word *path) //registruje cestu pro potvoru
  {
  mob_paths[mob]=path;
  mob_path_ptr[mob]=path;
  }

static void free_path(int mob)      //vymaze cestu potvore
  {
  free(mob_paths[mob]);
  mob_paths[mob]=NULL;
  mob_path_ptr[mob]=NULL;
  }

void send_mob_to(int m,word *path)
  {
  if (mob_paths[m]!=NULL)
    {
    free_path(m);
    }
  register_mob_path(m,path);
  mobs[m].stay_strategy |= MOB_WALK;
  }

void smeruj_moba(TMOB *m,int smer)
  {
  int val=128+MOB_DIST*smer;

  switch (m->dir)
    {
    case 0:m->headx=val;break;
    case 1:m->heady=val;break;
    case 2:m->headx=-val;break;
    case 3:m->heady=-val;break;
    }
  }

void save_enemy_paths(TMPFILE_WR *f)
  {
  short i;
  int s;
  word *w;

  for(i=0;i<MAX_MOBS;i++) if (mob_paths[i]!=NULL)
    {
    temp_storage_write(&i,2*1,f);
    w=mob_path_ptr[i];
    s=1;while(*w++) s++;
    temp_storage_write(&s,1*sizeof(s),f);
    temp_storage_write(mob_path_ptr[i],s*2,f);
    }
  i=-1;
  temp_storage_write(&i,1*2,f);
  }

int load_enemy_paths(TMPFILE_RD *f)
  {
  short i=-1;
  int s;
  word *w;

  for(i=0;i<MAX_MOBS;i++) free_path(i);
  temp_storage_read(&i,2*1,f);
  while(i!=-1)
    {
      temp_storage_read(&s,1*sizeof(s),f);
    w=NewArr(word,s);
    temp_storage_read(w,s*2,f);
    register_mob_path(i,w);
    if (temp_storage_read(&i,2*1,f)==0) return 1;
    }
  return 0;
  }

static void mob_reload(EVENT_MSG *msg,void **_){
  static int counter=0;

  if (msg->msg == E_KOUZLO_KOLO) {
     {
     if (insleep) return;
     if (counter++==10)
        {
        TMOB *m;
        int32_t vl;

        static int last;
        counter=0;
        while (last<MAX_MOBS)
           {
           vl=mobs[last].vlajky;
		   if (game_extras & EX_RESPAWN_MONSTERS) vl |= MOB_RELOAD;
           if (~vl & MOB_LIVE && vl & MOB_RELOAD) break; else last++;
           }
        if (last==MAX_MOBS)
           for(last=0;last<MAX_MOBS;last++)
             {
             vl=mobs[last].vlajky;
  		     if (game_extras & EX_RESPAWN_MONSTERS) vl |= MOB_RELOAD;
             if (~vl & MOB_LIVE && vl & MOB_RELOAD) break;
             }
        if (last==MAX_MOBS) return;
        m=mobs+last;
        last++;
        if (map_coord[m->home_pos].flags & MC_PLAYER ||
            mob_map[m->home_pos]) return;
        m->vlajky|=MOB_LIVE;
        m->vlajky&=~MOB_IN_BATTLE;
        m->lives=m->vlastnosti[VLS_MAXHIT];
        m->sector=m->home_pos;
        m->locx=m->headx=m->locy=m->heady=128;
        memset(m->inv,0,sizeof(m->inv));
        play_sample_at_sector(H_SND_TELEPOUT,viewsector,m->home_pos,0,0);
        add_spectxtr(m->home_pos,H_TELEP_PCX,14,1,0);
        refresh_mob_map();
        debug_text="New monster arrived to the dungeon !";
        SEND_LOG("(RELOAD) Mob reloaded: '%s' at sector %d",m->name,m->home_pos);
        free_path(m-mobs);
        }
     }
  }
}

void init_mobs()
  {
  memset(mobs,0,sizeof(mobs));
  mob_map=getmem(mapsize);
  memset(mob_map,0,mapsize);
  memset(mob_paths,0,sizeof(mob_paths));
  memset(mob_path_ptr,0,sizeof(mob_path_ptr));
  send_message(E_DONE,E_KOUZLO_KOLO,mob_reload);
  send_message(E_ADD,E_KOUZLO_KOLO,mob_reload);
  }

static void register_mob_graphics(int num,char *name_part,char *anims,char *seq)
  {
  char fulname[14];
  char znaky[]=MOB_ZNAKY;
  int i,j,a;

  strcpy(fulname,name_part);
  strcat(fulname,"??.PCX");
  a=num;
  for(i=0;i<6;i++)
     {
     for(j=0;j<16;j++)
        {
        if (anims[i])
           {
           fulname[6]=znaky[i];
           if (j<=anims[i])
              {
              fulname[7]=*seq++;
              def_handle(a,fulname,pcx_8bit_nopal,SR_ENEMIES);
              }
           a++;
           }
        else
           {
           fulname[6]=znaky[0];
           if (j<=anims[0])
              {
              fulname[7]=*seq++;
              def_handle(a,fulname,pcx_8bit_nopal,SR_ENEMIES);
              }
           a++;
           }
        if (*seq=='\r')
           {
		   char buff[256];
           closemode();
           sprintf(buff,"Soubor sekvence %s obsahuje chybne udaje nebo je sekvence je moc kratka\n", fulname);
		   display_error(buff);
           exit(0);
           }
        }
     seq=strchr(seq,'\n')+1;
     }
  }




static void register_mob_sounds(int hlptr,word *sounds)
  {
  int i,z;

  for(i=0;i<4;i++)
     {
     z=sounds[i];
     if (z)
        {
        def_handle(hlptr,sound_table[z-1],wav_load,SR_ZVUKY);
        }
     hlptr++;
     }
  }

static char miri_middle(TMOB *p) //procedura zjisti zda li potvora miri do dveri
  {
  int ss;

  ss=(p->sector<<2)+p->dir;
  return (map_sides[ss].lclip!=0);
  }


static char pick_item_corpse(TMOB *m,char query)
  {
  short *p=NULL;
  int sector=m->sector;      //sektor kde se bude mrtvola prohledavat
  if (map_coord[sector].flags & MC_DEAD_PLR) //lezi tam vubec nejaka mrtvola?
     {
     int i;
     THUMAN *h;        //najdi mezi hraci mrtvolu ktera tam lezi
     for(i=0,h=postavy;i<POCET_POSTAV && p==NULL;i++,h++) if (!h->lives && h->used && h->sektor==sector)
        {
        int i;         //podivej se ji do inventare....
        for(i=0;i<HUMAN_RINGS && p==NULL;i++) if (h->prsteny[i]) p=&h->prsteny[i];
        //nejprve ber prsteny
        for(i=0;i<HUMAN_PLACES && p==NULL;i++) if (h->wearing[i]) p=&h->wearing[i];
        //pak seber zbrane a brneni
        for(i=0;i<h->inv_size && p==NULL;i++) if (h->inv[i]) p=&h->inv[i];
        //teprve potom se podivej co ma v inv.
        }
     }
  if (p!=NULL)  //nasel jsi neco?
     if (query==PK_QUERY) return 1;  //pokud se jednalo o dotaz, tak pouze vrat 1
     else
        {
        int i;
        for(i=0;i<MOBS_INV;i++) if (m->inv[i]==0) break; //zjisti jestli mas misto
        if (i==MOBS_INV) return 0; //nemas, mas smulu....
        m->inv[i]=*p;  //prenes predmet od postavy do sveho inventare.
        *p=0; //ale na puvodnim miste tento predmet znic.
        }
  else return 0;
  return 1;
  }

static char seber_predmet(TMOB *m)
  {
  short *p=NULL,*q;
  int i,j,c,z;

  for(j=0,c=0;j<MOBS_INV;j++,c+=(m->inv[j]==0));
  i=0;
  do
     {
     for(;i<4 && p==NULL;i+=(p==NULL)) pop_item(m->sector,i,0,&p);
     if (i==4) return 1;
     z=count_items_total(p);
     if (z>c)
        {
        push_item(m->sector,i,p);
        free(p);
        p=NULL;
        }
     i++;
     }
  while (z>c);
  if (z<=c) for(q=p,j=0;j<MOBS_INV && (*q);j++) if (m->inv[j]==0) m->inv[j]=(abs(*q++));
  free(p);
  return 0;
  }

static void mob_sound_event(TMOB *m, int event) {
    if (m->sounds[event] && m->vlajky & MOB_LIVE
            && ~m->vlastnosti[VLS_KOUZLA] & SPL_STONED) {
        if (event == MBS_WALK) {
            play_sample_at_sector(
                    m->cislo_vzoru + 16 * 6 + event + monster_block, viewsector,
                    m->sector, m - mobs + 256,
                    (m->vlajky & MOB_SAMPLE_LOOP) != 0);
        } else {
            play_sample_at_sector(
                    m->cislo_vzoru + 16 * 6 + event + monster_block, viewsector,
                    m->sector, 0, 0);
        }
    }
}

void load_enemies(short *data,int size,int *grptr,TMOB *template,int32_t tsize)
  {
  int i;
  short cisla[256];

  monster_block=*grptr;
  memset(cisla,0xff,sizeof(cisla));
  size>>=2;
  for(i=0;i<MAX_MOBS;i++) free_path(i);
  if (size>MAX_MOBS-MOB_START) size=MAX_MOBS-MOB_START;
  for(i=0;i<size;i++)
     {
     int j,c,cnt;
     TMOB *b;

     b=template;
     cnt=tsize/sizeof(TMOB);
     c=data[1] & 0xfff;
     for(j=0;j<cnt && b[j].cislo_vzoru!=c;j++);
     if (j!=cnt && data[0]<mapsize && map_sectors[data[0]].sector_type && c<cnt)
        {
        mobs[i]=b[j];
        if (~mobs[i].vlajky & MOB_MOBILE) mob_map[data[0]]=i+MOB_START;
        if (mobs[i].palette>0)mobs[i].palette=rnd(mobs[i].palette);else mobs[i].palette=abs(mobs[i].palette);
        mobs[i].sector=data[0];
        mobs[i].dir=(data[1]>>14)&0x3;
        mobs[i].home_pos=data[0];
        mobs[i].vlajky|=MOB_LIVE;
        if (mobs[i].speed<1)
          {
  	      char buff[256];
          closemode();
          sprintf(buff,"Nestvura cislo #%d (%s) je spatne definovana (rychlost)",i,mobs[i].name);
		  display_error(buff);
          exit(1);
          }
        cisla[i]=mobs[i].cislo_vzoru;
        for(j=0;j<i;j++) if (cisla[j]==cisla[i]) break;
        if (j!=i) mobs[i].cislo_vzoru=mobs[j].cislo_vzoru;
        else
           {
           char s[20];

           sprintf(s,"%s.SEQ",mobs[i].mobs_name);
           def_handle(grptr[0]++,s,NULL,SR_ENEMIES);
           mobs[i].cislo_vzoru=*grptr-monster_block;
           register_mob_graphics(*grptr,mobs[i].mobs_name,mobs[i].anim_counts,ablock(grptr[0]-1));
           grptr[0]+=6*16;
           register_mob_sounds(*grptr,mobs[i].sounds);
           grptr[0]+=4;
           sprintf(s,"%s.COL",mobs[i].mobs_name);
           def_handle(grptr[0],s,col_load,SR_ENEMIES);
           grptr[0]++;
           }
        }
     else
        {
        mobs[i].cislo_vzoru=-1;
        mobs[i].vlajky&=~MOB_LIVE;
        }
     for(j=0;j<6;j++) if (mobs[i].anim_counts[j]==0) mobs[i].anim_counts[j]=mobs[i].anim_counts[0];
     data+=2;
     }
  for(;i<MAX_MOBS;i++) mobs[i].vlajky&=~MOB_LIVE;
  }

int mob_pocet_vychodu(int sector,int dir)
  {

  sector<<=2;
  return ((map_sides[sector+dir].flags & SD_MONST_IMPS)==0)+
         ((map_sides[sector+((dir+1) & 3)].flags & SD_MONST_IMPS)==0)+
         ((map_sides[sector+((dir-1) & 3)].flags & SD_MONST_IMPS)==0);
  }

  //retval: 0 - sector is free
  //retval: 1 - sector is not passable
  //retval: 2 - players on sector
 char mob_check_next_sector(int sect,int dir,char alone,char passable)
  {
  int i;

  if (map_sides[(sect<<2)+dir].flags & SD_MONST_IMPS) return 1;
  sect=map_sectors[sect].step_next[dir];
  if ((map_coord[sect].flags & MC_PLAYER) && (~passable & MOB_PASSABLE)) return 2;
  i=mob_map[sect];
  if (i==0) return 0;
  if (alone & MOB_BIG) return 1;
  i-=MOB_START;
  if (mobs[i].stay_strategy & MOB_BIG) return 1;
  if (mobs[i].next==0) return 0;
  return 1;
  }


int mob_vyber_vychod(int r,int sector,int dir,char alone,char mobile)
  {
  int cnt=0,i;
  dir=(dir-1)&3;
  for(;r>0 && cnt<5;cnt++,dir=(dir+1)&3)
     if (!(i=mob_check_next_sector(sector,dir,alone,mobile)))
        {
        r--;
        if (!r) break;
        }
  if (cnt==5) dir=-1;
  return dir;
  }

char je_mozne_videt(int sector1,int sector2,int flag)
  {
  int x1,y1;
  int x2,y2;
  int xs,ys;
  int x,y,ly,s;

  if (map_coord[sector1].layer!=map_coord[sector2].layer) return 0;
  x1=map_coord[sector1].x;x2=map_coord[sector2].x;
  y1=map_coord[sector1].y;y2=map_coord[sector2].y;
  xs=x1-x2;
  ys=y1-y2;
  if (xs==0 && ys==0) return 1;
  s=sector1;
  ly=0;
  if (xs>=0)
     {
     for(x=0;x<=xs;x++)
        {
        y=(x+1)*ys/(xs+1);
        while (y>ly)
           if ((map_sides[(s<<2)].flags & flag)!=(unsigned)flag)
              {
              s=map_sectors[s].step_next[0];
              ly++;
              }
           else return 0;
        while (y<ly)
           if ((map_sides[(s<<2)+2].flags & flag)!=(unsigned)flag)
              {
              s=map_sectors[s].step_next[2];
              ly--;
              }
           else return 0;
        if (x!=xs) {
           if ((map_sides[(s<<2)+3].flags & flag)!=(unsigned)flag) s=map_sectors[s].step_next[3];
        } else return 0;
        }
     }
  else
  if (xs<0)
     {
     for(x=0;x>=xs;x--)
        {
        y=(x-1)*ys/(xs-1);
        while (y>ly)
           if ((map_sides[(s<<2)].flags & flag)!=(unsigned)flag)
              {
              s=map_sectors[s].step_next[0];
              ly++;
              }
           else return 0;
        while (y<ly)
           if ((map_sides[(s<<2)+2].flags & flag)!=(unsigned)flag)
              {
              s=map_sectors[s].step_next[2];
              ly--;
              }
           else return 0;
        if (x!=xs) {
           if ((map_sides[(s<<2)+1].flags & flag)!=(unsigned)flag) s=map_sectors[s].step_next[1];
        } else return 0;
        }
     }
  return s==sector2;
  }

int q_vidis_postavu(int sector,int dir,TMOB *p,int *otocit_se,char ret)
  {
  int i,z=-1;
  int xs,ys,nd,d,dis=255;

  if (p->vlastnosti[VLS_KOUZLA] & SPL_BLIND) return -1;
  if (p->vlastnosti[VLS_KOUZLA] & SPL_FEAR) return -1;
  if (p->flee_num==100 && !insleep) return -1;
  for(i=0;i<POCET_POSTAV;i++)
     {
     char ok=0;
     THUMAN *ps=&postavy[i];
     xs=map_coord[sector].x-map_coord[postavy[i].sektor].x;
     ys=map_coord[sector].y-map_coord[postavy[i].sektor].y;
     d=MAX(abs(xs),abs(ys));
     if (d<=p->dohled && (!(ps->vlastnosti[VLS_KOUZLA] & SPL_INVIS)||p->vlajky & MOB_SENSE) && ps->used && ps->lives)
        switch(dir)
           {
           case 0:ok=ys>=0;break;
           case 1:ok=xs<=0;break;
           case 2:ok=ys<=0;break;
           case 3:ok=xs>=0;break;
           }
     if (ok)
        if (je_mozne_videt(sector,postavy[i].sektor,SD_MONST_IMPS | SD_PLAY_IMPS))
           {
            int alt;
            if (ys>=abs(xs)) {nd=0;alt=xs>0?3:1;}
            else if (xs>=abs(ys)) {nd=3;alt=ys>0?0:2;}
            else if (ys<=(-abs(xs))) {nd=2;alt=xs>0?1:3;}
            else if (xs<=(-abs(ys))) {nd=1;alt=ys>0?2:0;}
            if (mob_check_next_sector(p->sector,nd,p->stay_strategy & MOB_BIG,p->vlajky & MOB_PASSABLE)==1)
            {
              nd=alt;
              if (mob_check_next_sector(p->sector,nd,p->stay_strategy & MOB_BIG,p->vlajky & MOB_PASSABLE)==1)
              {
                nd=(alt+2)&3;
                if (mob_check_next_sector(p->sector,nd,p->stay_strategy & MOB_BIG,p->vlajky & MOB_PASSABLE)==1)
                {
                  nd=(alt+3)&3;
                }
              }
            }
           }
        else d=255;
     else d=255;
     if (d!=255)
        {
        d*=2;
        if (xs!=0 && ys!=0) d+=3;//dej prednost tem co jsou na tve souradnici.
        if (d<dis || (d==dis && rnd(10)<3))
           {
           dis=d;*otocit_se=nd;att_sect=postavy[i].sektor;
           z=i;
           }
        }
     }
  if (dis==255) dis=-2;
  if (ret)return z;else return dis/2;
  }

int uhni_mobe(char on,int num,int kolik)
  {
  TMOB *p;

  p=&mobs[num];
  if (miri_middle(p)) kolik=0;
  if (on)
  {
  if (p->headx!=255 && p->headx!=0 && p->headx!=128) return 128-p->headx;
  if (p->heady!=255 && p->heady!=0 && p->heady!=128) return 128-p->heady;
  }
  if (p->headx!=255 && p->headx!=0) p->headx=128+kolik;
  if (p->heady!=255 && p->heady!=0) p->heady=128+kolik;
  return -kolik;
  }


void stop_mob(TMOB *p)
  {
  int num1;
  TMOB *q;

  p->mode=MBA_NONE;
  num1=mob_map[p->sector];
  if (num1) q=&mobs[num1-MOB_START];else q=p;
  if (p==q) {
     if (p->next) {
        q=mobs+p->next-MOB_START;
     } else {
        q=NULL;
     }
  }
  if (p->stay_strategy & MOB_BIG)
     {
     p->headx=128;
     p->heady=128;
     goto end;
     }
  if (q!=NULL && p->dir!=q->dir)
     {
     p->headx=mob_batt_x[p->dir];
     p->heady=mob_batt_y[p->dir];
     q->headx=mob_batt_x[q->dir];
     q->heady=mob_batt_y[q->dir];
     mob_walk_sound(p);
     mob_walk_sound(q);
     return;
     }
  if (q!=NULL && p->dir==q->dir)
     {
     if (miri_middle(q))
        {
        p->dir=(p->dir+1) & 3;
        stop_mob(q);
        }
     }
  else
     {
     if (p->dir & 1)
      {
      p->headx=mob_batt_x[p->dir];
      if (p->heady==255 || p->heady==0) p->heady=128;
      }
     else
      {
      p->heady=mob_batt_y[p->dir];
      if (p->headx==255 || p->headx==0) p->headx=128;
      }
     goto end;
     }
    {
     switch (p->dir)
        {
        case 0:
        case 2:if (q->headx==128) {
                    if (p->headx<128) q->headx=128+MOB_DIST;else q->headx=128-MOB_DIST;
                }
               p->headx=-q->headx;
               p->heady=mob_batt_y[p->dir];
               break;
        case 1:
        case 3:if (q->heady==128) {
                    if (p->heady<128) q->heady=128+MOB_DIST;else q->heady=128-MOB_DIST;
                }
               p->heady=-q->heady;
               p->headx=mob_batt_x[p->dir];break;
        }
     }
  end:
  mob_walk_sound(p);
  }

void stop_all_mobs()
  {
  int i;
  for(i=0;i<MAX_MOBS;i++)
     if (mobs[i].vlajky & MOB_LIVE) stop_mob(mobs+i);
  }

void stop_all_mobs_on_sector(int sector)
  {
  TMOB *m;
  int mm=mob_map[sector];

  while (mm)
    {
    m=mobs+mm-MOB_START;
    stop_mob(m);
    mm=m->next;
    }
  }

char mob_test_na_bitvu(TMOB *p)
  {
  int x,d;
  char c=0;
  char pt;

  if (nohassle) return 0;
  if (p->stay_strategy & MOB_WATCH)
   if ((d=q_vidis_postavu(p->sector,p->dir,p,&x,0))>-1)
        {
        p->stay_strategy|=MOB_WALK;
        if (mob_check_next_sector(p->sector,x,p->stay_strategy & MOB_BIG,p->vlajky & MOB_PASSABLE)!=1)
         {
         pt=(p->headx==p->locx && p->heady==p->locy);
         if (p->dir!=x || pt)
           {
           p->dir=x;
           if (!(p->stay_strategy & MOB_BIG) || pt)
              {
              p->headx=mob_go_x[x];
              p->heady=mob_go_y[x];
              }
           else
              {
              p->headx=128;
              p->heady=128;
              }
           }
         if (q_zacit_souboj(p,d,att_sect))
           {
           stop_mob(p);
           p->vlajky|=MOB_IN_BATTLE;
           if (!battle) zacni_souboj(p,d,att_sect);
           }
         c=1;
         }
        if (c) mob_sound_event(p,MBS_WALK);
        return c;
        }
  return 0;
  }

char return_home(TMOB *p,int *smer)
  {
  word *path;
  int i;

  i=p->dir;
  if (!mob_check_next_sector(p->sector,(i+1)&3,p->stay_strategy & MOB_BIG,0) || !mob_check_next_sector(p->sector,(i+3)&3,p->stay_strategy & MOB_BIG,0)) return 1;
  if (p->sector==p->home_pos) return 1;
  najdi_cestu(p->sector,p->home_pos,SD_MONST_IMPS,&path,(p->stay_strategy & MOB_BIG)?1:2);
  if (path==NULL)
     {
     return 1;
     }
  for(i=0;i<4 && map_sectors[p->sector].step_next[i]!=path[0];i++);
  free(path);
  if (i==4) return 1;
  if (mob_check_next_sector(p->sector,i,p->stay_strategy & MOB_BIG,0)) return 1;
  *smer=i;
  return 0;
  }

static int jdi_po_ceste(int old,TMOB *p)
  {
  int i,s;
  word *c;

  if (p->mode==MBA_FLEE && !p->actions--)   //v pride uteku pocitej kroky
     {
     p->mode=0;
     p->vlajky&=~MOB_IN_BATTLE;
     return old;
     }
  c=mob_path_ptr[p-mobs];       //vem cestu
  if (c==NULL) return old;      //neni -> konec
  if (*c==0)                    //na konci -> dealokace a konec
     {
     free_path(p-mobs);
     p->mode=0;
     p->vlajky&=~MOB_IN_BATTLE;
     return old;
     }
  s=p->sector;
  for(i=0;i<4;i++) if (map_sectors[s].step_next[i]==*c) break;
  if (i==4) return old;
  old=i;
  c++;
  mob_path_ptr[p-mobs]=c;       //uloz_ukazatel
  return old;
  }

void rozhodni_o_smeru(TMOB *p)
  {
  int sect,dir,r,v,c,alone,oldwalk,passable;
  int vdir=-1;

  alone=p->stay_strategy & MOB_BIG;
  passable=p->vlajky & MOB_PASSABLE;
  sect=p->sector;
  dir=p->dir;
  if (mob_paths[p-mobs]!=NULL) c=jdi_po_ceste(-1,p);else c=-1;
  if (c!=-1) {
    if (!mob_check_next_sector(sect,c,alone,passable))
     {
     p->headx=mob_go_x[c];
     p->heady=mob_go_y[c];
     p->dir=c;
     goto end1;
     }
    else
     {
     if (mob_path_ptr[p-mobs]-mob_paths[p-mobs]>1) mob_path_ptr[p-mobs]-=2;
     }
  }
  if (p->vlajky & MOB_IN_BATTLE)
     {
     stop_mob(p);
     goto end1;
     }
  if (call_mob_event(p->specproc,SMPR_WALK,p)) goto end1;
  oldwalk=p->walk_data;
  if (mob_test_na_bitvu(p)) return;
  p->vlajky&=~MOB_IN_BATTLE;
  c=map_sectors[sect].sector_type;
  c-=S_SMER;
  if (c>=0 && c<4 && !mob_check_next_sector(sect,c,alone,passable))
     {
     if (p->headx==p->locx && p->heady==p->locy) dir=c+4;else dir=c;
     }
  else
     {
     v=mob_pocet_vychodu(sect,dir);
     if (v==p->walk_data && !mob_check_next_sector(sect,dir,alone,passable))
        if (p->headx==p->locx && p->heady==p->locy) dir=p->dir+4;else dir=p->dir;
     else
        {
        r=1;
				if (v==0) v=1;
        p->walk_data=v;
        if (v==1 && p->stay_strategy & MOB_GUARD) r=return_home(p,&dir);
        if (r)
           {
           if (v<2) r=1;else r=rnd(v)+1;
           vdir=dir=mob_vyber_vychod(r,sect,dir,alone,passable);
           //if ( p->stay_strategy & MOB_WATCH && rnd(100)<20 && lv<128 && dir!=p->dir) dir=-1;
           }
        }
     }
 if (dir==-1)
     {
     dir=p->dir;p->dir=(p->dir+2)&3;
     stop_mob(p);
     p->walk_data=rnd(32)+223;
     if (vdir!=-1)p->dir=vdir;
     }
  else
     if (p->dir!=dir || miri_middle(p))
       {
        if (p->stay_strategy & MOB_BIG && (p->locx!=p->headx || p->locy!=p->heady))
        {
        stop_mob(p);
        p->walk_data=oldwalk;
        }
        else if (((p->dir-dir) &0x3)==2 && (p->headx!=p->locx || p->heady!=p->locy))
           {
           stop_mob(p);
           p->walk_data=0;
           }
        else
           {
           dir&=3;
           p->headx=mob_go_x[dir];
           p->heady=mob_go_y[dir];
           p->dir=dir;
           }
       }
  end1:
  if (p->headx!=p->locx || p->heady!=p->locy) mob_sound_event(p,MBS_WALK);
  }

void krok_moba(TMOB *p)
 {
  if (!mob_check_next_sector(p->sector,p->dir,p->stay_strategy,p->vlajky))
     {
        p->headx=mob_go_x[p->dir];
        p->heady=mob_go_y[p->dir];
     }
  }

static char get_view_mirror=0;

int get_view(TMOB *p,int dirmob,int action,int curdir)
  {
  int view;int pos;
  int xs,ys;

  get_view_mirror=0;
  if (action==MOB_ATTACK) pos=4;
  else if (action==MOB_TO_HIT) pos=5;
  else if (action==MOB_DEATH)
     {
     return 1;
     }
  else
     {
     xs=p->headx-p->locx;
     ys=p->heady-p->locy;
	 if (!(game_extras & EX_WALKDIAGONAL) || p->stay_strategy & MOB_BIG)
	   if (ys!=0 && xs!=0) {
		  if (p->dir==1 || p->dir==3) xs=0;else ys=0;
	   }
     if (xs) dirmob=(xs<0)?3:1;
     if (ys) dirmob=(ys<0)?0:2;
     pos=(2+dirmob-curdir)&0x3;
	 if (game_extras & EX_WALKDIAGONAL && !( p->stay_strategy & MOB_BIG))
	   {
	   switch (curdir & 0x3)
		  {
		  case 0: if (p->locx>p->headx) pos=1;else if (p->locx<p->headx) pos=3;break;
		  case 1: if (p->locy>p->heady) pos=1;else if (p->locy<p->heady) pos=3;break;
		  case 2: if (p->locx>p->headx) pos=3;else if (p->locx<p->headx) pos=1;break;
		  case 3: if (p->locy>p->heady) pos=3;else if (p->locy<p->heady) pos=1;break;
		 }
	   }
     }
  if (pos==3) get_view_mirror=pos=1;
  if (p->anim_counter==-1) view=pos*16;
  else view=pos*16+(p->anim_counter % p->anim_counts[pos])+1;
  return view;
  }

void get_pos(int x,int y,int *xpos,int *ypos,int dir)
  {
  switch(dir)
     {
     case 0:*xpos=x;*ypos=-y;break;
     case 1:*xpos=y;*ypos=x;break;
     case 2:*xpos=-x;*ypos=y;break;
     case 3:*xpos=-y;*ypos=-x;break;
     }
  }

/*
void draw_blood(int zasah,int celx,int cely,int posx,int posy)
  {
  draw_placed_texture(ablock(H_MZASAH1+zasah-1),celx,cely,posx+64,posy+64,75,0);
  }
*/
static void *mob_select_palette(TMOB *p)
  {
  char *palet;

  palet=ablock(p->cislo_vzoru+6*16+4+monster_block);
  return palet+(p->palette)*PIC_FADE_PAL_SIZE;
  }

static void CheckMobStoned(int num)
{
  if (mobs[num].vlastnosti[VLS_KOUZLA] & SPL_STONED)
  {
      TMOB *p=mobs+num;
      TMOBSAVEDATA *save;
      if (!mobsavedata)
      {
        mobsavedata=(TMOBSAVEDATA **)getmem(sizeof(TMOBSAVEDATA *)*MAX_MOBS);
        memset(mobsavedata,0,sizeof(TMOBSAVEDATA)*MAX_MOBS);
      }
      save=mobsavedata[num];
      if (save==NULL)
      {
        save=mobsavedata[num]=(TMOBSAVEDATA *)getmem(sizeof(TMOBSAVEDATA));
        save->anim_counter=p->anim_counter;
        save->anim_phase=p->anim_phase;
        save->dir=p->dir;
      }
      else
      {
        p->anim_counter=save->anim_counter;
        p->anim_phase=save->anim_phase;
        p->dir=save->dir;
        p->headx=p->locx;
        p->heady=p->locy;
      }
  }
  else
  {
    if (mobsavedata && mobsavedata[num])
    {
      free(mobsavedata[num]);
      mobsavedata[num]=0;
    }
  }


}

void draw_mob_call(int num,int curdir,int celx,int cely,char shiftup)
  {
  TMOB *p,*q;
  int view,vw;
  int view2,vw2;
  DRW_ENEMY drw1,drw2;

  set_font(H_FLITT5,RGB555(31,31,0));
  CheckMobStoned(num-MOB_START);
  p=&mobs[num-MOB_START];
  shiftup|=(p->stay_strategy & MOB_BIG);

  get_pos(p->locx-128,p->locy-128,&drw1.posx,&drw1.posy,curdir);
  view=get_view(p,p->dir,p->anim_phase,curdir);
  vw=p->cislo_vzoru+view+monster_block;
  if (p->vlastnosti[VLS_KOUZLA] & SPL_INVIS) {drw1.txtr=NULL;vw=0;}else drw1.txtr=ablock(vw);
  drw1.celx=celx;
  drw1.cely=cely;
  drw1.mirror=get_view_mirror;
  drw1.adjust=p->adjusting[view];
  drw1.shiftup=shiftup;
  drw1.num=p->lives;
  drw1.palette=mob_select_palette(p);
  drw1.stoned= (p->vlastnosti[VLS_KOUZLA] & SPL_STONED)!=0;
  see_monster|=(~p->vlajky & MOB_PASSABLE);
  if (p->next)
     {
     CheckMobStoned(p->next-MOB_START);
     q=&mobs[p->next-MOB_START];
     get_pos(q->locx-128,q->locy-128,&drw2.posx,&drw2.posy,curdir);
     view2=get_view(q,q->dir,q->anim_phase,curdir);
     vw2=view2+q->cislo_vzoru+monster_block;
     drw2.shiftup=shiftup;
     drw2.celx=celx;
     drw2.cely=cely;
     alock(vw);
     alock(vw+6*16+5);
     if (q->vlastnosti[VLS_KOUZLA] & SPL_INVIS) {drw2.txtr=NULL;vw2=0;}else drw2.txtr=ablock(vw2);
     drw2.mirror=get_view_mirror;
     alock(vw2);
     alock(vw2+6*16+5);
     drw2.adjust=q->adjusting[view2];
     drw2.num=q->lives;
     drw2.palette=mob_select_palette(q);
     drw2.stoned=(q->vlastnosti[VLS_KOUZLA] & SPL_STONED)!=0;
     see_monster|=(~q->vlajky & MOB_PASSABLE);
     }
  else
     {
     view2=-1;
     draw_enemy(&drw1);
     return;
     }
  if (drw1.posy>drw2.posy)
     {
     draw_enemy(&drw1);
     draw_enemy(&drw2);
     }
  else
     {
     draw_enemy(&drw2);
     draw_enemy(&drw1);
     }
  aunlock(vw);
  aunlock(vw2);
  aunlock(vw+6*16+5);
  aunlock(vw2+6*16+5);
  }

void draw_mob(int num,int curdir,int celx,int cely,char shiftup)
  {
  int ss=(num<<2);
  num=mob_map[num];
  if (!num) return;
  set_lclip_rclip(celx,cely,map_sides[ss+((curdir+3)&3)].lclip,map_sides[ss+((curdir+1)&3)].lclip);
  draw_mob_call(num,curdir,celx,cely,shiftup);
  }


void otoc_moba(TMOB *p)
  {
  p->walk_data=255;
  rozhodni_o_smeru(p);
  }


static int mob_check_teleport(int sect,int num)
  {
  int i;
  if (!ISTELEPORTSECT(sect)) return sect;
  play_sample_at_sector(H_SND_TELEPOUT,viewsector,sect,0,0);
  add_spectxtr(sect,H_TELEP_PCX,14,1,0);
  sect=map_sectors[sect].sector_tag;
  play_sample_at_sector(H_SND_TELEPOUT,viewsector,sect,0,0);
  add_spectxtr(sect,H_TELEP_PCX,14,1,0);
  if (map_coord[sect].flags & MC_PLAYER)
     {
     THUMAN *h=postavy;
     for(i=0;i<POCET_POSTAV;i++,h++) if (h->sektor==sect) player_hit(h,h->lives,0);
     }
  mobs[num].locx=128;
  mobs[num].locy=128;
  return sect;
  }

void mob_step_next(int num,int sect,int dir,char *change)
  {
  int c,numm,d;
  TMOB *p;

  *change+=128;
  numm=num+MOB_START;
  if (~mobs[num].vlajky & MOB_MOBILE)
     {
     c=mob_map[sect];
     if (c!=numm)
       mobs[c-MOB_START].next=0;
      else mob_map[sect]=mobs[num].next;
     mobs[num].next=0;
     recheck_button(sect,1);
     }
  sect=map_sectors[sect].step_next[dir];
  if (~mobs[num].vlajky & MOB_MOBILE)
     {
     sect=mob_check_teleport(sect,num);
     c=mob_map[sect];
     if (c)
        {
        mobs[num].next=c;
        d=uhni_mobe(1,num,0);
        if (d) d=uhni_mobe(1,c-MOB_START,d);
        else d=uhni_mobe(1,c-MOB_START,-MOB_DIST);
        }
     mob_map[sect]=numm;
     recheck_button(sect,1);
     }
  mobs[num].sector=sect;
  p=&mobs[num];
  p->dir=dir;
  rozhodni_o_smeru(p);
  if (p->next)
     uhni_mobe(0,num,d);
  if (p->stay_strategy & MOB_PICKING)
     {
     for(c=0;c<4;c++) if (map_items[(p->sector<<2)+c]!=NULL) break;
     if (c==4 && pick_item_corpse(p,PK_QUERY)) c=0;
     if (c!=4)
        {
        stop_mob(p);
        p->stay_strategy|=MOB_PICK;
        }
     }
  }

void mob_check(int num,TMOB *p)
  {
  int sect,q,z;

  sect=p->sector;
  q=p->stay_strategy & MOB_BIG;
  z=p->vlajky & MOB_PASSABLE;
  if (p->locy<64) {
     if (mob_check_next_sector(sect,0,q,z)) otoc_moba(p);
     else mob_step_next(num,sect,0,&p->locy);
  } else if (p->locx>191) {
     if (mob_check_next_sector(sect,1,q,z)) otoc_moba(p);
     else mob_step_next(num,sect,1,&p->locx);
  } else if (p->locy>191) {
     if (mob_check_next_sector(sect,2,q,z)) otoc_moba(p);
     else mob_step_next(num,sect,2,&p->locy);
  } else if (p->locx<64) {
     if (mob_check_next_sector(sect,3,q,z)) otoc_moba(p);
     else mob_step_next(num,sect,3,&p->locx);
  }
  if (battle && p->mode!=MBA_FLEE)
     {
     mob_sound_event(p,MBS_WALK);
     stop_mob(p);
     }
  }

/*void mobs_attack(TMOB *p)
  {
  int sect,dir,asect;

  if (p->actions<=0) return;
  neco_v_pohybu=1;
  sect=p->sector;
  dir=p->dir;
  asect=map_sectors[sect].step_next[dir];
  if (!asect || map_sides[sect*4+dir].flags & SD_MONST_IMPS)
     {
     rozhodni_o_smeru(p);
     p->headx=mob_go_x[p->dir];
     p->heady=mob_go_y[p->dir];
     p->anim_phase=0;
     return;
     }
  if (!(map_coord[asect].flags & MC_PLAYER))
     {
     rozhodni_o_smeru(p);
     p->headx=mob_go_x[p->dir];
     p->heady=mob_go_y[p->dir];
     p->anim_phase=0;
     return;
     }
  p->anim_phase=MOB_ATTACK;
  p->csektor=asect;
  viewsector=asect;
  viewdir=(dir+2)&3;
  }*/

void vymaz_zasahy(THE_TIMER *q)
  {
  if (q->userdata[1]!=postavy[q->userdata[0]].dostal) return;
  postavy[q->userdata[0]].dostal=0;
  bott_draw(0);
  }

static int drop_inventory(TMOB *p)
  {
  int i,x,y,pl;
  short c[]={0,0};

  for(i=-1;i<MOBS_INV;i++)
     if (p->inv[i] || (i<0 && p->money))
        {
        if (p->locx>128) x=1;else if (p->locx<128) x=-1;else x=rnd(2)*2-1;
        if (p->locy>128) y=1;else if (p->locy<128) y=-1;else y=rnd(2)*2-1;
        pl=0;if (x>0) pl++;
        if (y>0) pl=3-pl;
        if (i<0)
           {
           int z=(int)p->money+(int)(rnd(40)-20)*(int)p->money/(int)100;
           c[0]=create_item_money(z);
           }
        else
           {
           c[0]=p->inv[i];
           p->inv[i]=0;
           }
        push_item(p->sector,pl,c);
        }
  return 0;
  }


void mob_check_death(int num,TMOB *p)
  {
  int sect;

  mob_dostal=0;
  bott_draw(0);
  if (p->lives>0) return;
  SEND_LOG("(GAME) Monster killed ... '%s'",p->name);
  sect=p->sector;
  p->vlajky&=~MOB_IN_BATTLE & ~MOB_LIVE;
  free_path(num);
  drop_inventory(p);
  if (mob_map[sect]==num+MOB_START) mob_map[sect]=p->next;
  else
     {
     p=&mobs[mob_map[sect]-MOB_START];
     p->next=0;
     }
  pozdrz_akci();
  }
extern char att_player;

void mob_hit(TMOB *mm,int dostal)
  {
  int ch;
	int mob_dostal,mob_dostal_pocet;

  if (mm->vlajky & MOB_PASSABLE) return;
  if (dostal>mm->vlastnosti[VLS_MAXHIT]) dostal=mm->vlastnosti[VLS_MAXHIT];
  mm->headx=mm->locx;
  mm->heady=mm->locy;
  mm->lives-=dostal;
  mob_dostal_pocet=dostal;
  mm->dostal+=dostal;
  if (dostal>0) mm->vlajky|=MOB_IN_BATTLE;
  //mm->stay_strategy|=MOB_WALK | MOB_WATCH;
  mm->dialog_flags|=0x2;
  if (mm->lives>mm->vlastnosti[VLS_MAXHIT]) mm->lives=mm->vlastnosti[VLS_MAXHIT];
  dlives=mm->lives;
  if (dostal>0)
     {
     ddostal=dostal;
     send_experience(mm,dostal);
     att_player=select_player;
     if (dostal<mm->lives) ch=dostal*3/mm->lives;else ch=2;
     mob_dostal=ch+1;
     bott_draw(0);
     if (mm->lives<1)
        {
        int xpos;
        switch (viewdir)
           {
           case 0:xpos=-(mm->locx-128);break;
           case 1:xpos=-(mm->locy-128);break;
           case 2:xpos=(mm->locx-128);break;
           case 3:xpos=(mm->locy-128);break;
           }
        add_spectxtr(mm->sector,H_KILL,10,1,xpos*23/10);
        mm->anim_phase=MOB_DEATH;
        }
     else mm->anim_phase=MOB_TO_HIT;
     mm->anim_counter=0;
     mm->mode=MBA_NONE;
     mob_sound_event(mm,MBS_HIT);
     battle|=dostal>0;
     if (vybrana_zbran>-1)  //utok zbrani?
        {
        int druh;
        if (vybrana_zbran!=0)  //neni to utok holyma rukama
           {
           TITEM *it;
           it=&glob_items[vybrana_zbran-1];
           druh=it->typ_zbrane;
           }
        else druh=TPW_OST;
        send_weapon_skill(druh);
        vybrana_zbran=-1;
        }
     }
	if (mob_dostal_pocet>0)draw_blood(1,mob_dostal,mob_dostal_pocet);
	}


void mob_strelba(TMOB *p)
  {
  int i;
  TITEM *t;

  for(i=0;i<item_count;i++) if (glob_items[i].umisteni==PL_SIP && glob_items[i].druh==TYP_VRHACI) break;
  if (i==item_count)
     {
     closemode();
     display_error("Nestvura nemuze strilet. Neni nadefinovan obekt sipu");
     exit(1);
     }
  t=glob_items+i;
  t->zmeny[VLS_MGSIL_H]=p->vlastnosti[VLS_MGSIL_H];  //adjust zmen v magickem utoku
  t->zmeny[VLS_MGSIL_L]=p->vlastnosti[VLS_MGSIL_L];
  t->zmeny[VLS_MGZIVEL]=p->vlastnosti[VLS_MGZIVEL];
  spell_throw(-((p-mobs)+1),i);
  letici_veci->flags &=~FLY_DESTROY;
  letici_veci->hit_bonus=p->vlastnosti[VLS_UTOK_L]+rnd(p->vlastnosti[VLS_UTOK_H]-p->vlastnosti[VLS_UTOK_L]+1);
  letici_veci->damage=p->vlastnosti[VLS_DAMAGE];
  p->dostal=0;
  }

static void knock_player_back(THUMAN *p,int dir)
  {
  int sect,sid,nsect;

  sect=p->sektor;sid=sect*4+dir;
  nsect=map_sectors[sect].step_next[dir];
  if (mob_map[nsect]) return;
  destroy_player_map();
  call_macro(sid,MC_EXIT);
  if (map_sides[sid].flags & SD_PLAY_IMPS)
     {
     call_macro(sid,MC_PASSFAIL);
     build_player_map();
     return;
     }
  else call_macro(sid,MC_PASSSUC);
  p->sektor=nsect;
  viewsector=nsect;
  check_postavy_teleport();
  build_player_map();
  recheck_button(nsect,1);
  recheck_button(sect,1);
  redraw_scene();
  hold_timer(TM_BACK_MUSIC,1);
  zooming_backward(ablock(H_BGR_BUFF));
  hold_timer(TM_BACK_MUSIC,0);
  showview(0,0,0,0);
  }

void PodporaStitu(THUMAN *h, short *vlastnosti)
  {
  int pos[]={PO_RUKA_L,PO_RUKA_R};
  int i;
  int factor=h->kondice*100/h->vlastnosti[VLS_KONDIC];
  for (i=0;i<2;i++)
    {
    if (h->wearing[pos[i]]!=0)
      {
      TITEM *it=glob_items+h->wearing[pos[i]]-1;
      if (it->zmeny[VLS_OBRAN_L] || it->zmeny[VLS_OBRAN_H])
        {
        vlastnosti[VLS_OBRAN_L]-=it->zmeny[VLS_OBRAN_L];
        vlastnosti[VLS_OBRAN_H]-=it->zmeny[VLS_OBRAN_H];
        vlastnosti[VLS_OBRAN_L]+=(2*it->zmeny[VLS_OBRAN_L])*factor/100;
        vlastnosti[VLS_OBRAN_H]+=(2*it->zmeny[VLS_OBRAN_H])*factor/100;
        }
      }
    }
  if (factor<20)
    {
    vlastnosti[VLS_OBRAT]=vlastnosti[VLS_OBRAT]*factor/20;
    }
  if (h->kondice) h->kondice--;
  }

void mobs_hit(TMOB *p)
  {
  int asect;
  int obet;
  int i,pocet;
  THE_TIMER *tt;
  int spec;char rr=1;char dead;
  THUMAN *h;
  short vlastnosti[VLS_MAX];

  if (p->mode==MBA_SPELL)
    {
     mob_cast(p->casting,p,p-mobs);
     p->dostal=0;
    }
  else
     if (p->stay_strategy & MOB_ROGUE) mob_strelba(p);
  else
     {
     asect=p->csektor;
     if (!(map_coord[asect].flags & MC_PLAYER)) return;
     pocet=0;
     for(i=0;i<POCET_POSTAV;i++)
     {
     THUMAN *p=&postavy[i];
     if (p->used && p->sektor==asect && p->lives) pocet++;
     }
  if (!pocet) abort();
  obet=1+rnd(pocet);
  for(i=0;obet>0;)
     {
     THUMAN *p;
     i++;
     if (i>=POCET_POSTAV) i=0;
     p=&postavy[i];
     if (p->used && p->sektor==asect && p->lives) obet--;
     }
  h=&postavy[i];
  if (h->utek)
     {
     pocet=10;
     h->utek--;
     }
  memcpy(vlastnosti,h->vlastnosti,sizeof(vlastnosti));
  spec=vlastnosti[VLS_KOUZLA];
  if (game_extras & EX_SHIELD_BLOCKING) PodporaStitu(h,vlastnosti);
  else uprav_podle_kondice(h,&pocet);
  h->dostal=vypocet_zasahu(p->vlastnosti,vlastnosti,pocet,0,0);  //vypocet zasahu
  if (h->dostal) p->dostal=0;
  if (spec & SPL_OKO)                    //oko za oko pro hrace
     {
     vybrana_zbran=-1;
     mob_hit(p,h->dostal);
     mob_check_death(p-mobs,p);
     }
  if (h->dostal && p->vlastnosti[VLS_KOUZLA] & SPL_KNOCK) knock_player_back(h,p->dir);
  if (p->vlastnosti[VLS_KOUZLA] & SPL_DRAIN)  //energy drain pro potvoru
     {
     p->lives+=h->dostal;
     if (p->lives>p->vlastnosti[VLS_MAXHIT])p->lives=p->vlastnosti[VLS_MAXHIT];
     }
  dead=player_hit(h,h->dostal,1);
  if (h->lives>h->vlastnosti[VLS_MAXHIT])  h->lives=h->vlastnosti[VLS_MAXHIT];
  if ((spec & SPL_TVAR) && (spec & SPL_OKO)) //nedovolena kombinace
     {
     char s[200];
     h->lives=0;
     sprintf(s,texty[73+(postavy[i].female==1)],postavy[i].jmeno);
     bott_disp_text(s);
     rr=0;
     dead=player_check_death(&postavy[i],0);
     }
  tt=add_to_timer(TM_CLEAR_ZASAHY,100,1,vymaz_zasahy);tt->userdata[0]=i;tt->userdata[1]=postavy[i].dostal;
  if (dead && hlubina_level>0)
    {
    select_player=i;
    vybrana_zbran=-1;
    mob_hit(p,p->lives); //hlubina (potvora je mrtva);
    }
  bott_draw(rr);
     }
  }


void mobs_live(int num)
  {
  TMOB *p;
  int xs,ys;

  p=&mobs[num];
  if (p->vlastnosti[VLS_KOUZLA] & SPL_STONED && p->lives>0)
    {
      p->vlajky &= ~MOB_IN_BATTLE;
      return;
    }
  if (p->sector>=mapsize)
     {
     char buff[256];
     closemode();
     sprintf(buff,"Potvora v neexistujicim sektoru (%d, %d) ",num,p->sector);
     display_error(buff);
     exit(1);
     }
  if (p->headx==p->locx && p->heady==p->locy && !p->anim_phase)
     {
     //zde se bude rozhodovat co dal;
     p->anim_counter=-1;
     stop_track_free(num+256);
     if (battle)
        {
        if (p->mode!=MBA_NONE && p->mode!=MBA_FLEE)
           {
           neco_v_pohybu=1;
           p->anim_phase=MOB_ATTACK;
           p->anim_counter=0;
           mob_sound_event(p,MBS_ATTACK);
           mobs_live(num);
           return;
           }
        if (p->mode==MBA_NONE) return;
        rozhodni_o_smeru(p);
        return;
        }
     if (p->stay_strategy & MOB_PICK)
        {
        if (!seber_predmet(p))  return;
        if (pick_item_corpse(p,PK_PICK)) return;
        p->stay_strategy&=~MOB_PICK;
        }
     if (p->stay_strategy & MOB_WALK)
        {
        if (p->walk_data>=224)
           if (++p->walk_data<255)
           {
           p->anim_counter=-1;
           return;
           }
        p->anim_counter=0;
        rozhodni_o_smeru(p);
        }
     else
        if (mob_map[p->sector]==num+MOB_START && (!p->next) )
        {
        p->headx=128;p->heady=128;
        }
     }
  else
     {
     if (p->anim_phase<MOB_ATTACK)
        {
        int spd;
        if (p->mode==MBA_FLEE)
           {
           int xr=abs(map_coord[p->sector].x-map_coord[viewsector].x);
           int yr=abs(map_coord[p->sector].y-map_coord[viewsector].y);
           if (xr>3 || yr>3) spd=100;else spd=2*p->speed;
           }
        else spd=p->speed;
        xs=p->headx-p->locx;
        ys=p->heady-p->locy;
		if (!(game_extras & EX_WALKDIAGONAL) || p->stay_strategy & MOB_BIG)
		  {
		  if (ys!=0 && xs!=0) {
			 if (p->dir==1 || p->dir==3) xs=0;else ys=0;
		  }}
        if (xs>spd) xs=spd;
        else if (xs<-spd) xs=-spd;
        if (ys>spd) ys=spd;
        else if (ys<-spd) ys=-spd;
        p->locx+=xs;
        p->locy+=ys;
		if (xs!=0 || ys!=0) neco_v_pohybu=1;
        if (p->locx>192 || p->locx<64 || p->locy>192 || p->locy<64) mob_check(num,p);
        }
     p->anim_counter++;
     if (p->anim_phase==MOB_ATTACK)
        {
		neco_v_pohybu=1;
        if (p->anim_counter==p->hit_pos) mobs_hit(p);
        if (p->anim_counter>=p->anim_counts[4])
           {
           if (p->lives<1) p->anim_phase=MOB_TO_HIT;else p->anim_phase=0;
           p->anim_counter=-1;
           p->mode=MBA_NONE;
           }
        }
     else
     if (p->anim_phase==MOB_TO_HIT && p->anim_counter>=p->anim_counts[5])
        {
		neco_v_pohybu=1;
        p->anim_phase=0;
        p->anim_counter=-1;
        mob_check_death(num,p);
        }
     else
     if (p->anim_phase==MOB_DEATH)
        {
		neco_v_pohybu=1;
        if (p->anim_counter==2) mob_check_death(num,p);
        else if (p->anim_counter>12)
           {
           p->anim_phase=0;
           p->anim_counter=-1;
           }
        }
     }
  }

void calc_mobs()
  {
  int i;

  neco_v_pohybu=0;
  for(i=0;i<MAX_MOBS;i++) if (mobs[i].vlajky & MOB_LIVE) mobs_live(i);
  if (folow_mode)
     {
     viewsector=mobs[(uint8_t)folow_mob].sector;
     viewdir=mobs[(uint8_t)folow_mob].dir;
     }
  }

void check_all_mobs()
  {
  int i;
  TMOB *p;
  char b=0;

  for(i=0;i<MAX_MOBS;i++)
     {
     p=&mobs[i];
     if (p->vlajky & MOB_LIVE)
        {
        mob_test_na_bitvu(p);
        if (p->vlajky & MOB_IN_BATTLE) b=1;
        }
     }
  battle=b;
  }

void check_all_mobs_battle() //kontroluje zda je nekdo v battle
  {
  int i;
  TMOB *p;
  char b=0;

  for(i=0;i<MAX_MOBS;i++)
     {
     p=&mobs[i];
     if (p->vlajky & MOB_LIVE)
        if (p->vlajky & MOB_IN_BATTLE)
          b=1;
     }
  battle=b;
  }


#define Hi(x) ((x)>>16)
#define Lo(x) ((x)& 0xffff)

int q_kolik_je_potvor(int sector)
  {
    if (mob_map[sector]) {
        if (mobs[mob_map[sector] - MOB_START].next) {
            return 2;
        }
        else if (mobs[mob_map[sector] - MOB_START].stay_strategy & MOB_BIG) {
            return 2;
        }
        else {
            return 1;
        }
    }
    return 0;
}

void najdi_cestu(word start,word konec,int flag,word **cesta, int iamcnt)
  {
  longint *stack;
  longint *stk_free;
  longint *stk_cur;
  char *ok_flags;

  *cesta=NULL;
  stk_free=stk_cur=stack=getmem(4*(mapsize+2));
  memset(ok_flags=getmem((mapsize+8)/8),0,(mapsize+8)/8);
  ok_flags[start>>3]|=1<<(start & 0x7);
  for(*stk_free++=start;stk_free!=stk_cur;stk_cur++)
     {
     uint8_t i;word s,d=0xFFFF,ss;
     s=(ss=Lo(*stk_cur))<<2;
     for(i=0;i<4;i++) if (!(map_sides[s+i].flags & flag))
        {
        char c;
        word w;
        d=map_sectors[ss].step_next[i];
        c=1<<(d & 0x7);
        w=d>>3;
        if (!(ok_flags[w] & c) && q_kolik_je_potvor(d)<iamcnt)
           {
           ok_flags[w]|=c;
          *stk_free++=d | ((stk_cur-stack)<<16);
           }
        if (d==konec) break;
        }
     if (d==konec) break;
     }
  if (stk_free!=stk_cur)
     {
     int count=0;
     longint *p,*z;
     word *x;

     z=p=stk_free-1;
     while (Lo(*p)!=start)
        {
        int l;
        count++;
        l=*p;
        p=Hi(l)+stack;
        *z--=Lo(l);
        }
     x=*cesta=getmem(count*2+2);
     z++;
     while (count--)
        {
        *x++=(word)(*z++);
        }
     *x++=0;
     }
  free(stack);
  free(ok_flags);
  }

void reakce_na_hluk(int mob,int smer)
  {
  if (mobs[mob].stay_strategy & MOB_LISTEN && !(mobs[mob].vlajky & MOB_IN_BATTLE))
     {
     mobs[mob].dir=smer;
     if (!battle)
        {
        mobs[mob].headx=mob_go_x[smer];
        mobs[mob].heady=mob_go_y[smer];
        }
     mobs[mob].stay_strategy|=MOB_WALK;
     }
  }

void sirit_zvuk(word start)
  {
  longint *stack;
  longint *stk_free;
  longint *stk_cur;
  char *ok_flags;

  stk_free=stk_cur=stack=getmem(4*(mapsize+2));
  memset(ok_flags=getmem((mapsize+8)/8),0,(mapsize+8)/8);
  ok_flags[start>>3]|=1<<(start & 0x7);
  for(*stk_free++=start;stk_free!=stk_cur;stk_cur++)
     {
     uint8_t i;word s,d,ss;
     s=(ss=Lo(*stk_cur))<<2;
     for(i=0;i<4;i++) if (!(map_sides[s+i].flags & SD_SOUND_IMPS))
        {
        char c;
        word w;
        d=map_sectors[ss].step_next[i];
        c=1<<(d & 0x7);
        w=d>>3;
        if (!(ok_flags[w] & c))
           {
           int mob;

           ok_flags[w]|=c;
           *stk_free++=d | ((stk_cur-stack)<<16);
           mob=mob_map[d]-MOB_START;
           if (mob>=0)
              {
              reakce_na_hluk(mob,(i+2)&3);
              if ((mob=mobs[mob].next-MOB_START)>=0) reakce_na_hluk(mob,(i+2)&3);
              }
           }
        }
     }
  free(stack);
  free(ok_flags);
  }

void refresh_mob_map()
  {
  int i,s;

  memset(mob_map,0,mapsize);
  for(i=0;i<MAX_MOBS;i++)
     if (mobs[i].vlajky & MOB_LIVE && ~mobs[i].vlajky & MOB_MOBILE)
        {
        s=mobs[i].sector;
        mobs[i].next=mob_map[s];
        mob_map[s]=i+MOB_START;
        }
  }

char track_mob(int sect,int dir)
  {
  if  (!(map_sides[(sect<<2)+dir].flags & SD_THING_IMPS))
    sect=map_sectors[sect].step_next[dir];else return 0;
  do
     {
     int i=-1;
     int m=mob_map[sect];

     if (map_coord[sect].flags & MC_PLAYER) return 1;
     while (m)
      {
      TMOB *mm=mobs+m-MOB_START;
      if (mm->stay_strategy & MOB_BIG) return 0;
      if (mm->dir!=i && i!=-1) return 0;
      stop_mob(mm);
      if (!mm->next && i==-1)
        if (mm->headx==128 || mm->heady==128) smeruj_moba(mm,1);
      i=mm->dir;m=mm->next;
      }
     if  (!(map_sides[(sect<<2)+dir].flags & SD_THING_IMPS))
       sect=map_sectors[sect].step_next[dir];
      else return 1;
     }
  while (1);
  }


//---------------------------------------------------------------------
/* Nasledujici procedury a funkce se volaji pro chovani potvory v bitve */

static word last_sector;
static TMOB *fleeing_mob;

static char valid_sectors(word sector)
  {
  int pp;

  last_sector=sector;
  if (map_coord[sector].flags & MC_MARKED) return 0; //nevyhovujici
  pp=q_kolik_je_potvor(sector);
  if (pp==2) return 0; //moc potvor - nevyhovujici
  if (fleeing_mob->stay_strategy & MOB_BIG && pp) return 0;
  pp=map_sectors[sector].sector_type;
  if (pp==S_DIRA || ISTELEPORT(pp)) return 0;
  return 1;
  }


char flee_monster_zac(TMOB *m)
  {
  int ss,s;
  int i,j;
  word *cesta,*c,cntr;
  for(j=0;j<POCET_POSTAV;j++)
     if (postavy[j].used && postavy[j].lives)
     {
     ss=(s=postavy[j].sektor)<<2;
     for(i=0;i<4;i++,ss++)
        if (~map_sides[ss].flags & SD_PLAY_IMPS)
          map_coord[map_sectors[s].step_next[i]].flags |= MC_MARKED; //oznac sektor jako nevyhovujici
     map_coord[s].flags |= MC_MARKED;
     }
  fleeing_mob=m;
  labyrinth_find_path(m->sector,65535,SD_MONST_IMPS,valid_sectors,NULL);
  i=labyrinth_find_path(m->sector,last_sector,SD_MONST_IMPS,valid_sectors,&cesta);
  for(j=0;j<mapsize;j++)map_coord[j].flags &= ~MC_MARKED;
  if (!i) return 0;
  for(cntr=0,c=cesta;cntr<6;cntr++,c++) if (!*c) break;
  free_path(m-mobs);
  register_mob_path(m-mobs,cesta);
  m->mode=MBA_FLEE;
  m->headx=m->locx+1;
  m->actions=6;
  m->dostal=0;
  return 1;
  }

char akce_moba_zac(TMOB *m)
  {
  int sect,flg,i,j;
  THUMAN *h;
  char flee;
  int perlives,dper,corlives;

  if (~m->vlajky & MOB_LIVE) return 1;
  dper=m->dostal*100/(m->lives+m->dostal);
  corlives=m->vlastnosti[VLS_MAXHIT]-m->flee_num*(m->vlastnosti[VLS_MAXHIT]-m->lives)/100;
  perlives=(100-m->flee_num)*corlives*q_kolik_je_potvor(m->sector)/(m->vlastnosti[VLS_MAXHIT]);
  perlives+=rnd(m->flee_num);
  dper+=rnd(m->flee_num);
  flee=dper>perlives || m->vlastnosti[VLS_KOUZLA] & SPL_FEAR;
     if (flee)
        {
        if (flee_monster_zac(m)) return 1;
        m->dostal=0;
        }
  if (call_mob_event(m->specproc,SMPR_ATTACK,m))
     {
     mob_walk_sound(m);
     return 0;
     }
  sect=map_sectors[m->sector].step_next[m->dir];
  flg=map_sides[(m->sector<<2)+m->dir].flags;
  if (!(flg & SD_PLAY_IMPS))
     if (map_coord[sect].flags & MC_PLAYER)
       for(i=0;i<POCET_POSTAV;i++)
           {
           THUMAN *p=&postavy[i];

           if (p->used && p->lives && p->sektor==sect)
              {
              if (((m->vlajky & MOB_CASTING) && get_spell_track(m->casting))|| m->stay_strategy & MOB_ROGUE)
                    {stop_all_mobs_on_sector(m->sector);smeruj_moba(m,0);}
              else stop_mob(m);
              viewsector=sect;
              viewdir=(m->dir+2) &3;
              m->csektor=sect;
              if ((m->vlajky & MOB_CASTING)&& rnd(100)<=(unsigned int)m->vlastnosti[VLS_SMAGIE]) m->mode=MBA_SPELL;else m->mode=MBA_ATTACK;
              bott_draw(1);
              return 0;
              }
           }
  for(i=0;i<4;i++)
     {
     sect=map_sectors[m->sector].step_next[i];
     flg=map_sides[(m->sector<<2)+i].flags;
     if (!(flg & SD_MONST_IMPS))
        if (map_coord[sect].flags & MC_PLAYER)
           for(j=0;j<POCET_POSTAV;j++)
              {
              THUMAN *p=&postavy[j];

              if (p->used && p->lives && p->sektor==sect && !(p->vlastnosti[VLS_KOUZLA] & SPL_INVIS))
                 {
                 m->dir=i;
                 stop_mob(m);
                 return 1;
                 }
              }
     }
  sect=m->sector;
  i=q_vidis_postavu(m->sector,m->dir,m,&j,1);
  h=postavy+i;
  if (i>-1) {
     if (((m->vlajky & MOB_CASTING && get_spell_track(m->casting))|| m->stay_strategy & MOB_ROGUE) &&
        (map_coord[m->sector].x==map_coord[h->sektor].x || map_coord[m->sector].y==map_coord[h->sektor].y)
        && track_mob(m->sector,m->dir))
        {
        m->dir=j;
        stop_all_mobs_on_sector(m->sector);
        if (~m->stay_strategy & MOB_ROGUE)
          m->mode=MBA_SPELL;
        else
          m->mode=MBA_ATTACK;
        smeruj_moba(m,0);
        viewsector=h->sektor;
        viewdir=(m->dir+2) & 3;
        return 0;
        }
     else
         {
         word *cesta;

         najdi_cestu(m->sector,postavy[i].sektor,SD_MONST_IMPS,&cesta,(m->stay_strategy & MOB_BIG)?1:2);
         if (cesta!=NULL)
           {
           for(j=0;j<4 && map_sectors[sect].step_next[j]!=cesta[0];j++);
           m->dir=j & 3;
           free(cesta);
           if (m->dir & 1)m->headx=mob_go_x[m->dir];else m->heady=mob_go_y[m->dir];
           mob_sound_event(m,MBS_WALK);
           return 1;
           }
         else
           return 1;
         }
  }
  rozhodni_o_smeru(m);
  if (m->dir & 1)m->headx=mob_go_x[m->dir];else m->heady=mob_go_y[m->dir];
//  m->headx=mob_go_x[m->dir];
//  m->heady=mob_go_y[m->dir];
  m->vlajky&=~MOB_IN_BATTLE;
  return 1;
  }

void mob_animuj()
  {
  int mob;

  for(mob=0;mob<MAX_MOBS;mob++)
     {
     if (!(mobs[mob].vlajky & MOB_LIVE)) continue;
     mobs_live(mob);
     }
  }

int vyber_potvoru(int sect,int dir,int *z)
  {
  TMOB *m1,*m2;
  int ww;
  int m,ch,x;

  if (map_sides[ww=((sect<<2)+dir)].flags & SD_PLAY_IMPS)
     {
     call_macro(ww,MC_WALLATTACK);
     return -1;
     }
  sect=map_sectors[sect].step_next[dir];
  if (!sect) return -1;
  if (!mob_map[sect]) return -1;
  m=mob_map[sect]-MOB_START;
  ch=mobs[m].next;*z=(ch!=0)+1;
  if (!ch) return m;
  m1=mobs+m;
  ch-=MOB_START;
  m2=mobs+ch;
  switch (dir)
     {
     case 2: if (m1->locy<m2->locy) return m;else if (m1->locy>m2->locy) return ch;break;
     case 1: if (m1->locx<m2->locx) return m;else if (m1->locx>m2->locx) return ch;break;
     case 0: if (m1->locy>m2->locy) return m;else if (m1->locy<m2->locy) return ch;break;
     case 3: if (m1->locx>m2->locx) return m;else if (m1->locx<m2->locx) return ch;break;
     }
  x=rnd(2);
  if (x) return ch;
  return m;
  }

static void knock_mob_back(TMOB *mm,int dir)
  {
  char chk;
  int i,sek,mnum,mms;

  if (call_mob_event(mm->specproc,SMPR_KNOCK,mm)) return;
  mms=mm->sector;mnum=mm-mobs+MOB_START;
  chk=mob_check_next_sector(mms,dir,mm->stay_strategy,mm->vlajky);
  if (chk) return;
  sek=map_sectors[mms].step_next[dir];
  i=mob_map[sek];
  if (mob_map[mms]!=mnum) mobs[mob_map[mms]-MOB_START].next=0;else mob_map[mms]=mm->next;
  if (i)
     {
     mm->next=i;
     uhni_mobe(1,i-1,0);
     }
  else mm->next=0;
  mob_map[sek]=mm-mobs+MOB_START;mm->sector=sek;
  recheck_button(mms,1);
  recheck_button(sek,1);
  }

int utok_na_sektor(THUMAN *p,TMOB *mm,int ch,int bonus)
  {
  int dostal;

  dostal=vypocet_zasahu(p->vlastnosti,mm->vlastnosti,ch,bonus,0);
  mob_hit(mm,dostal);
  if (dostal && p->vlastnosti[VLS_KOUZLA] & SPL_KNOCK) knock_mob_back(mm,p->direction);
  if (mm->vlastnosti[VLS_KOUZLA] & SPL_OKO)  //oko za oko pro potvoru
     {
     p->lives-=dostal;
     player_check_death(p,0);
     }
  if (dostal)
     {
     mm->dir=(p->direction+2)&3;
     play_sample_at_sector(H_SND_SWHIT1+rnd(2),viewsector,viewsector,0,0);
     if (p->vlastnosti[VLS_KOUZLA] & SPL_DRAIN)
        {
        p->lives+=dostal*8/(rnd(16)+16);
        if (p->lives>p->vlastnosti[VLS_MAXHIT]) p->lives=p->vlastnosti[VLS_MAXHIT];
        }
     }
  else
     {
     ddostal=0;
     play_sample_at_sector(H_SND_SWMISS1+rnd(2),viewsector,viewsector,0,0);
     }
  mm->vlajky|=MOB_IN_BATTLE;
  neco_v_pohybu=1;
  return ddostal;
  }

void sleep_enemy(char regen)
  {
  int i;

  for(i=0;i<MAX_MOBS;i++)
     {
     TMOB *m=&mobs[i];

     if (m->vlajky & MOB_LIVE && m->lives>0)
        {
        if (regen)
           {
           m->lives+=m->vlastnosti[VLS_HPREG];
           if (m->lives>m->vlastnosti[VLS_MAXHIT]) m->lives=m->vlastnosti[VLS_MAXHIT];
           }
        if (m->stay_strategy & MOB_WALK)
           {
           m->locx=m->headx;
           m->locy=m->heady;
           mob_check(i,m);
           if (m->locx<64 || m->locx>192) m->locx=128;
           if (m->locy<64 || m->locy>192) m->locy=128;
           if (m->locx==m->headx && m->locy==m->heady) rozhodni_o_smeru(m);
           }
        }

     }
  }

static int mob_mob_alter(int num)
  {
  int i;
  num-=MOB_START;
  if (num<0) return 0xff;
  if (mobs[num].dialog>-1 && ~mobs[num].vlajky & MOB_PASSABLE) start_dialog(mobs[num].dialog,num);
  else if (mobs[num].stay_strategy & MOB_WATCH)
     {
     stop_mob(mobs+num);
     if (!battle) battle=1;
     }
  i=mob_mob_alter(mobs[num].next);
  return (mobs[num].vlajky & MOB_PASSABLE) & i;
  }

int mob_alter(int sect)
  {
  char p;
  att_player=0xff;
  p=mob_mob_alter(mob_map[sect]);
/*  if (p)
    {
    int i;THUMAN *h;
    for (i=0;i<POCET_POSTAV;i++)
      {
      h=postavy+group_sort[i];if (h->used && h->lives && h->groupnum==cur_group)
        {att_player=group_sort[i];break;}
      }
    }*/
  return p;
  }

void regen_all_mobs()
  {
  int i;
  TMOB *m;

  for(i=0,m=mobs;i<MAX_MOBS;i++,m++) if (m->vlajky & MOB_LIVE && m->vlastnosti[VLS_HPREG])
    m->lives=m->vlastnosti[VLS_MAXHIT];
  }

