1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 
23 /*****************************************************************************
24  * name:		be_ai_weap.c
25  *
26  * desc:		weapon AI
27  *
28  * $Archive: /MissionPack/code/botlib/be_ai_weap.c $
29  *
30  *****************************************************************************/
31 
32 #include "../qcommon/q_shared.h"
33 #include "l_libvar.h"
34 #include "l_log.h"
35 #include "l_memory.h"
36 #include "l_utils.h"
37 #include "l_script.h"
38 #include "l_precomp.h"
39 #include "l_struct.h"
40 #include "aasfile.h"
41 #include "botlib.h"
42 #include "be_aas.h"
43 #include "be_aas_funcs.h"
44 #include "be_interface.h"
45 #include "be_ai_weight.h"		//fuzzy weights
46 #include "be_ai_weap.h"
47 
48 //#define DEBUG_AI_WEAP
49 
50 //structure field offsets
51 #define WEAPON_OFS(x) (size_t)&(((weaponinfo_t *)0)->x)
52 #define PROJECTILE_OFS(x) (size_t)&(((projectileinfo_t *)0)->x)
53 
54 //weapon definition
55 static fielddef_t weaponinfo_fields[] =
56 {
57 {"number", WEAPON_OFS(number), FT_INT},						//weapon number
58 {"name", WEAPON_OFS(name), FT_STRING},							//name of the weapon
59 {"level", WEAPON_OFS(level), FT_INT},
60 {"model", WEAPON_OFS(model), FT_STRING},						//model of the weapon
61 {"weaponindex", WEAPON_OFS(weaponindex), FT_INT},			//index of weapon in inventory
62 {"flags", WEAPON_OFS(flags), FT_INT},							//special flags
63 {"projectile", WEAPON_OFS(projectile), FT_STRING},			//projectile used by the weapon
64 {"numprojectiles", WEAPON_OFS(numprojectiles), FT_INT},	//number of projectiles
65 {"hspread", WEAPON_OFS(hspread), FT_FLOAT},					//horizontal spread of projectiles (degrees from middle)
66 {"vspread", WEAPON_OFS(vspread), FT_FLOAT},					//vertical spread of projectiles (degrees from middle)
67 {"speed", WEAPON_OFS(speed), FT_FLOAT},						//speed of the projectile (0 = instant hit)
68 {"acceleration", WEAPON_OFS(acceleration), FT_FLOAT},		//"acceleration" * time (in seconds) + "speed" = projectile speed
69 {"recoil", WEAPON_OFS(recoil), FT_FLOAT|FT_ARRAY, 3},		//amount of recoil the player gets from the weapon
70 {"offset", WEAPON_OFS(offset), FT_FLOAT|FT_ARRAY, 3},		//projectile start offset relative to eye and view angles
71 {"angleoffset", WEAPON_OFS(angleoffset), FT_FLOAT|FT_ARRAY, 3},//offset of the shoot angles relative to the view angles
72 {"extrazvelocity", WEAPON_OFS(extrazvelocity), FT_FLOAT},//extra z velocity the projectile gets
73 {"ammoamount", WEAPON_OFS(ammoamount), FT_INT},				//ammo amount used per shot
74 {"ammoindex", WEAPON_OFS(ammoindex), FT_INT},				//index of ammo in inventory
75 {"activate", WEAPON_OFS(activate), FT_FLOAT},				//time it takes to select the weapon
76 {"reload", WEAPON_OFS(reload), FT_FLOAT},						//time it takes to reload the weapon
77 {"spinup", WEAPON_OFS(spinup), FT_FLOAT},						//time it takes before first shot
78 {"spindown", WEAPON_OFS(spindown), FT_FLOAT},				//time it takes before weapon stops firing
79 {NULL, 0, 0, 0}
80 };
81 
82 //projectile definition
83 static fielddef_t projectileinfo_fields[] =
84 {
85 {"name", PROJECTILE_OFS(name), FT_STRING},					//name of the projectile
86 {"model", PROJECTILE_OFS(model), FT_STRING},					//model of the projectile
87 {"flags", PROJECTILE_OFS(flags), FT_INT},						//special flags
88 {"gravity", PROJECTILE_OFS(gravity), FT_FLOAT},				//amount of gravity applied to the projectile [0,1]
89 {"damage", PROJECTILE_OFS(damage), FT_INT},					//damage of the projectile
90 {"radius", PROJECTILE_OFS(radius), FT_FLOAT},				//radius of damage
91 {"visdamage", PROJECTILE_OFS(visdamage), FT_INT},			//damage of the projectile to visible entities
92 {"damagetype", PROJECTILE_OFS(damagetype), FT_INT},		//type of damage (combination of the DAMAGETYPE_? flags)
93 {"healthinc", PROJECTILE_OFS(healthinc), FT_INT},			//health increase the owner gets
94 {"push", PROJECTILE_OFS(push), FT_FLOAT},						//amount a player is pushed away from the projectile impact
95 {"detonation", PROJECTILE_OFS(detonation), FT_FLOAT},		//time before projectile explodes after fire pressed
96 {"bounce", PROJECTILE_OFS(bounce), FT_FLOAT},				//amount the projectile bounces
97 {"bouncefric", PROJECTILE_OFS(bouncefric), FT_FLOAT}, 	//amount the bounce decreases per bounce
98 {"bouncestop", PROJECTILE_OFS(bouncestop), FT_FLOAT},		//minimum bounce value before bouncing stops
99 //recurive projectile definition??
100 {NULL, 0, 0, 0}
101 };
102 
103 static structdef_t weaponinfo_struct =
104 {
105 	sizeof(weaponinfo_t), weaponinfo_fields
106 };
107 static structdef_t projectileinfo_struct =
108 {
109 	sizeof(projectileinfo_t), projectileinfo_fields
110 };
111 
112 //weapon configuration: set of weapons with projectiles
113 typedef struct weaponconfig_s
114 {
115 	int numweapons;
116 	int numprojectiles;
117 	projectileinfo_t *projectileinfo;
118 	weaponinfo_t *weaponinfo;
119 } weaponconfig_t;
120 
121 //the bot weapon state
122 typedef struct bot_weaponstate_s
123 {
124 	struct weightconfig_s *weaponweightconfig;		//weapon weight configuration
125 	int *weaponweightindex;							//weapon weight index
126 } bot_weaponstate_t;
127 
128 static bot_weaponstate_t *botweaponstates[MAX_CLIENTS+1];
129 static weaponconfig_t *weaponconfig;
130 
131 //========================================================================
132 //
133 // Parameter:				-
134 // Returns:					-
135 // Changes Globals:		-
136 //========================================================================
BotValidWeaponNumber(int weaponnum)137 int BotValidWeaponNumber(int weaponnum)
138 {
139 	if (weaponnum <= 0 || weaponnum > weaponconfig->numweapons)
140 	{
141 		botimport.Print(PRT_ERROR, "weapon number out of range\n");
142 		return qfalse;
143 	} //end if
144 	return qtrue;
145 } //end of the function BotValidWeaponNumber
146 //========================================================================
147 //
148 // Parameter:				-
149 // Returns:					-
150 // Changes Globals:		-
151 //========================================================================
BotWeaponStateFromHandle(int handle)152 bot_weaponstate_t *BotWeaponStateFromHandle(int handle)
153 {
154 	if (handle <= 0 || handle > MAX_CLIENTS)
155 	{
156 		botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle);
157 		return NULL;
158 	} //end if
159 	if (!botweaponstates[handle])
160 	{
161 		botimport.Print(PRT_FATAL, "invalid move state %d\n", handle);
162 		return NULL;
163 	} //end if
164 	return botweaponstates[handle];
165 } //end of the function BotWeaponStateFromHandle
166 //===========================================================================
167 //
168 // Parameter:				-
169 // Returns:					-
170 // Changes Globals:		-
171 //===========================================================================
172 #ifdef DEBUG_AI_WEAP
DumpWeaponConfig(weaponconfig_t * wc)173 void DumpWeaponConfig(weaponconfig_t *wc)
174 {
175 	FILE *fp;
176 	int i;
177 
178 	fp = Log_FileStruct();
179 	if (!fp) return;
180 	for (i = 0; i < wc->numprojectiles; i++)
181 	{
182 		WriteStructure(fp, &projectileinfo_struct, (char *) &wc->projectileinfo[i]);
183 		Log_Flush();
184 	} //end for
185 	for (i = 0; i < wc->numweapons; i++)
186 	{
187 		WriteStructure(fp, &weaponinfo_struct, (char *) &wc->weaponinfo[i]);
188 		Log_Flush();
189 	} //end for
190 } //end of the function DumpWeaponConfig
191 #endif //DEBUG_AI_WEAP
192 //===========================================================================
193 //
194 // Parameter:				-
195 // Returns:					-
196 // Changes Globals:		-
197 //===========================================================================
LoadWeaponConfig(char * filename)198 weaponconfig_t *LoadWeaponConfig(char *filename)
199 {
200 	int max_weaponinfo, max_projectileinfo;
201 	token_t token;
202 	char path[MAX_PATH];
203 	int i, j;
204 	source_t *source;
205 	weaponconfig_t *wc;
206 	weaponinfo_t weaponinfo;
207 
208 	max_weaponinfo = (int) LibVarValue("max_weaponinfo", "32");
209 	if (max_weaponinfo < 0)
210 	{
211 		botimport.Print(PRT_ERROR, "max_weaponinfo = %d\n", max_weaponinfo);
212 		max_weaponinfo = 32;
213 		LibVarSet("max_weaponinfo", "32");
214 	} //end if
215 	max_projectileinfo = (int) LibVarValue("max_projectileinfo", "32");
216 	if (max_projectileinfo < 0)
217 	{
218 		botimport.Print(PRT_ERROR, "max_projectileinfo = %d\n", max_projectileinfo);
219 		max_projectileinfo = 32;
220 		LibVarSet("max_projectileinfo", "32");
221 	} //end if
222 	strncpy(path, filename, MAX_PATH);
223 	PC_SetBaseFolder(BOTFILESBASEFOLDER);
224 	source = LoadSourceFile(path);
225 	if (!source)
226 	{
227 		botimport.Print(PRT_ERROR, "counldn't load %s\n", path);
228 		return NULL;
229 	} //end if
230 	//initialize weapon config
231 	wc = (weaponconfig_t *) GetClearedHunkMemory(sizeof(weaponconfig_t) +
232 										max_weaponinfo * sizeof(weaponinfo_t) +
233 										max_projectileinfo * sizeof(projectileinfo_t));
234 	wc->weaponinfo = (weaponinfo_t *) ((char *) wc + sizeof(weaponconfig_t));
235 	wc->projectileinfo = (projectileinfo_t *) ((char *) wc->weaponinfo +
236 										max_weaponinfo * sizeof(weaponinfo_t));
237 	wc->numweapons = max_weaponinfo;
238 	wc->numprojectiles = 0;
239 	//parse the source file
240 	while(PC_ReadToken(source, &token))
241 	{
242 		if (!strcmp(token.string, "weaponinfo"))
243 		{
244 			Com_Memset(&weaponinfo, 0, sizeof(weaponinfo_t));
245 			if (!ReadStructure(source, &weaponinfo_struct, (char *) &weaponinfo))
246 			{
247 				FreeMemory(wc);
248 				FreeSource(source);
249 				return NULL;
250 			} //end if
251 			if (weaponinfo.number < 0 || weaponinfo.number >= max_weaponinfo)
252 			{
253 				botimport.Print(PRT_ERROR, "weapon info number %d out of range in %s\n", weaponinfo.number, path);
254 				FreeMemory(wc);
255 				FreeSource(source);
256 				return NULL;
257 			} //end if
258 			Com_Memcpy(&wc->weaponinfo[weaponinfo.number], &weaponinfo, sizeof(weaponinfo_t));
259 			wc->weaponinfo[weaponinfo.number].valid = qtrue;
260 		} //end if
261 		else if (!strcmp(token.string, "projectileinfo"))
262 		{
263 			if (wc->numprojectiles >= max_projectileinfo)
264 			{
265 				botimport.Print(PRT_ERROR, "more than %d projectiles defined in %s\n", max_projectileinfo, path);
266 				FreeMemory(wc);
267 				FreeSource(source);
268 				return NULL;
269 			} //end if
270 			Com_Memset(&wc->projectileinfo[wc->numprojectiles], 0, sizeof(projectileinfo_t));
271 			if (!ReadStructure(source, &projectileinfo_struct, (char *) &wc->projectileinfo[wc->numprojectiles]))
272 			{
273 				FreeMemory(wc);
274 				FreeSource(source);
275 				return NULL;
276 			} //end if
277 			wc->numprojectiles++;
278 		} //end if
279 		else
280 		{
281 			botimport.Print(PRT_ERROR, "unknown definition %s in %s\n", token.string, path);
282 			FreeMemory(wc);
283 			FreeSource(source);
284 			return NULL;
285 		} //end else
286 	} //end while
287 	FreeSource(source);
288 	//fix up weapons
289 	for (i = 0; i < wc->numweapons; i++)
290 	{
291 		if (!wc->weaponinfo[i].valid) continue;
292 		if (!wc->weaponinfo[i].name[0])
293 		{
294 			botimport.Print(PRT_ERROR, "weapon %d has no name in %s\n", i, path);
295 			FreeMemory(wc);
296 			return NULL;
297 		} //end if
298 		if (!wc->weaponinfo[i].projectile[0])
299 		{
300 			botimport.Print(PRT_ERROR, "weapon %s has no projectile in %s\n", wc->weaponinfo[i].name, path);
301 			FreeMemory(wc);
302 			return NULL;
303 		} //end if
304 		//find the projectile info and copy it to the weapon info
305 		for (j = 0; j < wc->numprojectiles; j++)
306 		{
307 			if (!strcmp(wc->projectileinfo[j].name, wc->weaponinfo[i].projectile))
308 			{
309 				Com_Memcpy(&wc->weaponinfo[i].proj, &wc->projectileinfo[j], sizeof(projectileinfo_t));
310 				break;
311 			} //end if
312 		} //end for
313 		if (j == wc->numprojectiles)
314 		{
315 			botimport.Print(PRT_ERROR, "weapon %s uses undefined projectile in %s\n", wc->weaponinfo[i].name, path);
316 			FreeMemory(wc);
317 			return NULL;
318 		} //end if
319 	} //end for
320 	if (!wc->numweapons) botimport.Print(PRT_WARNING, "no weapon info loaded\n");
321 	botimport.Print(PRT_MESSAGE, "loaded %s\n", path);
322 	return wc;
323 } //end of the function LoadWeaponConfig
324 //===========================================================================
325 //
326 // Parameter:				-
327 // Returns:					-
328 // Changes Globals:		-
329 //===========================================================================
WeaponWeightIndex(weightconfig_t * wwc,weaponconfig_t * wc)330 int *WeaponWeightIndex(weightconfig_t *wwc, weaponconfig_t *wc)
331 {
332 	int *index, i;
333 
334 	//initialize item weight index
335 	index = (int *) GetClearedMemory(sizeof(int) * wc->numweapons);
336 
337 	for (i = 0; i < wc->numweapons; i++)
338 	{
339 		index[i] = FindFuzzyWeight(wwc, wc->weaponinfo[i].name);
340 	} //end for
341 	return index;
342 } //end of the function WeaponWeightIndex
343 //===========================================================================
344 //
345 // Parameter:				-
346 // Returns:					-
347 // Changes Globals:		-
348 //===========================================================================
BotFreeWeaponWeights(int weaponstate)349 void BotFreeWeaponWeights(int weaponstate)
350 {
351 	bot_weaponstate_t *ws;
352 
353 	ws = BotWeaponStateFromHandle(weaponstate);
354 	if (!ws) return;
355 	if (ws->weaponweightconfig) FreeWeightConfig(ws->weaponweightconfig);
356 	if (ws->weaponweightindex) FreeMemory(ws->weaponweightindex);
357 } //end of the function BotFreeWeaponWeights
358 //===========================================================================
359 //
360 // Parameter:				-
361 // Returns:					-
362 // Changes Globals:		-
363 //===========================================================================
BotLoadWeaponWeights(int weaponstate,char * filename)364 int BotLoadWeaponWeights(int weaponstate, char *filename)
365 {
366 	bot_weaponstate_t *ws;
367 
368 	ws = BotWeaponStateFromHandle(weaponstate);
369 	if (!ws) return BLERR_CANNOTLOADWEAPONWEIGHTS;
370 	BotFreeWeaponWeights(weaponstate);
371 	//
372 	ws->weaponweightconfig = ReadWeightConfig(filename);
373 	if (!ws->weaponweightconfig)
374 	{
375 		botimport.Print(PRT_FATAL, "couldn't load weapon config %s\n", filename);
376 		return BLERR_CANNOTLOADWEAPONWEIGHTS;
377 	} //end if
378 	if (!weaponconfig) return BLERR_CANNOTLOADWEAPONCONFIG;
379 	ws->weaponweightindex = WeaponWeightIndex(ws->weaponweightconfig, weaponconfig);
380 	return BLERR_NOERROR;
381 } //end of the function BotLoadWeaponWeights
382 //===========================================================================
383 //
384 // Parameter:				-
385 // Returns:					-
386 // Changes Globals:		-
387 //===========================================================================
BotGetWeaponInfo(int weaponstate,int weapon,weaponinfo_t * weaponinfo)388 void BotGetWeaponInfo(int weaponstate, int weapon, weaponinfo_t *weaponinfo)
389 {
390 	bot_weaponstate_t *ws;
391 
392 	if (!BotValidWeaponNumber(weapon)) return;
393 	ws = BotWeaponStateFromHandle(weaponstate);
394 	if (!ws) return;
395 	if (!weaponconfig) return;
396 	Com_Memcpy(weaponinfo, &weaponconfig->weaponinfo[weapon], sizeof(weaponinfo_t));
397 } //end of the function BotGetWeaponInfo
398 //===========================================================================
399 //
400 // Parameter:				-
401 // Returns:					-
402 // Changes Globals:		-
403 //===========================================================================
BotChooseBestFightWeapon(int weaponstate,int * inventory)404 int BotChooseBestFightWeapon(int weaponstate, int *inventory)
405 {
406 	int i, index, bestweapon;
407 	float weight, bestweight;
408 	weaponconfig_t *wc;
409 	bot_weaponstate_t *ws;
410 
411 	ws = BotWeaponStateFromHandle(weaponstate);
412 	if (!ws) return 0;
413 	wc = weaponconfig;
414 	if (!weaponconfig) return 0;
415 
416 	//if the bot has no weapon weight configuration
417 	if (!ws->weaponweightconfig) return 0;
418 
419 	bestweight = 0;
420 	bestweapon = 0;
421 	for (i = 0; i < wc->numweapons; i++)
422 	{
423 		if (!wc->weaponinfo[i].valid) continue;
424 		index = ws->weaponweightindex[i];
425 		if (index < 0) continue;
426 		weight = FuzzyWeight(inventory, ws->weaponweightconfig, index);
427 		if (weight > bestweight)
428 		{
429 			bestweight = weight;
430 			bestweapon = i;
431 		} //end if
432 	} //end for
433 	return bestweapon;
434 } //end of the function BotChooseBestFightWeapon
435 //===========================================================================
436 //
437 // Parameter:				-
438 // Returns:					-
439 // Changes Globals:		-
440 //===========================================================================
BotResetWeaponState(int weaponstate)441 void BotResetWeaponState(int weaponstate)
442 {
443 	struct weightconfig_s *weaponweightconfig;
444 	int *weaponweightindex;
445 	bot_weaponstate_t *ws;
446 
447 	ws = BotWeaponStateFromHandle(weaponstate);
448 	if (!ws) return;
449 	weaponweightconfig = ws->weaponweightconfig;
450 	weaponweightindex = ws->weaponweightindex;
451 
452 	//Com_Memset(ws, 0, sizeof(bot_weaponstate_t));
453 	ws->weaponweightconfig = weaponweightconfig;
454 	ws->weaponweightindex = weaponweightindex;
455 } //end of the function BotResetWeaponState
456 //========================================================================
457 //
458 // Parameter:				-
459 // Returns:					-
460 // Changes Globals:		-
461 //========================================================================
BotAllocWeaponState(void)462 int BotAllocWeaponState(void)
463 {
464 	int i;
465 
466 	for (i = 1; i <= MAX_CLIENTS; i++)
467 	{
468 		if (!botweaponstates[i])
469 		{
470 			botweaponstates[i] = GetClearedMemory(sizeof(bot_weaponstate_t));
471 			return i;
472 		} //end if
473 	} //end for
474 	return 0;
475 } //end of the function BotAllocWeaponState
476 //========================================================================
477 //
478 // Parameter:				-
479 // Returns:					-
480 // Changes Globals:		-
481 //========================================================================
BotFreeWeaponState(int handle)482 void BotFreeWeaponState(int handle)
483 {
484 	if (handle <= 0 || handle > MAX_CLIENTS)
485 	{
486 		botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle);
487 		return;
488 	} //end if
489 	if (!botweaponstates[handle])
490 	{
491 		botimport.Print(PRT_FATAL, "invalid move state %d\n", handle);
492 		return;
493 	} //end if
494 	BotFreeWeaponWeights(handle);
495 	FreeMemory(botweaponstates[handle]);
496 	botweaponstates[handle] = NULL;
497 } //end of the function BotFreeWeaponState
498 //===========================================================================
499 //
500 // Parameter:				-
501 // Returns:					-
502 // Changes Globals:		-
503 //===========================================================================
BotSetupWeaponAI(void)504 int BotSetupWeaponAI(void)
505 {
506 	char *file;
507 
508 	file = LibVarString("weaponconfig", "weapons.c");
509 	weaponconfig = LoadWeaponConfig(file);
510 	if (!weaponconfig)
511 	{
512 		botimport.Print(PRT_FATAL, "couldn't load the weapon config\n");
513 		return BLERR_CANNOTLOADWEAPONCONFIG;
514 	} //end if
515 
516 #ifdef DEBUG_AI_WEAP
517 	DumpWeaponConfig(weaponconfig);
518 #endif //DEBUG_AI_WEAP
519 	//
520 	return BLERR_NOERROR;
521 } //end of the function BotSetupWeaponAI
522 //===========================================================================
523 //
524 // Parameter:				-
525 // Returns:					-
526 // Changes Globals:		-
527 //===========================================================================
BotShutdownWeaponAI(void)528 void BotShutdownWeaponAI(void)
529 {
530 	int i;
531 
532 	if (weaponconfig) FreeMemory(weaponconfig);
533 	weaponconfig = NULL;
534 
535 	for (i = 1; i <= MAX_CLIENTS; i++)
536 	{
537 		if (botweaponstates[i])
538 		{
539 			BotFreeWeaponState(i);
540 		} //end if
541 	} //end for
542 } //end of the function BotShutdownWeaponAI
543 
544