source: abuse/trunk/src/menu.cpp

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

core: rename vec2i to ivec2 and update matrix.h from Lol Engine.

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