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

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

imlib: refactor a few image methods so that they use vec2i.

File size: 18.5 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(vec2i(x1, y1), vec2i(x1, y2), br);
109  main_screen->Line(vec2i(x1 + 1, y1), vec2i(x2, y1), br);
110  main_screen->Line(vec2i(x2, y1 + 1), vec2i(x2, y2), dr);
111  main_screen->Line(vec2i(x1 + 1, y2), vec2i(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(vec2i(mx + 1, by1), vec2i(mx + mw - 2, by2),
246                             wm->bright_color());
247
248      color+=cdir;
249
250      if (color<12 || color>256)
251      {
252    cdir=-cdir;
253    color+=cdir;
254      }
255      wm->flush_screen();
256      main_screen->PutImage(save, vec2i(mx + 1, by1));
257    } else { Timer tmp; tmp.WaitMs(10); }
258
259  } while (!done);
260  if (last_color_time)
261    delete last_color_time;
262  delete save;
263  the_game->draw(the_game->state==SCENE_STATE);
264
265  if (choice!=-1)
266  {
267    void *val=nth(choice,args);
268    if (item_type(val)==L_CONS_CELL)   // is there another value that the user want us to return?
269      return lnumber_value(lcdr(val));
270  }
271  return choice;
272}
273
274static void create_volume_window()
275{
276    volume_window = new VolumeWindow();
277    volume_window->inm->allow_no_selections();
278    volume_window->inm->clear_current();
279    volume_window->show();
280
281    wm->grab_focus(volume_window);
282    wm->flush_screen();
283
284    while(volume_window)
285    {
286        Event ev;
287
288        do
289        {
290            wm->get_event(ev);
291        }
292        while(ev.type == EV_MOUSE_MOVE && wm->IsPending());
293
294        wm->flush_screen();
295
296        if(ev.type == EV_CLOSE_WINDOW
297                 || (ev.type == EV_KEY && ev.key == JK_ESC))
298        {
299            wm->close_window(volume_window);
300            volume_window = NULL;
301        }
302
303        if(!volume_window)
304            break;
305
306        if(ev.type == EV_MESSAGE)
307        {
308            char const *s;
309
310            switch(ev.message.id)
311            {
312            case ID_SFX_UP:
313                sfx_volume += 16;
314                if(sfx_volume > 127)
315                    sfx_volume = 127;
316                volume_window->draw_sfx_vol();
317                s = "sfx/ambtech1.wav";
318                if(sound_avail & SFX_INITIALIZED)
319                    cache.sfx(cache.reg(s, s, SPEC_EXTERN_SFX, 1))
320                        ->play(sfx_volume);
321                break;
322            case ID_SFX_DOWN:
323                sfx_volume -= 16;
324                if(sfx_volume < 0)
325                    sfx_volume = 0;
326                volume_window->draw_sfx_vol();
327                s = "sfx/ambtech1.wav";
328                if(sound_avail & SFX_INITIALIZED)
329                    cache.sfx(cache.reg(s, s, SPEC_EXTERN_SFX, 1))
330                        ->play(sfx_volume);
331                break;
332
333            case ID_MUSIC_UP:
334                music_volume += 16;
335                if(music_volume > 127)
336                    music_volume = 127;
337                volume_window->draw_music_vol();
338                if(current_song)
339                    current_song->set_volume(music_volume);
340                break;
341            case ID_MUSIC_DOWN:
342                music_volume -= 16;
343                if(music_volume < 0)
344                    music_volume = 0;
345                volume_window->draw_music_vol();
346                if(current_song)
347                    current_song->set_volume(music_volume);
348                break;
349            }
350        }
351    }
352
353    wm->close_window(volume_window);
354}
355
356void save_difficulty()
357{
358  FILE *fp=open_FILE("hardness.lsp","wb");
359  if (!fp)
360    dprintf("Unable to write to file hardness.lsp\n");
361  else
362  {
363    fprintf(fp,"(setf difficulty '");
364    if (DEFINEDP(symbol_value(l_difficulty)))
365    {
366      if (symbol_value(l_difficulty)==l_extreme)
367        fprintf(fp,"extreme)\n");
368      else if (symbol_value(l_difficulty)==l_hard)
369        fprintf(fp,"hard)\n");
370      else if (symbol_value(l_difficulty)==l_easy)
371        fprintf(fp,"easy)\n");
372      else
373        fprintf(fp,"medium)\n");
374    } else
375       fprintf(fp,"medium)\n");
376    fclose(fp);
377  }
378}
379
380void fade_out(int steps);
381void fade_in(image *im, int steps);
382
383
384void show_sell(int abortable)
385{
386  LSymbol *ss = LSymbol::FindOrCreate("sell_screens");
387  if (!DEFINEDP(ss->GetValue()))
388  {
389    LSpace *sp = LSpace::Current;
390    LSpace::Current = &LSpace::Perm;
391//    char *prog="((\"art/help.spe\" . \"sell2\")(\"art/help.spe\" . \"sell4\")(\"art/help.spe\" . \"sell3\")(\"art/fore/endgame.spe\" . \"credit\"))";
392//    char *prog="((\"art/fore/endgame.spe\" . \"credit\") (\"art/help.spe\" . \"sell6\"))";
393    char const *prog = "((\"art/fore/endgame.spe\" . \"credit\"))";
394    ss->SetValue(LObject::Compile(prog));
395    LSpace::Current = sp;
396  }
397
398  if (DEFINEDP(ss->GetValue()))
399  {
400    image blank(vec2i(2, 2)); blank.clear();
401    wm->set_mouse_shape(blank.copy(),0,0);      // don't show mouse
402
403    LObject *tmp = (LObject *)ss->GetValue();
404    int quit=0;
405    while (tmp && !quit)
406    {
407      int im=cache.reg_object("art/help.spe",CAR(tmp),SPEC_IMAGE,1);
408      fade_in(cache.img(im),16);
409
410      Event ev;
411      do
412      { wm->flush_screen();
413    wm->get_event(ev);
414      } while (ev.type!=EV_KEY);
415      if (ev.key==JK_ESC && abortable)
416        quit=1;
417      fade_out(16);
418      tmp = (LObject *)CDR(tmp);
419    }
420    wm->set_mouse_shape(cache.img(c_normal)->copy(),1,1);
421  }
422}
423
424
425void menu_handler(Event &ev, InputManager *inm)
426{
427  switch (ev.type)
428  {
429    case EV_MESSAGE :
430    {
431      switch (ev.message.id)
432      {
433    case ID_LIGHT_OFF :
434    if (!volume_window)
435    {
436      gamma_correct(pal,1);
437    } break;
438    case ID_RETURN :
439    if (!volume_window)
440    {
441      the_game->set_state(RUN_STATE);
442    } break;
443    case ID_START_GAME :
444    if (!volume_window)
445    {
446      the_game->load_level(level_file);
447      the_game->set_state(RUN_STATE);
448      view *v;
449      for (v=player_list; v; v=v->next)
450        if (v->focus)
451          v->reset_player();
452
453    } break;
454
455
456        case ID_LOAD_PLAYER_GAME :
457    if (!volume_window)
458    {
459      int got_level=load_game(0,symbol_str("LOAD"));
460      the_game->reset_keymap();
461      if (got_level)
462      {
463        char name[255];
464        sprintf(name,"%ssave%04d.spe", get_save_filename_prefix(), got_level);
465
466        the_game->load_level(name);
467        the_game->set_state(RUN_STATE);
468      }
469    } break;
470
471
472    case ID_VOLUME :
473    if (!volume_window)
474    { create_volume_window(); } break;
475
476    case ID_MEDIUM :
477    {
478      l_difficulty->SetValue(l_medium);
479      save_difficulty();
480    } break;
481    case ID_HARD :
482    {
483      l_difficulty->SetValue(l_hard);
484      save_difficulty();
485    } break;
486    case ID_EXTREME :
487    {
488      l_difficulty->SetValue(l_extreme);
489      save_difficulty();
490    } break;
491    case ID_EASY :
492    {
493      l_difficulty->SetValue(l_easy);
494      save_difficulty();
495    } break;
496
497    case ID_NETWORKING :
498    {
499      if (!volume_window)
500      {
501        net_configuration *cfg=new net_configuration;
502        if (cfg->input())
503        {
504          if (main_net_cfg) delete main_net_cfg;
505          main_net_cfg=cfg;
506        } else delete cfg;
507        the_game->draw(0);
508        inm->redraw();
509      }
510    } break;
511
512    case ID_SHOW_SELL :
513    if (!volume_window)
514    {
515      show_sell(1);
516      main_screen->clear();
517      if (title_screen>=0)
518      {
519        image *im = cache.img(title_screen);
520        main_screen->PutImage(im, main_screen->Size() / 2 - im->Size() / 2);
521      }
522      inm->redraw();
523      fade_in(NULL,8);
524      wm->flush_screen();
525
526    } break;
527      } break;
528    } break;
529    case EV_CLOSE_WINDOW :
530    {
531      if (ev.window==volume_window)
532      { wm->close_window(volume_window); volume_window=NULL; }
533    } break;
534  }
535}
536
537void *current_demo=NULL;
538
539static ico_button *load_icon(int num, int id, int x, int y, int &h, ifield *next, char const *key)
540{
541  char name[20];
542  char const *base = "newi";
543  int a,b,c;
544  sprintf(name,"%s%04d.pcx",base,num*3+1);
545  a=cache.reg("art/icons.spe",name,SPEC_IMAGE,1);
546
547  sprintf(name,"%s%04d.pcx",base,num*3+2);
548  b=cache.reg("art/icons.spe",name,SPEC_IMAGE,1);
549
550  sprintf(name,"%s%04d.pcx",base,num*3+3);
551  c=cache.reg("art/icons.spe",name,SPEC_IMAGE,1);
552
553  h=cache.img(a)->Size().y;
554
555  return new ico_button(x,y,id,b,b,a,c,next,-1,key);
556}
557
558ico_button *make_default_buttons(int x,int &y, ico_button *append_list)
559{
560  int h;
561  int diff_on;
562
563  if (DEFINEDP(symbol_value(l_difficulty)))
564  {
565    if (symbol_value(l_difficulty)==l_extreme)
566      diff_on=3;
567    else if (symbol_value(l_difficulty)==l_hard)
568      diff_on=2;
569    else if (symbol_value(l_difficulty)==l_easy)
570      diff_on=0;
571    else
572      diff_on=1;
573  } else  diff_on=3;
574
575
576  ico_button *start=load_icon(0,ID_START_GAME,x,y,h,NULL,"ic_start");                         y+=h;
577
578  ico_switch_button *set=NULL;
579  if (!main_net_cfg || (main_net_cfg->state!=net_configuration::SERVER && main_net_cfg->state!=net_configuration::CLIENT))
580  {
581    set=new ico_switch_button(x,y,ID_NULL,diff_on,
582                         load_icon(3,ID_EASY,x,y,h,
583                         load_icon(8,ID_MEDIUM,x,y,h,
584                             load_icon(9,ID_HARD,x,y,h,
585                                     load_icon(10,ID_EXTREME,x,y,h,NULL,"ic_extreme"),
586                                  "ic_hard"),"ic_medium"),"ic_easy"),NULL);         y+=h;
587
588  }
589
590  ico_button *color=load_icon(4,ID_LIGHT_OFF,x,y,h,NULL,"ic_gamma");                          y+=h;
591  ico_button *volume=load_icon(5,ID_VOLUME,x,y,h,NULL,"ic_volume");                            y+=h;
592  ico_button *sell=NULL;
593
594  if (prot)
595  {
596    sell=load_icon(11,ID_NETWORKING,x,y,h,NULL,"ic_networking");
597    y+=h;
598  } else
599  {
600    sell=load_icon(2,ID_SHOW_SELL,x,y,h,NULL,"ic_sell");
601    y+=h;
602  }
603  ico_button *quit=load_icon(6,ID_QUIT,x,y,h,NULL,"ic_quit");                                y+=h;
604
605  if (set)
606  {
607    start->next=set;
608    set->next=color;
609  }
610  else start->next=color;
611
612
613  color->next=volume;
614  if (sell)
615  {
616    volume->next=sell;
617    sell->next=quit;
618  } else volume->next=quit;
619
620  ico_button *list=append_list;
621
622  if (append_list)
623  {
624    while (append_list->next)
625      append_list=(ico_button *)append_list->next;
626    append_list->next=start;
627  } else list=start;
628
629  return list;
630}
631
632
633ico_button *make_conditional_buttons(int x,int &y)
634{
635  ico_button *start_list=NULL;
636  int h;
637  if (current_level)       // should we include a return icon?
638  {
639    start_list=load_icon(7,ID_RETURN,x,y,h,NULL,"ic_return");                       y+=h;
640  }
641
642
643  ico_button *load;
644  if (show_load_icon())
645  { load= load_icon(1,ID_LOAD_PLAYER_GAME,x,y,h,NULL,"ic_load");                     y+=h; }
646  else load=NULL;
647
648  if (start_list) start_list->next=load;
649  else start_list=load;
650
651  return start_list;
652}
653
654void main_menu()
655{
656    int y=yres/2-100;
657    ico_button *list=make_conditional_buttons(xres-33,y);
658    list=make_default_buttons(xres-33,y,list);
659
660    InputManager *inm=new InputManager(main_screen,list);
661    inm->allow_no_selections();
662    inm->clear_current();
663
664    main_screen->AddDirty(0, 0, 320, 200);
665
666    Event ev;
667
668    int stop_menu=0;
669    time_marker start;
670    wm->flush_screen();
671    do
672    {
673        time_marker new_time;
674
675        if (wm->IsPending())
676        {
677            do
678            {
679                wm->get_event(ev);
680            } while (ev.type==EV_MOUSE_MOVE && wm->IsPending());
681            inm->handle_event(ev,NULL);
682            if (ev.type==EV_KEY && ev.key==JK_ESC)
683                wm->Push(new Event(ID_QUIT,NULL));
684
685            menu_handler(ev,inm);
686            start.get_time();
687
688            wm->flush_screen();
689        }
690        else
691        {
692            // ECS - Added so that main menu doesn't grab 100% of CPU
693            Timer tmp; tmp.WaitMs(30);
694        }
695
696        if (new_time.diff_time(&start)>10)
697        {
698            if (volume_window)
699                start.get_time();
700            else
701            {
702                if (!current_demo)
703                {
704                    LSymbol *d = LSymbol::FindOrCreate("demos");
705                    if (DEFINEDP(d->GetValue()))
706                        current_demo = d->GetValue();
707                }
708                if (current_demo)
709                {
710                    demo_man.set_state(demo_manager::PLAYING,lstring_value(CAR(current_demo)));
711                    stop_menu=1;
712                    current_demo=CDR(current_demo);
713                }
714            }
715        }
716
717        if (volume_window) stop_menu=0;  // can't exit with volume window open
718        else if (main_net_cfg && main_net_cfg->restart_state()) stop_menu=1;
719        else if (the_game->state==RUN_STATE) stop_menu=1;
720        else if (ev.type==EV_MESSAGE)
721        {
722            if (ev.message.id==ID_START_GAME || ev.message.id==ID_RETURN)
723                stop_menu=1;
724            else if (ev.message.id==ID_QUIT)
725            {
726                if (confirm_quit())
727                    stop_menu=1;
728                else
729                {
730                    ev.type=EV_SPURIOUS;
731                    start.get_time();
732                }
733            }
734        }
735    } while (!stop_menu);
736
737    delete inm;
738
739    if (ev.type==EV_MESSAGE && ev.message.id==ID_QUIT)   // propogate the quit message
740        the_game->end_session();
741}
742
743
Note: See TracBrowser for help on using the repository browser.