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

Last change on this file since 651 was 651, checked in by Sam Hocevar, 9 years ago

build: add a --disable-network compilation flag and get rid of most of
the CELLOS_LV2 ifdefs.

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