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

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

imlib: make JCFont use vec2i classes.

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