Logo Search packages:      
Sourcecode: abuse-sdl version File versions  Download package

level.cpp

#include "light.hpp"
#include "level.hpp"
#include "game.hpp"
#include "intsect.hpp"
#include "lisp.hpp"
#include "dprint.hpp"
#include "particle.hpp"
#include "objects.hpp"
#include "jrand.hpp"
#include "macs.hpp"
#include "clisp.hpp"
#include "status.hpp"
#include "dev.hpp"
#include "demo.hpp"
#include "pcxread.hpp"
#include "profile.hpp"
#include "sbar.hpp"
#include "cop.hpp"
#include "nfserver.hpp"
#include "lisp_gc.hpp"

#if (defined(__MACH__) || !defined(__APPLE__))
#include <sys/stat.h>
#endif

#include <time.h>

level *current_level;

game_object *level::attacker(game_object *who)
{ 
  long d=0x7fffffff;
  game_object *c=NULL;
  view *f=the_game->first_view;
  for (;f;f=f->next)
  {
    if (f->focus)
    {
      long tmp_d=abs(f->focus->x-who->x)+abs(f->focus->y-who->y);
      if (tmp_d<d)
      {
      d=tmp_d;
      c=f->focus;
      }
    }
  }
  CONDITION(c,"no attacker found");
  return c; 
} 



int level::is_attacker(game_object *who)
{ 
  return who->controller()!=NULL;
}


game_object *level::main_character()
{
  return the_game->first_view->focus;
}

void level::load_fail()
{
  if (map_fg)    jfree(map_fg);   map_fg=NULL;
  if (map_bg)    jfree(map_bg);   map_bg=NULL;
  if (Name)      jfree(Name);     Name=NULL;

  first_active=NULL;
  view *f=player_list;
  for (;f;f=f->next)
    if (f->focus)
      current_level->remove_object(f->focus);

  while (first)
  {
    first_active=first;
    first=first->next;
    if (dev_cont)
      dev_cont->notify_deleted_object(first_active);
    delete first_active;
  }

  while (area_list)
  {
    area_controller *l=area_list;
    area_list=area_list->next;
    delete l;
  }

  last=NULL;
  delete_panims();
  delete_all_lights();

}

level::~level() 
{ 
  load_fail();
  if (attack_list) jfree(attack_list);
  if (target_list) jfree(target_list);
  if (block_list) jfree(block_list);
  if (all_block_list) jfree(all_block_list);
  if (first_name) jfree(first_name);
}

void level::restart()
{
  view *f;
  game_object *found=NULL,*o;
  f=the_game->first_view;
  for (o=first;f && o;o=o->next)
  {
    while (f && !f->focus) f=f->next;
    if (f)
    {
      if (!strcmp(object_names[o->otype],"START"))
      {
      if (!found) found=o;
      f->focus->x=o->x;
      f->focus->y=o->y;          
      f->focus->set_hp(get_ability(f->focus->otype,start_hp));
      f->focus->set_state(stopped);
      f=f->next;
      }
    }
  }
  while (f)
  {
    if (f->focus)
    {
      f->focus->x=found->x;
      f->focus->y=found->y;          
      f->focus->set_hp(get_ability(f->focus->otype,start_hp));
      f->focus->set_state(stopped);      
    }
    f=f->next;
  }
}


void level::next_focus()
{
/*  int i;
  for (i=0;i<total_objs;i++)
    if (obj[i]==the_game->first_view->focus)
    {      
      int tries=total_objs;
      do
      {
      i++;
      if (i==total_objs)
        i=0;
      the_game->first_view->focus=obj[i];
      }  while ((!the_game->first_view->focus->is_playable() || 
             the_game->first_view->focus->hp<=0) && tries--);
      return ;
    }             */  
}

void level::unactivate_all()
{
  first_active=NULL;
  game_object *o=first;
  attack_total=0;  // reset the attack list
  target_total=0;
  block_total=0;
  all_block_total=0;

  for (;o;o=o->next)
    o->active=0;
}


void level::pull_actives(game_object *o, game_object *&last_active, int &t)
{
  int i=o->total_objects();
  for (;i;i--)        // pull any linked object into active list
  {
    game_object *other=o->get_object(i-1);
    if (!other->active)
    {
      other->active=1;
      if (other->can_block())              // if object can block other player, keep a list for fast testing
      {
      add_block(other);
      add_all_block(other);
      } else if (other->hurtable())
        add_all_block(other);

      t++;
      last_active->next_active=other;
      last_active=other;      
      pull_actives(o,last_active,t);
    }
  }
}

int level::add_actives(long x1, long y1, long x2, long y2)
{
  int t=0;
  game_object *last_active=NULL;
  if (first_active)
    for (last_active=first_active;last_active->next_active;last_active=last_active->next_active);

  game_object *o=first;
  for (;o;o=o->next)
  {    
    if (!o->active)
    {
      long xr=figures[o->otype]->rangex,
           yr=figures[o->otype]->rangey;

      if (o->x+xr>=x1 && o->x-xr<=x2 && o->y+yr>=y1 && o->y-yr<=y2)
      {
      
      if (o->can_block())              // if object can block other player, keep a list for fast testing
      {
        add_block(o);
        add_all_block(o);
      } else if (o->hurtable())
          add_all_block(o);
      

      o->active=1;
      t++;
      if (!first_active)      
        first_active=o; 
      else
          last_active->next_active=o;
      last_active=o;

      pull_actives(o,last_active,t);      
      }  
    }  
  }
  if (last_active)
    last_active->next_active=NULL;
  return t;
}


int level::add_drawables(long x1, long y1, long x2, long y2)
{
  int t=0,ft=0;
  game_object *last_active=NULL;
  if (first_active)
  {
    for (last_active=first_active;last_active->next_active;last_active=last_active->next_active);
  } else ft=1;

  game_object *o=first;
  for (;o;o=o->next)
  {    
    if (ft || !o->active)
    {
      long xr=figures[o->otype]->draw_rangex,
      yr=figures[o->otype]->draw_rangey;

      if (o->x+xr>=x1 && o->x-xr<=x2 && o->y+yr>=y1 && o->y-yr<=y2)
      {
      t++;
      if (!first_active)      
      first_active=o;   
      else
      last_active->next_active=o;
      last_active=o;
      o->active=1;
      } else if (ft) o->active=0;  // if this is the first pass, then mark objects not in this ranges as not active
    }
  }
  if (last_active)
    last_active->next_active=NULL;
  return t;
}


view *level::make_view_list(int nplayers)
{
  int startable;
  CONDITION(nplayers>0,"make_view_list with <=0 players!\n");
  view *f=NULL;
  int j,use_type=current_start_type;
  figures[use_type]->cache_in();
  game_object *o,*last_start=NULL;
  int num=0;
  
  for (j=0,o=first;o && j<nplayers;o=o->next)
  {
    if (!strcmp(object_names[o->otype],"START"))
    {
      f=new view(create(use_type,o->x,o->y),f,num); num++;
      f->focus->set_controller(f);
      add_object_after(f->focus,o);
      j++;
      last_start=o;
    }
  }
 
  // if we couldn't find enough starts then create the rest of the players at the original start
  startable=j;  // if we haven't created anyone yet, it's because we can't

  for (;j<nplayers;j++)
  {
    if (startable)
    {
      game_object *o=create(use_type,f->focus->x,f->focus->y);
      f=new view(o,f,num); num++;
      f->focus->set_controller(f);
      add_object_after(o,last_start);
    }
    else 
    {
      f=new view(NULL,f,num); 
      num++;
    }
  }
  return f;
}

void level::wall_push()
{
  long sx1,sy1,sx2,sy2,xv,yv;
  game_object *o=first_active;
  for (;o;o=o->next_active)
  {
    if (o->pushable())
    {
      o->picture_space(sx1,sy1,sx2,sy2);
      xv=sx1-o->x;
      yv=0;
      o->try_move(o->x,o->y-1,xv,yv,1);         // check for wall pushes on the left using feet
      if (xv!=sx1-o->x)                         // is the character in the wall?
      {
      xv=-xv;
      o->try_move(o->x,o->y-1,xv,yv,1);       // see how far to the right we can push the character
      o->x+=xv;
      } else
      {
      xv=sx2-o->x;
      o->try_move(o->x,o->y-1,xv,yv,1);      // now check the right of the character for a wall
      if (xv!=sx2-o->x)
      {
        xv=-xv;
        o->try_move(o->x,o->y-1,xv,yv,1);
        o->x+=xv;
      }
      }           
    }
  }
}


void level::try_pushback(game_object *subject,game_object *target)
{
  if (subject->pushable() && target->pushable() && 
      subject->state!=dead && target->state!=dead &&
      subject->state!=dieing && target->state!=dieing)
  {
    int b1=subject->push_range(),b2=target->push_range();
    if (abs(subject->x-target->x)<b1+b2)
    {
      long tmove=b1+b2-abs(subject->x-target->x),xv,yv=0,xv2;
      if (subject->x>target->x)
        xv=tmove/2;
      else xv=-tmove/2;
      xv2=-xv;

      subject->try_move(subject->x,subject->y,xv,yv,3);
      subject->x+=xv;

      yv=0;
      target->try_move(target->x,target->y,xv2,yv,3);
      target->x+=xv2;     
    }
  }
}

/*
void level::check_collisions()
{
  game_object *target,*reciever=NULL;
  long sx1,sy1,sx2,sy2,tx1,ty1,tx2,ty2,hitx,hity,
      s_centerx,t_centerx;

  for (game_object *subject=first_active;subject;subject=subject->next_active)
  {
    subject->picture_space(sx1,sy1,sx2,sy2);   
    s_centerx=subject->x_center();

    int hit=0;
    reciever=NULL;
    for (target=first_active;target;target=target->next_active)
    {
      if (target!=subject)
      {
      target->picture_space(tx1,ty1,tx2,ty2);

        if (!(sx2<tx1 || sx1>tx2 || sy1>ty2 || sy2<ty1))  // are they semi/overlapping?
        {    
        try_pushback(subject,target);
        if (subject->can_hurt(target))    // see if we can hurt him before calculating
        {       
          t_centerx=target->x_center();
          point_list *s_hit,*t_damage;
                    
          s_hit=subject->current_figure()->hit;   
          t_damage=target->current_figure()->damage;

          unsigned char *s_dat=s_hit->data,
          *t_dat;
          int i,j;
          for (i=(int)s_hit->tot-1;i>0 && !hit;i--)
          {
            for (t_dat=t_damage->data,j=(int)t_damage->tot-1;j>0 && !hit;j--)
            {
            long x1,y1,x2,y2,          // define the two line segments to check
            xp1,yp1,xp2,yp2;

            xp1=target->x+target->tx(*t_dat);  t_dat++;           
            yp1=target->y+target->ty(*t_dat);  t_dat++;
            xp2=target->x+target->tx(*t_dat); 
            yp2=target->y+target->ty(t_dat[1]); 

            x1=subject->x+subject->tx(s_dat[0]);  
            y1=subject->y+subject->ty(s_dat[1]); 
            x2=subject->x+subject->tx(s_dat[2]); 
            y2=subject->y+subject->ty(s_dat[3]); 
            

            // ok, now we know which line segemnts to check for intersection
            // now check to see if (x1,y1-x2,y2) intercest with (xp1,yp1-xp2,yp2)
            int _x2=x2,_y2=y2;            
            setback_intersect(x1, y1, x2, y2, xp1, yp1, xp2, yp2,0);


            if (x2!=_x2 || _y2!=y2)
            {
              reciever=target;
              hitx=((x1+x2)/2+(xp1+xp2)/2)/2;
              hity=((y1+y1)/2+(yp1+yp2)/2)/2;
            }
            } 
            s_dat+=2;
          }     
        }
      }
      }  
    }    
    if (reciever)
    {
      reciever->do_damage((int)subject->current_figure()->hit_damage,subject,hitx,hity,0,0);
      subject->note_attack(reciever);
      hit=1;
    }
  }
}
*/

game_object *level::boundary_setback(game_object *subject, long x1, long y1, long &x2, long &y2)
{
  game_object *l=NULL;
  long tx1,ty1,tx2,ty2,t_centerx;
  game_object *target=first_active;
  game_object **blist=block_list;
  int t=block_total;
  for (;t;t--,blist++)
  { 
    target=*blist;
    if (target!=subject && (target->total_objects()==0 || target->get_object(0)!=subject))
    {
      target->picture_space(tx1,ty1,tx2,ty2);
      if (!((x2<tx1 && x1<tx1) || (x1>tx2 && x2>tx2) ||
          (y1>ty2 && y2>ty2) || (y1<ty1 && y2<ty1)))  // are they semi/overlapping?
      {         
      t_centerx=target->x_center();
      boundary *t_damage;
      if (target->direction>0)
      t_damage=target->current_figure()->f_damage;
      else
      t_damage=target->current_figure()->b_damage;
      unsigned char *t_dat=t_damage->data,*ins=t_damage->inside;
      int iter=t_damage->tot-1;
      while(iter-->0)
      {
        long xp1=target->x+target->tx(*t_dat);  t_dat++;          
        long yp1=target->y+target->ty(*t_dat);  t_dat++;
        long xp2=target->x+target->tx(*t_dat); 
        long yp2=target->y+target->ty(t_dat[1]); 

        // now check to see if (x1,y1-x2,y2) intercest with (xp1,yp1-xp2,yp2)
        if (*ins)
        {                 
          if (setback_intersect(x1,y1,x2,y2,xp1,yp1,xp2,yp2,1))
          l=target;
        }       
        else
        {
          if (setback_intersect(x1,y1,x2,y2,xp1,yp1,xp2,yp2,-1))
          l=target;
        }       
        ins++;
        
      }       
      }      
    }   
  }
  return l;       // return the last person we intersected  
}


game_object *level::all_boundary_setback(game_object *subject, long x1, long y1, long &x2, long &y2)
{
  game_object *l=NULL;
  long tx1,ty1,tx2,ty2,t_centerx;
  game_object *target=first_active;
  game_object **blist=all_block_list;
  int t=all_block_total;
  for (;t;t--,blist++)
  { 
    target=*blist;
    if (target!=subject && (target->total_objects()==0 || target->get_object(0)!=subject))
    {
      target->picture_space(tx1,ty1,tx2,ty2);
      if (!((x2<tx1 && x1<tx1) || (x1>tx2 && x2>tx2) ||
          (y1>ty2 && y2>ty2) || (y1<ty1 && y2<ty1)))  // are they semi/overlapping?
      {         
      t_centerx=target->x_center();
      boundary *t_damage;
      if (target->direction>0)
      t_damage=target->current_figure()->f_damage;
      else
      t_damage=target->current_figure()->b_damage;
      unsigned char *t_dat=t_damage->data,*ins=t_damage->inside;
      int iter=t_damage->tot-1;
      while(iter-->0)
      {
        long xp1=target->x+target->tx(*t_dat);  t_dat++;          
        long yp1=target->y+target->ty(*t_dat);  t_dat++;
        long xp2=target->x+target->tx(*t_dat); 
        long yp2=target->y+target->ty(t_dat[1]); 

        // now check to see if (x1,y1-x2,y2) intercest with (xp1,yp1-xp2,yp2)
        if (*ins)
        {                 
          if (setback_intersect(x1,y1,x2,y2,xp1,yp1,xp2,yp2,1))
          l=target;
        }       
        else
        {
          if (setback_intersect(x1,y1,x2,y2,xp1,yp1,xp2,yp2,-1))
          l=target;
        }       
        ins++;      
      }       
      }      
    }   
  }
  return l;       // return the last person we intersected  
}


//bFILE *rcheck=NULL,*rcheck_lp=NULL;

void level::interpolate_draw_objects(view *v)
{
  long old_x,old_y;
  current_view=v;
  
  game_object *o=first_active;
  for (;o;o=o->next_active)
  {
    old_x=o->x;
    old_y=o->y;
    o->x=(o->last_x+o->x)/2;
    o->y=(o->last_y+o->y)/2;
    o->last_x=old_x;
    o->last_y=old_y;
  }

  for (o=first_active;o;o=o->next_active)
    o->draw();

  for (o=first_active;o;o=o->next_active)
  {
    o->x=o->last_x;
    o->y=o->last_y;
  }
}

bFILE *rcheck=NULL,*rcheck_lp=NULL;

extern int sshot_fcount,screen_shot_on;

int level::tick()
{
  game_object *o,*l=NULL,  // l is last, used for delete
              *cur;        // cur is current object, NULL if object deletes it's self
  int ret=1;

  if (profiling())
    profile_reset();

/*  // test to see if demo is in sync
  if (current_demo_mode()==DEMO_PLAY)
  {
    if (!rcheck) rcheck=open_file("rcheck","rb");
    long x=rcheck->read_long();
    if (x!=rand_on)
      dprintf("off!\n");
  } else if (current_demo_mode()==DEMO_RECORD)
  {
    if (!rcheck) 
    {
      rcheck=open_file("rcheck","wb");
      rcheck_lp=open_file("rcheck.lp","wb");
    }
    rcheck->write_long(rand_on);
  } else
  {
    if (rcheck)
    {
      delete rcheck;
      rcheck=NULL;
    }
    if (rcheck_lp)
    {
      delete rcheck_lp;
      rcheck_lp=NULL;
    }
  }*/
  
  for (o=first_active;o;)
  {       
    o->last_x=o->x;
    o->last_y=o->y;
    cur=o;
    view *c=o->controller();
    if (!(dev&SUSPEND_MODE) || c)
    {
      o->set_flags(o->flags()&(0xff-FLAG_JUST_HIT-FLAG_JUST_BLOCKED));

      if (c)
      {      
      area_controller *a,*smallest=NULL;
      long smallest_size=0xfffffff;
      for (a=area_list;a;a=a->next)
        if (o->x>=a->x && o->y>=a->y && o->x<=a->x+a->w && o->y<=a->y+a->h)
        {
          long size=a->w*a->h;
          if (size<smallest_size)
          {
            smallest=a;
            smallest_size=size;
          }
        }

      if (c->local_player())
      {
        if (!shutdown_lighting)       // should we initiate a lighting shutdown?
        {
          if (massive_frame_panic>30)
          {
            shutdown_lighting=100;
            shutdown_lighting_value=c->ambient;
          }
        } else if (massive_frame_panic)  // do we need brighten towards 63?
        {
          if (shutdown_lighting_value<63)
            shutdown_lighting_value++;
        } else if (shutdown_lighting>1)        // delay for some time before turning back on
          shutdown_lighting--;
        else if (shutdown_lighting_value!=c->ambient) // do we need to lower light toward real ambient?
        {
          if (abs(shutdown_lighting_value-c->ambient)<4)
            shutdown_lighting_value=c->ambient;
          else
            if (shutdown_lighting_value<c->ambient)
              shutdown_lighting_value+=4;
          else if (shutdown_lighting_value>c->ambient)
            shutdown_lighting_value-=4;
        } else shutdown_lighting=0;                    // back to normal
      }

      if (smallest)
        c->configure_for_area(smallest);


      o->move(c->x_suggestion,c->y_suggestion,c->b1_suggestion|(c->b2_suggestion<<1)|
            (c->b3_suggestion<<2));

      if (o->otype!=current_start_type)
      {
        long fmp=o->fmp();      
        int reduce=figures[o->otype]->morph_power;
        if (reduce)
        {
          fmp-=reduce;
          o->add_power(fmp>>16);
          o->set_fmp(fmp&0xffff);
          if (o->mp()<=0)
          o->morph_into(current_start_type,NULL,-1,9);
        }
      }

      l=o;
      o=o->next_active;       
      }
      else if (!o->decide())      // if object returns 0, delete it... I don't like 0's :)
      {
      game_object *p=o;
      o=o->next_active;
      delete_object(p);
      cur=NULL;
      } else 
      {
      o=o->next_active;
      l=o;
      }
    } else
    {
      o=o->next_active;
      l=o;      
    }

    clear_tmp();

    if (cur)
    {
      point_list *p=cur->current_figure()->hit;  // see if this character is on an attack frame
      if (p && p->tot)
        add_attacker(cur);               // if so add him to attack list for later collision detect
      
      if (cur->hurtable())                    // add to target list if is hurtable
        add_target(cur);
      
    }

  }
  tick_panims();

  check_collisions();
//  wall_push();

  set_tick_counter(tick_counter()+1);

  if (sshot_fcount!=-1)
  {
    sshot_fcount++;
    if ((sshot_fcount%70)==0)
    {    
      char name[100];
      sprintf(name,"shot%04d.pcx",screen_shot_on++);
      write_PCX(screen,pal,name);
    }
  }

  return ret;
}

void level::set_tick_counter(ulong x) 
{ 
  ctick=x; 
}

void level::draw_areas(view *v)
{
  long sx1,sy1,sx2,sy2;
  area_controller *a=area_list;
  for (;a;a=a->next)
  {
    int c1,c2;
    if (a->active)
    {
      c1=morph_sel_frame_color;
      c2=eh->bright_color();
    } else 
    {
      c2=morph_sel_frame_color;
      c1=eh->bright_color();
    }
    
    the_game->game_to_mouse(a->x,a->y,v,sx1,sy1);
    the_game->game_to_mouse(a->x+a->w,a->y+a->h,v,sx2,sy2);
    screen->rectangle(sx1,sy1,sx2,sy2,c1);
    screen->bar(sx1-1,sy1-1,sx1+1,sy1+1,c2);
    screen->bar(sx2-1,sy2-1,sx2+1,sy2+1,c2);
  }
}

void level::draw_objects(view *v)
{
  current_view=v;
  game_object *o=first_active;
  if (dev&MAP_MODE)
  {
    for (;o;o=o->next_active)
      o->map_draw();
  } else
  {
    for (;o;o=o->next_active)
      o->draw();
  }

  clear_tmp();
}  

void calc_bgsize(unsigned short fgw, unsigned short  fgh, unsigned short  &bgw, unsigned short  &bgh)
{
  bgw=fgw/ASPECT+8;
  bgh=fgh/ASPECT+8;  
}


void level::set_size(int w, int h) 
{
  if (w*h>200000)
  {
    the_game->show_help(symbol_str("too_big"));
    return ; 
  }

  unsigned short *new_fg,*new_bg;
  new_fg=(unsigned short *)jmalloc(w*h*sizeof(short),"Map fg : resized");
  memset(new_fg,0,w*h*sizeof(short));

  int x,y,miny=(h<fg_height)? h : fg_height,minx=(w<fg_width)? w : fg_width;
  
  unsigned short nbw,nbh;
  calc_bgsize(w,h,nbw,nbh);
  
  new_bg=(unsigned short *)jmalloc((int)nbw*(int)nbh*sizeof(short),"map bg : resized");
  memset(new_bg,0,(int)nbw*(int)nbh*sizeof(short));

  for (y=0;y<miny;y++)
    for (x=0;x<minx;x++)
      new_fg[x+y*w]=get_fg(x,y);

  miny=(nbh<bg_height) ? nbh : bg_height;
  minx=(nbw<bg_width) ? nbw : bg_width;

  for (y=0;y<miny;y++)
    for (x=0;x<minx;x++)
      new_bg[x+y*nbw]=get_bg(x,y);

  jfree(map_fg);
  jfree(map_bg);
  map_fg=new_fg;
  map_bg=new_bg;
  fg_width=w;
  fg_height=h;
  bg_height=nbh;
  bg_width=nbw; 

  char msg[80];
  sprintf(msg,"Level %s size now %d %d\n",name(),foreground_width(),foreground_height());
  the_game->show_help(msg);
}


int locate_var(bFILE *fp, spec_directory *sd, char *str, int size)
{
  spec_entry *se=sd->find(str);
  if (se)
  {
    fp->seek(se->offset,0);
    if (RC_type_size(fp->read_byte())!=size)
      return 0;
    else return 1;
  } 
  return 0;
}


// load objects assumes current objects have already been disposed of
void level::old_load_objects(spec_directory *sd, bFILE *fp)
{
  spec_entry *se=sd->find("objects");
  total_objs=0;
  first=last=first_active=NULL;
  int i,j;
  if (se)
  {
    fp->seek(se->offset,0);
    /******************************* Read debug info ******************************/
    short old_tot=fp->read_short();  
    unsigned short *o_remap=(unsigned short *)jmalloc(old_tot*2,"obj remap array");    
    char old_name[150];      
    for (i=0;i<old_tot;i++)
    {
      fp->read(old_name,fp->read_byte());    // read the name
      for (o_remap[i]=0xffff,j=0;j<total_objects;j++)  // check for matching current name
      {
      if (!strcmp(old_name,object_names[j]))
          o_remap[i]=j;
      }
    }


    /***************************** Read state names *********************************/
    int old_stot=fp->read_short();
    unsigned char *s_remap=(unsigned char *)jmalloc(old_stot,"state remap array");
    for (i=0;i<old_stot;i++)
    {
      fp->read(old_name,fp->read_byte());
      s_remap[i]=stopped;           // non exsitant states get mapped into stopped state
      for (j=0;j<MAX_STATE;j++)                  // see if old state exist now
      if (!strcmp(state_names[j],old_name))
         s_remap[i]=j;
    }
    total_objs=fp->read_long();    

    se=sd->find("type");
    if (se)
    {
      fp->seek(se->offset,0);
      last=NULL;
      if (fp->read_byte()==RC_S)    //  read type array, this should be type RC_S
      {
      for (i=0;i<total_objs;i++)
      {
        ushort t=fp->read_short();
        game_object *p=new game_object(o_remap[t],1);
        clear_tmp();
        if (!first) first=p; else last->next=p;
        last=p; p->next=NULL;         
      }

       
      se=sd->find("state");
      if (se)
      {
        fp->seek(se->offset,0);
        if (fp->read_byte()==RC_S)    //  read state array, this should be type RC_S
        {
          game_object *l=first;
          for (i=0;i<total_objs;i++,l=l->next)
          {
            character_state s=(character_state)s_remap[fp->read_short()];
            if (l->otype!=0xffff)
            {
            if (l->has_sequence((character_state)s))
              l->state=s;
            else l->state=stopped;
            l->current_frame=0;
            }
          }
        }
      }

      int frame_var=0;
      int i=0;
      for (;i<TOTAL_OBJECT_VARS;i++)
        if (!strcmp(object_descriptions[i].name,"cur_frame"))
          frame_var=i;
          
      int j=0;
      for (;j<default_simple.total_vars();j++)
      {
        spec_entry *se=sd->find(object_descriptions[j].name);
        if (se)
        {
          fp->seek(se->offset,0);
          int t=object_descriptions[j].type;
          if (fp->read_byte()!=t)
            dprintf("Warning : load level -> var '%s' size changed\n");
          else
          {
            game_object *f=first;
            for (;f;f=f->next)
            {            
            switch (t)
            {
              case RC_C : f->set_var(j,fp->read_byte()); break;
              case RC_S : f->set_var(j,fp->read_short()); break;
              case RC_L : f->set_var(j,fp->read_long()); break;
            }

            // check to make sure the frame number is not out of bounds from the time
            // it was last saved
            if (j==frame_var) 
            {
              if (f->otype!=0xffff && f->current_frame>=
                  figures[f->otype]->get_sequence(f->state)->length())
                f->current_frame=0;
            }
            } 
          }     
        } else dprintf("Warning : load level -> no previous var %s\n",default_simple.var_name(j));
      }
      }
    }
    
    

    jfree(o_remap);
    jfree(s_remap);
  }  
  
}


// load objects assumes current objects have already been disposed of
void level::load_objects(spec_directory *sd, bFILE *fp)
{
  spec_entry *se=sd->find("object_descripitions"); 
  total_objs=0;
  first=last=first_active=NULL;
  int i,j;
  if (!se)
  {
    old_load_objects(sd,fp);
    return ;
  }
  else if (se)
  {
    fp->seek(se->offset,0);
    short old_tot=fp->read_short();
    se=sd->find("describe_names");
    if (!se || !old_tot)
      return ;

    unsigned short *o_remap=(unsigned short *)jmalloc(old_tot*2,"obj remap array");    
    unsigned short *o_backmap=(unsigned short *)jmalloc(total_objects*2,"obj remap array");    
    memset(o_backmap,0xff,total_objects*2);
    char old_name[150];      
    for (i=0;i<old_tot;i++)
    {
      fp->read(old_name,fp->read_byte());    // read the name
      for (o_remap[i]=0xffff,j=0;j<total_objects;j++)  // check for matching current name
      {
      if (!strcmp(old_name,object_names[j]))
      {
          o_remap[i]=j;
        o_backmap[j]=i;
      }
      }
    }    

    se=sd->find("describe_states");
    if (!se) { jfree(o_remap); jfree(o_backmap); return ; }
    short **s_remap=(short **)jmalloc(old_tot*sizeof(short *),"big state remap array");
    short *s_remap_totals=(short *)jmalloc(old_tot*sizeof(short),"big state rmp totals");
    fp->seek(se->offset,0);
    int i=0;
    for (;i<old_tot;i++)
    {
      short t=fp->read_short();
      s_remap_totals[i]=t;
      if (t)
      {
        s_remap[i]=(short *)jmalloc(t*sizeof(short),"state remap");
      int j=0;
      for (;j<t;j++)
        *(s_remap[i]+j)=stopped;    // if no remap found, then go to stopped state
      }
      else s_remap[i]=0;

      int j=0;
      for (;j<t;j++)
      {
      fp->read(old_name,fp->read_byte());
      int new_type=o_remap[i];      
      if (new_type<total_objects)     // make sure old object still exsist
      {
        int k=0;
        for (;k<figures[new_type]->ts;k++)
        {
          if (figures[new_type]->seq[k] &&
             !strcmp(lstring_value(symbol_name(figures[new_type]->seq_syms[k])),old_name))
          *(s_remap[i]+j)=k;
        }
      }
      }
    }

    short **v_remap=NULL;
    short *v_remap_totals=NULL;
    int load_vars=1;
    se=sd->find("describe_lvars");
    if (se)
    {
      v_remap=(short **)jmalloc(old_tot*sizeof(short *),"big var remap array");
      v_remap_totals=(short *)jmalloc(old_tot*sizeof(short),"big var rmp totals");

      fp->seek(se->offset,0);
      int i=0;
      for (;i<old_tot;i++)
      {
      short t=fp->read_short();
      v_remap_totals[i]=t;
      if (t)
      {
        v_remap[i]=(short *)jmalloc(t*sizeof(short),"var remap");
        memset(v_remap[i],0xff,t*sizeof(short));
      } else { v_remap[i]=NULL; }
      int j=0;
      for (;j<t;j++)
      {
        fp->read(old_name,fp->read_byte());
        int new_type=o_remap[i];
        if (new_type!=0xffff)        // make sure old object still exsist
        {
          int k=0;
          for (;k<figures[new_type]->tiv;k++)
          {
            if (figures[new_type]->vars[k])
            {
            if (!strcmp(lstring_value(symbol_name(figures[new_type]->vars[k])),old_name))
              *(v_remap[i]+j)=figures[new_type]->var_index[k];
            }
          }
        }
      }
      }
      load_vars=1;
    }
    
    se=sd->find("object_list");
    if (se)
    {
      total_objs=fp->read_long();    

      se=sd->find("type");
      if (se)
      {
      fp->seek(se->offset,0);
      last=NULL;
      if (fp->read_byte()==RC_S)    //  read type array, this should be type RC_S
      {
        int i=0;
        for (;i<total_objs;i++)
        {
          ushort t=fp->read_short();
          game_object *p=new game_object(o_remap[t],1);
          clear_tmp();
          if (!first) first=p; else last->next=p;
          last=p; p->next=NULL;             
        }
       
        se=sd->find("state");
        if (se)
        {
          fp->seek(se->offset,0);
          if (fp->read_byte()==RC_S)    //  read state array, this should be type RC_S
          {
            game_object *l=first;
            for (i=0;i<total_objs;i++,l=l->next)
            {
            int st=fp->read_short();
            if (l->otype==0xffff)
              l->state=stopped;
            else
            {
              character_state s=(character_state)(*(s_remap[o_backmap[l->otype]]+st));
              if (l->has_sequence((character_state)s))
                  l->state=s;
              else l->state=stopped;
              l->current_frame=0;
            }
            }
          }
        }
      
        se=sd->find("lvars");
        if (se && load_vars)
        {
          fp->seek(se->offset,0);
          int abort=0;
          game_object *o=first;
          for (;o && !abort;o=o->next)
          {
            short ot=fp->read_short();
            int k=0;
            for (;k<ot;k++)
            {
            if (fp->read_byte()!=RC_L) abort=1;
            else
            {
              long v=fp->read_long();
              if (o->otype!=0xffff)     // non-exstant object
              {
                int remap=*(v_remap[o_backmap[o->otype]]+k);
                if (remap!=-1 && figures[o->otype]->tiv>=k)
                {
                  o->lvars[remap]=v;       
                }
              }
            }
            }
          }
        }

        int frame_var=0;
        for (i=0;i<TOTAL_OBJECT_VARS;i++)
          if (!strcmp(object_descriptions[i].name,"cur_frame"))
            frame_var=i;
          

        int j=0;
        for (;j<default_simple.total_vars();j++)
        {
          spec_entry *se=sd->find(object_descriptions[j].name);
          if (se)
          {
            fp->seek(se->offset,0);
            int t=object_descriptions[j].type;
            if (fp->read_byte()!=t)
              dprintf("Warning : load level -> var '%s' size changed\n");
            else
            {
            game_object *f=first;
            for (;f;f=f->next)
            {            
              switch (t)
              {
                case RC_C : 
                { f->set_var(j,fp->read_byte()); } break;
                case RC_S : 
                { f->set_var(j,fp->read_short()); } break;
                case RC_L : 
                { f->set_var(j,fp->read_long()); } break;
              }
              
              // check to make sure the frame number is not out of bounds from the time
              // it was last saved
              if (j==frame_var) 
              {
                if (f->otype!=0xffff && f->current_frame>=
                  figures[f->otype]->get_sequence(f->state)->length())
                f->current_frame=0;
              }
            } 
            }         
          } else dprintf("Warning : load level -> no previous var %s\n",default_simple.var_name(j));
        }
      }
      }
    }
    
    int k=0;
    for (;k<old_tot;k++)
    {
      if (s_remap_totals[k])
        jfree(s_remap[k]);
    }

    int l=0;
    for (;l<old_tot;l++)
    {
      if (v_remap_totals[l])
        jfree(v_remap[l]);
    }
    jfree(v_remap_totals);
    jfree(s_remap_totals);
    jfree(o_remap);
    jfree(o_backmap);
    jfree(s_remap);
    jfree(v_remap);
  }  
  
}

level::level(spec_directory *sd, bFILE *fp, char *lev_name)
{
  spec_entry *e;
  area_list=NULL;

  attack_list=NULL;
  attack_list_size=attack_total=0;

  target_list=NULL;
  target_list_size=target_total=0;

  block_list=NULL;
  block_list_size=block_total=0;

  all_block_list=NULL;
  all_block_list_size=all_block_total=0;
  first_name=NULL;

  the_game->need_refresh();

  char cmd[100];
  sprintf(cmd,symbol_str("loading"),lev_name);
  stack_stat stat(cmd);
  Name=strcpy((char *)jmalloc(strlen(lev_name)+1,"lev name"),lev_name);

  e=sd->find("first name");
  if (e)
  {
    fp->seek(e->offset,0);
    int len=fp->read_byte();   // read the length of the string
    first_name=(char *)jmalloc(len,"level first name");
    fp->read(first_name,len);    // read the string
  } else
  {
    first_name=(char *)jmalloc(strlen(Name)+1,"level first name");    
    strcpy(first_name,Name);
  }

  e=sd->find("fgmap");
  int no_fg=0,no_bg=0;

  if (e)
  {    
    fp->seek(e->offset,0);
    fg_width=fp->read_long();
    fg_height=fp->read_long();
    map_fg=(unsigned short *)jmalloc(2*fg_width*fg_height,"Map fg : loaded");
    fp->read((char *)map_fg,2*fg_width*fg_height);
    int t=fg_width*fg_height;
    unsigned short *map=map_fg;
    while (t) { *map=lstl(*map); map++; t--; }
  } else
  {
    the_game->show_help("Warning foreground map missing");
    no_fg=1;
  }
  stat_man->update(5);

  e=sd->find("bgmap");
  if (e)
  {
    fp->seek(e->offset,0);
    bg_width=fp->read_long();
    bg_height=fp->read_long();
    map_bg=(unsigned short *)jmalloc(2*bg_width*bg_height,"Map bg : loaded");
    fp->read((char *)map_bg,2*bg_width*bg_height);
    int t=bg_width*bg_height;
    unsigned short *map=map_bg;
    while (t) { *map=lstl(*map); map++; t--; } 
  } else
  {
    the_game->show_help("Warning background map missing");
    no_bg=1;
  }

  if (no_fg && !no_bg)
  {
    fg_width=bg_width;
    fg_height=bg_height;
    map_fg=(unsigned short *)jmalloc(2*fg_width*fg_height,"Map fg : loaded");
    memset(map_fg,0,2*fg_width*fg_height);
  }

  if (no_bg)
  {
    bg_width=fg_width/8+8;
    bg_height=fg_height/8+8;
    map_bg=(unsigned short *)jmalloc(2*bg_width*bg_height,"Map bg : loaded");
    memset(map_bg,0,2*bg_width*bg_height);
  }
  stat_man->update(10);

  /***************** Check map for non exsistant tiles **************************/
  long i,w;
  unsigned short *m;  
  spec_entry *load_all=sd->find("player_info");
  for (i=0,w=fg_width*fg_height,m=map_fg;i<w;i++,m++)
  {
    if (!load_all)
      (*m)=(*m)&(~0x8000);    // clear the has-seen bit on the tile

    if (fgvalue(*m)>=nforetiles || foretiles[fgvalue(*m)]<0) 
      *m=0;  
  }

  for (i=0,w=bg_width*bg_height,m=map_bg;i<w;i++,m++)
  {
    if ( (bgvalue(*m)>=nbacktiles) || backtiles[bgvalue(*m)]<0) 
       *m=0;  
  }

  load_options(sd,fp);
  stat_man->update(15);

//  first=first_active=last=NULL;
  load_objects(sd,fp);
  stat_man->update(25);

  object_node *players,*objs;
  players=make_player_onodes();
  objs=make_not_list(players); 



  read_lights(sd,fp,lev_name);
  load_links(fp,sd,objs,players);
  int players_got_loaded=load_player_info(fp,sd,objs);


  game_object *l=first;
  for (;l;)
  {
    game_object *p=l;
    l=l->next;
    if (p->otype==0xffff || p->x<0 || p->y<0)
      delete_object(p);
  }

  load_cache_info(sd,fp);

  if (!players_got_loaded)
  {
    level *old=current_level;
    current_level=this;
   
    object_node *list=NULL;
    list=make_not_list(list);     // create a list of the object list in case objects change positions

    object_node *ln=list;
    for (;ln;ln=ln->next)
      ln->me->reload_notify();
    delete_object_list(list);

    current_level=old;

    insert_players();
  }

  delete_object_list(players);
  delete_object_list(objs);

}


/*
   [object_descriptions] 2 total_type
   for(1..total_types)
   {
     ["object_names"]  1,(name)

     ["object_states"]  2(total),<2=number,1,name>

     ["object_lvars"]   2(total),<1(type),1,name>   
   }

  [object_list]
   4 total_objects    
   for(1..total_objects)
   {
     ["type"]
     ["state"]
     ["lvars"]
     ...
   }

*/


void get_prof_assoc_filename(char *filename, char *prof_filename)
{
  char *s1,*s2,*dot=NULL;
  for (s1=filename,s2=prof_filename,dot=NULL;*s1;s1++,s2++)
  {
    *s2=*s1;
    if (*s1=='.') dot=s2;    
  }
  if (dot) s2=dot+1;

  *(s2++)='c'; 
  *(s2++)='p'; 
  *(s2++)='f'; 
  *s2=0;
}

void level::level_loaded_notify()
{
  char *n;
  if (first_name)
    n=first_name;
  else
    n=name();
  if (strstr(n,"levels/level"))
  {
    char nm[100];
    sprintf(nm,"music/abuse%c%c.hmi",n[12],n[13]);
    bFILE *fp=open_file(nm,"rb");
    if (fp->open_failure())
    {
      delete fp;
    }
    else
    {
      if (current_song) { current_song->stop(); delete current_song; }

      delete fp;
      current_song=new song(nm);
      current_song->play(music_volume);
    }
  }
  
/*  if (DEFINEDP(symbol_function(l_level_loaded)))
  {
    int sp=current_space;
    current_space=PERM_SPACE;

    void *arg_list=NULL;
    p_ref r1(arg_list);
    push_onto_list(new_lisp_string(n),arg_list);
    eval_function((lisp_symbol *)l_level_loaded,arg_list);

    current_space=sp;
  } */
}


bFILE *level::create_dir(char *filename, int save_all,
                   object_node *save_list, object_node *exclude_list)
{
  spec_directory sd;
  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"Copyright 1995 Crack dot Com, All Rights reserved",NULL,0,0));
  if (first_name)
    sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"first name",NULL,strlen(first_name)+2,0));
    


  sd.add_by_hand(new spec_entry(SPEC_GRUE_FGMAP,"fgmap",NULL,4+4+fg_width*fg_height*2,0));
  sd.add_by_hand(new spec_entry(SPEC_GRUE_BGMAP,"bgmap",NULL,4+4+bg_width*bg_height*2,0));
  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"bg_scroll_rate",NULL,1+4*4,0));

  int ta=0;
  area_controller *a=area_list;
  for (;a;a=a->next) ta++;

  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"area_list.v1",NULL,1+ta*(4*11)+4,0));
 
  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"tick_counter",NULL,1+4,0));



  // how many object types are we goint to save, use a short to specify how many
  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"object_descripitions",NULL,2,0));


  int size=0;
  int i=0;
  for (;i<total_objects;i++)       // now save the names of the objects so if ordering
    size+=1+strlen(object_names[i])+1;    // changes in future versions we can adjust in load
  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"describe_names",NULL,size,0));


  size=0;                                
  for (i=0;i<total_objects;i++) 
  {
    size+=2;  // total number of states
    int j=0;
    for (;j<figures[i]->ts;j++)
      if (figures[i]->seq[j])
        size+=1+strlen(lstring_value(symbol_name(figures[i]->seq_syms[j])))+1;
  }
  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"describe_states",NULL,size,0));



  size=0;
  for (i=0;i<total_objects;i++) 
  {
    size+=2;  // total number of variables
    int j=0;
    for (;j<figures[i]->tiv;j++)
      if (figures[i]->vars[j])
        size+=1+strlen(lstring_value(symbol_name(figures[i]->vars[j])))+1;
  }
  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"describe_lvars",NULL,size,0));

 

  // how many objects are we goint to save, use a long to specify how many
  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"object_list",NULL,4,0)); 
  
  long t=0;
  object_node *o=save_list;
  for (;o;o=o->next)
    t++;

  // type and state aren't normal records because they will be remapped on loading
  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"type",NULL,1+2*t,0));
  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"state",NULL,1+2*t,0));


  // now save all the lvars for each object
  for (size=0,o=save_list;o;o=o->next)
    size+=figures[o->me->otype]->tv*5+2;
  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"lvars",NULL,size,0));


  for (i=0;i<TOTAL_OBJECT_VARS;i++)
    sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,object_descriptions[i].name,NULL,1+
                    RC_type_size(object_descriptions[i].type)*t,0));

  add_light_spec(&sd,Name);


  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"object_links",NULL,1+4+total_object_links(save_list)*8,0));
  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"light_links",NULL,1+4+total_light_links(save_list)*8,0));

  if (save_all)
  {
    t=0;
    view *v=player_list;
    for (;v;v=v->next) t++; 
    sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"player_info",NULL,t*4+4,0));

    int tv=total_view_vars();
    int i=0;
    for (;i<tv;i++)
      sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,get_view_var_name(i),NULL,1+4*t,0));
    sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"random_start",NULL,5,0));

    sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"weapon_array",NULL,1+4+total_weapons*4*t,0));

    int name_len=0;
    for (v=player_list;v;v=v->next)
      name_len+=strlen(v->name)+2;
    sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"player_names",NULL,name_len,0));

    sd.add_by_hand(new spec_entry(SPEC_IMAGE,"thumb nail",NULL,4+160*(100+eh->font()->height()*2),0));
  }

  sd.calc_offsets();

  return sd.write(filename);
}

void scale_put(image *im, image *screen, int x, int y, short new_width, short new_height);

void level::write_thumb_nail(bFILE *fp, image *im)
{
  image *i=new image(160,100+eh->font()->height()*2);
  i->clear();
  scale_put(im,i,0,0,160,100);
  if (first_name)
    eh->font()->put_string(i,80-strlen(first_name)*eh->font()->width()/2,100,first_name);

  time_t t;
  t=time(NULL);
  char buf[80];

  strftime(buf,80,"%T %A %B %d",localtime(&t));  
  eh->font()->put_string(i,80-strlen(buf)*eh->font()->width()/2,100+eh->font()->height(),buf);

  fp->write_short(i->width());
  fp->write_short(i->height());
  int y=0;
  for (;y<i->height();y++)
    fp->write(i->scan_line(y),i->width());

  delete i;
}

void level::write_player_info(bFILE *fp, object_node *save_list)
{
  long t=0;
  view *v=player_list;
  for (;v;v=v->next) t++; 
  fp->write_long(t);

  for (v=player_list;v;v=v->next)
    fp->write_long(object_to_number_in_list(v->focus,save_list));

  int tv=total_view_vars();
  int i=0;
  for (;i<tv;i++)
  {
    fp->write_byte(RC_L);
    for (v=player_list;v;v=v->next)
      fp->write_long(v->get_view_var_value(i));
  }

  fp->write_byte(RC_L);
  fp->write_long(rand_on);

  fp->write_byte(RC_L);
  fp->write_long(total_weapons);
  for (v=player_list;v;v=v->next)
    for (i=0;i<total_weapons;i++)
      fp->write_long(v->weapons[i]);

  for (v=player_list;v;v=v->next)
  {
    int len=strlen(v->name)+1;
    fp->write_byte(len);
    fp->write(v->name,len);
  }
}


int level::load_player_info(bFILE *fp, spec_directory *sd, object_node *save_list)
{
  int ret;
  spec_entry *se=sd->find("player_info");
  if (se)
  {
    fp->seek(se->offset,0);

    int set_first_view=0;
    if (the_game->first_view==player_list) set_first_view=1;
    int my_player_number=-1;

    view *v=player_list;
    for (;v;v=v->next)
    { v->suggest.send_view=0;
      v->suggest.send_weapon_change=0;
    }

    for (v=player_list;v;v=v->next)
      if (v->local_player()) 
         my_player_number=v->player_number;

    while (player_list)    // delete all of the views (they will get recreated)
    {
      v=player_list;
      if (v->focus)
      {
        if (v->focus->controller())
         v->focus->set_controller(NULL);
      delete v->focus;
      }

      player_list=player_list->next;
      delete v;
    }

    long total_players=fp->read_long();
    view *last=NULL;
    int i=0;
    for (;i<total_players;i++)    
    {
      game_object *o=number_to_object_in_list(fp->read_long(),save_list);
      v=new view(o,NULL,0);
      if (o) o->set_controller(v);
      if (player_list)
        last->next=v;
      else player_list=v;
      last=v;
    }
    if (set_first_view)
      the_game->first_view=player_list;
    
    for (i=0;i<total_view_vars();i++)
    {
      char *find_name=get_view_var_name(i);
      se=sd->find(find_name);

      if (se)
      {
      fp->seek(se->offset,0);
      if (fp->read_byte()==RC_L)
      {
        for (v=player_list;v;v=v->next)
            v->set_view_var_value(i,fp->read_long());
      }
      } else 
      {
      for (v=player_list;v;v=v->next)
        v->set_view_var_value(i,0);
      }
    }

    se=sd->find("random_start");      // start of index into random table
    if (se)
    {
      fp->seek(se->offset,0);
      if (fp->read_byte()==RC_L)
        rand_on=fp->read_long();
    } else rand_on=0;

    se=sd->find("weapon_array");
    if (se)
    {
      fp->seek(se->offset,0);
      if (fp->read_byte()==RC_L)
      {
      long m=fp->read_long();  // read how many weapons exsisted when last saved
      int i;
      for (v=player_list;v;v=v->next)       
      {
        for (i=0;i<m;i++)
        {
          long x=fp->read_long();
          if (i<total_weapons)
          {
            v->weapons[i]=x;
            v->last_weapons[i]=x;
          }
        }
      }
      }
    }  else 
    { 
      for (v=player_list;v;v=v->next)       
      {
      memset(v->last_weapons,0xff,total_weapons*sizeof(long));
      memset(v->weapons,0xff,total_weapons*sizeof(long));
      }
    }

    se=sd->find("player_names");
    if (se)
    {
      fp->seek(se->offset,0);
      for (v=player_list;v;v=v->next)       
      {
      uchar len=fp->read_byte();
      fp->read(v->name,len);
      }
    }

    ret=1;
    recalc_local_view_space();
    
  } else 
  {
    void *fun=make_find_symbol("set_player_defaults");
    if (DEFINEDP(symbol_function(fun)))
    {      
      view *f;
      game_object *o=current_object;
      for (f=player_list;f;f=f->next)
      {
      if (f->focus)
      {
        current_object=f->focus;
        void *m=mark_heap(TMP_SPACE);
        eval_function((lisp_symbol *)fun,NULL);
        restore_heap(m,TMP_SPACE);
      }
      }     
      current_object=o;
    }
    ret=0;
  }

  view *vw;
  for (vw=player_list;vw;vw=vw->next)
  {
    if (total_weapons && !vw->has_weapon(vw->current_weapon))
    {
      vw->suggest.send_weapon_change=1;
      vw->suggest.new_weapon=0;      
    }
  }

  return ret;
}


void level::write_objects(bFILE *fp, object_node *save_list)
{
  // record information in the file about what the data structures look like
  // right now, so if they change later, they don't get get screwed up
  fp->write_short(total_objects);   // mark how many objects we know about right now  

  int i=0;
  for (;i<total_objects;i++)   // loop through all the object types we know of
  {    
    fp->write_byte(strlen(object_names[i])+1);                    // sizeof name    
    fp->write(object_names[i],strlen(object_names[i])+1);      // write object name
  }
    

  // write state numbers and names for each object
  for (i=0;i<total_objects;i++)
  {
    int total=0;
    int j=0;
    for (;j<figures[i]->ts;j++)
      if (figures[i]->seq[j]) total++;
    fp->write_short(total);

    for (j=0;j<figures[i]->ts;j++)
      if (figures[i]->seq[j])
      {
      char *state_name=lstring_value(symbol_name(figures[i]->seq_syms[j]));
      fp->write_byte(strlen(state_name)+1);
      fp->write(state_name,strlen(state_name)+1);
      }
  }

  
  // write object lvar names
  for (i=0;i<total_objects;i++)
  {
    fp->write_short(figures[i]->tv);
    int j,x;
    
    for (x=0;x<figures[i]->tv;x++)
    {
      for (j=0;j<figures[i]->tiv;j++)
      {
        if (figures[i]->vars[j] && figures[i]->var_index[j]==x)
      {
        char *var_name=lstring_value(symbol_name(figures[i]->vars[j]));
        fp->write_byte(strlen(var_name)+1);
        fp->write(var_name,strlen(var_name)+1);
      }
      }
    }
  }
  
  long t=0;
  object_node *o=save_list;
  for (;o;o=o->next) t++;
  fp->write_long(t);


  fp->write_byte(RC_S);                                    // save type info for each record
  for (o=save_list;o;o=o->next) fp->write_short(o->me->type());    

  fp->write_byte(RC_S);                                    // save state info for each record
  for (o=save_list;o;o=o->next) fp->write_short(o->me->reduced_state());

  for (o=save_list;o;o=o->next)                            // save lvars
  {
    fp->write_short(figures[o->me->otype]->tv);
    for (i=0;i<figures[o->me->otype]->tv;i++) 
    {
      fp->write_byte(RC_L);                           // for now the only type allowed is long
      fp->write_long(o->me->lvars[i]);
    }
  }

  for (i=0;i<default_simple.total_vars();i++)
  {
    int t=object_descriptions[i].type;
    fp->write_byte(t);
    for (o=save_list;o;o=o->next)
    {
      switch (t)
      {     
      case RC_C :
      { fp->write_byte(o->me->get_var(i)); } break;
      case RC_S :
      { fp->write_short(o->me->get_var(i)); } break;
      case RC_L :
      { fp->write_long(o->me->get_var(i)); } break;
      }
    }
  }
}


long level::total_object_links(object_node *list)
{
  long tl=0;
  for (object_node *o=list;o;o=o->next)
    tl+=o->me->total_objects();
  return tl;
}

long level::total_light_links(object_node *list)
{
  long tl=0;
  for (object_node *o=list;o;o=o->next)
    tl+=o->me->total_lights();
  return tl;
}

void level::write_links(bFILE *fp, object_node *save_list, object_node *exclude_list)
{
  fp->write_byte(RC_L);  
  fp->write_long(total_object_links(save_list));

  int x=1;
  object_node *o=save_list;

  for (;o;o=o->next,x++)
  {
    int i=0;
    for (;i<o->me->total_objects();i++)
    {
      fp->write_long(x);
      long x=object_to_number_in_list(o->me->get_object(i),save_list); 
      if (x)
        fp->write_long(x);
      else                            // save links to excluded items as negative
        fp->write_long((long)(-(object_to_number_in_list(o->me,exclude_list))));
    }
  }

  fp->write_byte(RC_L);  
  fp->write_long(total_light_links(save_list));

  x=1;
  for (o=save_list;o;o=o->next,x++)
  {
    int i=0;
    for (;i<o->me->total_lights();i++)
    {
      fp->write_long(x);
      fp->write_long(light_to_number(o->me->get_light(i)));
    }
  }

}


void level::load_links(bFILE *fp, spec_directory *sd, 
                   object_node *save_list, object_node *exclude_list)
{
  spec_entry *se=sd->find("object_links");
  if (se)
  {
    fp->seek(se->offset,0);
    if (fp->read_byte()==RC_L)
    {
      long t=fp->read_long();
      while (t)
      {
      long x1=fp->read_long();
      CONDITION(x1>=0,"expected x1 for object link to be > 0\n");
      long x2=fp->read_long();
      game_object *p,*q=number_to_object_in_list(x1,save_list);
      if (x2>0)
        p=number_to_object_in_list(x2,save_list);
      else p=number_to_object_in_list(-x2,exclude_list);
      if (q)
        q->add_object(p);
      else dprintf("bad object link\n");

      t--;
      } 
    }
  }

  se=sd->find("light_links");
  if (se)
  {
    fp->seek(se->offset,0);
    if (fp->read_byte()==RC_L)
    {
      long t=fp->read_long();
      while (t)
      {
      long x1=fp->read_long();
      long x2=fp->read_long();
      game_object *p=number_to_object_in_list(x1,save_list);
      if (p)
        p->add_light(number_to_light(x2));
      else dprintf("bad object/light link\n");
      t--;
      } 
    }
  }

}


void level::write_options(bFILE *fp)
{
  // save background scroll rate
  fp->write_byte(RC_L);
  fp->write_long(bg_xmul);
  fp->write_long(bg_xdiv);
  fp->write_long(bg_ymul);
  fp->write_long(bg_ydiv);

  fp->write_byte(RC_L);
  int ta=0;
  area_controller *a=area_list;
  for (;a;a=a->next) ta++;
  fp->write_long(ta);
  for (a=area_list;a;a=a->next)
  {
    fp->write_long(a->x);
    fp->write_long(a->y);
    fp->write_long(a->w);
    fp->write_long(a->h);
    fp->write_long(a->active);

    fp->write_long(a->ambient);
    fp->write_long(a->view_xoff);
    fp->write_long(a->view_yoff);
    fp->write_long(a->ambient_speed);
    fp->write_long(a->view_xoff_speed);
    fp->write_long(a->view_yoff_speed);
  }
  fp->write_byte(RC_L);
  fp->write_long(tick_counter());
}

void level::load_options(spec_directory *sd, bFILE *fp)
{
  spec_entry *se=sd->find("bg_scroll_rate");
  if (se)
  {
    fp->seek(se->offset,0);
    if (fp->read_byte()!=RC_L)
    { bg_xmul=bg_ymul=1; bg_xdiv=bg_ydiv=8; }
    else
    {
      bg_xmul=fp->read_long();
      bg_xdiv=fp->read_long();
      bg_ymul=fp->read_long();
      bg_ydiv=fp->read_long();
    }
  } else { bg_xmul=bg_ymul=1; bg_xdiv=bg_ydiv=8; }

  se=sd->find("area_list.v1");
  if (se)
  {
    fp->seek(se->offset,0);
    if (fp->read_byte()==RC_L)
    {
      area_controller *l=NULL,*p;
      long ta=fp->read_long();
      int i=0;
      for (;i<ta;i++)
      {
      long x,y,w,h;
      x=fp->read_long();
      y=fp->read_long();
      w=fp->read_long();
      h=fp->read_long();      
      p=new area_controller(x,y,w,h,NULL);
      if (l) l->next=p;
      else area_list=p;
      l=p;
      p->active=fp->read_long();
      p->ambient=fp->read_long();
      p->view_xoff=fp->read_long();
      p->view_yoff=fp->read_long();
      p->ambient_speed=fp->read_long();
      p->view_xoff_speed=fp->read_long();
      p->view_yoff_speed=fp->read_long();
      }
    }
  }

  se=sd->find("tick_counter");
  if (se)
  {
    fp->seek(se->offset,0);
    if (fp->read_byte()==RC_L)    
      set_tick_counter(fp->read_long());
    else set_tick_counter(0);
  } else set_tick_counter(0);
}


void level::write_cache_prof_info()
{
  if (cash.prof_is_on())
  {
    char pf_name[100];
    if (first_name)
      get_prof_assoc_filename(first_name,pf_name);
    else
      get_prof_assoc_filename(Name,pf_name);


    spec_directory sd;
    sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"cache profile info",NULL,cash.prof_size(),0));
    sd.calc_offsets();
    jFILE *fp2=sd.write(pf_name);
    if (!fp2)
      the_game->show_help("Unable to open cache profile output file");
    else
    {
      cash.prof_write(fp2);
      delete fp2;
    }
    sd.delete_entries();
  }

}

void level::load_cache_info(spec_directory *sd, bFILE *fp)
{
  if (!DEFINEDP(symbol_value(l_empty_cache)) || !symbol_value(l_empty_cache))
  {
    char pf_name[100];
    if (first_name)
      get_prof_assoc_filename(first_name,pf_name);  // get cache info from orignal filename if this is a savegame
    else
      get_prof_assoc_filename(Name,pf_name);


    cash.load_cache_prof_info(pf_name,this);
  }
}


int level::save(char *filename, int save_all)
{
      char name[255], bkname[255];

      sprintf( name, "%s%s", get_save_filename_prefix(), filename );
      sprintf( bkname, "%slevsave.bak", get_save_filename_prefix() );
      if( !save_all && DEFINEDP( symbol_value( l_keep_backup ) ) &&
            symbol_value( l_keep_backup ) )   // make a backup
      {
            bFILE *fp = open_file( name, "rb" );    // does file already exist?
            if( !fp->open_failure() )
            {
                  unlink( bkname );
                  bFILE *bk = open_file( bkname, "wb" );
                  if( bk->open_failure() )
                        dprintf("unable to open backup file %s\n", bkname );
                  else
                  {
                        long size = fp->file_size();
                        uchar *buf = (uchar *)jmalloc(0x1000,"copy buf");
                        int tr = 1;
                        while( size && tr )
                        {
                              int tr = fp->read(buf,0x1000);
                              if( tr )
                              tr = bk->write(buf,tr);
                              size -= tr;
                        }
                        jfree(buf);
                  }
                  delete bk;
#if (defined(__MACH__) || !defined(__APPLE__))
                  chmod( bkname, S_IRWXU | S_IRWXG | S_IRWXO );
#endif
            }
            delete fp;
      }

      // if we are not doing a savegame then change the first_name to this name
      if( !save_all )
      {
            if( first_name )
                  jfree(first_name);
            first_name = (char *)jmalloc( strlen( name ) + 1, "level first name" );
            strcpy( first_name, name );
      }

      object_node *players, *objs;
      if( save_all )
            players = NULL;
      else
            players = make_player_onodes();

      objs = make_not_list(players);     // negate the above list

      bFILE *fp = create_dir( name, save_all, objs, players);
      if( fp != NULL )
      {
            if( !fp->open_failure() )
            {
                  if( first_name )
                  {
                        fp->write_byte( strlen( first_name ) + 1 );
                        fp->write( first_name, strlen( first_name ) + 1 );
                  }
                  else
                  {
                        fp->write_byte( 1 );
                        fp->write_byte( 0 );
                  }

                  fp->write_long( fg_width );
                  fp->write_long( fg_height );

                  int t  = fg_width * fg_height;
                  ushort *rm = map_fg;
                  for (;t;t--,rm++)
                  {
                        ushort x = *rm;
                        x = lstl(x);            // convert to intel endianess
                        *rm = x;
                  }

                  fp->write( (char *)map_fg, 2 * fg_width * fg_height );
                  t = fg_width * fg_height;
                  rm = map_fg;
                  for (;t;t--,rm++)
                  {
                        ushort x = *rm;
                        x = lstl( x );            // convert to intel endianess
                        *rm = x;
                  }

                  fp->write_long( bg_width );
                  fp->write_long( bg_height );
                  t = bg_width * bg_height;
                  rm = map_bg;

                  for (;t;t--,rm++)
                  {
                        ushort x=*rm;
                        x = lstl( x );          // convert to intel endianess
                        *rm = x;
                  }

                  fp->write( (char *)map_bg, 2 * bg_width * bg_height );
                  rm = map_bg;
                  t = bg_width*bg_height;

                  for (;t;t--,rm++)
                  {
                        ushort x = *rm;
                        x = lstl( x );          // convert to intel endianess
                        *rm = x;
                  }

                  write_options( fp );
                  write_objects( fp, objs );
                  write_lights( fp );
                  write_links( fp, objs, players );
                  if( save_all )
                  {
                        write_player_info( fp, objs );
                        write_thumb_nail( fp,screen );
                  }

                  delete fp;
#if (defined(__MACH__) || !defined(__APPLE__))
                  chmod( name, S_IRWXU | S_IRWXG | S_IRWXO );
#endif
                  write_cache_prof_info();
            }
            else
            {
                  the_game->show_help( "Unable to open file for saving\n" );
                  delete fp;
                  return 0;
            }
      }
      else
      {
            the_game->show_help( "Unable to open file for saving.\n" );
            printf( "\nFailed to save game.\n" );
            printf( "I was trying to save to: '%s'\n\tPath: '%s'\n\tFile: '%s'\n", name, get_save_filename_prefix(), filename );
            printf( "\nPlease send an email to:\n\ttrandor@labyrinth.net.au\nwith these details.\nThanks.\n" );
            return 0;
      }

      delete_object_list(players);
      delete_object_list(objs);

      return 1;
}

level::level(int width, int height, char *name)
{
  the_game->need_refresh();
  area_list=NULL;
  set_tick_counter(0);

  attack_list=NULL;
  attack_list_size=attack_total=0;

  target_list=NULL;
  target_list_size=target_total=0;

  block_list=NULL;
  block_list_size=block_total=0;

  all_block_list=NULL;
  all_block_list_size=all_block_total=0;
 
  Name=NULL;
  first_name=NULL;

  set_name(name);
  first=first_active=NULL;
  
  fg_width=width;
  fg_height=height;
  calc_bgsize(fg_width,fg_height,bg_width,bg_height);
  
  map_bg=(unsigned short *)jmalloc(sizeof(short)*bg_width*bg_height,"map bg");
  map_fg=(unsigned short *)jmalloc(sizeof(short)*fg_width*fg_height,"map fg");



  memset(map_bg,0,sizeof(short)*bg_width*bg_height);
  memset(map_fg,0,sizeof(short)*fg_width*fg_height);

  int i;  
  for (i=0;i<fg_width;i++)
  {    
    map_fg[i]=1;
    map_fg[fg_width*(fg_height-1)+i]=1;
  }
  for (i=0;i<fg_height;i++)
  {    
    map_fg[fg_width*i]=1;
    map_fg[fg_width*i+fg_width-1]=1;
  }
  
  total_objs=0;  
  insert_players();
}


void level::add_object(game_object *new_guy)
{
  total_objs++;
  new_guy->next=NULL;
  if (figures[new_guy->otype]->get_cflag(CFLAG_ADD_FRONT))
  {
    if (!first)
      first=new_guy;
    else
      last->next=new_guy;
    last=new_guy;
  } else
  {
    if (!first)
      last=first=new_guy;
    else
    {
      new_guy->next=first;
      first=new_guy;
    }
  }
}

void level::add_object_after(game_object *new_guy,game_object *who)
{
  if (!who) add_object(new_guy);
  else
  {
    total_objs++;
    if (who==last) last=new_guy;
    new_guy->next=who->next;
    who->next=new_guy;
  }
}

void level::delete_object(game_object *who)
{
  remove_object(who);
  delete who;
}

void level::remove_block(game_object *who)
{
  int i=0,j;
  game_object **o=block_list;
  for (;i<block_total;i++)
  {
    if (*o==who)        // is this object in the block list?
    {
      block_total--;    // squish the block list in
      o++;
      for (j=i;j<block_total;j++)
        block_list[j]=block_list[j+1];
    } else o++;    
  }
}


// searches through the all_block list for who and if it finds it deletes it
void level::remove_all_block(game_object *who)
{
  int i=0,j;
  game_object **o=all_block_list;
  for (;i<all_block_total;i++)
  {
    if (*o==who)        // is this object in the block list?
    {
      all_block_total--;    // squish the block list in
      o++;
      for (j=i;j<all_block_total;j++)
        all_block_list[j]=all_block_list[j+1];
    } else o++;    
  }
}

void level::remove_object(game_object *who)
{
  if (dev_cont)
    dev_cont->notify_deleted_object(who);

  if (who==first)
  {
    if (who==last) last=NULL;
    first=first->next;
  }
  else
  {
    game_object *o=first;
    for (;o && o->next!=who;o=o->next);
    if (o)
    {
      o->next=who->next;
      if (!o->next) last=o;
    }
    else return ;     // if object is not in level, don't try to do anything else
  }
  total_objs--;


  if (first_active==who) 
    first_active=who->next_active;
  else
  {
    game_object *o=first_active;
    for (;o && o->next_active!=who;o=o->next_active);
    if (o)
      o->next_active=who->next_active;
  }

  if (who->flags()&KNOWN_FLAG)
  {
    game_object *o=first;
    for (;o;o=o->next)
    {
      int t=o->total_objects();
      int i=0;
      for (;i<t;i++)
        if (o->get_object(i)==who)
      {
        o->remove_object(who);
        t=o->total_objects();
      }
    }
  }

  if (who->otype<0xffff)
  {
    if (who->can_block())  // remove object from block list and all_block if nessasary
    {
      remove_block(who);
      remove_all_block(who);
    } else if (who->hurtable()) 
      remove_all_block(who);
  }


  int t=who->total_objects();
  while (t) { who->remove_object(who->get_object(0)); t--; }

  t=who->total_lights();
  while (t) { who->remove_light(who->get_light(0)); t--; }
}

void level::to_front(game_object *o)  // move to end of list, so we are drawn last, therefore top 
{
  if (o==last) return ;
  first_active=NULL;     // make sure nothing goes screwy with the active list

  if (o==first)  
    first=first->next;  
  else
  {
    game_object *w=first;
    for (;w && w->next!=o;w=w->next);
    if (!w) return ;
    w->next=o->next;
  }

  last->next=o;
  o->next=NULL;
  last=o;
}

void level::to_back(game_object *o)   // to make the character drawn in back, put at front of list
{  
  if (o==first) return;
  first_active=NULL;     // make sure nothing goes screwy with the active list  
  
  game_object *w=first;
  for (;w && w->next!=o;w=w->next);
  if (!w) return;
  if (last==o)
    last=w;
  w->next=o->next;
  o->next=first;
  first=o;
}


game_object *level::find_self(game_object *me)
{
  return me;
}

game_object *level::find_object(long x, long y)
{
  long x1,y1,x2,y2;  
  game_object *o=first;
  for (;o;o=o->next)
  {
    o->picture_space(x1,y1,x2,y2);    
    if (x<x2 && x>=x1 && y<y2 && y>=y1)
      return o;
  }
  return NULL; 
}

long last_tile_hit_x,last_tile_hit_y;

#define remapx(x) (x==0 ? -1 : x==tl-1 ? tl+1 : x)
#define remapy(y) (y==0 ? -1 : y==th-1 ? th+1 : y)

void level::foreground_intersect(long x1, long y1, long &x2, long &y2)
{
/*  if (x1==x2)
  { vforeground_intersect(x1,y1,y2);
    return ;
  }  */

  long tl=the_game->ftile_width(),th=the_game->ftile_height(),
    j,
    xp1,yp1,xp2,yp2,    // starting and ending points of block line segment 
    swap;               // temp var
  long blockx1,blocky1,blockx2,blocky2,block,bx,by;
  point_list *block_list;
  unsigned char *bdat;

  blockx1=x1;
  blocky1=y1;
  blockx2=x2;
  blocky2=y2;
  if (blockx1>blockx2) { swap=blockx1; blockx1=blockx2; blockx2=swap; }
  if (blocky1>blocky2) { swap=blocky1; blocky1=blocky2; blocky2=swap; }
  blockx1=(blockx1-2)/tl-1;
  blockx2=(blockx2+tl+2)/tl+1;
  blocky1=(blocky1-2)/th-1;
  blocky2=(blocky2+th+2)/th+1;


  if (blockx2>=foreground_width()) { x2=tl*foreground_width()-1; }
  if (blocky2>=foreground_height()) { y2=th*foreground_height()-1; }  
  blockx1=max(blockx1,0);
  blocky1=max(blocky1,0);  

  if ((blockx1>blockx2) || (blocky1>blocky2)) return ;

  // now check all the map positions this line could intersect
  for (bx=blockx1;bx<=blockx2;bx++)
  {
    for (by=blocky1;by<=blocky2;by++)
    {
      block=the_game->get_map_fg(bx,by);
      if (block>BLACK)        // don't check BLACK, should be no points in it
      {
        // now check the all the line segments in the block
        foretile *f=the_game->get_fg(block);
        block_list=f->points;
        unsigned char total=block_list->tot;
        bdat=block_list->data;
        unsigned char *ins=f->points->inside;
      long xo=bx*tl,yo=by*th;
        for (j=0;j<total-1;j++,ins++)
        {
          // find the starting and ending points for this segment
        xp1=xo+remapx(*bdat);
        bdat++;

        yp1=yo+remapy(*bdat);
        bdat++;

        xp2=xo+remapx(*bdat);
        yp2=yo+remapy(bdat[1]);


        long ox2=x2,oy2=y2;
          if (*ins)       
            setback_intersect(x1,y1,x2,y2,xp1,yp1,xp2,yp2,1); 
          else
            setback_intersect(x1,y1,x2,y2,xp1,yp1,xp2,yp2,-1); 
        if (ox2!=x2 || oy2!=y2)
        {
          last_tile_hit_x=bx;
          last_tile_hit_y=by;
        }

        }       
      }
    }
  }  
}


void level::vforeground_intersect(long x1, long y1, long &y2)
{
  long tl=f_wid,th=f_hi,
    j,
    xp1,yp1,xp2,yp2;    // starting and ending points of block line segment temp var
  long blocky1,blocky2,block,bx,by,checkx;
  point_list *block_list;
  unsigned char *bdat;

  int y_addback;
  if (y1>y2)
  {
    blocky1=y2/th;
    blocky2=y1/th;    
    y_addback=blocky2*f_hi;
  } else
  {
    blocky1=y1/th;
    blocky2=y2/th;
    y_addback=blocky1*f_hi;
  }

  y1-=y_addback;
  y2-=y_addback;

  bx=x1/f_wid;
  checkx=x1-bx*f_wid;


  // now check all the map positions this line could intersect

  for (by=blocky1;by<=blocky2;by++,y1-=f_hi,y2-=f_hi,y_addback+=f_hi)
  {
    block=the_game->get_map_fg(bx,by);

    // now check the all the line segments in the block
    foretile *f=the_game->get_fg(block);
    block_list=f->points;

    unsigned char total=block_list->tot;
    bdat=block_list->data;
    unsigned char *ins=f->points->inside;

//    long xo=bx*tl,yo=by*th;
    for (j=0;j<total-1;j++,ins++)
    {
      // find the starting and ending points for this segment
      xp1=remapx(*bdat);
      bdat++;

      yp1=remapy(*bdat);
      bdat++;

      xp2=remapx(*bdat);
      yp2=remapy(bdat[1]);


      long oy2=y2;
      if (*ins)     
        setback_intersect(checkx,y1,checkx,y2,xp1,yp1,xp2,yp2,1); 
      else
        setback_intersect(checkx,y1,checkx,y2,xp1,yp1,xp2,yp2,-1); 
      if (oy2!=y2)
      {
      last_tile_hit_x=bx;
      last_tile_hit_y=by;
      }
    }
  }
  y2+=y_addback;
}



void level::send_signal(long signal)
{
  if (signal)   // signal 0 is never sent!
  {
    game_object *o=first_active;
    for (;o;o=o->next_active)
      o->recieve_signal(signal);  
  }
}


int level::crush(game_object *by_who, int xamount, int yamount)
{
  long xv,yv,crushed=0;  
  game_object *o=first_active;
  for (;o;o=o->next_active)
  {
    if (o->hurtable() && o!=by_who)
    {      
      xv=-xamount;
      yv=-yamount;    
      if (o->try_move(o->x,o->y,xv,yv,3)==by_who)
      {      
      xv=xamount;
      yv=yamount;
      o->try_move(o->x,o->y,xv,yv,3);
      if (xv==0 && yv==0)
      {
        if (o->state!=dead && o->state!=dieing)
          o->do_damage(by_who->current_figure()->hit_damage,by_who,o->x,o->y,0,0);

/*      {         
          if (o->has_sequence(dieing))
            o->set_state(dieing);
          else o->set_state(dead);
            o->hp=0;        
        }       */
        crushed=1;        
      }
      }    
    }    
  }

  return crushed;  
}


int level::platform_push(game_object *by_who, int xamount, int yamount)
{
  int failed=0;
  long xv,yv;
  game_object *o=first_active;
  for (;o;o=o->next_active)  
  {
    if (o->is_playable() && o->state!=dieing && o->state!=dead)  
    {      
      // check to see if the platform is going up and will run into us.      
      long tvx,tvy;
      if (yamount<0)
      {
      tvx=-xamount;
      tvy=-yamount;
      if (o->try_move(o->x,o->y,tvx,tvy,1)==by_who)
      {
        o->x+=tvx;
        o->y+=tvy;       
      }
      }

/*      xv=xamount;    
      yv=yamount;
      tvx,tvy;
      if (xv>0) tvx=xv+1; else if (xv<0) tvx=xv-1; else tvx=0;
      if (yv>0) tvy=yv+1; else if (yv<0) tvx=yv-1; else tvy=0;
      if (o->try_move(o->x,o->y,tvx,tvy,1)==by_who)  // we the platform hit us?
      {
      o->x+=tvx;
      o->y+=tvy;       
      }*/

      xv=0;   
      yv=2;
      if (o->try_move(o->x,o->y,xv,yv,1)==by_who)  // are we standing on the platform?
      {      
      by_who->x=-by_who->x;
      xv=xamount;
      yv=yamount;
      o->try_move(o->x,o->y,xv,yv,3); 
      if (xv!=xamount || yv!=yamount) failed=1;
      o->x+=xv;
      o->y+=yv;
      by_who->x=-by_who->x;
      }    
    }    
  }
  return !failed;
}

int level::push_characters(game_object *by_who, int xamount, int yamount)
{
  long xv,yv;
  int failed=0;
  game_object *o=first_active;
  for (;o;o=o->next_active)  
  {
    if ((o->is_playable() || o->pushable()) && o->state!=dieing && o->state!=dead)  
    {      
      xv=-xamount;    
      yv=-yamount;
      long tvx,tvy;
      if (xv>0) tvx=xv+1; else if (xv<0) tvx=xv-1; else tvx=0;
      if (yv>0) tvy=yv+1; else if (yv<0) tvx=yv-1; else tvy=0;
      if (o->try_move(o->x+xamount,o->y+yamount,tvx,tvy,3)==by_who)
      {      
      xv=(xamount-tvx);
      yv=(yamount-tvy);
      o->try_move(o->x,o->y,xv,yv,3);       
      o->x+=xv;
      o->y+=yv;
      if (xv!=xamount-tvx || yv!=yamount-tvy)
        failed=1;
      }    
    }    
  }
  return !failed;
}

game_object *level::find_xrange(int x, int y, int type, int xd)
{
  long find_ydist=100000;
  game_object *find=NULL;
  game_object *o=first_active;
  for (;o;o=o->next_active)  
  {
    if (o->otype==type)
    {
      int x_dist=abs(x-o->x);
      int y_dist=abs(y-o->y);

      if (x_dist<xd  && y_dist<find_ydist)
      {   
      find_ydist=y_dist;
      find=o;
      }
    }
  }
  return find;
}


game_object *level::find_xclosest(int x, int y, int type, game_object *who)
{
  long find_ydist=100000,find_xdist=0xffffff;
  game_object *find=NULL;
  game_object *o=first_active;
  for (;o;o=o->next_active)  
  {
    if (o->otype==type && o!=who)
    {
      int x_dist=abs(x-o->x);
      if (x_dist<find_xdist)
      {
      find_xdist=x_dist;
      find_ydist=abs(y-o->y);
      find=o;
      }
      else if (x_dist==find_xdist)
      {
      int y_dist=abs(y-o->y);
      if (y_dist<find_ydist)
      {
        find_ydist=y_dist;
        find=o;
      }
      }
    }
  }
  return find;
}

game_object *level::find_closest(int x, int y, int type, game_object *who)
{
  long find_dist=100000;
  game_object *find=NULL;
  game_object *o=first_active;
  for (;o;o=o->next_active)  
  {
    if (o->otype==type && o!=who)
    {
      int d=(x-o->x)*(x-o->x)+(y-o->y)*(y-o->y);
      if (d<find_dist)
      {
      find=o;
      find_dist=d;
      }
    }
  }
  return find;
}



void level::remove_light(light_source *which)
{
  if (which->known)
  {
    game_object *o=first;
    for (;o;o=o->next)
    {
      int t=o->total_lights();
      int i=0;
      for (;i<t;i++)
        if (o->get_light(i)==which)
        o->remove_light(o->get_light(i));
    }
  }
  delete_light(which);
}


game_object *level::find_type(int type, int skip)
{
  game_object *l=NULL;
  game_object *o=first;
  for (;o;o=o->next)
  {
    if (o->otype==type)
    {
      if (!skip)
        return o;      
      skip--;
      l=o;
    }
  }
  return l;
}

void level::hurt_radius(long x, long y,long r, long m, game_object *from, game_object *exclude, 
                  int max_push)
{
  if (r<1) return ;   // avoid dev vy zero
  game_object *o=first_active;
  for (;o;o=o->next_active)
  {
    if (o!=exclude && o->hurtable())
    {
      long y1=o->y,y2=o->y-o->picture()->height();
      long cx=abs(o->x-x),cy1=abs(y1-y),d1,d2,cy2=abs(y2-y);
      if (cx<cy1)
        d1=cx+cy1-(cx>>1);
      else d1=cx+cy1-(cy1>>1);

      if (cx<cy2)
        d2=cx+cy2-(cx>>1);
      else d2=cx+cy2-(cy2>>1);
      if (d2<d1)
        d1=d2;



      if (d1<r)
      {

      int px=(r-cx)*max_push/r,py=(r-cy1)*max_push/r;
      if (o->x<x)
          px=-px;
      if (o->y<y)
          py=-py;
      o->do_damage((r-d1)*m/r,from,x,y1,px,py);
      }


    }
  }

}



game_object *level::get_random_start(int min_player_dist, view *exclude)
{
  int t=0;
  game_object *o=first;
  for (;o;o=o->next)
    if (o->otype==start_position_type) t++;    // count how many starts there are in the level

  if (t==0) return NULL;                       // there aren't any starts in level!

  int retries=t;
  do
  {
    int ctry=jrandom(t)+1;
    game_object *n=first;
    for (n=first;ctry && n;n=n->next)
    {
      if (n->otype==start_position_type) 
      {
      o=n;
        ctry--;
      }
    }

    int too_close=0;
    view *v=player_list;
    for (;v;v=v->next)
    { 
      if (v!=exclude)
      {
      long cx=abs(v->x_center()-o->x),cy=abs(v->y_center()-o->y),d;
      if (cx<cy)
          d=cx+cy-(cx>>1);
      else d=cx+cy-(cy>>1);
      if (d<min_player_dist) too_close=1;
      }
    }
    if (too_close) retries--;
    else retries=0;
  } while (retries);

  return o;
}





void level::insert_players()
{

  int start=0;
  int i=0;
  for (;i<total_objects;i++)
    if (!strcmp(object_names[i],"START"))
      start=i;

  view *f=player_list;
  for (;f;f=f->next)  
  {
    game_object *st=find_type(start,f->player_number);      
    if (st)
    {
      f->focus->x=st->x;
      f->focus->y=st->y;
    }
    add_object_after(f->focus,st);
  }

}


void level::add_attacker(game_object *who)
{
  if (attack_total>=attack_list_size)  // see if we need to grow the list size..
  {
    attack_list_size++;
    attack_list=(game_object **)jrealloc(attack_list,sizeof(game_object *)*attack_list_size,
                                          "attack_list");    
  }
  attack_list[attack_total]=who;
  attack_total++;
}



void level::add_target(game_object *who)
{
  if (target_total>=target_list_size)  // see if we need to grow the list size..
  {
    target_list_size++;
    target_list=(game_object **)jrealloc(target_list,sizeof(game_object *)*target_list_size,
                                          "target_list");    
  }
  target_list[target_total]=who;
  target_total++;
}



void level::add_block(game_object *who)
{
  if (block_total>=block_list_size)  // see if we need to grow the list size..
  {
    block_list_size++;
    block_list=(game_object **)jrealloc(block_list,sizeof(game_object *)*block_list_size,
                                          "block_list");    
  }
  block_list[block_total]=who;
  block_total++;
}


void level::add_all_block(game_object *who)
{
  if (all_block_total>=all_block_list_size)  // see if we need to grow the list size..
  {
    all_block_list_size++;
    all_block_list=(game_object **)jrealloc(all_block_list,sizeof(game_object *)*all_block_list_size,
                                          "all_block_list");    
  }
  all_block_list[all_block_total]=who;
  all_block_total++;
}


game_object *level::find_object_in_area(long x, long y, long x1, long y1, long x2, long y2,
                             Cell *list, game_object *exclude)
{
  game_object *closest=NULL;
  long closest_distance=0xfffffff,distance,xo,yo;
  game_object *o=first_active;
  for (;o;o=o->next_active)
  {
    long xp1,yp1,xp2,yp2;
    o->picture_space(xp1,yp1,xp2,yp2);


    if (!(xp1>x2 || xp2<x1 || yp1>y2 || yp2<y1) && o!=exclude)
    {
      // check to see if the type is in the list
      Cell *v=list;
      for (;!NILP(v) && lnumber_value(CAR(v))!=o->otype;v=CDR(v));
      if (!NILP(v))
      {
      xo=abs(o->x-x);
      yo=abs(o->y-y);
      distance=xo*xo+yo*yo;
      if (distance<closest_distance)
      {
        closest_distance=distance;
        closest=o;
      }
      }
    }
  }
  return closest;
}




game_object *level::find_object_in_angle(long x, long y, long start_angle, long end_angle,
                            void *list, game_object *exclude)
{
  game_object *closest=NULL;
  long closest_distance=0xfffffff,distance,xo,yo;
  game_object *o=first_active;
  for (;o;o=o->next_active)
  {
    long angle=lisp_atan2(o->y-y,o->x-x);
    if (((start_angle<=end_angle && (angle>=start_angle && angle<=end_angle))
      || (start_angle>end_angle && (angle>=start_angle || angle<=end_angle)))
      && o!=exclude)
    {
      // check to see if the type is in the list
      Cell *v=(Cell *)list;
      for (;!NILP(v) && lnumber_value(CAR(v))!=o->otype;v=CDR(v));
      if (!NILP(v))
      {
      xo=abs(o->x-x);
      yo=abs(o->y-y);
      distance=xo*xo+yo*yo;
      if (distance<closest_distance)
      {
        closest_distance=distance;
        closest=o;
      }
      }
    }
  }
  return closest;
}


object_node *level::make_not_list(object_node *list)
{
  object_node *f=NULL,*l=NULL;
  game_object *o=first;
  for (;o;o=o->next)
  {
    if (!object_to_number_in_list(o,list))
    {
      object_node *q=new object_node(o,NULL);
      if (f)
        l->next=q;
      else f=q;
      l=q;
    }
  }
  return f;
}

FILE *open_FILE(char *filename, char *mode);

void level::write_object_info(char *filename)
{
  FILE *fp=open_FILE(filename,"wb");
  if (fp)
  {
    int i=0;
    game_object *o=first;
    for (;o;o=o->next)
    {
      fprintf(fp,"%3d %s %4ld %4ld %4ld %4ld %04d\n",i++,object_names[o->otype],o->x,o->y,
            o->xvel(),o->yvel(),o->current_frame);
    }
    fclose(fp);
  }
}


area_controller::area_controller(long X, long Y, long W, long H, area_controller *Next)
{ 
  x=X; y=Y; w=W; h=H; 
  next=Next; active=0; 

  ambient=-1;
  view_xoff=-1;
  view_yoff=-1;
  ambient_speed=2; 
  view_xoff_speed=4;
  view_yoff_speed=4;
}

Generated by  Doxygen 1.6.0   Back to index