Source code

Revision control

1
// vim: set ts=4 sw=4 tw=99 noet:
2
//
3
// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO").
4
// Copyright (C) The AMX Mod X Development Team.
5
//
6
// This software is licensed under the GNU General Public License, version 3 or higher.
7
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
9
10
#include "amxmodx.h"
11
#include "CMenu.h"
12
#include "newmenus.h"
13
#include "format.h"
14
15
ke::Vector<Menu *> g_NewMenus;
16
CStack<int> g_MenuFreeStack;
17
18
void ClearMenus()
19
{
20
for (size_t i = 0; i < g_NewMenus.length(); i++)
21
{
22
delete g_NewMenus[i];
23
}
24
25
g_NewMenus.clear();
26
while (!g_MenuFreeStack.empty())
27
{
28
g_MenuFreeStack.pop();
29
}
30
}
31
32
void validate_menu_text(char *str)
33
{
34
if (!g_coloredmenus)
35
{
36
size_t offs = 0;
37
while (*str)
38
{
39
if (*str == '\\')
40
{
41
str++;
42
char c = tolower(*str);
43
if (c == 'r' || c == 'w'
44
|| c== 'y' || c == 'd')
45
{
46
str++;
47
offs += 2;
48
continue;
49
}
50
}
51
if (offs)
52
{
53
*(str-offs) = *str;
54
}
55
str++;
56
}
57
if (offs)
58
{
59
*(str-offs) = '\0';
60
}
61
}
62
}
63
64
Menu *get_menu_by_id(int id)
65
{
66
if (id < 0 || size_t(id) >= g_NewMenus.length() || !g_NewMenus[id])
67
return NULL;
68
69
return g_NewMenus[id];
70
}
71
72
bool CloseNewMenus(CPlayer *pPlayer)
73
{
74
// If the stupid handler keeps drawing menus,
75
// We need to keep cancelling them. But we put in a quick infinite loop
76
// counter to prevent this from going nuts.
77
78
int loops = 0;
79
Menu *pMenu;
80
81
while ((pMenu = get_menu_by_id(pPlayer->newmenu)))
82
{
83
pMenu->Close(pPlayer->index);
84
85
if (++loops >= 10)
86
{
87
return false;
88
}
89
}
90
91
return true;
92
}
93
94
Menu::Menu(const char *title, AMX *amx, int fid, bool use_ml) : m_Title(title), m_ItemColor("\\r"),
95
m_NeverExit(false), m_ForceExit(false), m_AutoColors(g_coloredmenus), thisId(0), func(fid),
96
isDestroying(false), pageCallback(-1), showPageNumber(true), useMultilingual(use_ml), amx(amx), items_per_page(7)
97
{
98
CPluginMngr::CPlugin *pPlugin = g_plugins.findPluginFast(amx);
99
menuId = g_menucmds.registerMenuId(title, amx);
100
101
if (strcmp(pPlugin->getName(), "war3ft.amxx") == 0)
102
{
103
const char *version = pPlugin->getVersion();
104
if (strncmp(pPlugin->getVersion(), "3.0 RC", 6) == 0
105
&& atoi(&version[6]) <= 8)
106
{
107
g_menucmds.registerMenuCmd(
108
g_plugins.findPluginFast(amx),
109
menuId,
110
-1,
111
g_forwards.duplicateSPForward(fid),
112
true);
113
}
114
}
115
116
m_OptNames[abs(MENU_BACK)] = "Back";
117
m_OptNames[abs(MENU_MORE)] = "More";
118
m_OptNames[abs(MENU_EXIT)] = "Exit";
119
}
120
121
Menu::~Menu()
122
{
123
for (size_t i = 0; i < m_Items.length(); i++)
124
{
125
delete m_Items[i];
126
}
127
128
unregisterSPForward(this->func);
129
unregisterSPForward(this->pageCallback);
130
131
m_Items.clear();
132
}
133
134
menuitem *Menu::AddItem(const char *name, const char *cmd, int access)
135
{
136
menuitem *pItem = new menuitem;
137
138
pItem->name = name;
139
pItem->cmd = cmd;
140
pItem->access = access;
141
pItem->id = m_Items.length();
142
pItem->handler = -1;
143
pItem->isBlank = false;
144
pItem->pfn = NULL;
145
146
m_Items.append(pItem);
147
148
return pItem;
149
}
150
151
menuitem *Menu::GetMenuItem(item_t item)
152
{
153
if (item >= m_Items.length())
154
return NULL;
155
156
return m_Items[item];
157
}
158
159
size_t Menu::GetItemCount()
160
{
161
return m_Items.length();
162
}
163
164
size_t Menu::GetPageCount()
165
{
166
size_t items = GetItemCount();
167
if (items_per_page == 0)
168
{
169
return 1;
170
}
171
172
return ((items/items_per_page) + ((items % items_per_page) ? 1 : 0));
173
}
174
175
int Menu::PagekeyToItem(page_t page, item_t key)
176
{
177
size_t start = page * items_per_page;
178
size_t num_pages = GetPageCount();
179
180
if (num_pages == 1 || !items_per_page)
181
{
182
if (key > m_Items.length())
183
{
184
return MENU_EXIT;
185
} else {
186
return key-1;
187
}
188
} else {
189
//first page
190
if (page == 0)
191
{
192
/* The algorithm for spaces here is same as a middle page. */
193
item_t new_key = key;
194
for (size_t i=start; i<(start+key-1) && i<m_Items.length(); i++)
195
{
196
for (size_t j=0; j<m_Items[i]->blanks.length(); j++)
197
{
198
if (m_Items[i]->blanks[j].EatNumber())
199
{
200
if (!new_key)
201
{
202
break;
203
}
204
new_key--;
205
}
206
if (!new_key)
207
{
208
break;
209
}
210
}
211
}
212
key = new_key;
213
if (key == items_per_page + 2)
214
{
215
return MENU_MORE;
216
} else if (key == items_per_page + 3) {
217
return MENU_EXIT;
218
} else {
219
return (start + key - 1);
220
}
221
} else if (page == num_pages - 1) {
222
//last page
223
item_t item_tracker = 0; // tracks how many valid items we have passed so far.
224
size_t remaining = m_Items.length() - start;
225
item_t new_key = key;
226
227
// For every item that takes up a slot (item or padded blank)
228
// we subtract one from new key.
229
// For every item (not blanks), we increase item_tracker.
230
// When new_key equals 0, item_tracker will then be set to
231
// whatever valid item was selected.
232
for (size_t i=m_Items.length() - remaining; i<m_Items.length(); i++)
233
{
234
item_tracker++;
235
236
if (new_key<=1) // If new_key is 0, or will be 0 after the next decrease
237
{
238
new_key=0;
239
break;
240
}
241
242
new_key--;
243
244
for (size_t j=0; j<m_Items[i]->blanks.length(); j++)
245
{
246
if (m_Items[i]->blanks[j].EatNumber())
247
{
248
new_key--;
249
}
250
if (!new_key)
251
{
252
break;
253
}
254
}
255
}
256
// If new_key doesn't equal zero, then a back/exit button was pressed.
257
if (new_key!=0)
258
{
259
if (key == items_per_page + 1)
260
{
261
return MENU_BACK;
262
}
263
else if (key == items_per_page + 3)
264
{
265
return MENU_EXIT;
266
}
267
// MENU_MORE should never happen here.
268
}
269
// otherwise our item is now start + item_tracker - 1
270
return (start + item_tracker - 1);
271
} else {
272
/* The algorithm for spaces here is a bit harder. We have to subtract
273
* one from the key for each space we find along the way.
274
*/
275
item_t new_key = key;
276
for (size_t i=start; i<(start+items_per_page-1) && i<m_Items.length(); i++)
277
{
278
for (size_t j=0; j<m_Items[i]->blanks.length(); j++)
279
{
280
if (m_Items[i]->blanks[j].EatNumber())
281
{
282
if (!new_key)
283
{
284
break;
285
}
286
new_key--;
287
}
288
if (!new_key)
289
{
290
break;
291
}
292
}
293
}
294
key = new_key;
295
if (key > items_per_page && (key-items_per_page<=3))
296
{
297
unsigned int num = key - items_per_page - 1;
298
static int map[] = {MENU_BACK, MENU_MORE, MENU_EXIT};
299
return map[num];
300
} else {
301
return (start + key - 1);
302
}
303
}
304
}
305
}
306
307
bool Menu::Display(int player, page_t page)
308
{
309
int keys = 0;
310
const char *str = GetTextString(player, page, keys);
311
312
if (!str)
313
return false;
314
315
static char buffer[2048];
316
int len = ke::SafeSprintf(buffer, sizeof(buffer), "%s", str);
317
318
CPlayer *pPlayer = GET_PLAYER_POINTER_I(player);
319
320
pPlayer->keys = keys;
321
pPlayer->menu = menuId;
322
pPlayer->newmenu = thisId;
323
pPlayer->page = (int)page;
324
325
UTIL_ShowMenu(pPlayer->pEdict, keys, -1, buffer, len);
326
327
return true;
328
}
329
330
void Menu::Close(int player)
331
{
332
CPlayer *pPlayer = GET_PLAYER_POINTER_I(player);
333
334
int status;
335
if (gpGlobals->time > pPlayer->menuexpire)
336
status = MENU_TIMEOUT;
337
else
338
status = MENU_EXIT;
339
340
pPlayer->keys = 0;
341
pPlayer->menu = 0;
342
pPlayer->newmenu = -1;
343
344
executeForwards(func,
345
static_cast<cell>(player),
346
static_cast<cell>(thisId),
347
static_cast<cell>(status));
348
}
349
350
const char *Menu::GetTextString(int player, page_t page, int &keys)
351
{
352
page_t pages = GetPageCount();
353
item_t numItems = GetItemCount();
354
355
if (page >= pages)
356
return NULL;
357
358
m_Text = nullptr;
359
360
361
auto title = m_Title.chars();
362
363
if (this->useMultilingual)
364
{
365
const auto language = playerlang(player);
366
const auto definition = translate(this->amx, language, title);
367
368
if (definition)
369
{
370
title = definition;
371
}
372
}
373
374
char buffer[255];
375
if (showPageNumber && items_per_page && (pages != 1))
376
{
377
if (m_AutoColors)
378
ke::SafeSprintf(buffer, sizeof(buffer), "\\y%s %d/%d\n\\w\n", title, page + 1, pages);
379
else
380
ke::SafeSprintf(buffer, sizeof(buffer), "%s %d/%d\n\n", title, page + 1, pages);
381
} else {
382
if (m_AutoColors)
383
ke::SafeSprintf(buffer, sizeof(buffer), "\\y%s\n\\w\n", title);
384
else
385
ke::SafeSprintf(buffer, sizeof(buffer), "%s\n\n", title);
386
}
387
388
m_Text = m_Text + buffer;
389
390
enum
391
{
392
Display_Back = (1<<0),
393
Display_Next = (1<<1),
394
};
395
396
int flags = Display_Back|Display_Next;
397
398
item_t start = page * items_per_page;
399
item_t end = 0;
400
if (items_per_page)
401
{
402
if (start + items_per_page >= numItems)
403
{
404
end = numItems;
405
flags &= ~Display_Next;
406
} else {
407
end = start + items_per_page;
408
}
409
} else {
410
end = numItems;
411
if (end > 10)
412
{
413
end = 10;
414
}
415
}
416
417
if (page == 0)
418
{
419
flags &= ~Display_Back;
420
}
421
422
menuitem *pItem = NULL;
423
424
int option = 0;
425
keys = 0;
426
bool enabled = true;
427
int ret = 0;
428
int slots = 0;
429
int option_display = 0;
430
431
for (item_t i = start; i < end; i++)
432
{
433
// reset enabled
434
enabled = true;
435
pItem = m_Items[i];
436
437
if (pItem->access && !(pItem->access & g_players[player].flags[0]))
438
{
439
enabled = false;
440
}
441
442
if (pItem->handler != -1)
443
{
444
ret = executeForwards(pItem->handler, static_cast<cell>(player), static_cast<cell>(thisId), static_cast<cell>(i));
445
if (ret == ITEM_ENABLED)
446
{
447
enabled = true;
448
} else if (ret == ITEM_DISABLED) {
449
enabled = false;
450
}
451
}
452
453
if (pItem->pfn)
454
{
455
ret = (pItem->pfn)(player, thisId, i);
456
if (ret == ITEM_ENABLED)
457
{
458
enabled = true;
459
} else if (ret == ITEM_DISABLED) {
460
enabled = false;
461
}
462
}
463
464
if (pItem->isBlank)
465
{
466
enabled = false;
467
}
468
469
if (enabled)
470
{
471
keys |= (1<<option);
472
}
473
474
option_display = ++option;
475
if (option_display == 10)
476
{
477
option_display = 0;
478
}
479
480
auto itemName = pItem->name.chars();
481
482
if (this->useMultilingual)
483
{
484
const auto language = playerlang(player);
485
const auto definition = translate(this->amx, language, itemName);
486
487
if (definition)
488
{
489
itemName = definition;
490
}
491
}
492
493
if (pItem->isBlank)
494
{
495
ke::SafeSprintf(buffer, sizeof(buffer), "%s\n", itemName);
496
}
497
else if (enabled)
498
{
499
if (m_AutoColors)
500
{
501
ke::SafeSprintf(buffer, sizeof(buffer), "%s%d.\\w %s\n", m_ItemColor.chars(),option_display, itemName);
502
} else {
503
ke::SafeSprintf(buffer, sizeof(buffer), "%d. %s\n", option_display, itemName);
504
}
505
} else {
506
if (m_AutoColors)
507
{
508
ke::SafeSprintf(buffer, sizeof(buffer), "\\d%d. %s\n\\w", option_display, itemName);
509
} else {
510
ke::SafeSprintf(buffer, sizeof(buffer), "#. %s\n", itemName);
511
}
512
}
513
slots++;
514
515
m_Text = m_Text + buffer;
516
517
//attach blanks
518
if (pItem->blanks.length())
519
{
520
for (size_t j=0; j<pItem->blanks.length(); j++)
521
{
522
if (pItem->blanks[j].EatNumber())
523
{
524
option++;
525
}
526
m_Text = m_Text + pItem->blanks[j].GetDisplay();
527
m_Text = m_Text + "\n";
528
slots++;
529
}
530
}
531
}
532
533
if (items_per_page)
534
{
535
/* Pad spaces until we reach the end of the max possible items */
536
for (unsigned int i=(unsigned)slots; i<items_per_page; i++)
537
{
538
m_Text = m_Text + "\n";
539
option++;
540
}
541
/* Make sure there is at least one visual pad */
542
m_Text = m_Text + "\n";
543
544
/* Don't bother if there is only one page */
545
if (pages > 1)
546
{
547
548
auto tempItemName = m_OptNames[abs(MENU_BACK)].chars();
549
550
if (this->useMultilingual)
551
{
552
const auto language = playerlang(player);
553
const auto definition = translate(this->amx, language, tempItemName);
554
555
if (definition)
556
{
557
tempItemName = definition;
558
}
559
}
560
561
if (flags & Display_Back)
562
{
563
keys |= (1<<option++);
564
if (m_AutoColors)
565
{
566
ke::SafeSprintf(buffer,
567
sizeof(buffer),
568
"%s%d. \\w%s\n",
569
m_ItemColor.chars(),
570
option == 10 ? 0 : option,
571
tempItemName);
572
} else {
573
ke::SafeSprintf(buffer,
574
sizeof(buffer),
575
"%d. %s\n",
576
option == 10 ? 0 : option,
577
tempItemName);
578
}
579
} else {
580
option++;
581
if (m_AutoColors)
582
{
583
ke::SafeSprintf(buffer,
584
sizeof(buffer),
585
"\\d%d. %s\n\\w",
586
option == 10 ? 0 : option,
587
tempItemName);
588
} else {
589
ke::SafeSprintf(buffer, sizeof(buffer), "#. %s\n", tempItemName);
590
}
591
}
592
m_Text = m_Text + buffer;
593
594
tempItemName = m_OptNames[abs(MENU_MORE)].chars();
595
596
if (this->useMultilingual)
597
{
598
const auto language = playerlang(player);
599
const auto definition = translate(this->amx, language, tempItemName);
600
601
if (definition)
602
{
603
tempItemName = definition;
604
}
605
}
606
607
if (flags & Display_Next)
608
{
609
keys |= (1<<option++);
610
if (m_AutoColors)
611
{
612
ke::SafeSprintf(buffer,
613
sizeof(buffer),
614
"%s%d. \\w%s\n",
615
m_ItemColor.chars(),
616
option == 10 ? 0 : option,
617
tempItemName);
618
} else {
619
ke::SafeSprintf(buffer,
620
sizeof(buffer),
621
"%d. %s\n",
622
option == 10 ? 0 : option,
623
tempItemName);
624
}
625
} else {
626
option++;
627
if (m_AutoColors)
628
{
629
ke::SafeSprintf(buffer,
630
sizeof(buffer),
631
"\\d%d. %s\n\\w",
632
option == 10 ? 0 : option,
633
tempItemName);
634
} else {
635
ke::SafeSprintf(buffer, sizeof(buffer), "#. %s\n", tempItemName);
636
}
637
}
638
m_Text = m_Text + buffer;
639
} else {
640
/* Keep padding */
641
option += 2;
642
}
643
}
644
645
if ((items_per_page && !m_NeverExit) || (m_ForceExit && numItems < 10))
646
{
647
auto exitName = m_OptNames[abs(MENU_EXIT)].chars();
648
649
if (this->useMultilingual)
650
{
651
const auto language = playerlang(player);
652
const auto definition = translate(this->amx, language, exitName);
653
654
if (definition)
655
{
656
exitName = definition;
657
}
658
}
659
660
/* Visual pad has not been added yet */
661
if (!items_per_page)
662
m_Text = m_Text + "\n";
663
664
keys |= (1<<option++);
665
if (m_AutoColors)
666
{
667
ke::SafeSprintf(buffer,
668
sizeof(buffer),
669
"%s%d. \\w%s\n",
670
m_ItemColor.chars(),
671
option == 10 ? 0 : option,
672
exitName);
673
} else {
674
ke::SafeSprintf(buffer,
675
sizeof(buffer),
676
"%d. %s\n",
677
option == 10 ? 0 : option,
678
exitName);
679
}
680
m_Text = m_Text + buffer;
681
}
682
683
return m_Text.ptr();
684
}
685
686
#define GETMENU(p) Menu *pMenu = get_menu_by_id(p); \
687
if (pMenu == NULL || pMenu->isDestroying) { \
688
LogError(amx, AMX_ERR_NATIVE, "Invalid menu id %d(%d)", p, g_NewMenus.length()); \
689
return 0; }
690
691
// native menu_create(const title[], const handler[], bool:ml = false);
692
static cell AMX_NATIVE_CALL menu_create(AMX *amx, cell *params)
693
{
694
enum args { arg_count, arg_title, arg_handler, arg_ml };
695
696
int length;
697
const auto title = get_amxstring(amx, params[arg_title], 0, length);
698
const auto handler = get_amxstring(amx, params[arg_handler], 1, length);
699
const auto callback = registerSPForwardByName(amx, handler, FP_CELL, FP_CELL, FP_CELL, FP_DONE);
700
701
if (callback == -1)
702
{
703
LogError(amx, AMX_ERR_NOTFOUND, R"(Invalid function "%s")", handler);
704
return 0;
705
}
706
707
validate_menu_text(title);
708
709
auto pMenu = new Menu(title, amx, callback, params[arg_ml] != 0);
710
711
if (g_MenuFreeStack.empty())
712
{
713
g_NewMenus.append(pMenu);
714
715
pMenu->thisId = static_cast<int>(g_NewMenus.length()) - 1;
716
}
717
else
718
{
719
const auto position = g_MenuFreeStack.front();
720
721
g_MenuFreeStack.pop();
722
g_NewMenus[position] = pMenu;
723
724
pMenu->thisId = position;
725
}
726
727
return pMenu->thisId;
728
}
729
730
static cell AMX_NATIVE_CALL menu_addblank(AMX *amx, cell *params)
731
{
732
GETMENU(params[1]);
733
734
if (params[2] && (!pMenu->items_per_page && pMenu->GetItemCount() >= 10))
735
{
736
LogError(amx, AMX_ERR_NATIVE, "Non-paginated menus are limited to 10 items.");
737
return 0;
738
}
739
740
if (!pMenu->m_Items.length())
741
{
742
LogError(amx, AMX_ERR_NATIVE, "Blanks can only be added after items.");
743
return 0;
744
}
745
746
menuitem *item = pMenu->m_Items[pMenu->m_Items.length() - 1];
747
748
BlankItem a;
749
750
a.SetBlank();
751
752
if (params[2] == 1)
753
a.SetEatNumber(true);
754
755
else
756
a.SetEatNumber(false);
757
758
item->blanks.append(ke::Move(a));
759
760
return 1;
761
}
762
static cell AMX_NATIVE_CALL menu_addtext(AMX *amx, cell *params)
763
{
764
GETMENU(params[1]);
765
766
if (params[2] && (!pMenu->items_per_page && pMenu->GetItemCount() >= 10))
767
{
768
LogError(amx, AMX_ERR_NATIVE, "Non-paginated menus are limited to 10 items.");
769
return 0;
770
}
771
772
if (!pMenu->m_Items.length())
773
{
774
LogError(amx, AMX_ERR_NATIVE, "Blanks can only be added after items.");
775
return 0;
776
}
777
778
menuitem *item = pMenu->m_Items[pMenu->m_Items.length() - 1];
779
780
BlankItem a;
781
782
int len;
783
a.SetText(get_amxstring(amx, params[2], 0, len));
784
785
if (params[3] == 1)
786
a.SetEatNumber(true);
787
788
else
789
a.SetEatNumber(false);
790
791
item->blanks.append(ke::Move(a));
792
793
return 1;
794
}
795
796
static cell AMX_NATIVE_CALL menu_addblank2(AMX *amx, cell *params)
797
{
798
GETMENU(params[1]);
799
800
if (!pMenu->items_per_page && pMenu->GetItemCount() >= 10)
801
{
802
LogError(amx, AMX_ERR_NATIVE, "Non-paginated menus are limited to 10 items.");
803
return 0;
804
}
805
806
menuitem *pItem = pMenu->AddItem("", "", 0);
807
pItem->isBlank = true;
808
809
return 1;
810
}
811
static cell AMX_NATIVE_CALL menu_addtext2(AMX *amx, cell *params)
812
{
813
int len;
814
char *name;
815
816
GETMENU(params[1]);
817
818
if (!pMenu->items_per_page && pMenu->GetItemCount() >= 10)
819
{
820
LogError(amx, AMX_ERR_NATIVE, "Non-paginated menus are limited to 10 items.");
821
return 0;
822
}
823
824
name = get_amxstring(amx, params[2], 0, len);
825
validate_menu_text(name);
826
827
menuitem *pItem = pMenu->AddItem(name, "", 0);
828
pItem->isBlank = true;
829
830
return 1;
831
}
832
833
//Adds an item to the menu (returns current item count - 1)
834
//native menu_additem(menu, const name[], const command[]="", access=0);
835
static cell AMX_NATIVE_CALL menu_additem(AMX *amx, cell *params)
836
{
837
int len;
838
char *name, *cmd;
839
int access;
840
841
GETMENU(params[1]);
842
843
if (!pMenu->items_per_page && pMenu->GetItemCount() >= 10)
844
{
845
LogError(amx, AMX_ERR_NATIVE, "Non-paginated menus are limited to 10 items.");
846
return 0;
847
}
848
849
name = get_amxstring(amx, params[2], 0, len);
850
validate_menu_text(name);
851
cmd = get_amxstring(amx, params[3], 1, len);
852
access = params[4];
853
854
menuitem *pItem = pMenu->AddItem(name, cmd, access);
855
856
pItem->handler = params[5];
857
858
return 1;
859
}
860
861
//Returns the number of pages in a menu
862
//native csdm_menu_pages(menu);
863
static cell AMX_NATIVE_CALL menu_pages(AMX *amx, cell *params)
864
{
865
GETMENU(params[1]);
866
return pMenu->GetPageCount();
867
}
868
869
//Returns the number of items in a menu
870
//native csdm_menu_items(menu);
871
static cell AMX_NATIVE_CALL menu_items(AMX *amx, cell *params)
872
{
873
GETMENU(params[1]);
874
875
return pMenu->GetItemCount();
876
}
877
878
//Builds the menu string for a specific page (set title to 0 to not include title)
879
//page indices start at 0!
880
static cell AMX_NATIVE_CALL menu_display(AMX *amx, cell *params)
881
{
882
auto handle = params[2];
883
GETMENU(handle);
884
885
int player = params[1];
886
int page = params[3];
887
888
if (player < 1 || player > gpGlobals->maxClients)
889
{
890
LogError(amx, AMX_ERR_NATIVE, "Invalid player id %d.", player);
891
return 0;
892
}
893
894
CPlayer* pPlayer = GET_PLAYER_POINTER_I(player);
895
896
if (!pPlayer->ingame)
897
{
898
LogError(amx, AMX_ERR_NATIVE, "Player %d is not in game.", player);
899
return 0;
900
}
901
902
if (!CloseNewMenus(pPlayer))
903
{
904
LogError(amx, AMX_ERR_NATIVE, "Plugin called menu_display when item=MENU_EXIT");
905
return 0;
906
}
907
908
if (!g_NewMenus[handle])
909
{
910
LogError(amx, AMX_ERR_NATIVE, "Invalid menu id %d (was previously destroyed).", handle);
911
return 0;
912
}
913
914
if (g_bmod_cstrike)
915
{
916
enum JoinState { Joined = 0 };
917
enum MenuState { Menu_OFF = 0, Menu_ChooseTeam = 1, Menu_ChooseAppearance = 3 };
918
919
GET_OFFSET("CBasePlayer", m_iJoiningState);
920
GET_OFFSET("CBasePlayer", m_iMenu);
921
922
if (get_pdata<int>(pPlayer->pEdict, m_iJoiningState) == Joined || (get_pdata<int>(pPlayer->pEdict, m_iMenu) != Menu_ChooseTeam && get_pdata<int>(pPlayer->pEdict, m_iMenu) != Menu_ChooseAppearance))
923
{
924
set_pdata<int>(pPlayer->pEdict, m_iMenu, Menu_OFF);
925
}
926
}
927
928
int time = -1;
929
if (params[0] / sizeof(cell) >= 4)
930
time = params[4];
931
932
if (time < 0)
933
pPlayer->menuexpire = static_cast<float>(INFINITE);
934
else
935
pPlayer->menuexpire = gpGlobals->time + static_cast<float>(time);
936
937
return pMenu->Display(player, page);
938
}
939
940
//Finds the id of a menu item for a specific page and key value.
941
//Note that key should be from 0-6, as it only displays 7 per page.
942
//page indices start at 0
943
//native menu_keyid(menu, page, key);
944
static cell AMX_NATIVE_CALL menu_find_id(AMX *amx, cell *params)
945
{
946
GETMENU(params[1]);
947
948
page_t page = static_cast<page_t>(params[2]);
949
item_t key = static_cast<item_t>(params[3]);
950
951
return pMenu->PagekeyToItem(page, key);
952
}
953
954
//Gets info about a menu option
955
//native menu_item_getinfo(menu, item, &access, command[], cmdlen, name[]="", namelen=0, &callback);
956
static cell AMX_NATIVE_CALL menu_item_getinfo(AMX *amx, cell *params)
957
{
958
GETMENU(params[1]);
959
960
menuitem *pItem = pMenu->GetMenuItem(static_cast<item_t>(params[2]));
961
962
if (!pItem)
963
return 0;
964
965
cell *addr = get_amxaddr(amx, params[3]);
966
addr[0] = pItem->access;
967
968
set_amxstring(amx, params[4], pItem->cmd.chars(), params[5]);
969
set_amxstring(amx, params[6], pItem->name.chars(), params[7]);
970
971
if (params[8])
972
{
973
addr = get_amxaddr(amx, params[8]);
974
if (addr)
975
addr[0] = pItem->handler;
976
}
977
978
return 1;
979
}
980
981
static cell AMX_NATIVE_CALL menu_makecallback(AMX *amx, cell *params)
982
{
983
int len;
984
char *name = get_amxstring(amx, params[1], 0, len);
985
986
int id = registerSPForwardByName(amx, name, FP_CELL, FP_CELL, FP_CELL, FP_DONE);
987
988
if (id == -1)
989
{
990
LogError(amx, AMX_ERR_NOTFOUND, "Invalid function %s", name);
991
return -1;
992
}
993
994
return id;
995
}
996
997
static cell AMX_NATIVE_CALL menu_item_setname(AMX *amx, cell *params)
998
{
999
GETMENU(params[1]);
1000
1001
menuitem *pItem = pMenu->GetMenuItem(static_cast<item_t>(params[2]));
1002
1003
if (!pItem)
1004
return 0;
1005
1006
int len;
1007
char *name;
1008
1009
name = get_amxstring(amx, params[3], 0, len);
1010
1011
pItem->name = name;
1012
1013
return 1;
1014
}
1015
1016
static cell AMX_NATIVE_CALL menu_item_setcmd(AMX *amx, cell *params)
1017
{
1018
GETMENU(params[1]);
1019
1020
menuitem *pItem = pMenu->GetMenuItem(static_cast<item_t>(params[2]));
1021
1022
if (!pItem)
1023
return 0;
1024
1025
int len;
1026
char *cmd;
1027
1028
cmd = get_amxstring(amx, params[3], 0, len);
1029
1030
pItem->cmd = cmd;
1031
1032
return 1;
1033
}
1034
1035
static cell AMX_NATIVE_CALL menu_item_setcall(AMX *amx, cell *params)
1036
{
1037
GETMENU(params[1]);
1038
1039
menuitem *pItem = pMenu->GetMenuItem(static_cast<item_t>(params[2]));
1040
1041
if (!pItem)
1042
return 0;
1043
1044
pItem->handler = params[3];
1045
1046
return 1;
1047
}
1048
1049
static cell AMX_NATIVE_CALL menu_item_setaccess(AMX *amx, cell *params)
1050
{
1051
GETMENU(params[1]);
1052
1053
menuitem *pItem = pMenu->GetMenuItem(static_cast<item_t>(params[2]));
1054
1055
if (!pItem)
1056
return 0;
1057
1058
pItem->access = params[3];
1059
1060
return 1;
1061
}
1062
1063
static cell AMX_NATIVE_CALL menu_setprop(AMX *amx, cell *params)
1064
{
1065
GETMENU(params[1]);
1066
1067
int len = params[0] / sizeof(cell);
1068
if (len < 3)
1069
{
1070
LogError(amx, AMX_ERR_NATIVE, "Expected 3 parameters");
1071
return 0;
1072
}
1073
1074
switch (params[2])
1075
{
1076
case MPROP_PAGE_CALLBACK:
1077
{
1078
const char *str = get_amxstring_null(amx, params[3], 0, len);
1079
if (str == nullptr)
1080
{
1081
unregisterSPForward(pMenu->pageCallback);
1082
pMenu->pageCallback = -1;
1083
break;
1084
}
1085
1086
int callback = registerSPForwardByName(amx, str, FP_CELL, FP_CELL, FP_CELL, FP_DONE);
1087
if (callback < 0)
1088
{
1089
LogError(amx, AMX_ERR_NATIVE, "Function %s not present", str);
1090
return 0;
1091
}
1092
1093
unregisterSPForward(pMenu->pageCallback);
1094
pMenu->pageCallback = callback;
1095
1096
break;
1097
}
1098
case MPROP_SHOWPAGE:
1099
{
1100
pMenu->showPageNumber = *get_amxaddr(amx, params[3]) != 0;
1101
break;
1102
}
1103
case MPROP_SET_NUMBER_COLOR:
1104
{
1105
char *str = get_amxstring(amx, params[3], 0, len);
1106
validate_menu_text(str);
1107
pMenu->m_ItemColor = str;
1108
break;
1109
}
1110
case MPROP_PERPAGE:
1111
{
1112
cell count = *get_amxaddr(amx, params[3]);
1113
if (count < 0 || count > 7)
1114
{
1115
LogError(amx, AMX_ERR_NATIVE, "Cannot set %d items per page", count);
1116
return 0;
1117
}
1118
pMenu->items_per_page = count;
1119
break;
1120
}
1121
case MPROP_BACKNAME:
1122
{
1123
char *str = get_amxstring(amx, params[3], 0, len);
1124
validate_menu_text(str);
1125
pMenu->m_OptNames[abs(MENU_BACK)] = str;
1126
break;
1127
}
1128
case MPROP_NEXTNAME:
1129
{
1130
char *str = get_amxstring(amx, params[3], 0, len);
1131
validate_menu_text(str);
1132
pMenu->m_OptNames[abs(MENU_MORE)] = str;
1133
break;
1134
}
1135
case MPROP_EXITNAME:
1136
{
1137
char *str = get_amxstring(amx, params[3], 0, len);
1138
validate_menu_text(str);
1139
pMenu->m_OptNames[abs(MENU_EXIT)] = str;
1140
break;
1141
}
1142
case MPROP_TITLE:
1143
{
1144
char *str = get_amxstring(amx, params[3], 0, len);
1145
pMenu->m_Title = str;
1146
break;
1147
}
1148
case MPROP_EXITALL:
1149
{
1150
cell ans = *get_amxaddr(amx, params[3]);
1151
if (ans == 1 || ans == 0)
1152
{
1153
pMenu->m_NeverExit = false;
1154
pMenu->m_ForceExit = false;
1155
} else if (ans == 2) {
1156
pMenu->m_NeverExit = false;
1157
pMenu->m_ForceExit = true;
1158
} else if (ans == -1) {
1159
pMenu->m_NeverExit = true;
1160
pMenu->m_ForceExit = false;
1161
}
1162
break;
1163
}
1164
case MPROP_ORDER:
1165
{
1166
/* Ignored as of 1.8.0 */
1167
break;
1168
}
1169
case MPROP_NOCOLORS:
1170
{
1171
pMenu->m_AutoColors = *get_amxaddr(amx, params[3]) ? false : true;
1172
break;
1173
}
1174
case MPROP_PADMENU:
1175
{
1176
/* Ignored as of 1.8.0 */
1177
break;
1178
}
1179
default:
1180
{
1181
LogError(amx, AMX_ERR_NATIVE, "Invalid menu setting: %d", params[1]);
1182
return 0;
1183
}
1184
}
1185
1186
return 1;
1187
}
1188
1189
#define GETMENU_R(p) Menu *pMenu = get_menu_by_id(p); \
1190
if (pMenu == NULL) { \
1191
LogError(amx, AMX_ERR_NATIVE, "Invalid menu id %d(%d)", p, g_NewMenus.length()); \
1192
return 0; }
1193
1194
static cell AMX_NATIVE_CALL menu_cancel(AMX *amx, cell *params)
1195
{
1196
int index = params[1];
1197
1198
if (index < 1 || index > gpGlobals->maxClients)
1199
{
1200
LogError(amx, AMX_ERR_NATIVE, "Player %d is not valid", index);
1201
return 0;
1202
}
1203
1204
CPlayer *player = GET_PLAYER_POINTER_I(index);
1205
if (!player->ingame)
1206
{
1207
LogError(amx, AMX_ERR_NATIVE, "Player %d is not in game", index);
1208
return 0;
1209
}
1210
1211
if (Menu *pMenu = get_menu_by_id(player->newmenu)) {
1212
pMenu->Close(player->index);
1213
return 1;
1214
}
1215
1216
return 0;
1217
}
1218
1219
static cell AMX_NATIVE_CALL menu_destroy(AMX *amx, cell *params)
1220
{
1221
GETMENU_R(params[1]);
1222
1223
if (pMenu->isDestroying)
1224
{
1225
return 0; //prevent infinite recursion
1226
}
1227
1228
pMenu->isDestroying = true;
1229
1230
CPlayer *player;
1231
for (int i=1; i<=gpGlobals->maxClients; i++)
1232
{
1233
player = GET_PLAYER_POINTER_I(i);
1234
if (player->newmenu == pMenu->thisId)
1235
{
1236
pMenu->Close(player->index);
1237
}
1238
}
1239
g_NewMenus[params[1]] = NULL;
1240
delete pMenu;
1241
g_MenuFreeStack.push(params[1]);
1242
1243
return 1;
1244
}
1245
1246
static cell AMX_NATIVE_CALL player_menu_info(AMX *amx, cell *params)
1247
{
1248
if (params[1] < 1 || params[1] > gpGlobals->maxClients)
1249
{