source: abuse/tags/pd/macabuse/src/level.c @ 475

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