source: abuse/trunk/src/level.cpp @ 16

Last change on this file since 16 was 16, checked in by Sam Hocevar, 18 years ago
  • shitloads of long -> int32_t changes for 64 bit safety.
File size: 72.7 KB
Line 
1#include "light.hpp"
2#include "level.hpp"
3#include "game.hpp"
4#include "intsect.hpp"
5#include "lisp.hpp"
6#include "dprint.hpp"
7#include "particle.hpp"
8#include "objects.hpp"
9#include "jrand.hpp"
10#include "macs.hpp"
11#include "clisp.hpp"
12#include "status.hpp"
13#include "dev.hpp"
14#include "demo.hpp"
15#include "pcxread.hpp"
16#include "profile.hpp"
17#include "sbar.hpp"
18#include "cop.hpp"
19#include "nfserver.hpp"
20#include "lisp_gc.hpp"
21
22#if (defined(__MACH__) || !defined(__APPLE__))
23#include <sys/stat.h>
24#endif
25
26#include <limits.h>
27#include <time.h>
28
29level *current_level;
30
31game_object *level::attacker(game_object *who)
32{
33  int32_t d=0x7fffffff;
34  game_object *c=NULL;
35  view *f=the_game->first_view;
36  for (;f;f=f->next)
37  {
38    if (f->focus)
39    {
40      int32_t tmp_d=abs(f->focus->x-who->x)+abs(f->focus->y-who->y);
41      if (tmp_d<d)
42      {
43        d=tmp_d;
44        c=f->focus;
45      }
46    }
47  }
48  CONDITION(c,"no attacker found");
49  return c;
50}
51
52
53
54int level::is_attacker(game_object *who)
55{
56  return who->controller()!=NULL;
57}
58
59
60game_object *level::main_character()
61{
62  return the_game->first_view->focus;
63}
64
65void level::load_fail()
66{
67  if (map_fg)    jfree(map_fg);   map_fg=NULL;
68  if (map_bg)    jfree(map_bg);   map_bg=NULL;
69  if (Name)      jfree(Name);     Name=NULL;
70
71  first_active=NULL;
72  view *f=player_list;
73  for (;f;f=f->next)
74    if (f->focus)
75      current_level->remove_object(f->focus);
76
77  while (first)
78  {
79    first_active=first;
80    first=first->next;
81    if (dev_cont)
82      dev_cont->notify_deleted_object(first_active);
83    delete first_active;
84  }
85
86  while (area_list)
87  {
88    area_controller *l=area_list;
89    area_list=area_list->next;
90    delete l;
91  }
92
93  last=NULL;
94  delete_panims();
95  delete_all_lights();
96
97}
98
99level::~level()
100{
101  load_fail();
102  if (attack_list) jfree(attack_list);
103  if (target_list) jfree(target_list);
104  if (block_list) jfree(block_list);
105  if (all_block_list) jfree(all_block_list);
106  if (first_name) jfree(first_name);
107}
108
109void level::restart()
110{
111  view *f;
112  game_object *found=NULL,*o;
113  f=the_game->first_view;
114  for (o=first;f && o;o=o->next)
115  {
116    while (f && !f->focus) f=f->next;
117    if (f)
118    {
119      if (!strcmp(object_names[o->otype],"START"))
120      {
121        if (!found) found=o;
122        f->focus->x=o->x;
123        f->focus->y=o->y;         
124        f->focus->set_hp(get_ability(f->focus->otype,start_hp));
125        f->focus->set_state(stopped);
126        f=f->next;
127      }
128    }
129  }
130  while (f)
131  {
132    if (f->focus)
133    {
134      f->focus->x=found->x;
135      f->focus->y=found->y;         
136      f->focus->set_hp(get_ability(f->focus->otype,start_hp));
137      f->focus->set_state(stopped);     
138    }
139    f=f->next;
140  }
141}
142
143
144void level::next_focus()
145{
146/*  int i;
147  for (i=0;i<total_objs;i++)
148    if (obj[i]==the_game->first_view->focus)
149    {     
150      int tries=total_objs;
151      do
152      {
153        i++;
154        if (i==total_objs)
155          i=0;
156        the_game->first_view->focus=obj[i];
157      }  while ((!the_game->first_view->focus->is_playable() ||
158                 the_game->first_view->focus->hp<=0) && tries--);
159      return ;
160    }         */ 
161}
162
163void level::unactivate_all()
164{
165  first_active=NULL;
166  game_object *o=first;
167  attack_total=0;  // reset the attack list
168  target_total=0;
169  block_total=0;
170  all_block_total=0;
171
172  for (;o;o=o->next)
173    o->active=0;
174}
175
176
177void level::pull_actives(game_object *o, game_object *&last_active, int &t)
178{
179  int i=o->total_objects();
180  for (;i;i--)        // pull any linked object into active list
181  {
182    game_object *other=o->get_object(i-1);
183    if (!other->active)
184    {
185      other->active=1;
186      if (other->can_block())              // if object can block other player, keep a list for fast testing
187      {
188        add_block(other);
189        add_all_block(other);
190      } else if (other->hurtable())
191        add_all_block(other);
192
193      t++;
194      last_active->next_active=other;
195      last_active=other;       
196      pull_actives(o,last_active,t);
197    }
198  }
199}
200
201int level::add_actives(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
202{
203  int t=0;
204  game_object *last_active=NULL;
205  if (first_active)
206    for (last_active=first_active;last_active->next_active;last_active=last_active->next_active);
207
208  game_object *o=first;
209  for (;o;o=o->next)
210  {   
211    if (!o->active)
212    {
213      int32_t xr=figures[o->otype]->rangex,
214           yr=figures[o->otype]->rangey;
215
216      if (o->x+xr>=x1 && o->x-xr<=x2 && o->y+yr>=y1 && o->y-yr<=y2)
217      {
218       
219        if (o->can_block())              // if object can block other player, keep a list for fast testing
220        {
221          add_block(o);
222          add_all_block(o);
223        } else if (o->hurtable())
224          add_all_block(o);
225     
226
227        o->active=1;
228        t++;
229        if (!first_active)     
230          first_active=o;       
231        else
232          last_active->next_active=o;
233        last_active=o;
234
235        pull_actives(o,last_active,t); 
236      } 
237    } 
238  }
239  if (last_active)
240    last_active->next_active=NULL;
241  return t;
242}
243
244
245int level::add_drawables(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
246{
247  int t=0,ft=0;
248  game_object *last_active=NULL;
249  if (first_active)
250  {
251    for (last_active=first_active;last_active->next_active;last_active=last_active->next_active);
252  } else ft=1;
253
254  game_object *o=first;
255  for (;o;o=o->next)
256  {   
257    if (ft || !o->active)
258    {
259      int32_t xr=figures[o->otype]->draw_rangex,
260      yr=figures[o->otype]->draw_rangey;
261
262      if (o->x+xr>=x1 && o->x-xr<=x2 && o->y+yr>=y1 && o->y-yr<=y2)
263      {
264        t++;
265        if (!first_active)     
266        first_active=o;
267        else
268        last_active->next_active=o;
269        last_active=o;
270        o->active=1;
271      } else if (ft) o->active=0;  // if this is the first pass, then mark objects not in this ranges as not active
272    }
273  }
274  if (last_active)
275    last_active->next_active=NULL;
276  return t;
277}
278
279
280view *level::make_view_list(int nplayers)
281{
282  int startable;
283  CONDITION(nplayers>0,"make_view_list with <=0 players!\n");
284  view *f=NULL;
285  int j,use_type=current_start_type;
286  figures[use_type]->cache_in();
287  game_object *o,*last_start=NULL;
288  int num=0;
289 
290  for (j=0,o=first;o && j<nplayers;o=o->next)
291  {
292    if (!strcmp(object_names[o->otype],"START"))
293    {
294      f=new view(create(use_type,o->x,o->y),f,num); num++;
295      f->focus->set_controller(f);
296      add_object_after(f->focus,o);
297      j++;
298      last_start=o;
299    }
300  }
301 
302  // if we couldn't find enough starts then create the rest of the players at the original start
303  startable=j;  // if we haven't created anyone yet, it's because we can't
304
305  for (;j<nplayers;j++)
306  {
307    if (startable)
308    {
309      game_object *o=create(use_type,f->focus->x,f->focus->y);
310      f=new view(o,f,num); num++;
311      f->focus->set_controller(f);
312      add_object_after(o,last_start);
313    }
314    else
315    {
316      f=new view(NULL,f,num);
317      num++;
318    }
319  }
320  return f;
321}
322
323void level::wall_push()
324{
325  int32_t sx1,sy1,sx2,sy2,xv,yv;
326  game_object *o=first_active;
327  for (;o;o=o->next_active)
328  {
329    if (o->pushable())
330    {
331      o->picture_space(sx1,sy1,sx2,sy2);
332      xv=sx1-o->x;
333      yv=0;
334      o->try_move(o->x,o->y-1,xv,yv,1);         // check for wall pushes on the left using feet
335      if (xv!=sx1-o->x)                         // is the character in the wall?
336      {
337        xv=-xv;
338        o->try_move(o->x,o->y-1,xv,yv,1);       // see how far to the right we can push the character
339        o->x+=xv;
340      } else
341      {
342        xv=sx2-o->x;
343        o->try_move(o->x,o->y-1,xv,yv,1);      // now check the right of the character for a wall
344        if (xv!=sx2-o->x)
345        {
346          xv=-xv;
347          o->try_move(o->x,o->y-1,xv,yv,1);
348          o->x+=xv;
349        }
350      }           
351    }
352  }
353}
354
355
356void level::try_pushback(game_object *subject,game_object *target)
357{
358  if (subject->pushable() && target->pushable() &&
359      subject->state!=dead && target->state!=dead &&
360      subject->state!=dieing && target->state!=dieing)
361  {
362    int b1=subject->push_range(),b2=target->push_range();
363    if (abs(subject->x-target->x)<b1+b2)
364    {
365      int32_t tmove=b1+b2-abs(subject->x-target->x),xv,yv=0,xv2;
366      if (subject->x>target->x)
367        xv=tmove/2;
368      else xv=-tmove/2;
369      xv2=-xv;
370
371      subject->try_move(subject->x,subject->y,xv,yv,3);
372      subject->x+=xv;
373
374      yv=0;
375      target->try_move(target->x,target->y,xv2,yv,3);
376      target->x+=xv2;     
377    }
378  }
379}
380
381/*
382void level::check_collisions()
383{
384  game_object *target,*reciever=NULL;
385  int32_t sx1,sy1,sx2,sy2,tx1,ty1,tx2,ty2,hitx,hity,
386      s_centerx,t_centerx;
387
388  for (game_object *subject=first_active;subject;subject=subject->next_active)
389  {
390    subject->picture_space(sx1,sy1,sx2,sy2);   
391    s_centerx=subject->x_center();
392
393    int hit=0;
394    reciever=NULL;
395    for (target=first_active;target;target=target->next_active)
396    {
397      if (target!=subject)
398      {
399        target->picture_space(tx1,ty1,tx2,ty2);
400
401        if (!(sx2<tx1 || sx1>tx2 || sy1>ty2 || sy2<ty1))  // are they semi/overlapping?
402        {   
403          try_pushback(subject,target);
404          if (subject->can_hurt(target))    // see if we can hurt him before calculating
405          {         
406            t_centerx=target->x_center();
407            point_list *s_hit,*t_damage;
408                         
409            s_hit=subject->current_figure()->hit;         
410            t_damage=target->current_figure()->damage;
411
412            unsigned char *s_dat=s_hit->data,
413            *t_dat;
414            int i,j;
415            for (i=(int)s_hit->tot-1;i>0 && !hit;i--)
416            {
417              for (t_dat=t_damage->data,j=(int)t_damage->tot-1;j>0 && !hit;j--)
418              {
419                int32_t x1,y1,x2,y2,          // define the two line segments to check
420                xp1,yp1,xp2,yp2;
421
422                xp1=target->x+target->tx(*t_dat);  t_dat++;           
423                yp1=target->y+target->ty(*t_dat);  t_dat++;
424                xp2=target->x+target->tx(*t_dat);
425                yp2=target->y+target->ty(t_dat[1]);
426
427                x1=subject->x+subject->tx(s_dat[0]); 
428                y1=subject->y+subject->ty(s_dat[1]);
429                x2=subject->x+subject->tx(s_dat[2]);
430                y2=subject->y+subject->ty(s_dat[3]);
431             
432
433                // ok, now we know which line segemnts to check for intersection
434                // now check to see if (x1,y1-x2,y2) intercest with (xp1,yp1-xp2,yp2)
435                int _x2=x2,_y2=y2;           
436                setback_intersect(x1, y1, x2, y2, xp1, yp1, xp2, yp2,0);
437
438
439                if (x2!=_x2 || _y2!=y2)
440                {
441                  reciever=target;
442                  hitx=((x1+x2)/2+(xp1+xp2)/2)/2;
443                  hity=((y1+y1)/2+(yp1+yp2)/2)/2;
444                }
445              }
446              s_dat+=2;
447            }       
448          }
449        }
450      } 
451    }   
452    if (reciever)
453    {
454      reciever->do_damage((int)subject->current_figure()->hit_damage,subject,hitx,hity,0,0);
455      subject->note_attack(reciever);
456      hit=1;
457    }
458  }
459}
460*/
461
462game_object *level::boundary_setback(game_object *subject, int32_t x1, int32_t y1, int32_t &x2, int32_t &y2)
463{
464  game_object *l=NULL;
465  int32_t tx1,ty1,tx2,ty2,t_centerx;
466  game_object *target=first_active;
467  game_object **blist=block_list;
468  int t=block_total;
469  for (;t;t--,blist++)
470  {
471    target=*blist;
472    if (target!=subject && (target->total_objects()==0 || target->get_object(0)!=subject))
473    {
474      target->picture_space(tx1,ty1,tx2,ty2);
475      if (!((x2<tx1 && x1<tx1) || (x1>tx2 && x2>tx2) ||
476            (y1>ty2 && y2>ty2) || (y1<ty1 && y2<ty1)))  // are they semi/overlapping?
477      {         
478        t_centerx=target->x_center();
479        boundary *t_damage;
480        if (target->direction>0)
481        t_damage=target->current_figure()->f_damage;
482        else
483        t_damage=target->current_figure()->b_damage;
484        unsigned char *t_dat=t_damage->data,*ins=t_damage->inside;
485        int iter=t_damage->tot-1;
486        while(iter-->0)
487        {
488          int32_t xp1=target->x+target->tx(*t_dat);  t_dat++;         
489          int32_t yp1=target->y+target->ty(*t_dat);  t_dat++;
490          int32_t xp2=target->x+target->tx(*t_dat);
491          int32_t yp2=target->y+target->ty(t_dat[1]);
492
493          // now check to see if (x1,y1-x2,y2) intercest with (xp1,yp1-xp2,yp2)
494          if (*ins)
495          {               
496            if (setback_intersect(x1,y1,x2,y2,xp1,yp1,xp2,yp2,1))
497            l=target;
498          }         
499          else
500          {
501            if (setback_intersect(x1,y1,x2,y2,xp1,yp1,xp2,yp2,-1))
502            l=target;
503          }         
504          ins++;
505         
506        }         
507      }     
508    }   
509  }
510  return l;       // return the last person we intersected 
511}
512
513
514game_object *level::all_boundary_setback(game_object *subject, int32_t x1, int32_t y1, int32_t &x2, int32_t &y2)
515{
516  game_object *l=NULL;
517  int32_t tx1,ty1,tx2,ty2,t_centerx;
518  game_object *target=first_active;
519  game_object **blist=all_block_list;
520  int t=all_block_total;
521  for (;t;t--,blist++)
522  {
523    target=*blist;
524    if (target!=subject && (target->total_objects()==0 || target->get_object(0)!=subject))
525    {
526      target->picture_space(tx1,ty1,tx2,ty2);
527      if (!((x2<tx1 && x1<tx1) || (x1>tx2 && x2>tx2) ||
528            (y1>ty2 && y2>ty2) || (y1<ty1 && y2<ty1)))  // are they semi/overlapping?
529      {         
530        t_centerx=target->x_center();
531        boundary *t_damage;
532        if (target->direction>0)
533        t_damage=target->current_figure()->f_damage;
534        else
535        t_damage=target->current_figure()->b_damage;
536        unsigned char *t_dat=t_damage->data,*ins=t_damage->inside;
537        int iter=t_damage->tot-1;
538        while(iter-->0)
539        {
540          int32_t xp1=target->x+target->tx(*t_dat);  t_dat++;         
541          int32_t yp1=target->y+target->ty(*t_dat);  t_dat++;
542          int32_t xp2=target->x+target->tx(*t_dat);
543          int32_t yp2=target->y+target->ty(t_dat[1]);
544
545          // now check to see if (x1,y1-x2,y2) intercest with (xp1,yp1-xp2,yp2)
546          if (*ins)
547          {               
548            if (setback_intersect(x1,y1,x2,y2,xp1,yp1,xp2,yp2,1))
549            l=target;
550          }         
551          else
552          {
553            if (setback_intersect(x1,y1,x2,y2,xp1,yp1,xp2,yp2,-1))
554            l=target;
555          }         
556          ins++;         
557        }         
558      }     
559    }   
560  }
561  return l;       // return the last person we intersected 
562}
563
564
565//bFILE *rcheck=NULL,*rcheck_lp=NULL;
566
567void level::interpolate_draw_objects(view *v)
568{
569  int32_t old_x,old_y;
570  current_view=v;
571 
572  game_object *o=first_active;
573  for (;o;o=o->next_active)
574  {
575    old_x=o->x;
576    old_y=o->y;
577    o->x=(o->last_x+o->x)/2;
578    o->y=(o->last_y+o->y)/2;
579    o->last_x=old_x;
580    o->last_y=old_y;
581  }
582
583  for (o=first_active;o;o=o->next_active)
584    o->draw();
585
586  for (o=first_active;o;o=o->next_active)
587  {
588    o->x=o->last_x;
589    o->y=o->last_y;
590  }
591}
592
593bFILE *rcheck=NULL,*rcheck_lp=NULL;
594
595extern int sshot_fcount,screen_shot_on;
596
597int level::tick()
598{
599  game_object *o,*l=NULL,  // l is last, used for delete
600              *cur;        // cur is current object, NULL if object deletes it's self
601  int ret=1;
602
603  if (profiling())
604    profile_reset();
605
606/*  // test to see if demo is in sync
607  if (current_demo_mode()==DEMO_PLAY)
608  {
609    if (!rcheck) rcheck=open_file("rcheck","rb");
610    int32_t x=rcheck->read_long();
611    if (x!=rand_on)
612      dprintf("off!\n");
613  } else if (current_demo_mode()==DEMO_RECORD)
614  {
615    if (!rcheck)
616    {
617      rcheck=open_file("rcheck","wb");
618      rcheck_lp=open_file("rcheck.lp","wb");
619    }
620    rcheck->write_long(rand_on);
621  } else
622  {
623    if (rcheck)
624    {
625      delete rcheck;
626      rcheck=NULL;
627    }
628    if (rcheck_lp)
629    {
630      delete rcheck_lp;
631      rcheck_lp=NULL;
632    }
633  }*/
634 
635  for (o=first_active;o;)
636  {       
637    o->last_x=o->x;
638    o->last_y=o->y;
639    cur=o;
640    view *c=o->controller();
641    if (!(dev&SUSPEND_MODE) || c)
642    {
643      o->set_flags(o->flags()&(0xff-FLAG_JUST_HIT-FLAG_JUST_BLOCKED));
644
645      if (c)
646      {     
647        area_controller *a,*smallest=NULL;
648        int32_t smallest_size=0xffffffff;
649        for (a=area_list;a;a=a->next)
650          if (o->x>=a->x && o->y>=a->y && o->x<=a->x+a->w && o->y<=a->y+a->h)
651          {
652            int32_t size=a->w*a->h;
653            if (size<smallest_size)
654            {
655              smallest=a;
656              smallest_size=size;
657            }
658          }
659
660        if (c->local_player())
661        {
662          if (!shutdown_lighting)       // should we initiate a lighting shutdown?
663          {
664            if (massive_frame_panic>30)
665            {
666              shutdown_lighting=100;
667              shutdown_lighting_value=c->ambient;
668            }
669          } else if (massive_frame_panic)  // do we need brighten towards 63?
670          {
671            if (shutdown_lighting_value<63)
672              shutdown_lighting_value++;
673          } else if (shutdown_lighting>1)        // delay for some time before turning back on
674            shutdown_lighting--;
675          else if (shutdown_lighting_value!=c->ambient) // do we need to lower light toward real ambient?
676          {
677            if (abs(shutdown_lighting_value-c->ambient)<4)
678              shutdown_lighting_value=c->ambient;
679            else
680              if (shutdown_lighting_value<c->ambient)
681                shutdown_lighting_value+=4;
682            else if (shutdown_lighting_value>c->ambient)
683              shutdown_lighting_value-=4;
684          } else shutdown_lighting=0;                    // back to normal
685        }
686
687        if (smallest)
688          c->configure_for_area(smallest);
689
690
691        o->move(c->x_suggestion,c->y_suggestion,c->b1_suggestion|(c->b2_suggestion<<1)|
692                (c->b3_suggestion<<2));
693
694        if (o->otype!=current_start_type)
695        {
696          int32_t fmp=o->fmp();     
697          int reduce=figures[o->otype]->morph_power;
698          if (reduce)
699          {
700            fmp-=reduce;
701            o->add_power(fmp>>16);
702            o->set_fmp(fmp&0xffff);
703            if (o->mp()<=0)
704            o->morph_into(current_start_type,NULL,-1,9);
705          }
706        }
707
708        l=o;
709        o=o->next_active;       
710      }
711      else if (!o->decide())      // if object returns 0, delete it... I don't like 0's :)
712      {
713        game_object *p=o;
714        o=o->next_active;
715        delete_object(p);
716        cur=NULL;
717      } else
718      {
719        o=o->next_active;
720        l=o;
721      }
722    } else
723    {
724      o=o->next_active;
725      l=o;     
726    }
727
728    clear_tmp();
729
730    if (cur)
731    {
732      point_list *p=cur->current_figure()->hit;  // see if this character is on an attack frame
733      if (p && p->tot)
734        add_attacker(cur);               // if so add him to attack list for later collision detect
735     
736      if (cur->hurtable())                    // add to target list if is hurtable
737        add_target(cur);
738     
739    }
740
741  }
742  tick_panims();
743
744  check_collisions();
745//  wall_push();
746
747  set_tick_counter(tick_counter()+1);
748
749  if (sshot_fcount!=-1)
750  {
751    sshot_fcount++;
752    if ((sshot_fcount%70)==0)
753    {   
754      char name[100];
755      sprintf(name,"shot%04d.pcx",screen_shot_on++);
756      write_PCX(screen,pal,name);
757    }
758  }
759
760  return ret;
761}
762
763void level::set_tick_counter(ulong x)
764{
765  ctick=x;
766}
767
768void level::draw_areas(view *v)
769{
770  int32_t sx1,sy1,sx2,sy2;
771  area_controller *a=area_list;
772  for (;a;a=a->next)
773  {
774    int c1,c2;
775    if (a->active)
776    {
777      c1=morph_sel_frame_color;
778      c2=eh->bright_color();
779    } else
780    {
781      c2=morph_sel_frame_color;
782      c1=eh->bright_color();
783    }
784   
785    the_game->game_to_mouse(a->x,a->y,v,sx1,sy1);
786    the_game->game_to_mouse(a->x+a->w,a->y+a->h,v,sx2,sy2);
787    screen->rectangle(sx1,sy1,sx2,sy2,c1);
788    screen->bar(sx1-1,sy1-1,sx1+1,sy1+1,c2);
789    screen->bar(sx2-1,sy2-1,sx2+1,sy2+1,c2);
790  }
791}
792
793void level::draw_objects(view *v)
794{
795  current_view=v;
796  game_object *o=first_active;
797  if (dev&MAP_MODE)
798  {
799    for (;o;o=o->next_active)
800      o->map_draw();
801  } else
802  {
803    for (;o;o=o->next_active)
804      o->draw();
805  }
806
807  clear_tmp();
808
809
810void calc_bgsize(uint16_t fgw, uint16_t  fgh, uint16_t  &bgw, uint16_t  &bgh)
811{
812  bgw=fgw/ASPECT+8;
813  bgh=fgh/ASPECT+8; 
814}
815
816
817void level::set_size(int w, int h)
818{
819  if (w*h>200000)
820  {
821    the_game->show_help(symbol_str("too_big"));
822    return ;
823  }
824
825  uint16_t *new_fg,*new_bg;
826  new_fg=(uint16_t *)jmalloc(w*h*sizeof(int16_t),"Map fg : resized");
827  memset(new_fg,0,w*h*sizeof(int16_t));
828
829  int x,y,miny=(h<fg_height)? h : fg_height,minx=(w<fg_width)? w : fg_width;
830 
831  uint16_t nbw,nbh;
832  calc_bgsize(w,h,nbw,nbh);
833 
834  new_bg=(uint16_t *)jmalloc((int)nbw*(int)nbh*sizeof(int16_t),"map bg : resized");
835  memset(new_bg,0,(int)nbw*(int)nbh*sizeof(int16_t));
836
837  for (y=0;y<miny;y++)
838    for (x=0;x<minx;x++)
839      new_fg[x+y*w]=get_fg(x,y);
840
841  miny=(nbh<bg_height) ? nbh : bg_height;
842  minx=(nbw<bg_width) ? nbw : bg_width;
843
844  for (y=0;y<miny;y++)
845    for (x=0;x<minx;x++)
846      new_bg[x+y*nbw]=get_bg(x,y);
847
848  jfree(map_fg);
849  jfree(map_bg);
850  map_fg=new_fg;
851  map_bg=new_bg;
852  fg_width=w;
853  fg_height=h;
854  bg_height=nbh;
855  bg_width=nbw;
856
857  char msg[80];
858  sprintf(msg,"Level %s size now %d %d\n",name(),foreground_width(),foreground_height());
859  the_game->show_help(msg);
860}
861
862
863int locate_var(bFILE *fp, spec_directory *sd, char *str, int size)
864{
865  spec_entry *se=sd->find(str);
866  if (se)
867  {
868    fp->seek(se->offset,0);
869    if (RC_type_size(fp->read_byte())!=size)
870      return 0;
871    else return 1;
872  }
873  return 0;
874}
875
876
877// load objects assumes current objects have already been disposed of
878void level::old_load_objects(spec_directory *sd, bFILE *fp)
879{
880  spec_entry *se=sd->find("objects");
881  total_objs=0;
882  first=last=first_active=NULL;
883  int i,j;
884  if (se)
885  {
886    fp->seek(se->offset,0);
887    /******************************* Read debug info ******************************/
888    int16_t old_tot=fp->read_short(); 
889    uint16_t *o_remap=(uint16_t *)jmalloc(old_tot*2,"obj remap array");   
890    char old_name[150];     
891    for (i=0;i<old_tot;i++)
892    {
893      fp->read(old_name,fp->read_byte());    // read the name
894      for (o_remap[i]=0xffff,j=0;j<total_objects;j++)  // check for matching current name
895      {
896        if (!strcmp(old_name,object_names[j]))
897          o_remap[i]=j;
898      }
899    }
900
901
902    /***************************** Read state names *********************************/
903    int old_stot=fp->read_short();
904    unsigned char *s_remap=(unsigned char *)jmalloc(old_stot,"state remap array");
905    for (i=0;i<old_stot;i++)
906    {
907      fp->read(old_name,fp->read_byte());
908      s_remap[i]=stopped;           // non exsitant states get mapped into stopped state
909      for (j=0;j<MAX_STATE;j++)                  // see if old state exist now
910        if (!strcmp(state_names[j],old_name))
911         s_remap[i]=j;
912    }
913    total_objs=fp->read_long();   
914
915    se=sd->find("type");
916    if (se)
917    {
918      fp->seek(se->offset,0);
919      last=NULL;
920      if (fp->read_byte()==RC_S)    //  read type array, this should be type RC_S
921      {
922        for (i=0;i<total_objs;i++)
923        {
924          ushort t=fp->read_short();
925          game_object *p=new game_object(o_remap[t],1);
926          clear_tmp();
927          if (!first) first=p; else last->next=p;
928          last=p; p->next=NULL;           
929        }
930
931       
932        se=sd->find("state");
933        if (se)
934        {
935          fp->seek(se->offset,0);
936          if (fp->read_byte()==RC_S)    //  read state array, this should be type RC_S
937          {
938            game_object *l=first;
939            for (i=0;i<total_objs;i++,l=l->next)
940            {
941              character_state s=(character_state)s_remap[fp->read_short()];
942              if (l->otype!=0xffff)
943              {
944                if (l->has_sequence((character_state)s))
945                  l->state=s;
946                else l->state=stopped;
947                l->current_frame=0;
948              }
949            }
950          }
951        }
952
953        int frame_var=0;
954        int i=0;
955        for (;i<TOTAL_OBJECT_VARS;i++)
956          if (!strcmp(object_descriptions[i].name,"cur_frame"))
957            frame_var=i;
958           
959        int j=0;
960        for (;j<default_simple.total_vars();j++)
961        {
962          spec_entry *se=sd->find(object_descriptions[j].name);
963          if (se)
964          {
965            fp->seek(se->offset,0);
966            int t=object_descriptions[j].type;
967            if (fp->read_byte()!=t)
968              dprintf("Warning : load level -> var '%s' size changed\n");
969            else
970            {
971              game_object *f=first;
972              for (;f;f=f->next)
973              {       
974                switch (t)
975                {
976                  case RC_C : f->set_var(j,fp->read_byte()); break;
977                  case RC_S : f->set_var(j,fp->read_short()); break;
978                  case RC_L : f->set_var(j,fp->read_long()); break;
979                }
980
981                // check to make sure the frame number is not out of bounds from the time
982                // it was last saved
983                if (j==frame_var)
984                {
985                  if (f->otype!=0xffff && f->current_frame>=
986                      figures[f->otype]->get_sequence(f->state)->length())
987                    f->current_frame=0;
988                }
989              }
990            }       
991          } else dprintf("Warning : load level -> no previous var %s\n",default_simple.var_name(j));
992        }
993      }
994    }
995   
996   
997
998    jfree(o_remap);
999    jfree(s_remap);
1000  } 
1001 
1002}
1003
1004
1005// load objects assumes current objects have already been disposed of
1006void level::load_objects(spec_directory *sd, bFILE *fp)
1007{
1008  spec_entry *se=sd->find("object_descripitions");
1009  total_objs=0;
1010  first=last=first_active=NULL;
1011  int i,j;
1012  if (!se)
1013  {
1014    old_load_objects(sd,fp);
1015    return ;
1016  }
1017  else if (se)
1018  {
1019    fp->seek(se->offset,0);
1020    int16_t old_tot=fp->read_short();
1021    se=sd->find("describe_names");
1022    if (!se || !old_tot)
1023      return ;
1024
1025    uint16_t *o_remap=(uint16_t *)jmalloc(old_tot*2,"obj remap array");   
1026    uint16_t *o_backmap=(uint16_t *)jmalloc(total_objects*2,"obj remap array");   
1027    memset(o_backmap,0xff,total_objects*2);
1028    char old_name[150];     
1029    for (i=0;i<old_tot;i++)
1030    {
1031      fp->read(old_name,fp->read_byte());    // read the name
1032      for (o_remap[i]=0xffff,j=0;j<total_objects;j++)  // check for matching current name
1033      {
1034        if (!strcmp(old_name,object_names[j]))
1035        {
1036          o_remap[i]=j;
1037          o_backmap[j]=i;
1038        }
1039      }
1040    }   
1041
1042    se=sd->find("describe_states");
1043    if (!se) { jfree(o_remap); jfree(o_backmap); return ; }
1044    int16_t **s_remap=(int16_t **)jmalloc(old_tot*sizeof(int16_t *),"big state remap array");
1045    int16_t *s_remap_totals=(int16_t *)jmalloc(old_tot*sizeof(int16_t),"big state rmp totals");
1046    fp->seek(se->offset,0);
1047    int i=0;
1048    for (;i<old_tot;i++)
1049    {
1050      int16_t t=fp->read_short();
1051      s_remap_totals[i]=t;
1052      if (t)
1053      {
1054        s_remap[i]=(int16_t *)jmalloc(t*sizeof(int16_t),"state remap");
1055        int j=0;
1056        for (;j<t;j++)
1057          *(s_remap[i]+j)=stopped;    // if no remap found, then go to stopped state
1058      }
1059      else s_remap[i]=0;
1060
1061      int j=0;
1062      for (;j<t;j++)
1063      {
1064        fp->read(old_name,fp->read_byte());
1065        int new_type=o_remap[i];       
1066        if (new_type<total_objects)     // make sure old object still exsist
1067        {
1068          int k=0;
1069          for (;k<figures[new_type]->ts;k++)
1070          {
1071            if (figures[new_type]->seq[k] &&
1072               !strcmp(lstring_value(symbol_name(figures[new_type]->seq_syms[k])),old_name))
1073            *(s_remap[i]+j)=k;
1074          }
1075        }
1076      }
1077    }
1078
1079    int16_t **v_remap=NULL;
1080    int16_t *v_remap_totals=NULL;
1081    int load_vars=1;
1082    se=sd->find("describe_lvars");
1083    if (se)
1084    {
1085      v_remap=(int16_t **)jmalloc(old_tot*sizeof(int16_t *),"big var remap array");
1086      v_remap_totals=(int16_t *)jmalloc(old_tot*sizeof(int16_t),"big var rmp totals");
1087
1088      fp->seek(se->offset,0);
1089      int i=0;
1090      for (;i<old_tot;i++)
1091      {
1092        int16_t t=fp->read_short();
1093        v_remap_totals[i]=t;
1094        if (t)
1095        {
1096          v_remap[i]=(int16_t *)jmalloc(t*sizeof(int16_t),"var remap");
1097          memset(v_remap[i],0xff,t*sizeof(int16_t));
1098        } else { v_remap[i]=NULL; }
1099        int j=0;
1100        for (;j<t;j++)
1101        {
1102          fp->read(old_name,fp->read_byte());
1103          int new_type=o_remap[i];
1104          if (new_type!=0xffff)        // make sure old object still exsist
1105          {
1106            int k=0;
1107            for (;k<figures[new_type]->tiv;k++)
1108            {
1109              if (figures[new_type]->vars[k])
1110              {
1111                if (!strcmp(lstring_value(symbol_name(figures[new_type]->vars[k])),old_name))
1112                  *(v_remap[i]+j)=figures[new_type]->var_index[k];
1113              }
1114            }
1115          }
1116        }
1117      }
1118      load_vars=1;
1119    }
1120   
1121    se=sd->find("object_list");
1122    if (se)
1123    {
1124      total_objs=fp->read_long();   
1125
1126      se=sd->find("type");
1127      if (se)
1128      {
1129        fp->seek(se->offset,0);
1130        last=NULL;
1131        if (fp->read_byte()==RC_S)    //  read type array, this should be type RC_S
1132        {
1133          int i=0;
1134          for (;i<total_objs;i++)
1135          {
1136            ushort t=fp->read_short();
1137            game_object *p=new game_object(o_remap[t],1);
1138            clear_tmp();
1139            if (!first) first=p; else last->next=p;
1140            last=p; p->next=NULL;                 
1141          }
1142       
1143          se=sd->find("state");
1144          if (se)
1145          {
1146            fp->seek(se->offset,0);
1147            if (fp->read_byte()==RC_S)    //  read state array, this should be type RC_S
1148            {
1149              game_object *l=first;
1150              for (i=0;i<total_objs;i++,l=l->next)
1151              {
1152                int st=fp->read_short();
1153                if (l->otype==0xffff)
1154                  l->state=stopped;
1155                else
1156                {
1157                  character_state s=(character_state)(*(s_remap[o_backmap[l->otype]]+st));
1158                  if (l->has_sequence((character_state)s))
1159                    l->state=s;
1160                  else l->state=stopped;
1161                  l->current_frame=0;
1162                }
1163              }
1164            }
1165          }
1166       
1167          se=sd->find("lvars");
1168          if (se && load_vars)
1169          {
1170            fp->seek(se->offset,0);
1171            int abort=0;
1172            game_object *o=first;
1173            for (;o && !abort;o=o->next)
1174            {
1175              int16_t ot=fp->read_short();
1176              int k=0;
1177              for (;k<ot;k++)
1178              {
1179                if (fp->read_byte()!=RC_L) abort=1;
1180                else
1181                {
1182                  int32_t v=fp->read_long();
1183                  if (o->otype!=0xffff)     // non-exstant object
1184                  {
1185                    int remap=*(v_remap[o_backmap[o->otype]]+k);
1186                    if (remap!=-1 && figures[o->otype]->tiv>=k)
1187                    {
1188                      o->lvars[remap]=v;       
1189                    }
1190                  }
1191                }
1192              }
1193            }
1194          }
1195
1196          int frame_var=0;
1197          for (i=0;i<TOTAL_OBJECT_VARS;i++)
1198            if (!strcmp(object_descriptions[i].name,"cur_frame"))
1199              frame_var=i;
1200           
1201
1202          int j=0;
1203          for (;j<default_simple.total_vars();j++)
1204          {
1205            spec_entry *se=sd->find(object_descriptions[j].name);
1206            if (se)
1207            {
1208              fp->seek(se->offset,0);
1209              int t=object_descriptions[j].type;
1210              if (fp->read_byte()!=t)
1211                dprintf("Warning : load level -> var '%s' size changed\n");
1212              else
1213              {
1214                game_object *f=first;
1215                for (;f;f=f->next)
1216                {             
1217                  switch (t)
1218                  {
1219                    case RC_C :
1220                    { f->set_var(j,fp->read_byte()); } break;
1221                    case RC_S :
1222                    { f->set_var(j,fp->read_short()); } break;
1223                    case RC_L :
1224                    { f->set_var(j,fp->read_long()); } break;
1225                  }
1226                 
1227                  // check to make sure the frame number is not out of bounds from the time
1228                  // it was last saved
1229                  if (j==frame_var)
1230                  {
1231                    if (f->otype!=0xffff && f->current_frame>=
1232                        figures[f->otype]->get_sequence(f->state)->length())
1233                    f->current_frame=0;
1234                  }
1235                }
1236              }     
1237            } else dprintf("Warning : load level -> no previous var %s\n",default_simple.var_name(j));
1238          }
1239        }
1240      }
1241    }
1242   
1243    int k=0;
1244    for (;k<old_tot;k++)
1245    {
1246      if (s_remap_totals[k])
1247        jfree(s_remap[k]);
1248    }
1249
1250    int l=0;
1251    for (;l<old_tot;l++)
1252    {
1253      if (v_remap_totals[l])
1254        jfree(v_remap[l]);
1255    }
1256    jfree(v_remap_totals);
1257    jfree(s_remap_totals);
1258    jfree(o_remap);
1259    jfree(o_backmap);
1260    jfree(s_remap);
1261    jfree(v_remap);
1262  } 
1263 
1264}
1265
1266level::level(spec_directory *sd, bFILE *fp, char *lev_name)
1267{
1268  spec_entry *e;
1269  area_list=NULL;
1270
1271  attack_list=NULL;
1272  attack_list_size=attack_total=0;
1273
1274  target_list=NULL;
1275  target_list_size=target_total=0;
1276
1277  block_list=NULL;
1278  block_list_size=block_total=0;
1279
1280  all_block_list=NULL;
1281  all_block_list_size=all_block_total=0;
1282  first_name=NULL;
1283
1284  the_game->need_refresh();
1285
1286  char cmd[100];
1287  sprintf(cmd,symbol_str("loading"),lev_name);
1288  stack_stat stat(cmd);
1289  Name=strcpy((char *)jmalloc(strlen(lev_name)+1,"lev name"),lev_name);
1290
1291  e=sd->find("first name");
1292  if (e)
1293  {
1294    fp->seek(e->offset,0);
1295    int len=fp->read_byte();   // read the length of the string
1296    first_name=(char *)jmalloc(len,"level first name");
1297    fp->read(first_name,len);    // read the string
1298  } else
1299  {
1300    first_name=(char *)jmalloc(strlen(Name)+1,"level first name");   
1301    strcpy(first_name,Name);
1302  }
1303
1304  e=sd->find("fgmap");
1305  int no_fg=0,no_bg=0;
1306
1307  if (e)
1308  {   
1309    fp->seek(e->offset,0);
1310    fg_width=fp->read_long();
1311    fg_height=fp->read_long();
1312    map_fg=(uint16_t *)jmalloc(2*fg_width*fg_height,"Map fg : loaded");
1313    fp->read((char *)map_fg,2*fg_width*fg_height);
1314    int t=fg_width*fg_height;
1315    uint16_t *map=map_fg;
1316    while (t) { *map=lstl(*map); map++; t--; }
1317  } else
1318  {
1319    the_game->show_help("Warning foreground map missing");
1320    no_fg=1;
1321  }
1322  stat_man->update(5);
1323
1324  e=sd->find("bgmap");
1325  if (e)
1326  {
1327    fp->seek(e->offset,0);
1328    bg_width=fp->read_long();
1329    bg_height=fp->read_long();
1330    map_bg=(uint16_t *)jmalloc(2*bg_width*bg_height,"Map bg : loaded");
1331    fp->read((char *)map_bg,2*bg_width*bg_height);
1332    int t=bg_width*bg_height;
1333    uint16_t *map=map_bg;
1334    while (t) { *map=lstl(*map); map++; t--; }
1335  } else
1336  {
1337    the_game->show_help("Warning background map missing");
1338    no_bg=1;
1339  }
1340
1341  if (no_fg && !no_bg)
1342  {
1343    fg_width=bg_width;
1344    fg_height=bg_height;
1345    map_fg=(uint16_t *)jmalloc(2*fg_width*fg_height,"Map fg : loaded");
1346    memset(map_fg,0,2*fg_width*fg_height);
1347  }
1348
1349  if (no_bg)
1350  {
1351    bg_width=fg_width/8+8;
1352    bg_height=fg_height/8+8;
1353    map_bg=(uint16_t *)jmalloc(2*bg_width*bg_height,"Map bg : loaded");
1354    memset(map_bg,0,2*bg_width*bg_height);
1355  }
1356  stat_man->update(10);
1357
1358  /***************** Check map for non exsistant tiles **************************/
1359  int32_t i,w;
1360  uint16_t *m; 
1361  spec_entry *load_all=sd->find("player_info");
1362  for (i=0,w=fg_width*fg_height,m=map_fg;i<w;i++,m++)
1363  {
1364    if (!load_all)
1365      (*m)=(*m)&(~0x8000);    // clear the has-seen bit on the tile
1366
1367    if (fgvalue(*m)>=nforetiles || foretiles[fgvalue(*m)]<0)
1368      *m=0; 
1369  }
1370
1371  for (i=0,w=bg_width*bg_height,m=map_bg;i<w;i++,m++)
1372  {
1373    if ( (bgvalue(*m)>=nbacktiles) || backtiles[bgvalue(*m)]<0)
1374       *m=0; 
1375  }
1376
1377  load_options(sd,fp);
1378  stat_man->update(15);
1379
1380//  first=first_active=last=NULL;
1381  load_objects(sd,fp);
1382  stat_man->update(25);
1383
1384  object_node *players,*objs;
1385  players=make_player_onodes();
1386  objs=make_not_list(players);
1387
1388
1389
1390  read_lights(sd,fp,lev_name);
1391  load_links(fp,sd,objs,players);
1392  int players_got_loaded=load_player_info(fp,sd,objs);
1393
1394
1395  game_object *l=first;
1396  for (;l;)
1397  {
1398    game_object *p=l;
1399    l=l->next;
1400    if (p->otype==0xffff || p->x<0 || p->y<0)
1401      delete_object(p);
1402  }
1403
1404  load_cache_info(sd,fp);
1405
1406  if (!players_got_loaded)
1407  {
1408    level *old=current_level;
1409    current_level=this;
1410   
1411    object_node *list=NULL;
1412    list=make_not_list(list);     // create a list of the object list in case objects change positions
1413
1414    object_node *ln=list;
1415    for (;ln;ln=ln->next)
1416      ln->me->reload_notify();
1417    delete_object_list(list);
1418
1419    current_level=old;
1420
1421    insert_players();
1422  }
1423
1424  delete_object_list(players);
1425  delete_object_list(objs);
1426
1427}
1428
1429
1430/*
1431   [object_descriptions] 2 total_type
1432   for(1..total_types)
1433   {
1434     ["object_names"]  1,(name)
1435
1436     ["object_states"]  2(total),<2=number,1,name>
1437
1438     ["object_lvars"]   2(total),<1(type),1,name>   
1439   }
1440
1441  [object_list]
1442   4 total_objects   
1443   for(1..total_objects)
1444   {
1445     ["type"]
1446     ["state"]
1447     ["lvars"]
1448     ...
1449   }
1450
1451*/
1452
1453
1454void get_prof_assoc_filename(char *filename, char *prof_filename)
1455{
1456  char *s1,*s2,*dot=NULL;
1457  for (s1=filename,s2=prof_filename,dot=NULL;*s1;s1++,s2++)
1458  {
1459    *s2=*s1;
1460    if (*s1=='.') dot=s2;   
1461  }
1462  if (dot) s2=dot+1;
1463
1464  *(s2++)='c';
1465  *(s2++)='p';
1466  *(s2++)='f';
1467  *s2=0;
1468}
1469
1470void level::level_loaded_notify()
1471{
1472  char *n;
1473  if (first_name)
1474    n=first_name;
1475  else
1476    n=name();
1477  if (strstr(n,"levels/level"))
1478  {
1479    char nm[100];
1480    sprintf(nm,"music/abuse%c%c.hmi",n[12],n[13]);
1481    bFILE *fp=open_file(nm,"rb");
1482    if (fp->open_failure())
1483    {
1484      delete fp;
1485    }
1486    else
1487    {
1488      if (current_song) { current_song->stop(); delete current_song; }
1489
1490      delete fp;
1491      current_song=new song(nm);
1492      current_song->play(music_volume);
1493    }
1494  }
1495 
1496/*  if (DEFINEDP(symbol_function(l_level_loaded)))
1497  {
1498    int sp=current_space;
1499    current_space=PERM_SPACE;
1500
1501    void *arg_list=NULL;
1502    p_ref r1(arg_list);
1503    push_onto_list(new_lisp_string(n),arg_list);
1504    eval_function((lisp_symbol *)l_level_loaded,arg_list);
1505
1506    current_space=sp;
1507  } */
1508}
1509
1510
1511bFILE *level::create_dir(char *filename, int save_all,
1512                         object_node *save_list, object_node *exclude_list)
1513{
1514  spec_directory sd;
1515  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"Copyright 1995 Crack dot Com, All Rights reserved",NULL,0,0));
1516  if (first_name)
1517    sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"first name",NULL,strlen(first_name)+2,0));
1518   
1519
1520
1521  sd.add_by_hand(new spec_entry(SPEC_GRUE_FGMAP,"fgmap",NULL,4+4+fg_width*fg_height*2,0));
1522  sd.add_by_hand(new spec_entry(SPEC_GRUE_BGMAP,"bgmap",NULL,4+4+bg_width*bg_height*2,0));
1523  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"bg_scroll_rate",NULL,1+4*4,0));
1524
1525  int ta=0;
1526  area_controller *a=area_list;
1527  for (;a;a=a->next) ta++;
1528
1529  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"area_list.v1",NULL,1+ta*(4*11)+4,0));
1530 
1531  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"tick_counter",NULL,1+4,0));
1532
1533
1534
1535  // how many object types are we goint to save, use a short to specify how many
1536  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"object_descripitions",NULL,2,0));
1537
1538
1539  int size=0;
1540  int i=0;
1541  for (;i<total_objects;i++)       // now save the names of the objects so if ordering
1542    size+=1+strlen(object_names[i])+1;    // changes in future versions we can adjust in load
1543  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"describe_names",NULL,size,0));
1544
1545
1546  size=0;                               
1547  for (i=0;i<total_objects;i++)
1548  {
1549    size+=2;  // total number of states
1550    int j=0;
1551    for (;j<figures[i]->ts;j++)
1552      if (figures[i]->seq[j])
1553        size+=1+strlen(lstring_value(symbol_name(figures[i]->seq_syms[j])))+1;
1554  }
1555  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"describe_states",NULL,size,0));
1556
1557
1558
1559  size=0;
1560  for (i=0;i<total_objects;i++)
1561  {
1562    size+=2;  // total number of variables
1563    int j=0;
1564    for (;j<figures[i]->tiv;j++)
1565      if (figures[i]->vars[j])
1566        size+=1+strlen(lstring_value(symbol_name(figures[i]->vars[j])))+1;
1567  }
1568  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"describe_lvars",NULL,size,0));
1569
1570 
1571
1572  // how many objects are we goint to save, use a int32_t to specify how many
1573  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"object_list",NULL,4,0));
1574 
1575  int32_t t=0;
1576  object_node *o=save_list;
1577  for (;o;o=o->next)
1578    t++;
1579
1580  // type and state aren't normal records because they will be remapped on loading
1581  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"type",NULL,1+2*t,0));
1582  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"state",NULL,1+2*t,0));
1583
1584
1585  // now save all the lvars for each object
1586  for (size=0,o=save_list;o;o=o->next)
1587    size+=figures[o->me->otype]->tv*5+2;
1588  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"lvars",NULL,size,0));
1589
1590
1591  for (i=0;i<TOTAL_OBJECT_VARS;i++)
1592    sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,object_descriptions[i].name,NULL,1+
1593                          RC_type_size(object_descriptions[i].type)*t,0));
1594
1595  add_light_spec(&sd,Name);
1596
1597
1598  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"object_links",NULL,1+4+total_object_links(save_list)*8,0));
1599  sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"light_links",NULL,1+4+total_light_links(save_list)*8,0));
1600
1601  if (save_all)
1602  {
1603    t=0;
1604    view *v=player_list;
1605    for (;v;v=v->next) t++;
1606    sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"player_info",NULL,t*4+4,0));
1607
1608    int tv=total_view_vars();
1609    int i=0;
1610    for (;i<tv;i++)
1611      sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,get_view_var_name(i),NULL,1+4*t,0));
1612    sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"random_start",NULL,5,0));
1613
1614    sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"weapon_array",NULL,1+4+total_weapons*4*t,0));
1615
1616    int name_len=0;
1617    for (v=player_list;v;v=v->next)
1618      name_len+=strlen(v->name)+2;
1619    sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"player_names",NULL,name_len,0));
1620
1621    sd.add_by_hand(new spec_entry(SPEC_IMAGE,"thumb nail",NULL,4+160*(100+eh->font()->height()*2),0));
1622  }
1623
1624  sd.calc_offsets();
1625
1626  return sd.write(filename);
1627}
1628
1629void scale_put(image *im, image *screen, int x, int y, short new_width, short new_height);
1630
1631void level::write_thumb_nail(bFILE *fp, image *im)
1632{
1633  image *i=new image(160,100+eh->font()->height()*2);
1634  i->clear();
1635  scale_put(im,i,0,0,160,100);
1636  if (first_name)
1637    eh->font()->put_string(i,80-strlen(first_name)*eh->font()->width()/2,100,first_name);
1638
1639  time_t t;
1640  t=time(NULL);
1641  char buf[80];
1642
1643  strftime(buf,80,"%T %A %B %d",localtime(&t)); 
1644  eh->font()->put_string(i,80-strlen(buf)*eh->font()->width()/2,100+eh->font()->height(),buf);
1645
1646  fp->write_short(i->width());
1647  fp->write_short(i->height());
1648  int y=0;
1649  for (;y<i->height();y++)
1650    fp->write(i->scan_line(y),i->width());
1651
1652  delete i;
1653}
1654
1655void level::write_player_info(bFILE *fp, object_node *save_list)
1656{
1657  int32_t t=0;
1658  view *v=player_list;
1659  for (;v;v=v->next) t++;
1660  fp->write_long(t);
1661
1662  for (v=player_list;v;v=v->next)
1663    fp->write_long(object_to_number_in_list(v->focus,save_list));
1664
1665  int tv=total_view_vars();
1666  int i=0;
1667  for (;i<tv;i++)
1668  {
1669    fp->write_byte(RC_L);
1670    for (v=player_list;v;v=v->next)
1671      fp->write_long(v->get_view_var_value(i));
1672  }
1673
1674  fp->write_byte(RC_L);
1675  fp->write_long(rand_on);
1676
1677  fp->write_byte(RC_L);
1678  fp->write_long(total_weapons);
1679  for (v=player_list;v;v=v->next)
1680    for (i=0;i<total_weapons;i++)
1681      fp->write_long(v->weapons[i]);
1682
1683  for (v=player_list;v;v=v->next)
1684  {
1685    int len=strlen(v->name)+1;
1686    fp->write_byte(len);
1687    fp->write(v->name,len);
1688  }
1689}
1690
1691
1692int level::load_player_info(bFILE *fp, spec_directory *sd, object_node *save_list)
1693{
1694  int ret;
1695  spec_entry *se=sd->find("player_info");
1696  if (se)
1697  {
1698    fp->seek(se->offset,0);
1699
1700    int set_first_view=0;
1701    if (the_game->first_view==player_list) set_first_view=1;
1702    int my_player_number=-1;
1703
1704    view *v=player_list;
1705    for (;v;v=v->next)
1706    { v->suggest.send_view=0;
1707      v->suggest.send_weapon_change=0;
1708    }
1709
1710    for (v=player_list;v;v=v->next)
1711      if (v->local_player())
1712         my_player_number=v->player_number;
1713
1714    while (player_list)    // delete all of the views (they will get recreated)
1715    {
1716      v=player_list;
1717      if (v->focus)
1718      {
1719        if (v->focus->controller())
1720         v->focus->set_controller(NULL);
1721        delete v->focus;
1722      }
1723
1724      player_list=player_list->next;
1725      delete v;
1726    }
1727
1728    int32_t total_players=fp->read_long();
1729    view *last=NULL;
1730    int i=0;
1731    for (;i<total_players;i++)   
1732    {
1733      game_object *o=number_to_object_in_list(fp->read_long(),save_list);
1734      v=new view(o,NULL,0);
1735      if (o) o->set_controller(v);
1736      if (player_list)
1737        last->next=v;
1738      else player_list=v;
1739      last=v;
1740    }
1741    if (set_first_view)
1742      the_game->first_view=player_list;
1743   
1744    for (i=0;i<total_view_vars();i++)
1745    {
1746      char *find_name=get_view_var_name(i);
1747      se=sd->find(find_name);
1748
1749      if (se)
1750      {
1751        fp->seek(se->offset,0);
1752        if (fp->read_byte()==RC_L)
1753        {
1754          for (v=player_list;v;v=v->next)
1755            v->set_view_var_value(i,fp->read_long());
1756        }
1757      } else
1758      {
1759        for (v=player_list;v;v=v->next)
1760          v->set_view_var_value(i,0);
1761      }
1762    }
1763
1764    se=sd->find("random_start");      // start of index into random table
1765    if (se)
1766    {
1767      fp->seek(se->offset,0);
1768      if (fp->read_byte()==RC_L)
1769        rand_on=fp->read_long();
1770    } else rand_on=0;
1771
1772    se=sd->find("weapon_array");
1773    if (se)
1774    {
1775      fp->seek(se->offset,0);
1776      if (fp->read_byte()==RC_L)
1777      {
1778        int32_t m=fp->read_long();  // read how many weapons exsisted when last saved
1779        int i;
1780        for (v=player_list;v;v=v->next)   
1781        {
1782          for (i=0;i<m;i++)
1783          {
1784            int32_t x=fp->read_long();
1785            if (i<total_weapons)
1786            {
1787              v->weapons[i]=x;
1788              v->last_weapons[i]=x;
1789            }
1790          }
1791        }
1792      }
1793    }  else
1794    {
1795      for (v=player_list;v;v=v->next)     
1796      {
1797        memset(v->last_weapons,0xff,total_weapons*sizeof(int32_t));
1798        memset(v->weapons,0xff,total_weapons*sizeof(int32_t));
1799      }
1800    }
1801
1802    se=sd->find("player_names");
1803    if (se)
1804    {
1805      fp->seek(se->offset,0);
1806      for (v=player_list;v;v=v->next)     
1807      {
1808        uchar len=fp->read_byte();
1809        fp->read(v->name,len);
1810      }
1811    }
1812
1813    ret=1;
1814    recalc_local_view_space();
1815   
1816  } else
1817  {
1818    void *fun=make_find_symbol("set_player_defaults");
1819    if (DEFINEDP(symbol_function(fun)))
1820    {     
1821      view *f;
1822      game_object *o=current_object;
1823      for (f=player_list;f;f=f->next)
1824      {
1825        if (f->focus)
1826        {
1827          current_object=f->focus;
1828          void *m=mark_heap(TMP_SPACE);
1829          eval_function((lisp_symbol *)fun,NULL);
1830          restore_heap(m,TMP_SPACE);
1831        }
1832      }
1833      current_object=o;
1834    }
1835    ret=0;
1836  }
1837
1838  view *vw;
1839  for (vw=player_list;vw;vw=vw->next)
1840  {
1841    if (total_weapons && !vw->has_weapon(vw->current_weapon))
1842    {
1843      vw->suggest.send_weapon_change=1;
1844      vw->suggest.new_weapon=0;     
1845    }
1846  }
1847
1848  return ret;
1849}
1850
1851
1852void level::write_objects(bFILE *fp, object_node *save_list)
1853{
1854  // record information in the file about what the data structures look like
1855  // right now, so if they change later, they don't get get screwed up
1856  fp->write_short(total_objects);   // mark how many objects we know about right now 
1857
1858  int i=0;
1859  for (;i<total_objects;i++)   // loop through all the object types we know of
1860  {   
1861    fp->write_byte(strlen(object_names[i])+1);                    // sizeof name   
1862    fp->write(object_names[i],strlen(object_names[i])+1);      // write object name
1863  }
1864   
1865
1866  // write state numbers and names for each object
1867  for (i=0;i<total_objects;i++)
1868  {
1869    int total=0;
1870    int j=0;
1871    for (;j<figures[i]->ts;j++)
1872      if (figures[i]->seq[j]) total++;
1873    fp->write_short(total);
1874
1875    for (j=0;j<figures[i]->ts;j++)
1876      if (figures[i]->seq[j])
1877      {
1878        char *state_name=lstring_value(symbol_name(figures[i]->seq_syms[j]));
1879        fp->write_byte(strlen(state_name)+1);
1880        fp->write(state_name,strlen(state_name)+1);
1881      }
1882  }
1883
1884 
1885  // write object lvar names
1886  for (i=0;i<total_objects;i++)
1887  {
1888    fp->write_short(figures[i]->tv);
1889    int j,x;
1890   
1891    for (x=0;x<figures[i]->tv;x++)
1892    {
1893      for (j=0;j<figures[i]->tiv;j++)
1894      {
1895        if (figures[i]->vars[j] && figures[i]->var_index[j]==x)
1896        {
1897          char *var_name=lstring_value(symbol_name(figures[i]->vars[j]));
1898          fp->write_byte(strlen(var_name)+1);
1899          fp->write(var_name,strlen(var_name)+1);
1900        }
1901      }
1902    }
1903  }
1904 
1905  int32_t t=0;
1906  object_node *o=save_list;
1907  for (;o;o=o->next) t++;
1908  fp->write_long(t);
1909
1910
1911  fp->write_byte(RC_S);                                    // save type info for each record
1912  for (o=save_list;o;o=o->next) fp->write_short(o->me->type());   
1913
1914  fp->write_byte(RC_S);                                    // save state info for each record
1915  for (o=save_list;o;o=o->next) fp->write_short(o->me->reduced_state());
1916
1917  for (o=save_list;o;o=o->next)                            // save lvars
1918  {
1919    fp->write_short(figures[o->me->otype]->tv);
1920    for (i=0;i<figures[o->me->otype]->tv;i++)
1921    {
1922      fp->write_byte(RC_L);                           // for now the only type allowed is int32_t
1923      fp->write_long(o->me->lvars[i]);
1924    }
1925  }
1926
1927  for (i=0;i<default_simple.total_vars();i++)
1928  {
1929    int t=object_descriptions[i].type;
1930    fp->write_byte(t);
1931    for (o=save_list;o;o=o->next)
1932    {
1933      switch (t)
1934      {         
1935        case RC_C :
1936        { fp->write_byte(o->me->get_var(i)); } break;
1937        case RC_S :
1938        { fp->write_short(o->me->get_var(i)); } break;
1939        case RC_L :
1940        { fp->write_long(o->me->get_var(i)); } break;
1941      }
1942    }
1943  }
1944}
1945
1946
1947int32_t level::total_object_links(object_node *list)
1948{
1949  int32_t tl=0;
1950  for (object_node *o=list;o;o=o->next)
1951    tl+=o->me->total_objects();
1952  return tl;
1953}
1954
1955int32_t level::total_light_links(object_node *list)
1956{
1957  int32_t tl=0;
1958  for (object_node *o=list;o;o=o->next)
1959    tl+=o->me->total_lights();
1960  return tl;
1961}
1962
1963void level::write_links(bFILE *fp, object_node *save_list, object_node *exclude_list)
1964{
1965  fp->write_byte(RC_L); 
1966  fp->write_long(total_object_links(save_list));
1967
1968  int x=1;
1969  object_node *o=save_list;
1970
1971  for (;o;o=o->next,x++)
1972  {
1973    int i=0;
1974    for (;i<o->me->total_objects();i++)
1975    {
1976      fp->write_long(x);
1977      int32_t x=object_to_number_in_list(o->me->get_object(i),save_list);
1978      if (x)
1979        fp->write_long(x);
1980      else                            // save links to excluded items as negative
1981        fp->write_long((int32_t)(-(object_to_number_in_list(o->me,exclude_list))));
1982    }
1983  }
1984
1985  fp->write_byte(RC_L); 
1986  fp->write_long(total_light_links(save_list));
1987
1988  x=1;
1989  for (o=save_list;o;o=o->next,x++)
1990  {
1991    int i=0;
1992    for (;i<o->me->total_lights();i++)
1993    {
1994      fp->write_long(x);
1995      fp->write_long(light_to_number(o->me->get_light(i)));
1996    }
1997  }
1998
1999}
2000
2001
2002void level::load_links(bFILE *fp, spec_directory *sd,
2003                       object_node *save_list, object_node *exclude_list)
2004{
2005  spec_entry *se=sd->find("object_links");
2006  if (se)
2007  {
2008    fp->seek(se->offset,0);
2009    if (fp->read_byte()==RC_L)
2010    {
2011      int32_t t=fp->read_long();
2012      while (t)
2013      {
2014        int32_t x1=fp->read_long();
2015        CONDITION(x1>=0,"expected x1 for object link to be > 0\n");
2016        int32_t x2=fp->read_long();
2017        game_object *p,*q=number_to_object_in_list(x1,save_list);
2018        if (x2>0)
2019          p=number_to_object_in_list(x2,save_list);
2020        else p=number_to_object_in_list(-x2,exclude_list);
2021        if (q)
2022          q->add_object(p);
2023        else dprintf("bad object link\n");
2024
2025        t--;
2026      }
2027    }
2028  }
2029
2030  se=sd->find("light_links");
2031  if (se)
2032  {
2033    fp->seek(se->offset,0);
2034    if (fp->read_byte()==RC_L)
2035    {
2036      int32_t t=fp->read_long();
2037      while (t)
2038      {
2039        int32_t x1=fp->read_long();
2040        int32_t x2=fp->read_long();
2041        game_object *p=number_to_object_in_list(x1,save_list);
2042        if (p)
2043          p->add_light(number_to_light(x2));
2044        else dprintf("bad object/light link\n");
2045        t--;
2046      }
2047    }
2048  }
2049
2050}
2051
2052
2053void level::write_options(bFILE *fp)
2054{
2055  // save background scroll rate
2056  fp->write_byte(RC_L);
2057  fp->write_long(bg_xmul);
2058  fp->write_long(bg_xdiv);
2059  fp->write_long(bg_ymul);
2060  fp->write_long(bg_ydiv);
2061
2062  fp->write_byte(RC_L);
2063  int ta=0;
2064  area_controller *a=area_list;
2065  for (;a;a=a->next) ta++;
2066  fp->write_long(ta);
2067  for (a=area_list;a;a=a->next)
2068  {
2069    fp->write_long(a->x);
2070    fp->write_long(a->y);
2071    fp->write_long(a->w);
2072    fp->write_long(a->h);
2073    fp->write_long(a->active);
2074
2075    fp->write_long(a->ambient);
2076    fp->write_long(a->view_xoff);
2077    fp->write_long(a->view_yoff);
2078    fp->write_long(a->ambient_speed);
2079    fp->write_long(a->view_xoff_speed);
2080    fp->write_long(a->view_yoff_speed);
2081  }
2082  fp->write_byte(RC_L);
2083  fp->write_long(tick_counter());
2084}
2085
2086void level::load_options(spec_directory *sd, bFILE *fp)
2087{
2088  spec_entry *se=sd->find("bg_scroll_rate");
2089  if (se)
2090  {
2091    fp->seek(se->offset,0);
2092    if (fp->read_byte()!=RC_L)
2093    { bg_xmul=bg_ymul=1; bg_xdiv=bg_ydiv=8; }
2094    else
2095    {
2096      bg_xmul=fp->read_long();
2097      bg_xdiv=fp->read_long();
2098      bg_ymul=fp->read_long();
2099      bg_ydiv=fp->read_long();
2100    }
2101  } else { bg_xmul=bg_ymul=1; bg_xdiv=bg_ydiv=8; }
2102
2103  se=sd->find("area_list.v1");
2104  if (se)
2105  {
2106    fp->seek(se->offset,0);
2107    if (fp->read_byte()==RC_L)
2108    {
2109      area_controller *l=NULL,*p;
2110      int32_t ta=fp->read_long();
2111      int i=0;
2112      for (;i<ta;i++)
2113      {
2114        int32_t x,y,w,h;
2115        x=fp->read_long();
2116        y=fp->read_long();
2117        w=fp->read_long();
2118        h=fp->read_long();     
2119        p=new area_controller(x,y,w,h,NULL);
2120        if (l) l->next=p;
2121        else area_list=p;
2122        l=p;
2123        p->active=fp->read_long();
2124        p->ambient=fp->read_long();
2125        p->view_xoff=fp->read_long();
2126        p->view_yoff=fp->read_long();
2127        p->ambient_speed=fp->read_long();
2128        p->view_xoff_speed=fp->read_long();
2129        p->view_yoff_speed=fp->read_long();
2130      }
2131    }
2132  }
2133
2134  se=sd->find("tick_counter");
2135  if (se)
2136  {
2137    fp->seek(se->offset,0);
2138    if (fp->read_byte()==RC_L)   
2139      set_tick_counter(fp->read_long());
2140    else set_tick_counter(0);
2141  } else set_tick_counter(0);
2142}
2143
2144
2145void level::write_cache_prof_info()
2146{
2147  if (cash.prof_is_on())
2148  {
2149    char pf_name[100];
2150    if (first_name)
2151      get_prof_assoc_filename(first_name,pf_name);
2152    else
2153      get_prof_assoc_filename(Name,pf_name);
2154
2155
2156    spec_directory sd;
2157    sd.add_by_hand(new spec_entry(SPEC_DATA_ARRAY,"cache profile info",NULL,cash.prof_size(),0));
2158    sd.calc_offsets();
2159    jFILE *fp2=sd.write(pf_name);
2160    if (!fp2)
2161      the_game->show_help("Unable to open cache profile output file");
2162    else
2163    {
2164      cash.prof_write(fp2);
2165      delete fp2;
2166    }
2167    sd.delete_entries();
2168  }
2169
2170}
2171
2172void level::load_cache_info(spec_directory *sd, bFILE *fp)
2173{
2174  if (!DEFINEDP(symbol_value(l_empty_cache)) || !symbol_value(l_empty_cache))
2175  {
2176    char pf_name[100];
2177    if (first_name)
2178      get_prof_assoc_filename(first_name,pf_name);  // get cache info from orignal filename if this is a savegame
2179    else
2180      get_prof_assoc_filename(Name,pf_name);
2181
2182
2183    cash.load_cache_prof_info(pf_name,this);
2184  }
2185}
2186
2187
2188int level::save(char *filename, int save_all)
2189{
2190        char name[255], bkname[255];
2191
2192        sprintf( name, "%s%s", get_save_filename_prefix(), filename );
2193        sprintf( bkname, "%slevsave.bak", get_save_filename_prefix() );
2194        if( !save_all && DEFINEDP( symbol_value( l_keep_backup ) ) &&
2195                symbol_value( l_keep_backup ) )   // make a backup
2196        {
2197                bFILE *fp = open_file( name, "rb" );    // does file already exist?
2198                if( !fp->open_failure() )
2199                {
2200                        unlink( bkname );
2201                        bFILE *bk = open_file( bkname, "wb" );
2202                        if( bk->open_failure() )
2203                                dprintf("unable to open backup file %s\n", bkname );
2204                        else
2205                        {
2206                                int32_t size = fp->file_size();
2207                                uchar *buf = (uchar *)jmalloc(0x1000,"copy buf");
2208                                int tr = 1;
2209                                while( size && tr )
2210                                {
2211                                        int tr = fp->read(buf,0x1000);
2212                                        if( tr )
2213                                        tr = bk->write(buf,tr);
2214                                        size -= tr;
2215                                }
2216                                jfree(buf);
2217                        }
2218                        delete bk;
2219#if (defined(__MACH__) || !defined(__APPLE__))
2220                        chmod( bkname, S_IRWXU | S_IRWXG | S_IRWXO );
2221#endif
2222                }
2223                delete fp;
2224        }
2225
2226        // if we are not doing a savegame then change the first_name to this name
2227        if( !save_all )
2228        {
2229                if( first_name )
2230                        jfree(first_name);
2231                first_name = (char *)jmalloc( strlen( name ) + 1, "level first name" );
2232                strcpy( first_name, name );
2233        }
2234
2235        object_node *players, *objs;
2236        if( save_all )
2237                players = NULL;
2238        else
2239                players = make_player_onodes();
2240
2241        objs = make_not_list(players);     // negate the above list
2242
2243        bFILE *fp = create_dir( name, save_all, objs, players);
2244        if( fp != NULL )
2245        {
2246                if( !fp->open_failure() )
2247                {
2248                        if( first_name )
2249                        {
2250                                fp->write_byte( strlen( first_name ) + 1 );
2251                                fp->write( first_name, strlen( first_name ) + 1 );
2252                        }
2253                        else
2254                        {
2255                                fp->write_byte( 1 );
2256                                fp->write_byte( 0 );
2257                        }
2258
2259                        fp->write_long( fg_width );
2260                        fp->write_long( fg_height );
2261
2262                        int t  = fg_width * fg_height;
2263                        ushort *rm = map_fg;
2264                        for (;t;t--,rm++)
2265                        {
2266                                ushort x = *rm;
2267                                x = lstl(x);            // convert to intel endianess
2268                                *rm = x;
2269                        }
2270
2271                        fp->write( (char *)map_fg, 2 * fg_width * fg_height );
2272                        t = fg_width * fg_height;
2273                        rm = map_fg;
2274                        for (;t;t--,rm++)
2275                        {
2276                                ushort x = *rm;
2277                                x = lstl( x );            // convert to intel endianess
2278                                *rm = x;
2279                        }
2280
2281                        fp->write_long( bg_width );
2282                        fp->write_long( bg_height );
2283                        t = bg_width * bg_height;
2284                        rm = map_bg;
2285
2286                        for (;t;t--,rm++)
2287                        {
2288                                ushort x=*rm;
2289                                x = lstl( x );          // convert to intel endianess
2290                                *rm = x;
2291                        }
2292
2293                        fp->write( (char *)map_bg, 2 * bg_width * bg_height );
2294                        rm = map_bg;
2295                        t = bg_width*bg_height;
2296
2297                        for (;t;t--,rm++)
2298                        {
2299                                ushort x = *rm;
2300                                x = lstl( x );          // convert to intel endianess
2301                                *rm = x;
2302                        }
2303
2304                        write_options( fp );
2305                        write_objects( fp, objs );
2306                        write_lights( fp );
2307                        write_links( fp, objs, players );
2308                        if( save_all )
2309                        {
2310                                write_player_info( fp, objs );
2311                                write_thumb_nail( fp,screen );
2312                        }
2313
2314                        delete fp;
2315#if (defined(__MACH__) || !defined(__APPLE__))
2316                        chmod( name, S_IRWXU | S_IRWXG | S_IRWXO );
2317#endif
2318                        write_cache_prof_info();
2319                }
2320                else
2321                {
2322                        the_game->show_help( "Unable to open file for saving\n" );
2323                        delete fp;
2324                        return 0;
2325                }
2326        }
2327        else
2328        {
2329                the_game->show_help( "Unable to open file for saving.\n" );
2330                printf( "\nFailed to save game.\n" );
2331                printf( "I was trying to save to: '%s'\n\tPath: '%s'\n\tFile: '%s'\n", name, get_save_filename_prefix(), filename );
2332                printf( "\nPlease send an email to:\n\ttrandor@labyrinth.net.au\nwith these details.\nThanks.\n" );
2333                return 0;
2334        }
2335
2336        delete_object_list(players);
2337        delete_object_list(objs);
2338
2339        return 1;
2340}
2341
2342level::level(int width, int height, char *name)
2343{
2344  the_game->need_refresh();
2345  area_list=NULL;
2346  set_tick_counter(0);
2347
2348  attack_list=NULL;
2349  attack_list_size=attack_total=0;
2350
2351  target_list=NULL;
2352  target_list_size=target_total=0;
2353
2354  block_list=NULL;
2355  block_list_size=block_total=0;
2356
2357  all_block_list=NULL;
2358  all_block_list_size=all_block_total=0;
2359 
2360  Name=NULL;
2361  first_name=NULL;
2362
2363  set_name(name);
2364  first=first_active=NULL;
2365 
2366  fg_width=width;
2367  fg_height=height;
2368  calc_bgsize(fg_width,fg_height,bg_width,bg_height);
2369 
2370  map_bg=(uint16_t *)jmalloc(sizeof(int16_t)*bg_width*bg_height,"map bg");
2371  map_fg=(uint16_t *)jmalloc(sizeof(int16_t)*fg_width*fg_height,"map fg");
2372
2373
2374
2375  memset(map_bg,0,sizeof(int16_t)*bg_width*bg_height);
2376  memset(map_fg,0,sizeof(int16_t)*fg_width*fg_height);
2377
2378  int i; 
2379  for (i=0;i<fg_width;i++)
2380  {   
2381    map_fg[i]=1;
2382    map_fg[fg_width*(fg_height-1)+i]=1;
2383  }
2384  for (i=0;i<fg_height;i++)
2385  {   
2386    map_fg[fg_width*i]=1;
2387    map_fg[fg_width*i+fg_width-1]=1;
2388  }
2389 
2390  total_objs=0; 
2391  insert_players();
2392}
2393
2394
2395void level::add_object(game_object *new_guy)
2396{
2397  total_objs++;
2398  new_guy->next=NULL;
2399  if (figures[new_guy->otype]->get_cflag(CFLAG_ADD_FRONT))
2400  {
2401    if (!first)
2402      first=new_guy;
2403    else
2404      last->next=new_guy;
2405    last=new_guy;
2406  } else
2407  {
2408    if (!first)
2409      last=first=new_guy;
2410    else
2411    {
2412      new_guy->next=first;
2413      first=new_guy;
2414    }
2415  }
2416}
2417
2418void level::add_object_after(game_object *new_guy,game_object *who)
2419{
2420  if (!who) add_object(new_guy);
2421  else
2422  {
2423    total_objs++;
2424    if (who==last) last=new_guy;
2425    new_guy->next=who->next;
2426    who->next=new_guy;
2427  }
2428}
2429
2430void level::delete_object(game_object *who)
2431{
2432  remove_object(who);
2433  delete who;
2434}
2435
2436void level::remove_block(game_object *who)
2437{
2438  int i=0,j;
2439  game_object **o=block_list;
2440  for (;i<block_total;i++)
2441  {
2442    if (*o==who)        // is this object in the block list?
2443    {
2444      block_total--;    // squish the block list in
2445      o++;
2446      for (j=i;j<block_total;j++)
2447        block_list[j]=block_list[j+1];
2448    } else o++;   
2449  }
2450}
2451
2452
2453// searches through the all_block list for who and if it finds it deletes it
2454void level::remove_all_block(game_object *who)
2455{
2456  int i=0,j;
2457  game_object **o=all_block_list;
2458  for (;i<all_block_total;i++)
2459  {
2460    if (*o==who)        // is this object in the block list?
2461    {
2462      all_block_total--;    // squish the block list in
2463      o++;
2464      for (j=i;j<all_block_total;j++)
2465        all_block_list[j]=all_block_list[j+1];
2466    } else o++;   
2467  }
2468}
2469
2470void level::remove_object(game_object *who)
2471{
2472  if (dev_cont)
2473    dev_cont->notify_deleted_object(who);
2474
2475  if (who==first)
2476  {
2477    if (who==last) last=NULL;
2478    first=first->next;
2479  }
2480  else
2481  {
2482    game_object *o=first;
2483    for (;o && o->next!=who;o=o->next);
2484    if (o)
2485    {
2486      o->next=who->next;
2487      if (!o->next) last=o;
2488    }
2489    else return ;     // if object is not in level, don't try to do anything else
2490  }
2491  total_objs--;
2492
2493
2494  if (first_active==who)
2495    first_active=who->next_active;
2496  else
2497  {
2498    game_object *o=first_active;
2499    for (;o && o->next_active!=who;o=o->next_active);
2500    if (o)
2501      o->next_active=who->next_active;
2502  }
2503
2504  if (who->flags()&KNOWN_FLAG)
2505  {
2506    game_object *o=first;
2507    for (;o;o=o->next)
2508    {
2509      int t=o->total_objects();
2510      int i=0;
2511      for (;i<t;i++)
2512        if (o->get_object(i)==who)
2513        {
2514          o->remove_object(who);
2515          t=o->total_objects();
2516        }
2517    }
2518  }
2519
2520  if (who->otype<0xffff)
2521  {
2522    if (who->can_block())  // remove object from block list and all_block if nessasary
2523    {
2524      remove_block(who);
2525      remove_all_block(who);
2526    } else if (who->hurtable())
2527      remove_all_block(who);
2528  }
2529
2530
2531  int t=who->total_objects();
2532  while (t) { who->remove_object(who->get_object(0)); t--; }
2533
2534  t=who->total_lights();
2535  while (t) { who->remove_light(who->get_light(0)); t--; }
2536}
2537
2538void level::to_front(game_object *o)  // move to end of list, so we are drawn last, therefore top
2539{
2540  if (o==last) return ;
2541  first_active=NULL;     // make sure nothing goes screwy with the active list
2542
2543  if (o==first) 
2544    first=first->next; 
2545  else
2546  {
2547    game_object *w=first;
2548    for (;w && w->next!=o;w=w->next);
2549    if (!w) return ;
2550    w->next=o->next;
2551  }
2552
2553  last->next=o;
2554  o->next=NULL;
2555  last=o;
2556}
2557
2558void level::to_back(game_object *o)   // to make the character drawn in back, put at front of list
2559
2560  if (o==first) return;
2561  first_active=NULL;     // make sure nothing goes screwy with the active list 
2562 
2563  game_object *w=first;
2564  for (;w && w->next!=o;w=w->next);
2565  if (!w) return;
2566  if (last==o)
2567    last=w;
2568  w->next=o->next;
2569  o->next=first;
2570  first=o;
2571}
2572
2573
2574game_object *level::find_self(game_object *me)
2575{
2576  return me;
2577}
2578
2579game_object *level::find_object(int32_t x, int32_t y)
2580{
2581  int32_t x1,y1,x2,y2; 
2582  game_object *o=first;
2583  for (;o;o=o->next)
2584  {
2585    o->picture_space(x1,y1,x2,y2);   
2586    if (x<x2 && x>=x1 && y<y2 && y>=y1)
2587      return o;
2588  }
2589  return NULL;
2590}
2591
2592int32_t last_tile_hit_x,last_tile_hit_y;
2593
2594#define remapx(x) (x==0 ? -1 : x==tl-1 ? tl+1 : x)
2595#define remapy(y) (y==0 ? -1 : y==th-1 ? th+1 : y)
2596
2597void level::foreground_intersect(int32_t x1, int32_t y1, int32_t &x2, int32_t &y2)
2598{
2599/*  if (x1==x2)
2600  { vforeground_intersect(x1,y1,y2);
2601    return ;
2602  }  */
2603
2604  int32_t tl=the_game->ftile_width(),th=the_game->ftile_height(),
2605    j,
2606    xp1,yp1,xp2,yp2,    // starting and ending points of block line segment
2607    swap;               // temp var
2608  int32_t blockx1,blocky1,blockx2,blocky2,block,bx,by;
2609  point_list *block_list;
2610  unsigned char *bdat;
2611
2612  blockx1=x1;
2613  blocky1=y1;
2614  blockx2=x2;
2615  blocky2=y2;
2616  if (blockx1>blockx2) { swap=blockx1; blockx1=blockx2; blockx2=swap; }
2617  if (blocky1>blocky2) { swap=blocky1; blocky1=blocky2; blocky2=swap; }
2618  blockx1=(blockx1-2)/tl-1;
2619  blockx2=(blockx2+tl+2)/tl+1;
2620  blocky1=(blocky1-2)/th-1;
2621  blocky2=(blocky2+th+2)/th+1;
2622
2623
2624  if (blockx2>=foreground_width()) { x2=tl*foreground_width()-1; }
2625  if (blocky2>=foreground_height()) { y2=th*foreground_height()-1; } 
2626  blockx1=max(blockx1,0);
2627  blocky1=max(blocky1,0); 
2628
2629  if ((blockx1>blockx2) || (blocky1>blocky2)) return ;
2630
2631  // now check all the map positions this line could intersect
2632  for (bx=blockx1;bx<=blockx2;bx++)
2633  {
2634    for (by=blocky1;by<=blocky2;by++)
2635    {
2636      block=the_game->get_map_fg(bx,by);
2637      if (block>BLACK)        // don't check BLACK, should be no points in it
2638      {
2639        // now check the all the line segments in the block
2640        foretile *f=the_game->get_fg(block);
2641        block_list=f->points;
2642        unsigned char total=block_list->tot;
2643        bdat=block_list->data;
2644        unsigned char *ins=f->points->inside;
2645        int32_t xo=bx*tl,yo=by*th;
2646        for (j=0;j<total-1;j++,ins++)
2647        {
2648          // find the starting and ending points for this segment
2649          xp1=xo+remapx(*bdat);
2650          bdat++;
2651
2652          yp1=yo+remapy(*bdat);
2653          bdat++;
2654
2655          xp2=xo+remapx(*bdat);
2656          yp2=yo+remapy(bdat[1]);
2657
2658
2659          int32_t ox2=x2,oy2=y2;
2660          if (*ins)       
2661            setback_intersect(x1,y1,x2,y2,xp1,yp1,xp2,yp2,1);
2662          else
2663            setback_intersect(x1,y1,x2,y2,xp1,yp1,xp2,yp2,-1);
2664          if (ox2!=x2 || oy2!=y2)
2665          {
2666            last_tile_hit_x=bx;
2667            last_tile_hit_y=by;
2668          }
2669
2670        }       
2671      }
2672    }
2673  } 
2674}
2675
2676
2677void level::vforeground_intersect(int32_t x1, int32_t y1, int32_t &y2)
2678{
2679  int32_t tl=f_wid,th=f_hi,
2680    j,
2681    xp1,yp1,xp2,yp2;    // starting and ending points of block line segment temp var
2682  int32_t blocky1,blocky2,block,bx,by,checkx;
2683  point_list *block_list;
2684  unsigned char *bdat;
2685
2686  int y_addback;
2687  if (y1>y2)
2688  {
2689    blocky1=y2/th;
2690    blocky2=y1/th;   
2691    y_addback=blocky2*f_hi;
2692  } else
2693  {
2694    blocky1=y1/th;
2695    blocky2=y2/th;
2696    y_addback=blocky1*f_hi;
2697  }
2698
2699  y1-=y_addback;
2700  y2-=y_addback;
2701
2702  bx=x1/f_wid;
2703  checkx=x1-bx*f_wid;
2704
2705
2706  // now check all the map positions this line could intersect
2707
2708  for (by=blocky1;by<=blocky2;by++,y1-=f_hi,y2-=f_hi,y_addback+=f_hi)
2709  {
2710    block=the_game->get_map_fg(bx,by);
2711
2712    // now check the all the line segments in the block
2713    foretile *f=the_game->get_fg(block);
2714    block_list=f->points;
2715
2716    unsigned char total=block_list->tot;
2717    bdat=block_list->data;
2718    unsigned char *ins=f->points->inside;
2719
2720//    int32_t xo=bx*tl,yo=by*th;
2721    for (j=0;j<total-1;j++,ins++)
2722    {
2723      // find the starting and ending points for this segment
2724      xp1=remapx(*bdat);
2725      bdat++;
2726
2727      yp1=remapy(*bdat);
2728      bdat++;
2729
2730      xp2=remapx(*bdat);
2731      yp2=remapy(bdat[1]);
2732
2733
2734      int32_t oy2=y2;
2735      if (*ins)   
2736        setback_intersect(checkx,y1,checkx,y2,xp1,yp1,xp2,yp2,1);
2737      else
2738        setback_intersect(checkx,y1,checkx,y2,xp1,yp1,xp2,yp2,-1);
2739      if (oy2!=y2)
2740      {
2741        last_tile_hit_x=bx;
2742        last_tile_hit_y=by;
2743      }
2744    }
2745  }
2746  y2+=y_addback;
2747}
2748
2749
2750
2751void level::send_signal(int32_t signal)
2752{
2753  if (signal)   // signal 0 is never sent!
2754  {
2755    game_object *o=first_active;
2756    for (;o;o=o->next_active)
2757      o->recieve_signal(signal); 
2758  }
2759}
2760
2761
2762int level::crush(game_object *by_who, int xamount, int yamount)
2763{
2764  int32_t xv,yv,crushed=0; 
2765  game_object *o=first_active;
2766  for (;o;o=o->next_active)
2767  {
2768    if (o->hurtable() && o!=by_who)
2769    {     
2770      xv=-xamount;
2771      yv=-yamount;   
2772      if (o->try_move(o->x,o->y,xv,yv,3)==by_who)
2773      {     
2774        xv=xamount;
2775        yv=yamount;
2776        o->try_move(o->x,o->y,xv,yv,3);
2777        if (xv==0 && yv==0)
2778        {
2779          if (o->state!=dead && o->state!=dieing)
2780            o->do_damage(by_who->current_figure()->hit_damage,by_who,o->x,o->y,0,0);
2781
2782/*        {           
2783            if (o->has_sequence(dieing))
2784              o->set_state(dieing);
2785            else o->set_state(dead);
2786              o->hp=0;     
2787          }         */
2788          crushed=1;     
2789        }
2790      }   
2791    }   
2792  }
2793
2794  return crushed; 
2795}
2796
2797
2798int level::platform_push(game_object *by_who, int xamount, int yamount)
2799{
2800  int failed=0;
2801  int32_t xv,yv;
2802  game_object *o=first_active;
2803  for (;o;o=o->next_active) 
2804  {
2805    if (o->is_playable() && o->state!=dieing && o->state!=dead) 
2806    {     
2807      // check to see if the platform is going up and will run into us.     
2808      int32_t tvx,tvy;
2809      if (yamount<0)
2810      {
2811        tvx=-xamount;
2812        tvy=-yamount;
2813        if (o->try_move(o->x,o->y,tvx,tvy,1)==by_who)
2814        {
2815          o->x+=tvx;
2816          o->y+=tvy;       
2817        }
2818      }
2819
2820/*      xv=xamount;   
2821      yv=yamount;
2822      tvx,tvy;
2823      if (xv>0) tvx=xv+1; else if (xv<0) tvx=xv-1; else tvx=0;
2824      if (yv>0) tvy=yv+1; else if (yv<0) tvx=yv-1; else tvy=0;
2825      if (o->try_move(o->x,o->y,tvx,tvy,1)==by_who)  // we the platform hit us?
2826      {
2827        o->x+=tvx;
2828        o->y+=tvy;       
2829      }*/
2830
2831      xv=0;   
2832      yv=2;
2833      if (o->try_move(o->x,o->y,xv,yv,1)==by_who)  // are we standing on the platform?
2834      {     
2835        by_who->x=-by_who->x;
2836        xv=xamount;
2837        yv=yamount;
2838        o->try_move(o->x,o->y,xv,yv,3);
2839        if (xv!=xamount || yv!=yamount) failed=1;
2840        o->x+=xv;
2841        o->y+=yv;
2842        by_who->x=-by_who->x;
2843      }   
2844    }   
2845  }
2846  return !failed;
2847}
2848
2849int level::push_characters(game_object *by_who, int xamount, int yamount)
2850{
2851  int32_t xv,yv;
2852  int failed=0;
2853  game_object *o=first_active;
2854  for (;o;o=o->next_active) 
2855  {
2856    if ((o->is_playable() || o->pushable()) && o->state!=dieing && o->state!=dead) 
2857    {     
2858      xv=-xamount;   
2859      yv=-yamount;
2860      int32_t tvx,tvy;
2861      if (xv>0) tvx=xv+1; else if (xv<0) tvx=xv-1; else tvx=0;
2862      if (yv>0) tvy=yv+1; else if (yv<0) tvx=yv-1; else tvy=0;
2863      if (o->try_move(o->x+xamount,o->y+yamount,tvx,tvy,3)==by_who)
2864      {     
2865        xv=(xamount-tvx);
2866        yv=(yamount-tvy);
2867        o->try_move(o->x,o->y,xv,yv,3);       
2868        o->x+=xv;
2869        o->y+=yv;
2870        if (xv!=xamount-tvx || yv!=yamount-tvy)
2871          failed=1;
2872      }   
2873    }   
2874  }
2875  return !failed;
2876}
2877
2878game_object *level::find_xrange(int x, int y, int type, int xd)
2879{
2880  int32_t find_ydist=100000;
2881  game_object *find=NULL;
2882  game_object *o=first_active;
2883  for (;o;o=o->next_active) 
2884  {
2885    if (o->otype==type)
2886    {
2887      int x_dist=abs(x-o->x);
2888      int y_dist=abs(y-o->y);
2889
2890      if (x_dist<xd  && y_dist<find_ydist)
2891      {   
2892        find_ydist=y_dist;
2893        find=o;
2894      }
2895    }
2896  }
2897  return find;
2898}
2899
2900
2901game_object *level::find_xclosest(int x, int y, int type, game_object *who)
2902{
2903  int32_t find_ydist=100000,find_xdist=0xffffff;
2904  game_object *find=NULL;
2905  game_object *o=first_active;
2906  for (;o;o=o->next_active) 
2907  {
2908    if (o->otype==type && o!=who)
2909    {
2910      int x_dist=abs(x-o->x);
2911      if (x_dist<find_xdist)
2912      {
2913        find_xdist=x_dist;
2914        find_ydist=abs(y-o->y);
2915        find=o;
2916      }
2917      else if (x_dist==find_xdist)
2918      {
2919        int y_dist=abs(y-o->y);
2920        if (y_dist<find_ydist)
2921        {
2922          find_ydist=y_dist;
2923          find=o;
2924        }
2925      }
2926    }
2927  }
2928  return find;
2929}
2930
2931game_object *level::find_closest(int x, int y, int type, game_object *who)
2932{
2933  int32_t find_dist=100000;
2934  game_object *find=NULL;
2935  game_object *o=first_active;
2936  for (;o;o=o->next_active) 
2937  {
2938    if (o->otype==type && o!=who)
2939    {
2940      int d=(x-o->x)*(x-o->x)+(y-o->y)*(y-o->y);
2941      if (d<find_dist)
2942      {
2943        find=o;
2944        find_dist=d;
2945      }
2946    }
2947  }
2948  return find;
2949}
2950
2951
2952
2953void level::remove_light(light_source *which)
2954{
2955  if (which->known)
2956  {
2957    game_object *o=first;
2958    for (;o;o=o->next)
2959    {
2960      int t=o->total_lights();
2961      int i=0;
2962      for (;i<t;i++)
2963        if (o->get_light(i)==which)
2964          o->remove_light(o->get_light(i));
2965    }
2966  }
2967  delete_light(which);
2968}
2969
2970
2971game_object *level::find_type(int type, int skip)
2972{
2973  game_object *l=NULL;
2974  game_object *o=first;
2975  for (;o;o=o->next)
2976  {
2977    if (o->otype==type)
2978    {
2979      if (!skip)
2980        return o;     
2981      skip--;
2982      l=o;
2983    }
2984  }
2985  return l;
2986}
2987
2988void level::hurt_radius(int32_t x, int32_t y,int32_t r, int32_t m, game_object *from, game_object *exclude,
2989                        int max_push)
2990{
2991  if (r<1) return ;   // avoid dev vy zero
2992  game_object *o=first_active;
2993  for (;o;o=o->next_active)
2994  {
2995    if (o!=exclude && o->hurtable())
2996    {
2997      int32_t y1=o->y,y2=o->y-o->picture()->height();
2998      int32_t cx=abs(o->x-x),cy1=abs(y1-y),d1,d2,cy2=abs(y2-y);
2999      if (cx<cy1)
3000        d1=cx+cy1-(cx>>1);
3001      else d1=cx+cy1-(cy1>>1);
3002
3003      if (cx<cy2)
3004        d2=cx+cy2-(cx>>1);
3005      else d2=cx+cy2-(cy2>>1);
3006      if (d2<d1)
3007        d1=d2;
3008
3009
3010
3011      if (d1<r)
3012      {
3013
3014        int px=(r-cx)*max_push/r,py=(r-cy1)*max_push/r;
3015        if (o->x<x)
3016          px=-px;
3017        if (o->y<y)
3018          py=-py;
3019        o->do_damage((r-d1)*m/r,from,x,y1,px,py);
3020      }
3021
3022
3023    }
3024  }
3025
3026}
3027
3028
3029
3030game_object *level::get_random_start(int min_player_dist, view *exclude)
3031{
3032  int t=0;
3033  game_object *o=first;
3034  for (;o;o=o->next)
3035    if (o->otype==start_position_type) t++;    // count how many starts there are in the level
3036
3037  if (t==0) return NULL;                       // there aren't any starts in level!
3038
3039  int retries=t;
3040  do
3041  {
3042    int ctry=jrandom(t)+1;
3043    game_object *n=first;
3044    for (n=first;ctry && n;n=n->next)
3045    {
3046      if (n->otype==start_position_type)
3047      {
3048        o=n;
3049        ctry--;
3050      }
3051    }
3052
3053    int too_close=0;
3054    view *v=player_list;
3055    for (;v;v=v->next)
3056    {
3057      if (v!=exclude)
3058      {
3059        int32_t cx=abs(v->x_center()-o->x),cy=abs(v->y_center()-o->y),d;
3060        if (cx<cy)
3061          d=cx+cy-(cx>>1);
3062        else d=cx+cy-(cy>>1);
3063        if (d<min_player_dist) too_close=1;
3064      }
3065    }
3066    if (too_close) retries--;
3067    else retries=0;
3068  } while (retries);
3069
3070  return o;
3071}
3072
3073
3074
3075
3076
3077void level::insert_players()
3078{
3079
3080  int start=0;
3081  int i=0;
3082  for (;i<total_objects;i++)
3083    if (!strcmp(object_names[i],"START"))
3084      start=i;
3085
3086  view *f=player_list;
3087  for (;f;f=f->next) 
3088  {
3089    game_object *st=find_type(start,f->player_number); 
3090    if (st)
3091    {
3092      f->focus->x=st->x;
3093      f->focus->y=st->y;
3094    }
3095    add_object_after(f->focus,st);
3096  }
3097
3098}
3099
3100
3101void level::add_attacker(game_object *who)
3102{
3103  if (attack_total>=attack_list_size)  // see if we need to grow the list size..
3104  {
3105    attack_list_size++;
3106    attack_list=(game_object **)jrealloc(attack_list,sizeof(game_object *)*attack_list_size,
3107                                                      "attack_list");   
3108  }
3109  attack_list[attack_total]=who;
3110  attack_total++;
3111}
3112
3113
3114
3115void level::add_target(game_object *who)
3116{
3117  if (target_total>=target_list_size)  // see if we need to grow the list size..
3118  {
3119    target_list_size++;
3120    target_list=(game_object **)jrealloc(target_list,sizeof(game_object *)*target_list_size,
3121                                                      "target_list");   
3122  }
3123  target_list[target_total]=who;
3124  target_total++;
3125}
3126
3127
3128
3129void level::add_block(game_object *who)
3130{
3131  if (block_total>=block_list_size)  // see if we need to grow the list size..
3132  {
3133    block_list_size++;
3134    block_list=(game_object **)jrealloc(block_list,sizeof(game_object *)*block_list_size,
3135                                                      "block_list");   
3136  }
3137  block_list[block_total]=who;
3138  block_total++;
3139}
3140
3141
3142void level::add_all_block(game_object *who)
3143{
3144  if (all_block_total>=all_block_list_size)  // see if we need to grow the list size..
3145  {
3146    all_block_list_size++;
3147    all_block_list=(game_object **)jrealloc(all_block_list,sizeof(game_object *)*all_block_list_size,
3148                                                      "all_block_list");   
3149  }
3150  all_block_list[all_block_total]=who;
3151  all_block_total++;
3152}
3153
3154
3155game_object *level::find_object_in_area(int32_t x, int32_t y, int32_t x1, int32_t y1, int32_t x2, int32_t y2,
3156                                     Cell *list, game_object *exclude)
3157{
3158  game_object *closest=NULL;
3159  int32_t closest_distance=0xfffffff,distance,xo,yo;
3160  game_object *o=first_active;
3161  for (;o;o=o->next_active)
3162  {
3163    int32_t xp1,yp1,xp2,yp2;
3164    o->picture_space(xp1,yp1,xp2,yp2);
3165
3166
3167    if (!(xp1>x2 || xp2<x1 || yp1>y2 || yp2<y1) && o!=exclude)
3168    {
3169      // check to see if the type is in the list
3170      Cell *v=list;
3171      for (;!NILP(v) && lnumber_value(CAR(v))!=o->otype;v=CDR(v));
3172      if (!NILP(v))
3173      {
3174        xo=abs(o->x-x);
3175        yo=abs(o->y-y);
3176        distance=xo*xo+yo*yo;
3177        if (distance<closest_distance)
3178        {
3179          closest_distance=distance;
3180          closest=o;
3181        }
3182      }
3183    }
3184  }
3185  return closest;
3186}
3187
3188
3189
3190
3191game_object *level::find_object_in_angle(int32_t x, int32_t y, int32_t start_angle, int32_t end_angle,
3192                                    void *list, game_object *exclude)
3193{
3194  game_object *closest=NULL;
3195  int32_t closest_distance=0xfffffff,distance,xo,yo;
3196  game_object *o=first_active;
3197  for (;o;o=o->next_active)
3198  {
3199    int32_t angle=lisp_atan2(o->y-y,o->x-x);
3200    if (((start_angle<=end_angle && (angle>=start_angle && angle<=end_angle))
3201        || (start_angle>end_angle && (angle>=start_angle || angle<=end_angle)))
3202        && o!=exclude)
3203    {
3204      // check to see if the type is in the list
3205      Cell *v=(Cell *)list;
3206      for (;!NILP(v) && lnumber_value(CAR(v))!=o->otype;v=CDR(v));
3207      if (!NILP(v))
3208      {
3209        xo=abs(o->x-x);
3210        yo=abs(o->y-y);
3211        distance=xo*xo+yo*yo;
3212        if (distance<closest_distance)
3213        {
3214          closest_distance=distance;
3215          closest=o;
3216        }
3217      }
3218    }
3219  }
3220  return closest;
3221}
3222
3223
3224object_node *level::make_not_list(object_node *list)
3225{
3226  object_node *f=NULL,*l=NULL;
3227  game_object *o=first;
3228  for (;o;o=o->next)
3229  {
3230    if (!object_to_number_in_list(o,list))
3231    {
3232      object_node *q=new object_node(o,NULL);
3233      if (f)
3234        l->next=q;
3235      else f=q;
3236      l=q;
3237    }
3238  }
3239  return f;
3240}
3241
3242FILE *open_FILE(char *filename, char *mode);
3243
3244void level::write_object_info(char *filename)
3245{
3246  FILE *fp=open_FILE(filename,"wb");
3247  if (fp)
3248  {
3249    int i=0;
3250    game_object *o=first;
3251    for (;o;o=o->next)
3252    {
3253      fprintf(fp,"%3d %s %4ld %4ld %4ld %4ld %04d\n",i++,object_names[o->otype],(long)o->x,(long)o->y,
3254              (long)o->xvel(),(long)o->yvel(),o->current_frame);
3255    }
3256    fclose(fp);
3257  }
3258}
3259
3260
3261area_controller::area_controller(int32_t X, int32_t Y, int32_t W, int32_t H, area_controller *Next)
3262{
3263  x=X; y=Y; w=W; h=H;
3264  next=Next; active=0;
3265
3266  ambient=-1;
3267  view_xoff=-1;
3268  view_yoff=-1;
3269  ambient_speed=2;
3270  view_xoff_speed=4;
3271  view_yoff_speed=4;
3272}
Note: See TracBrowser for help on using the repository browser.