source: abuse/branches/lol/src/level.cpp @ 732

Last change on this file since 732 was 732, checked in by Sam Hocevar, 8 years ago

build: SDL2 compilation fixes.

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