forked from APGRoboCop/foxbot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdll.cpp
7096 lines (6419 loc) · 285 KB
/
dll.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//
// FoXBot - AI Bot for Halflife's Team Fortress Classic
//
// (http://foxbot.net)
//
// dll.cpp
//
// Copyright (C) 2003 - Tom "Redfox" Simpson
//
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License for more details at:
// http://www.gnu.org/copyleft/gpl.html
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
#include "extdll.h"
#include <enginecallback.h>
//#include "util.h"
//#include "cbase.h"
#include <entity_state.h>
//#include <osdep.h>
//#ifndef __linux__ //Fix by Globoss - [APG]RoboCop[CL]
//#include <tchar.h>
//#endif
#include "bot.h"
#include "bot_func.h"
#include "bot_weapons.h"
#include "waypoint.h"
// meta mod includes
#include <dllapi.h>
#include <meta_api.h>
// new stuff for botcam
#include <cbase.h>
#include "player.h"
#include "botcam.h"
#define VER_MAJOR 0
#define VER_MINOR 791
#define VER_BUILD 0
#define MENU_NONE 0
#define MENU_1 1
#define MENU_2 2
#define MENU_3 3
#define MENU_4 4
#define MENU_5 5
#define MENU_6 6
#define MENU_7 7
cvar_t foxbot = { "foxbot", "0.791", FCVAR_SERVER | FCVAR_UNLOGGED, 0, NULL };
cvar_t enable_foxbot = { "enable_foxbot", "1", FCVAR_SERVER | FCVAR_UNLOGGED, 0, NULL };
cvar_t sv_bot = { "bot", "", 0, 0, NULL };
extern GETENTITYAPI other_GetEntityAPI;
extern GETNEWDLLFUNCTIONS other_GetNewDLLFunctions;
extern enginefuncs_t g_engfuncs;
extern int debug_engine;
extern globalvars_t *gpGlobals;
extern char *g_argv;
extern bool g_waypoint_on;
extern bool g_waypoint_cache;
extern bool g_auto_waypoint;
extern bool g_path_waypoint;
extern bool g_find_waypoint;
extern long g_find_wp;
extern bool g_path_connect;
extern bool g_path_oneway;
extern bool g_path_twoway;
extern int wpt1;
extern int wpt2;
extern int pipeCheckFrame;
extern int num_waypoints; // number of waypoints currently in use
extern WAYPOINT waypoints[MAX_WAYPOINTS];
extern float wp_display_time[MAX_WAYPOINTS];
extern bot_t bots[32];
extern bool botJustJoined[MAX_BOTS]; // tracks if bot is new to the game
// bot settings //////////////////
bool defensive_chatter = TRUE;
bool offensive_chatter = TRUE;
bool b_observer_mode = FALSE;
bool b_botdontshoot = FALSE;
bool b_botdontmove = FALSE;
int bot_chat = 500;
int bot_allow_moods = 1; // whether bots can have different personality traits or not
int bot_allow_humour = 1; // whether bots can choose to do daft things or not
bool bot_can_use_teleporter = TRUE;
bool bot_can_build_teleporter = TRUE;
int bot_use_grenades = 2;
bool bot_team_balance = FALSE;
static bool bot_bot_balance = FALSE;
int min_bots = -1;
int max_bots = -1;
static int bot_total_varies = 0;
static float bot_create_interval = 3.0f;
int botskill_upper = 1;
int botskill_lower = 3;
int bot_skill_1_aim = 20; // accuracy for skill 1 bots
int bot_aim_per_skill = 10; // accuracy modifier for bots from skill 1 downwards
bool bot_xmas = FALSE; // silly stuff
bool g_bot_debug = FALSE;
int spectate_debug = 0; // spectators can trigger debug messages from bots
// waypoint author
extern char waypoint_author[256];
// this tracks the number of bots "wanting" to play on the server
// it should never be less than min_bots or more than max_bots
static int interested_bots = -1;
extern bot_weapon_t weapon_defs[MAX_WEAPONS];
int RoleStatus[] = { 50, 50, 50, 50 };
extern bool g_area_def;
extern AREA areas[MAX_WAYPOINTS];
extern int num_areas;
extern struct msg_com_struct msg_com[MSG_MAX];
extern char msg_msg[64][MSG_MAX];
const static double double_pi = 3.1415926535897932384626433832795;
// define the sources that a bot option/setting can be changed from
// used primarily by the changeBotSetting() function
enum {
SETTING_SOURCE_CLIENT_COMMAND, // command issued at console by host on Listen server
SETTING_SOURCE_SERVER_COMMAND, // command issued to the server directly
SETTING_SOURCE_CONFIG_FILE
}; // command issued by a config file
static FILE* fp;
DLL_FUNCTIONS other_gFunctionTable;
DLL_GLOBAL const Vector g_vecZero = Vector(0, 0, 0);
int mod_id = TFC_DLL;
int m_spriteTexture = 0;
static int isFakeClientCommand = 0;
static int fake_arg_count;
static float bot_check_time = 30.0f;
static edict_t* first_player = NULL;
int num_bots = 0;
int prev_num_bots = 0;
bool g_GameRules = FALSE;
edict_t* clients[32];
static float welcome_time = 0.0;
static int welcome_index = -1;
static bool welcome_sent = FALSE;
static int g_menu_waypoint;
static int g_menu_state = 0;
float is_team_play = 0.0;
bool checked_teamplay = FALSE;
// char team_names[MAX_TEAMS][MAX_TEAMNAME_LENGTH];
int num_teams = 0;
edict_t* pent_info_tfdetect = NULL;
edict_t* pent_info_ctfdetect = NULL;
//edict_t* pent_info_frontline = NULL;
edict_t* pent_item_tfgoal = NULL;
int max_team_players[4];
int team_class_limits[4];
int team_allies[4]; // bit mapped allies BLUE, RED, YELLOW, and GREEN
int max_teams = 0;
FLAG_S flags[MAX_FLAGS];
int num_flags = 0;
static FILE* bot_cfg_fp = NULL;
// changed..cus we set it else where
static bool need_to_open_cfg = FALSE;
static bool need_to_open_cfg2 = FALSE;
static int cfg_file = 1;
// my display stuff...
static bool display_bot_vars = TRUE;
static float display_start_time;
static bool script_loaded = FALSE;
static bool script_parsed = FALSE;
static short scanpos;
static bool player_vis[8];
static float bot_cfg_pause_time = 0.0;
static float respawn_time = 0.0;
static bool spawn_time_reset = FALSE;
bool botcamEnabled = FALSE;
int flf_bug_fix;
int flf_bug_check;
chatClass chat; // bot chat stuff
// waypoint menu entries
char* show_menu_1 = { "Waypoint Tags\n\n1. Team Specific\n2. Locations\n3. Items\n4. Actions p1\n5. Actions p2\n6. "
"Control Points\n7. Exit" };
char* show_menu_2 = { "Team Specific Tags\n\n1. Team 1\n2. Team 2\n3. Team 3\n4. Team 4\n5. Exit" };
char* show_menu_3 = { "Location Tags\n\n1. Flag Location\n2. Flag Goal Location\n3. Exit" };
char* show_menu_4 = { "Item Tags\n\n1. Health\n2. Armour\n3. Ammo\n4. Exit" };
char* show_menu_5 = { "Action Tags p1\n\n1. Defend (Soldier/HW/Demo)\n2. Defend(Demoman Only)\n3. Sniper\n4. Build "
"Sentry\n5. Rotate SG 180\n6. Build TP Entrance\n7. Build TP Exit\n8. Exit" };
char* show_menu_6 = { "Action Tags p2\n\n1. RJ/CJ\n2. Jump\n3. Wait For Lift\n4. Walk\n5. Detpack(Clear "
"passageway)\n6. Detpack(Seal passageway)\n7. Path Check\n8. Exit" };
char* show_menu_7 = {
"Waypoint Tags\n\n1. Point1\n2. Point2\n3. Point3\n4. Point4\n5. Point5\n6. Point6\n7. Point7\n8. Point8\n9 Exit"
};
// meta mod shiznit
extern bool mr_meta;
char prevmapname[32];
extern bool attack[4]; // teams attack
extern bool defend[4]; // teams defend
extern bool blue_av[8];
extern bool red_av[8];
extern bool green_av[8];
extern bool yellow_av[8];
char arg[255];
int playersPerTeam[4];
bool is_team[4];
float last_frame_time;
float last_frame_time_prev;
extern char sz_error_check[255];
// FUNCTION PROTOTYPES /////////////////
static void DisplayBotInfo();
void BotNameInit(void);
void UpdateClientData(const struct edict_s* ent, int sendweapons, struct clientdata_s* cd);
static void varyBotTotal(void);
static void TeamBalanceCheck(void);
static void BotBalanceTeams_Casual(void);
static bool BotBalanceTeams(int a, int b);
static bool BBotBalanceTeams(int a, int b);
static bool HBalanceTeams(int a, int b);
static void ProcessBotCfgFile(void);
static void changeBotSetting(const char* settingName,
int* setting,
const char* arg1,
const int minValue,
const int maxValue,
const int settingSource);
static void kickBots(int totalToKick, const int team);
static void kickRandomBot(void);
static void ClearKickedBotsData(const int botIndex, const bool eraseBotsName);
// void UTIL_CSavePent(CBaseEntity *pent);
void UTIL_HudMessage(CBaseEntity* pEntity, const hudtextparms_t& textparms, const char* pMessage)
{
if(!pEntity)
return;
MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, ENT(pEntity->pev));
WRITE_BYTE(TE_TEXTMESSAGE);
WRITE_BYTE(textparms.channel & 0xFF);
WRITE_SHORT(FixedSigned16(textparms.x, 1 << 13));
WRITE_SHORT(FixedSigned16(textparms.y, 1 << 13));
WRITE_BYTE(textparms.effect);
WRITE_BYTE(textparms.r1);
WRITE_BYTE(textparms.g1);
WRITE_BYTE(textparms.b1);
WRITE_BYTE(textparms.a1);
WRITE_BYTE(textparms.r2);
WRITE_BYTE(textparms.g2);
WRITE_BYTE(textparms.b2);
WRITE_BYTE(textparms.a2);
WRITE_SHORT(FixedUnsigned16(textparms.fadeinTime, 1 << 8));
WRITE_SHORT(FixedUnsigned16(textparms.fadeoutTime, 1 << 8));
WRITE_SHORT(FixedUnsigned16(textparms.holdTime, 1 << 8));
if(textparms.effect == 2)
WRITE_SHORT(FixedUnsigned16(textparms.fxTime, 1 << 8));
if(strlen(pMessage) < 512) {
WRITE_STRING(pMessage);
} else {
char temp[512];
strncpy(temp, pMessage, 511);
temp[511] = '\0';
WRITE_STRING(temp);
}
MESSAGE_END();
}
void KewlHUDNotify(edict_t* pEntity, const char* msg_name)
{
MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, pEntity);
WRITE_BYTE(TE_TEXTMESSAGE);
WRITE_BYTE(3 & 0xFF);
WRITE_SHORT(FixedSigned16(1, -1 << 13));
WRITE_SHORT(FixedSigned16(1, -1 << 13));
WRITE_BYTE(2); // effect
WRITE_BYTE(205);
WRITE_BYTE(205);
WRITE_BYTE(10);
WRITE_BYTE(255);
WRITE_BYTE(0);
WRITE_BYTE(0);
WRITE_BYTE(255);
WRITE_BYTE(255);
WRITE_SHORT(FixedUnsigned16(0.03, 1 << 8));
WRITE_SHORT(FixedUnsigned16(1, 1 << 8));
WRITE_SHORT(FixedUnsigned16(6, 1 << 8));
WRITE_SHORT(FixedUnsigned16(4, 1 << 8));
WRITE_STRING(msg_name);
MESSAGE_END();
}
// If bot_total_varies is active then this function will periodically alter
// the number of bots that want to play on the server.
static void varyBotTotal(void)
{
if(bot_total_varies == 0)
return; // just in case
// this governs when the number of bots wanting to play will next change
static float f_interested_bots_change = 0;
if(f_interested_bots_change < gpGlobals->time) {
if(bot_total_varies == 3) // busy server(players coming/going often)
f_interested_bots_change = gpGlobals->time + random_float(10.0f, 120.0f);
else if(bot_total_varies == 2)
f_interested_bots_change = gpGlobals->time + random_float(40.0f, 360.0f);
else // slow changes in number of bots
f_interested_bots_change = gpGlobals->time + random_float(90.0f, 600.0f);
// UTIL_BotLogPrintf("interested_bots:%d, time:%f, next change:%f\n",
// interested_bots, gpGlobals->time, f_interested_bots_change);
// try and get some bots interested in joining the
// game if the game has just started
if(interested_bots < 0) {
if(max_bots > 0 && max_bots > min_bots) {
if(min_bots > -1)
interested_bots = random_long(min_bots, max_bots);
else
interested_bots = random_long(1, max_bots);
} else
interested_bots = min_bots;
}
// randomly increase/decrease the number of interested bots
if(max_bots > 0 && max_bots > min_bots) {
if(random_long(1, 1000) > 500) {
// favor increasing the bots
// if max_bots is reached decrease the bots
if(interested_bots < max_bots)
++interested_bots;
else if(min_bots > 0) {
if(interested_bots > min_bots)
--interested_bots;
} else if(interested_bots > 0)
--interested_bots;
} else {
// favor decreasing the bots
// if min_bots is reached increase the bots
if(min_bots > 0 && interested_bots > min_bots)
--interested_bots;
else if(min_bots < 1 && interested_bots > 0)
--interested_bots;
else if(interested_bots < max_bots)
++interested_bots;
}
}
// UTIL_BotLogPrintf("interested_bots changed to:%d\n", interested_bots);
} else {
// make sure f_interested_bots_change is sane
// (gpGlobals->time resets on map change)
if((f_interested_bots_change - 601.0f) > gpGlobals->time)
f_interested_bots_change = gpGlobals->time + random_float(10.0f, 120.0f);
}
}
// This function is the core function for checking if the teams need balancing
// and making sure the bots switch teams if they are supposed to.
static void TeamBalanceCheck(void)
{
if(mod_id != TFC_DLL) // Fix for bot_team_balance? [APG]RoboCop[CL]
return;
if(bot_team_balance && !bot_bot_balance) {
// team 1 has more than team 2?
bool done = BotBalanceTeams(1, 2);
if(!done)
done = BotBalanceTeams(1, 3);
if(!done)
done = BotBalanceTeams(1, 4);
if(!done)
done = BotBalanceTeams(2, 1);
if(!done)
done = BotBalanceTeams(2, 3);
if(!done)
done = BotBalanceTeams(2, 4);
if(!done)
done = BotBalanceTeams(3, 1);
if(!done)
done = BotBalanceTeams(3, 2);
if(!done)
done = BotBalanceTeams(3, 4);
if(!done)
done = BotBalanceTeams(4, 1);
if(!done)
done = BotBalanceTeams(4, 2);
if(!done)
done = BotBalanceTeams(4, 3);
}
if(bot_bot_balance) {
// team 1 has more than team 2?
bool done = BBotBalanceTeams(1, 2);
if(!done)
done = BBotBalanceTeams(1, 3);
if(!done)
done = BBotBalanceTeams(1, 4);
if(!done)
done = BBotBalanceTeams(2, 1);
if(!done)
done = BBotBalanceTeams(2, 3);
if(!done)
done = BBotBalanceTeams(2, 4);
if(!done)
done = BBotBalanceTeams(3, 1);
if(!done)
done = BBotBalanceTeams(3, 2);
if(!done)
done = BBotBalanceTeams(3, 4);
if(!done)
done = BBotBalanceTeams(4, 1);
if(!done)
done = BBotBalanceTeams(4, 2);
if(!done)
done = BBotBalanceTeams(4, 3);
if(!done && bot_team_balance) {
// balance the humans!
// team 1 has more than team 2?
done = HBalanceTeams(1, 2);
if(!done)
done = HBalanceTeams(1, 3);
if(!done)
done = HBalanceTeams(1, 4);
if(!done)
done = HBalanceTeams(2, 1);
if(!done)
done = HBalanceTeams(2, 3);
if(!done)
done = HBalanceTeams(2, 4);
if(!done)
done = HBalanceTeams(3, 1);
if(!done)
done = HBalanceTeams(3, 2);
if(!done)
done = HBalanceTeams(3, 4);
if(!done)
done = HBalanceTeams(4, 1);
if(!done)
done = HBalanceTeams(4, 2);
if(!done)
HBalanceTeams(4, 3);
}
}
// if auto-balance is switched off then let the bots
// balance the teams if they "feel" like it
else if(!bot_team_balance && !bot_bot_balance)
BotBalanceTeams_Casual();
}
// This function should only be called if bot_team_balance is switched off.
// It allows bots to decide for themselves if they "feel" like switching teams
// to balance the teams.
static void BotBalanceTeams_Casual(void)
{
if(mod_id != TFC_DLL) // Fix for bot_team_balance? [APG]RoboCop[CL]
return;
static float nextBalanceCheck = 5.0f;
// perform a balance check every random number of seconds
if(nextBalanceCheck < gpGlobals->time)
nextBalanceCheck = gpGlobals->time + random_float(30.0f, 120.0f);
else {
// make sure nextBalanceCheck is sane(gpGlobals->time resets on map change)
if((nextBalanceCheck - 600.0f) > gpGlobals->time)
nextBalanceCheck = gpGlobals->time + random_float(15.0f, 120.0f);
return;
}
// find out which teams have player limits, and are full
int i;
bool team_is_full[4] = { FALSE, FALSE, FALSE, FALSE };
for(i = 0; i < 4; i++) {
if(max_team_players[i] > 0 && playersPerTeam[i] >= max_team_players[i])
team_is_full[i] = TRUE;
}
/* UTIL_BotLogPrintf("team_is_full[] - 0:%d, 1:%d, 2:%d, 3:%d\n",
team_is_full[0], team_is_full[1], team_is_full[2], team_is_full[3]);*/
// find out which teams have the most and the fewest players
int smallest_team = -1;
int biggest_team = -1;
int size_difference = -1;
for(i = 0; i < 3; i++) {
// stop comparing if the next team isn't used on this map
if(is_team[i + 1] == FALSE)
break;
// is this team bigger than the next one?
if(!team_is_full[i + 1] && playersPerTeam[i] > playersPerTeam[i + 1]) {
biggest_team = i;
smallest_team = i + 1;
size_difference = playersPerTeam[i] - playersPerTeam[i + 1];
}
// or is the next team bigger?
else if(!team_is_full[i] && playersPerTeam[i] < playersPerTeam[i + 1]) {
biggest_team = i + 1;
smallest_team = i;
size_difference = playersPerTeam[i + 1] - playersPerTeam[i];
}
if(size_difference > 1)
break; // we've found a big enough imbalance
}
// UTIL_BotLogPrintf("\nbiggest_team:%d, smallest_team:%d, size_difference:%d\n",
// biggest_team + 1, smallest_team + 1, size_difference);
if(size_difference < 2 || biggest_team < 0 || smallest_team < 0)
return;
// add one to both teams as the bots use team numbers 1 to 4, not 0 to 3
++biggest_team;
++smallest_team;
// let only one bot change team(if it wants to) once per function call
for(i = 0; i < 32; i++) {
// is this an active bot on the bigger team?
// and is it willing to change teams?
if(bots[i].is_used && bots[i].pEdict->v.team == biggest_team && random_long(1, 1000) < bots[i].trait.fairplay) {
// UTIL_BotLogPrintf("vteam:%d, team:%d\n",
// bots[i].pEdict->v.team, bots[i].bot_team);
char msg[16];
_snprintf(msg, 16, "%d", smallest_team);
msg[15] = '\0';
// FakeClientCommand(bots[i].pEdict, "jointeam", msg, NULL);
bots[i].bot_team = smallest_team; // choose your team
bots[i].not_started = TRUE; // join the team, pick a class
bots[i].start_action = MSG_TFC_IDLE;
bots[i].create_time = gpGlobals->time + 2.0;
ClearKickedBotsData(i, FALSE);
// UTIL_BotLogPrintf("joined team:%d, vteam:%d, team:%d\n",
// smallest_team, bots[i].pEdict->v.team, bots[i].bot_team);
break; // job done, success!
}
}
}
static bool BotBalanceTeams(int a, int b)
{
if(playersPerTeam[a - 1] - 1 > playersPerTeam[b - 1] &&
(max_team_players[b - 1] > playersPerTeam[b - 1] || max_team_players[b - 1] == 0) && is_team[b - 1]) {
for(int i = 31; i >= 0; i--) {
// is this slot used?
if(bots[i].is_used && bots[i].pEdict->v.team == a) {
char msg[32];
_snprintf(msg, 32, "%d", b);
// FakeClientCommand(bots[i].pEdict, "jointeam", msg, NULL);
bots[i].bot_team = b; // choose your team
bots[i].not_started = TRUE; // join the team, pick a class
bots[i].start_action = MSG_TFC_IDLE;
bots[i].create_time = gpGlobals->time + 2.0;
ClearKickedBotsData(i, FALSE);
return TRUE;
}
}
}
return FALSE;
}
// used with the bot_bot_balance setting
static bool BBotBalanceTeams(int a, int b)
{
// now just set up teams to include bots in them :D
int bteams[4] = { 0, 0, 0, 0 };
for(int i = 0; i < 32; i++) //<32
{
if(bots[i].is_used) {
char* infobuffer;
char cl_name[128];
cl_name[0] = '\0';
infobuffer = (*g_engfuncs.pfnGetInfoKeyBuffer)(bots[i].pEdict);
strcpy(cl_name, g_engfuncs.pfnInfoKeyValue(infobuffer, "name"));
if(cl_name[0] != '\0') {
int team = (bots[i].pEdict->v.team) - 1;
if(team >= 0 && team < 4)
bteams[team] = bteams[team] + 1;
}
}
}
if(bteams[a - 1] - 1 > bteams[b - 1] &&
(max_team_players[b - 1] > playersPerTeam[b - 1] || max_team_players[b - 1] == 0) && is_team[b - 1]) {
for(int i = 31; i >= 0; i--) {
// is this slot used?
if(bots[i].is_used && bots[i].pEdict->v.team == a) {
char msg[32];
_snprintf(msg, 32, "%d", b);
// FakeClientCommand(bots[i].pEdict, "jointeam", msg, NULL);
bots[i].bot_team = b; // choose your team
bots[i].not_started = TRUE; // join the team, pick a class
bots[i].start_action = MSG_TFC_IDLE;
bots[i].create_time = gpGlobals->time + 2.0;
ClearKickedBotsData(i, FALSE);
return TRUE;
}
}
}
return FALSE;
}
// This function will balance the human players amongst the teams by forcing
// them to switch teams if necessary.
static bool HBalanceTeams(int a, int b)
{
if((playersPerTeam[a - 1] - 1) > playersPerTeam[b - 1] &&
(max_team_players[b - 1] > playersPerTeam[b - 1] || max_team_players[b - 1] == 0) && is_team[b - 1]) {
for(int i = 1; i <= 32; i++) {
bool not_bot = TRUE;
for(int j = 1; j <= 32; j++) {
if(bots[j].is_used && bots[j].pEdict == INDEXENT(i))
not_bot = FALSE;
}
if(not_bot && INDEXENT(i) != NULL) {
if(INDEXENT(i)->v.team == a && INDEXENT(i)->v.netname != 0) {
CLIENT_COMMAND(INDEXENT(i), UTIL_VarArgs("jointeam %d\n", b));
return TRUE;
}
}
}
}
return FALSE;
}
// This function is called once upon startup
void GameDLLInit(void)
{
CVAR_REGISTER(&foxbot);
CVAR_REGISTER(&sv_bot);
CVAR_REGISTER(&enable_foxbot);
for(int i = 0; i < 32; i++)
clients[i] = NULL;
// initialize the bots array of structures...
memset(bots, 0, sizeof(bots));
// read the bot names from the bot name file
BotNameInit();
// read the chat strings from the bot chat file
chat.readChatFile();
if(!mr_meta)
(*other_gFunctionTable.pfnGameInit)();
else
SET_META_RESULT(MRES_HANDLED);
}
// Constructor for the chatClass class
// sets up the names of the chat section headers
chatClass::chatClass(void)
{
this->sectionNames[CHAT_TYPE_GREETING] = "[GREETINGS]";
this->sectionNames[CHAT_TYPE_KILL_HI] = "[KILL WINNING]";
this->sectionNames[CHAT_TYPE_KILL_LOW] = "[KILL LOSING]";
this->sectionNames[CHAT_TYPE_KILLED_HI] = "[KILLED WINNING]";
this->sectionNames[CHAT_TYPE_KILLED_LOW] = "[KILLED LOSING]";
this->sectionNames[CHAT_TYPE_SUICIDE] = "[SUICIDE]";
int i, j;
// explicitly clear all the chat strings
for(i = 0; i < TOTAL_CHAT_TYPES; i++) {
this->stringCount[i] = 0;
for(j = 0; j < MAX_CHAT_STRINGS; j++) {
this->strings[i][j] = "";
}
for(j = 0; j < 5; j++) {
this->recentStrings[i][j] = -1;
}
}
}
// This function is responsible for reading in the chat from the bot chat file.
void chatClass::readChatFile(void)
{
char filename[256];
UTIL_BuildFileName(filename, 255, "foxbot_chat.txt", NULL);
FILE* bfp = fopen(filename, "r");
if(bfp == NULL) {
UTIL_BotLogPrintf("Unable to read from the Foxbot chat file. The bots will not chat.");
bot_chat = 0; // stop the bots trying to chat
return;
}
char buffer[MAX_CHAT_LENGTH] = "";
char* ptr;
int i;
size_t length;
int chat_section = -1;
while(UTIL_ReadFileLine(buffer, MAX_CHAT_LENGTH, bfp)) {
// ignore comment lines
if(buffer[0] == '#')
continue;
length = strlen(buffer);
// turn '\n' into '\0'
if(buffer[length - 1] == '\n') {
buffer[length - 1] = 0;
length--;
}
// change %n to %s
if((ptr = strstr(buffer, "%n")) != NULL)
*(ptr + 1) = 's';
// watch out for the chat section headers
if(buffer[0] == '[') {
bool newSectionFound = FALSE;
for(i = 0; i < TOTAL_CHAT_TYPES; i++) {
if(buffer == this->sectionNames[i]) {
chat_section = i;
newSectionFound = TRUE;
}
}
if(newSectionFound)
continue;
}
// this line is not a comment, empty, or a section header
// so treat it as a chat string and load it up
if(chat_section != -1 && this->stringCount[chat_section] < MAX_CHAT_STRINGS && length > 0) {
// UTIL_BotLogPrintf("buffer %s %d\n", buffer, this->stringCount[chat_section]);
this->strings[chat_section][this->stringCount[chat_section]] = buffer;
++this->stringCount[chat_section];
}
}
fclose(bfp);
}
// This function will return a C ASCII string pointer to a randomly selected
// chat message of the type defined by chatSection.
// Some chat strings use a players name with the "%n" specifier, so you can
// specify a players name with playerName, or set it to NULL.
void chatClass::pickRandomChatString(char* msg, size_t maxLength, const int chatSection, const char* playerName)
{
msg[0] = '\0'; // just in case
// make sure this chat section contains at least one chat string
if(this->stringCount[chatSection] < 1)
return;
int i, recentCount = 0;
int randomIndex;
bool used;
// try to pick a string that hasn't been used recently
while(recentCount < 5) {
randomIndex = random_long(0, this->stringCount[chatSection] - 1);
used = FALSE;
for(i = 0; i < 5; i++) {
if(this->recentStrings[chatSection][i] == randomIndex)
used = TRUE;
}
if(used)
++recentCount;
else
break; // found an unused chat string
}
// push the selected string on to the list of recently used strings
for(i = 4; i > 0; i--) {
this->recentStrings[chatSection][i] = this->recentStrings[chatSection][i - 1];
}
this->recentStrings[chatSection][0] = randomIndex;
// set up the message string
// is "%s" in the text?
if(playerName != NULL && strstr(this->strings[chatSection][randomIndex].c_str(), "%s") != NULL) {
_snprintf(msg, maxLength, this->strings[chatSection][randomIndex].c_str(), playerName);
} else
_snprintf(msg, maxLength, this->strings[chatSection][randomIndex].c_str());
msg[maxLength - 1] = '\0';
}
int DispatchSpawn(edict_t* pent)
{
if(gpGlobals->deathmatch) {
char* pClassname = (char*)STRING(pent->v.classname);
if(debug_engine) {
fp = UTIL_OpenFoxbotLog();
fprintf(fp, "DispatchSpawn: %p %s\n", (void*)pent, pClassname);
if(pent->v.model != 0)
fprintf(fp, " model=%s\n", STRING(pent->v.model));
if(pent->v.target != 0)
fprintf(fp, " t=%s\n", STRING(pent->v.target));
if(pent->v.targetname != 0)
fprintf(fp, " tn=%s\n", STRING(pent->v.targetname));
fclose(fp);
}
if(strcmp(pClassname, "worldspawn") == 0) {
// do level initialization stuff here...
WaypointInit();
WaypointLoad(NULL);
AreaDefLoad(NULL);
// my clear var for lev reload..
strcpy(prevmapname, "null");
pent_info_tfdetect = NULL;
pent_info_ctfdetect = NULL;
//pent_info_frontline = NULL;
pent_item_tfgoal = NULL;
int index;
for(index = 0; index < 4; index++) {
max_team_players[index] = 0; // no player limit
team_class_limits[index] = 0; // no class limits
team_allies[index] = 0;
}
max_teams = 0;
is_team[0] = FALSE;
is_team[1] = FALSE;
is_team[2] = FALSE;
is_team[3] = FALSE;
num_flags = 0;
g_waypoint_cache = FALSE;
if(g_waypoint_on || g_area_def) {
// precache stuff here
PRECACHE_MODEL("sprites/dot.spr");
PRECACHE_MODEL("sprites/arrow1.spr");
PRECACHE_MODEL("sprites/gargeye1.spr");
PRECACHE_MODEL("sprites/cnt1.spr");
PRECACHE_MODEL("models/w_medkit.mdl");
PRECACHE_MODEL("models/r_armor.mdl");
PRECACHE_MODEL("models/backpack.mdl");
PRECACHE_MODEL("models/p_sniper.mdl");
PRECACHE_MODEL("models/flag.mdl");
PRECACHE_MODEL("models/bigrat.mdl");
PRECACHE_MODEL("models/sentry1.mdl");
PRECACHE_MODEL("models/w_longjump.mdl");
PRECACHE_MODEL("models/detpack.mdl");
PRECACHE_MODEL("models/teleporter.mdl");
PRECACHE_MODEL("models/mechgibs.mdl");
PRECACHE_SOUND("weapons/xbow_hit1.wav"); // waypoint add
PRECACHE_SOUND("weapons/mine_activate.wav"); // waypoint delete
PRECACHE_SOUND("common/wpn_hudoff.wav"); // path add/delete start
PRECACHE_SOUND("common/wpn_hudon.wav"); // path add/delete done
PRECACHE_SOUND("common/wpn_moveselect.wav"); // path add/delete cancel
PRECACHE_SOUND("common/wpn_denyselect.wav"); // path add/delete error
g_waypoint_cache = TRUE;
}
PRECACHE_MODEL("models/presentlg.mdl");
PRECACHE_MODEL("models/presentsm.mdl");
PRECACHE_SOUND("barney/c1a4_ba_octo4.wav");
PRECACHE_SOUND("misc/b2.wav");
PRECACHE_SOUND("misc/party2.wav");
PRECACHE_SOUND("misc/party1.wav");
m_spriteTexture = PRECACHE_MODEL("sprites/lgtning.spr");
g_GameRules = TRUE;
is_team_play = 0.0;
// memset(team_names, 0, sizeof(team_names));
num_teams = 0;
checked_teamplay = FALSE;
respawn_time = 0.0;
spawn_time_reset = FALSE;
prev_num_bots = num_bots;
num_bots = 0;
flf_bug_fix = 0;
flf_bug_check = 0;
bot_check_time = gpGlobals->time + 30.0;
}
}
if(!mr_meta)
return (*other_gFunctionTable.pfnSpawn)(pent);
else
RETURN_META_VALUE(MRES_HANDLED, 0);
}
void DispatchThink(edict_t* pent)
{
if(FStrEq(STRING(pent->v.classname), "entity_botcam")) {
TraceResult tr;
int off_f = 16;
UTIL_MakeVectors(pent->v.euser1->v.v_angle);
if(pent->v.euser1 != NULL && !FNullEnt(pent->v.euser1) && pent->v.owner != NULL && !FNullEnt(pent->v.owner)) {
bot_t* pBot = UTIL_GetBotPointer(pent->v.euser1);
UTIL_TraceLine(pent->v.euser1->v.origin + pent->v.euser1->v.view_ofs + gpGlobals->v_forward * off_f,
pent->v.euser1->v.origin + pent->v.euser1->v.view_ofs + gpGlobals->v_forward * 4000, ignore_monsters,
pent->v.euser1, &tr);
Vector st;
Vector end;
MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, pent->v.owner);
WRITE_BYTE(TE_TEXTMESSAGE);
WRITE_BYTE(2 & 0xFF);
WRITE_SHORT(FixedSigned16(1, -1 << 13));
WRITE_SHORT(FixedSigned16(1, 0 << 13));
WRITE_BYTE(1); // effect
WRITE_BYTE(255);
WRITE_BYTE(255);
WRITE_BYTE(255);
WRITE_BYTE(255);
WRITE_BYTE(255);
WRITE_BYTE(255);
WRITE_BYTE(255);
WRITE_BYTE(255);
WRITE_SHORT(FixedUnsigned16(1, 1 << 8));
WRITE_SHORT(FixedUnsigned16(3, 1 << 8));
WRITE_SHORT(FixedUnsigned16(1, 1 << 8));
// Try putting together a string for more information in botcam
char msg[255];
if(pBot->mission == ROLE_ATTACKER) {