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

Last change on this file since 636 was 635, checked in by Sam Hocevar, 11 years ago

lisp: refactor Lisp spaces so that they are real objects, and get rid
of the unused USER_SPACE.

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