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

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

game: refactor the mouse/game coordinate conversion logic.

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