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

Last change on this file since 518 was 518, checked in by Sam Hocevar, 11 years ago

imlib: refactor dirty_rect clipping coordiantes so that the upper
bound is no longer inclusive. It will make things easier in the future.

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