source: abuse/branches/lol/src/menu.cpp @ 732

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

build: SDL2 compilation fixes.

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