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

Last change on this file since 489 was 489, checked in by Sam Hocevar, 12 years ago

lisp: no LispSymbol? members are anonymous pointers any longer.

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