source: abuse/trunk/src/menu.cpp @ 494

Last change on this file since 494 was 494, checked in by Sam Hocevar, 10 years ago

style: remove trailing spaces, fix copyright statements.

File size: 18.1 KB
Line 
1/*
2 *  Abuse - dark 2D side-scrolling platform game
3 *  Copyright (c) 1995 Crack dot Com
4 *  Copyright (c) 2005-2011 Sam Hocevar <sam@hocevar.net>
5 *
6 *  This software was released into the Public Domain. As with most public
7 *  domain software, no warranty is made or implied by Crack dot Com or
8 *  Jonathan Clark.
9 */
10
11#include "config.h"
12
13#include <math.h>
14
15#include "dev.h"
16
17#include "ui/volumewindow.h"
18
19#include "menu.h"
20#include "lisp.h"
21#include "game.h"
22#include "timing.h"
23#include "game.h"
24#include "id.h"
25#include "pmenu.h"
26#include "gui.h"
27#include "property.h"
28#include "clisp.h"
29#include "gamma.h"
30#include "dprint.h"
31#include "demo.h"
32#include "loadgame.h"
33#include "scroller.h"
34#include "netcfg.h"
35#include "sock.h"
36
37extern net_protocol *prot;
38
39static VolumeWindow *volume_window;
40
41//percent is 0..256
42void tint_area(int x1, int y1, int x2, int y2, int r_to, int g_to, int b_to, int percent)
43{
44  int x,y;
45  short cx1,cy1,cx2,cy2;
46  screen->get_clip(cx1,cy1,cx2,cy2);
47  if (x1<cx1) x1=cx1;
48  if (y1<cy1) y1=cy1;
49  if (x2>cx2) x2=cx2;
50  if (y2>cy2) y2=cy2;
51  if (x2<x1 || y2<y1) return ;
52
53  percent=256-percent;
54
55  screen->lock();
56  for (y=y1; y<=y2; y++)
57  {
58    uint8_t *sl=screen->scan_line(y)+x1;
59    for (x=x1; x<=x2; x++,sl++)
60    {
61      uint8_t *paddr=(uint8_t *)pal->addr()+(*sl)*3;
62      uint8_t r=((*(paddr++))-r_to)*percent/256+r_to;
63      uint8_t g=((*(paddr++))-g_to)*percent/256+g_to;
64      uint8_t b=((*(paddr++))-b_to)*percent/256+b_to;
65      *sl=color_table->lookup_color((r)>>3,(g)>>3,(b)>>3);
66    }
67  }
68  screen->add_dirty(x1,y1,x2,y2);
69  screen->unlock();
70}
71
72void darken_area(int x1, int y1, int x2, int y2, int amount)
73{
74  int x,y;
75  short cx1,cy1,cx2,cy2;
76  screen->get_clip(cx1,cy1,cx2,cy2);
77  if (x1<cx1) x1=cx1;
78  if (y1<cy1) y1=cy1;
79  if (x2>cx2) x2=cx2;
80  if (y2>cy2) y2=cy2;
81  if (x2<x1 || y2<y1) return ;
82
83  screen->lock();
84  for (y=y1; y<=y2; y++)
85  {
86    uint8_t *sl=screen->scan_line(y)+x1;
87    for (x=x1; x<=x2; x++,sl++)
88    {
89      uint8_t *paddr=(uint8_t *)pal->addr()+(*sl)*3;
90      uint8_t r=(*(paddr++))*amount/256;
91      uint8_t g=(*(paddr++))*amount/256;
92      uint8_t b=(*(paddr++))*amount/256;
93      *sl=color_table->lookup_color((r)>>3,(g)>>3,(b)>>3);
94    }
95  }
96  screen->add_dirty(x1,y1,x2,y2);
97  screen->unlock();
98}
99
100void dark_widget(int x1, int y1, int x2, int y2, int br, int dr, int amount)
101{
102  screen->add_dirty(x1,y1,x2,y2);
103  screen->line(x1,y1,x1,y2,br);
104  screen->line(x1+1,y1,x2,y1,br);
105  screen->line(x2,y1+1,x2,y2,dr);
106  screen->line(x1+1,y2,x2,y2,dr);
107  darken_area(x1+1,y1+1,x2-1,y2-1,amount);
108}
109
110char *men_str(void *arg)
111{
112  switch (item_type(arg))
113  {
114    case L_STRING :
115    { return lstring_value(arg); } break;
116    case L_CONS_CELL :
117    { return lstring_value(CAR(arg)); } break;
118    default :
119    {
120      ((LObject *)arg)->Print();
121      printf(" is not a valid menu option\n");
122      exit(0);
123    }
124  }
125  return NULL;
126}
127
128int menu(void *args, JCFont *font)             // reurns -1 on esc
129{
130  main_menu();
131  char *title=NULL;
132  if (!NILP(CAR(args)))
133    title=lstring_value(CAR(args));
134  Cell *def=lcar(lcdr(lcdr(args)));
135  args=CAR(CDR(args));
136
137  int options = ((LList *)args)->GetLength();
138  int mh=(font->height()+1)*options+10,maxw=0;
139
140  Cell *c=(Cell *)args;
141  for (; !NILP(c); c=CDR(c))
142  {
143    if( strlen(men_str(CAR(c))) > (unsigned)maxw)
144      maxw = strlen(men_str(CAR(c)));
145  }
146
147  int mw=(font->width())*maxw+20;
148  int mx=screen->width()/2-mw/2,
149      my=screen->height()/2-mh/2;
150
151
152  screen->add_dirty(mx,my,mx+mw-1,my+mh-1);
153
154  if (title)
155  {
156    int tl=strlen(title)*font->width();
157    int tx=screen->width()/2-tl/2;
158    dark_widget(tx-2,my-font->height()-4,tx+tl+2,my-2,wm->medium_color(),wm->dark_color(),180);
159    font->put_string(screen,tx+1,my-font->height()-2,title,wm->bright_color());
160  }
161
162  dark_widget(mx,my,mx+mw-1,my+mh-1,wm->medium_color(),wm->dark_color(),200);
163
164
165  int y=my+5;
166  for (c=(Cell *)args; !NILP(c); c=CDR(c))
167  {
168    char *ms=men_str(CAR(c));
169    font->put_string(screen,mx+10+1,y+1,ms,wm->black());
170    font->put_string(screen,mx+10,y,ms,wm->bright_color());
171    y+=font->height()+1;
172  }
173
174  wm->flush_screen();
175  event ev;
176  int choice=0,done=0;
177  int bh=font->height()+3;
178  image *save=new image(mw-2,bh);
179  int color=128,cdir=50;
180
181  time_marker *last_color_time=NULL;
182  if (!NILP(def))
183    choice=lnumber_value(def);
184  do
185  {
186    wm->flush_screen();
187    if (wm->event_waiting())
188    {
189      wm->get_event(ev);
190      if (ev.type==EV_KEY)
191      {
192    switch (ev.key)
193    {
194      case JK_ESC :
195      { choice=-1; done=1; } break;
196      case JK_ENTER :
197      { done=1; } break;
198      case JK_DOWN :
199      { if (choice<options-1)
200        choice++;
201      else choice=0;
202      } break;
203      case JK_UP :
204      {
205        if (choice>0)
206        choice--;
207        else choice=options-1;
208      } break;
209    }
210      } else if (ev.type==EV_MOUSE_BUTTON && ev.mouse_button)
211      {
212    if (ev.mouse_move.x>mx && ev.mouse_move.x<mx+mw && ev.mouse_move.y>my &&
213        ev.mouse_move.y<my+mh)
214    {
215      int msel=(ev.mouse_move.y-my)/(font->height()+1);
216      if (msel>=options) msel=options-1;
217      if (msel==choice)                    // clicked on already selected item, return it
218        done=1;
219      else choice=msel;                    // selects an item
220    }
221      }
222    }
223
224    time_marker cur_time;
225    if (!last_color_time || (int)(cur_time.diff_time(last_color_time)*1000)>120)
226    {
227      if (last_color_time)
228        delete last_color_time;
229      last_color_time=new time_marker;
230
231      int by1=(font->height()+1)*choice+my+5-2;
232      int by2=by1+bh-1;
233
234      screen->put_part(save,0,0,mx+1,by1,mx+mw-2,by2);
235      tint_area(mx+1,by1,mx+mw-2,by2,63,63,63,color);
236
237      char *cur=men_str(nth(choice,args));
238      font->put_string(screen,mx+10+1,by1+3,cur,wm->black());
239      font->put_string(screen,mx+10,by1+2,cur,wm->bright_color());
240      screen->rectangle(mx+1,by1,mx+mw-2,by2,wm->bright_color());
241
242      color+=cdir;
243
244      if (color<12 || color>256)
245      {
246    cdir=-cdir;
247    color+=cdir;
248      }
249      wm->flush_screen();
250      save->put_image(screen,mx+1,by1);
251    } else milli_wait(10);
252
253  } while (!done);
254  if (last_color_time)
255    delete last_color_time;
256  delete save;
257  the_game->draw(the_game->state==SCENE_STATE);
258
259  if (choice!=-1)
260  {
261    void *val=nth(choice,args);
262    if (item_type(val)==L_CONS_CELL)   // is there another value that the user want us to return?
263      return lnumber_value(lcdr(val));
264  }
265  return choice;
266}
267
268static void create_volume_window()
269{
270    volume_window = new VolumeWindow();
271    volume_window->inm->allow_no_selections();
272    volume_window->inm->clear_current();
273    volume_window->show();
274
275    wm->grab_focus(volume_window);
276    wm->flush_screen();
277
278    while(volume_window)
279    {
280        event ev;
281
282        do
283        {
284            wm->get_event(ev);
285        }
286        while(ev.type == EV_MOUSE_MOVE && wm->event_waiting());
287
288        wm->flush_screen();
289
290        if(ev.type == EV_CLOSE_WINDOW
291                 || (ev.type == EV_KEY && ev.key == JK_ESC))
292        {
293            wm->close_window(volume_window);
294            volume_window = NULL;
295        }
296
297        if(!volume_window)
298            break;
299
300        if(ev.type == EV_MESSAGE)
301        {
302            char const *s;
303
304            switch(ev.message.id)
305            {
306            case ID_SFX_UP:
307                sfx_volume += 16;
308                if(sfx_volume > 127)
309                    sfx_volume = 127;
310                volume_window->draw_sfx_vol();
311                s = "sfx/ambtech1.wav";
312                if(sound_avail & SFX_INITIALIZED)
313                    cache.sfx(cache.reg(s, s, SPEC_EXTERN_SFX, 1))
314                        ->play(sfx_volume);
315                break;
316            case ID_SFX_DOWN:
317                sfx_volume -= 16;
318                if(sfx_volume < 0)
319                    sfx_volume = 0;
320                volume_window->draw_sfx_vol();
321                s = "sfx/ambtech1.wav";
322                if(sound_avail & SFX_INITIALIZED)
323                    cache.sfx(cache.reg(s, s, SPEC_EXTERN_SFX, 1))
324                        ->play(sfx_volume);
325                break;
326
327            case ID_MUSIC_UP:
328                music_volume += 16;
329                if(music_volume > 127)
330                    music_volume = 127;
331                volume_window->draw_music_vol();
332                if(current_song)
333                    current_song->set_volume(music_volume);
334                break;
335            case ID_MUSIC_DOWN:
336                music_volume -= 16;
337                if(music_volume < 0)
338                    music_volume = 0;
339                volume_window->draw_music_vol();
340                if(current_song)
341                    current_song->set_volume(music_volume);
342                break;
343            }
344        }
345    }
346
347    wm->close_window(volume_window);
348}
349
350void save_difficulty()
351{
352  FILE *fp=open_FILE("hardness.lsp","wb");
353  if (!fp)
354    dprintf("Unable to write to file hardness.lsp\n");
355  else
356  {
357    fprintf(fp,"(setf difficulty '");
358    if (DEFINEDP(symbol_value(l_difficulty)))
359    {
360      if (symbol_value(l_difficulty)==l_extreme)
361        fprintf(fp,"extreme)\n");
362      else if (symbol_value(l_difficulty)==l_hard)
363        fprintf(fp,"hard)\n");
364      else if (symbol_value(l_difficulty)==l_easy)
365        fprintf(fp,"easy)\n");
366      else
367        fprintf(fp,"medium)\n");
368    } else
369       fprintf(fp,"medium)\n");
370    fclose(fp);
371  }
372}
373
374void fade_out(int steps);
375void fade_in(image *im, int steps);
376
377
378void show_sell(int abortable)
379{
380  LSymbol *ss = LSymbol::FindOrCreate("sell_screens");
381  if (!DEFINEDP(ss->GetValue()))
382  {
383    int sp=current_space;
384    current_space=PERM_SPACE;
385//    char *prog="((\"art/help.spe\" . \"sell2\")(\"art/help.spe\" . \"sell4\")(\"art/help.spe\" . \"sell3\")(\"art/endgame.spe\" . \"credit\"))";
386//    char *prog="((\"art/endgame.spe\" . \"credit\") (\"art/help.spe\" . \"sell6\"))";
387    char const *prog="((\"art/endgame.spe\" . \"credit\"))";
388    ss->SetValue((LObject *)compile(prog));
389    current_space=sp;
390  }
391
392  if (DEFINEDP(ss->GetValue()))
393  {
394    image blank(2,2); blank.clear();
395    wm->set_mouse_shape(blank.copy(),0,0);      // don't show mouse
396
397    LObject *tmp = (LObject *)ss->GetValue();
398    int quit=0;
399    while (tmp && !quit)
400    {
401      int im=cache.reg_object("art/help.spe",CAR(tmp),SPEC_IMAGE,1);
402      fade_in(cache.img(im),16);
403
404      event ev;
405      do
406      { wm->flush_screen();
407    wm->get_event(ev);
408      } while (ev.type!=EV_KEY);
409      if (ev.key==JK_ESC && abortable)
410        quit=1;
411      fade_out(16);
412      tmp = (LObject *)CDR(tmp);
413    }
414    wm->set_mouse_shape(cache.img(c_normal)->copy(),1,1);
415  }
416}
417
418
419void menu_handler(event &ev, InputManager *inm)
420{
421  switch (ev.type)
422  {
423    case EV_MESSAGE :
424    {
425      switch (ev.message.id)
426      {
427    case ID_LIGHT_OFF :
428    if (!volume_window)
429    {
430      gamma_correct(pal,1);
431    } break;
432    case ID_RETURN :
433    if (!volume_window)
434    {
435      the_game->set_state(RUN_STATE);
436    } break;
437    case ID_START_GAME :
438    if (!volume_window)
439    {
440      the_game->load_level(level_file);
441      the_game->set_state(RUN_STATE);
442      view *v;
443      for (v=player_list; v; v=v->next)
444        if (v->focus)
445          v->reset_player();
446
447    } break;
448
449
450        case ID_LOAD_PLAYER_GAME :
451    if (!volume_window)
452    {
453      int got_level=load_game(0,symbol_str("LOAD"));
454      the_game->reset_keymap();
455      if (got_level)
456      {
457        char name[255];
458        sprintf(name,"%ssave%04d.spe", get_save_filename_prefix(), got_level);
459
460        the_game->load_level(name);
461        the_game->set_state(RUN_STATE);
462      }
463    } break;
464
465
466    case ID_VOLUME :
467    if (!volume_window)
468    { create_volume_window(); } break;
469
470    case ID_MEDIUM :
471    {
472      l_difficulty->SetValue(l_medium);
473      save_difficulty();
474    } break;
475    case ID_HARD :
476    {
477      l_difficulty->SetValue(l_hard);
478      save_difficulty();
479    } break;
480    case ID_EXTREME :
481    {
482      l_difficulty->SetValue(l_extreme);
483      save_difficulty();
484    } break;
485    case ID_EASY :
486    {
487      l_difficulty->SetValue(l_easy);
488      save_difficulty();
489    } break;
490
491    case ID_NETWORKING :
492    {
493      if (!volume_window)
494      {
495        net_configuration *cfg=new net_configuration;
496        if (cfg->input())
497        {
498          if (main_net_cfg) delete main_net_cfg;
499          main_net_cfg=cfg;
500        } else delete cfg;
501        the_game->draw(0);
502        inm->redraw();
503      }
504    } break;
505
506    case ID_SHOW_SELL :
507    if (!volume_window)
508    {
509      show_sell(1);
510      screen->clear();
511      if (title_screen>=0)
512      {
513        image *tit=cache.img(title_screen);
514          tit->put_image(screen,screen->width()/2-tit->width()/2,
515                          screen->height()/2-tit->height()/2);
516      }
517      inm->redraw();
518      fade_in(NULL,8);
519      wm->flush_screen();
520
521    } break;
522      } break;
523    } break;
524    case EV_CLOSE_WINDOW :
525    {
526      if (ev.window==volume_window)
527      { wm->close_window(volume_window); volume_window=NULL; }
528    } break;
529  }
530}
531
532void *current_demo=NULL;
533
534static ico_button *load_icon(int num, int id, int x, int y, int &h, ifield *next, char const *key)
535{
536  char name[20];
537  char const *base = "newi";
538  int a,b,c;
539  sprintf(name,"%s%04d.pcx",base,num*3+1);
540  a=cache.reg("art/icons.spe",name,SPEC_IMAGE,1);
541
542  sprintf(name,"%s%04d.pcx",base,num*3+2);
543  b=cache.reg("art/icons.spe",name,SPEC_IMAGE,1);
544
545  sprintf(name,"%s%04d.pcx",base,num*3+3);
546  c=cache.reg("art/icons.spe",name,SPEC_IMAGE,1);
547
548  h=cache.img(a)->height();
549
550  return new ico_button(x,y,id,b,b,a,c,next,-1,key);
551}
552
553ico_button *make_default_buttons(int x,int &y, ico_button *append_list)
554{
555  int h;
556  int diff_on;
557
558  if (DEFINEDP(symbol_value(l_difficulty)))
559  {
560    if (symbol_value(l_difficulty)==l_extreme)
561      diff_on=3;
562    else if (symbol_value(l_difficulty)==l_hard)
563      diff_on=2;
564    else if (symbol_value(l_difficulty)==l_easy)
565      diff_on=0;
566    else
567      diff_on=1;
568  } else  diff_on=3;
569
570
571  ico_button *start=load_icon(0,ID_START_GAME,x,y,h,NULL,"ic_start");                         y+=h;
572
573  ico_switch_button *set=NULL;
574  if (!main_net_cfg || (main_net_cfg->state!=net_configuration::SERVER && main_net_cfg->state!=net_configuration::CLIENT))
575  {
576    set=new ico_switch_button(x,y,ID_NULL,diff_on,
577                         load_icon(3,ID_EASY,x,y,h,
578                         load_icon(8,ID_MEDIUM,x,y,h,
579                             load_icon(9,ID_HARD,x,y,h,
580                                     load_icon(10,ID_EXTREME,x,y,h,NULL,"ic_extreme"),
581                                  "ic_hard"),"ic_medium"),"ic_easy"),NULL);         y+=h;
582
583  }
584
585  ico_button *color=load_icon(4,ID_LIGHT_OFF,x,y,h,NULL,"ic_gamma");                          y+=h;
586  ico_button *volume=load_icon(5,ID_VOLUME,x,y,h,NULL,"ic_volume");                            y+=h;
587  ico_button *sell=NULL;
588
589  if (prot)
590  {
591    sell=load_icon(11,ID_NETWORKING,x,y,h,NULL,"ic_networking");
592    y+=h;
593  } else
594  {
595    sell=load_icon(2,ID_SHOW_SELL,x,y,h,NULL,"ic_sell");
596    y+=h;
597  }
598  ico_button *quit=load_icon(6,ID_QUIT,x,y,h,NULL,"ic_quit");                                y+=h;
599
600  if (set)
601  {
602    start->next=set;
603    set->next=color;
604  }
605  else start->next=color;
606
607
608  color->next=volume;
609  if (sell)
610  {
611    volume->next=sell;
612    sell->next=quit;
613  } else volume->next=quit;
614
615  ico_button *list=append_list;
616
617  if (append_list)
618  {
619    while (append_list->next)
620      append_list=(ico_button *)append_list->next;
621    append_list->next=start;
622  } else list=start;
623
624  return list;
625}
626
627
628ico_button *make_conditional_buttons(int x,int &y)
629{
630  ico_button *start_list=NULL;
631  int h;
632  if (current_level)       // should we include a return icon?
633  {
634    start_list=load_icon(7,ID_RETURN,x,y,h,NULL,"ic_return");                       y+=h;
635  }
636
637
638  ico_button *load;
639  if (show_load_icon())
640  { load= load_icon(1,ID_LOAD_PLAYER_GAME,x,y,h,NULL,"ic_load");                     y+=h; }
641  else load=NULL;
642
643  if (start_list) start_list->next=load;
644  else start_list=load;
645
646  return start_list;
647}
648
649void main_menu()
650{
651    int y=yres/2-100;
652    ico_button *list=make_conditional_buttons(xres-33,y);
653    list=make_default_buttons(xres-33,y,list);
654
655    InputManager *inm=new InputManager(screen,list);
656    inm->allow_no_selections();
657    inm->clear_current();
658
659    screen->add_dirty(0,0,319,199);
660
661    event ev;
662
663    int stop_menu=0;
664    time_marker start;
665    wm->flush_screen();
666    do
667    {
668        time_marker new_time;
669
670        if (wm->event_waiting())
671        {
672            do
673            {
674                wm->get_event(ev);
675            } while (ev.type==EV_MOUSE_MOVE && wm->event_waiting());
676            inm->handle_event(ev,NULL);
677            if (ev.type==EV_KEY && ev.key==JK_ESC)
678                wm->push_event(new event(ID_QUIT,NULL));
679
680            menu_handler(ev,inm);
681            start.get_time();
682
683            wm->flush_screen();
684        }
685        else
686        {
687            // ECS - Added so that main menu doesn't grab 100% of CPU
688            milli_wait(30);
689        }
690
691        if (new_time.diff_time(&start)>10)
692        {
693            if (volume_window)
694                start.get_time();
695            else
696            {
697                if (!current_demo)
698                {
699                    LSymbol *d = LSymbol::FindOrCreate("demos");
700                    if (DEFINEDP(d->GetValue()))
701                        current_demo = d->GetValue();
702                }
703                if (current_demo)
704                {
705                    demo_man.set_state(demo_manager::PLAYING,lstring_value(CAR(current_demo)));
706                    stop_menu=1;
707                    current_demo=CDR(current_demo);
708                }
709            }
710        }
711
712        if (volume_window) stop_menu=0;  // can't exit with volume window open
713        else if (main_net_cfg && main_net_cfg->restart_state()) stop_menu=1;
714        else if (the_game->state==RUN_STATE) stop_menu=1;
715        else if (ev.type==EV_MESSAGE)
716        {
717            if (ev.message.id==ID_START_GAME || ev.message.id==ID_RETURN)
718                stop_menu=1;
719            else if (ev.message.id==ID_QUIT)
720            {
721                if (confirm_quit())
722                    stop_menu=1;
723                else
724                {
725                    ev.type=EV_SPURIOUS;
726                    start.get_time();
727                }
728            }
729        }
730    } while (!stop_menu);
731
732    delete inm;
733
734    if (ev.type==EV_MESSAGE && ev.message.id==ID_QUIT)   // propogate the quit message
735        the_game->end_session();
736}
737
738
Note: See TracBrowser for help on using the repository browser.