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

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

game: get rid of milli_wait() and rewrite the fixed framerate handling.

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