1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (“RTCW MP Source Code”).
8 
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 
30 /*****************************************************************************
31  * name:		be_ai_weight.c
32  *
33  * desc:		fuzzy logic
34  *
35  *
36  *****************************************************************************/
37 
38 #include "../qcommon/q_shared.h"
39 #include "l_memory.h"
40 #include "l_log.h"
41 #include "l_utils.h"
42 #include "l_script.h"
43 #include "l_precomp.h"
44 #include "l_struct.h"
45 #include "l_libvar.h"
46 #include "aasfile.h"
47 #include "botlib.h"
48 #include "be_aas.h"
49 #include "be_aas_funcs.h"
50 #include "be_interface.h"
51 #include "be_ai_weight.h"
52 
53 #define MAX_INVENTORYVALUE          999999
54 #define EVALUATERECURSIVELY
55 
56 #define MAX_WEIGHT_FILES            128
57 weightconfig_t  *weightFileList[MAX_WEIGHT_FILES];
58 
59 //===========================================================================
60 //
61 // Parameter:				-
62 // Returns:					-
63 // Changes Globals:		-
64 //===========================================================================
ReadValue(source_t * source,float * value)65 int ReadValue( source_t *source, float *value ) {
66 	token_t token;
67 
68 	if ( !PC_ExpectAnyToken( source, &token ) ) {
69 		return qfalse;
70 	}
71 	if ( !strcmp( token.string, "-" ) ) {
72 		SourceWarning(source, "negative value set to zero");
73 
74 		if(!PC_ExpectAnyToken(source, &token))
75 		{
76 			SourceError(source, "Missing return value");
77 			return qfalse;
78 		}
79 	}
80 	if ( token.type != TT_NUMBER ) {
81 		SourceError( source, "invalid return value %s", token.string );
82 		return qfalse;
83 	}
84 
85 	*value = token.floatvalue;
86 	return qtrue;
87 } //end of the function ReadValue
88 //===========================================================================
89 //
90 // Parameter:				-
91 // Returns:					-
92 // Changes Globals:		-
93 //===========================================================================
ReadFuzzyWeight(source_t * source,fuzzyseperator_t * fs)94 int ReadFuzzyWeight( source_t *source, fuzzyseperator_t *fs ) {
95 	if ( PC_CheckTokenString( source, "balance" ) ) {
96 		fs->type = WT_BALANCE;
97 		if ( !PC_ExpectTokenString( source, "(" ) ) {
98 			return qfalse;
99 		}
100 		if ( !ReadValue( source, &fs->weight ) ) {
101 			return qfalse;
102 		}
103 		if ( !PC_ExpectTokenString( source, "," ) ) {
104 			return qfalse;
105 		}
106 		if ( !ReadValue( source, &fs->minweight ) ) {
107 			return qfalse;
108 		}
109 		if ( !PC_ExpectTokenString( source, "," ) ) {
110 			return qfalse;
111 		}
112 		if ( !ReadValue( source, &fs->maxweight ) ) {
113 			return qfalse;
114 		}
115 		if ( !PC_ExpectTokenString( source, ")" ) ) {
116 			return qfalse;
117 		}
118 	} //end if
119 	else
120 	{
121 		fs->type = 0;
122 		if ( !ReadValue( source, &fs->weight ) ) {
123 			return qfalse;
124 		}
125 		fs->minweight = fs->weight;
126 		fs->maxweight = fs->weight;
127 	} //end if
128 	if ( !PC_ExpectTokenString( source, ";" ) ) {
129 		return qfalse;
130 	}
131 	return qtrue;
132 } //end of the function ReadFuzzyWeight
133 //===========================================================================
134 //
135 // Parameter:				-
136 // Returns:					-
137 // Changes Globals:		-
138 //===========================================================================
FreeFuzzySeperators_r(fuzzyseperator_t * fs)139 void FreeFuzzySeperators_r( fuzzyseperator_t *fs ) {
140 	if ( !fs ) {
141 		return;
142 	}
143 	if ( fs->child ) {
144 		FreeFuzzySeperators_r( fs->child );
145 	}
146 	if ( fs->next ) {
147 		FreeFuzzySeperators_r( fs->next );
148 	}
149 	FreeMemory( fs );
150 } //end of the function FreeFuzzySeperators
151 //===========================================================================
152 //
153 // Parameter:			-
154 // Returns:				-
155 // Changes Globals:		-
156 //===========================================================================
FreeWeightConfig2(weightconfig_t * config)157 void FreeWeightConfig2( weightconfig_t *config ) {
158 	int i;
159 
160 	for ( i = 0; i < config->numweights; i++ )
161 	{
162 		FreeFuzzySeperators_r( config->weights[i].firstseperator );
163 		if ( config->weights[i].name ) {
164 			FreeMemory( config->weights[i].name );
165 		}
166 	} //end for
167 	FreeMemory( config );
168 } //end of the function FreeWeightConfig2
169 //===========================================================================
170 //
171 // Parameter:			-
172 // Returns:				-
173 // Changes Globals:		-
174 //===========================================================================
FreeWeightConfig(weightconfig_t * config)175 void FreeWeightConfig( weightconfig_t *config ) {
176 	if ( !LibVarGetValue( "bot_reloadcharacters" ) ) {
177 		return;
178 	}
179 	FreeWeightConfig2( config );
180 } //end of the function FreeWeightConfig
181 //===========================================================================
182 //
183 // Parameter:			-
184 // Returns:				-
185 // Changes Globals:		-
186 //===========================================================================
ReadFuzzySeperators_r(source_t * source)187 fuzzyseperator_t *ReadFuzzySeperators_r( source_t *source ) {
188 	int newindent, index, def, founddefault;
189 	token_t token;
190 	fuzzyseperator_t *fs, *lastfs, *firstfs;
191 
192 	founddefault = qfalse;
193 	firstfs = NULL;
194 	lastfs = NULL;
195 	if ( !PC_ExpectTokenString( source, "(" ) ) {
196 		return NULL;
197 	}
198 	if ( !PC_ExpectTokenType( source, TT_NUMBER, TT_INTEGER, &token ) ) {
199 		return NULL;
200 	}
201 	index = token.intvalue;
202 	if ( !PC_ExpectTokenString( source, ")" ) ) {
203 		return NULL;
204 	}
205 	if ( !PC_ExpectTokenString( source, "{" ) ) {
206 		return NULL;
207 	}
208 	if ( !PC_ExpectAnyToken( source, &token ) ) {
209 		return NULL;
210 	}
211 	do
212 	{
213 		def = !strcmp( token.string, "default" );
214 		if ( def || !strcmp( token.string, "case" ) ) {
215 			fs = (fuzzyseperator_t *) GetClearedMemory( sizeof( fuzzyseperator_t ) );
216 			fs->index = index;
217 			if ( lastfs ) {
218 				lastfs->next = fs;
219 			} else { firstfs = fs;}
220 			lastfs = fs;
221 			if ( def ) {
222 				if ( founddefault ) {
223 					SourceError( source, "switch already has a default" );
224 					FreeFuzzySeperators_r( firstfs );
225 					return NULL;
226 				} //end if
227 				fs->value = MAX_INVENTORYVALUE;
228 				founddefault = qtrue;
229 			} //end if
230 			else
231 			{
232 				if ( !PC_ExpectTokenType( source, TT_NUMBER, TT_INTEGER, &token ) ) {
233 					FreeFuzzySeperators_r( firstfs );
234 					return NULL;
235 				} //end if
236 				fs->value = token.intvalue;
237 			} //end else
238 			if ( !PC_ExpectTokenString( source, ":" ) || !PC_ExpectAnyToken( source, &token ) ) {
239 				FreeFuzzySeperators_r( firstfs );
240 				return NULL;
241 			} //end if
242 			newindent = qfalse;
243 			if ( !strcmp( token.string, "{" ) ) {
244 				newindent = qtrue;
245 				if ( !PC_ExpectAnyToken( source, &token ) ) {
246 					FreeFuzzySeperators_r( firstfs );
247 					return NULL;
248 				} //end if
249 			} //end if
250 			if ( !strcmp( token.string, "return" ) ) {
251 				if ( !ReadFuzzyWeight( source, fs ) ) {
252 					FreeFuzzySeperators_r( firstfs );
253 					return NULL;
254 				} //end if
255 			} //end if
256 			else if ( !strcmp( token.string, "switch" ) ) {
257 				fs->child = ReadFuzzySeperators_r( source );
258 				if ( !fs->child ) {
259 					FreeFuzzySeperators_r( firstfs );
260 					return NULL;
261 				} //end if
262 			} //end else if
263 			else
264 			{
265 				SourceError( source, "invalid name %s", token.string );
266 				return NULL;
267 			} //end else
268 			if ( newindent ) {
269 				if ( !PC_ExpectTokenString( source, "}" ) ) {
270 					FreeFuzzySeperators_r( firstfs );
271 					return NULL;
272 				} //end if
273 			} //end if
274 		} //end if
275 		else
276 		{
277 			FreeFuzzySeperators_r( firstfs );
278 			SourceError( source, "invalid name %s", token.string );
279 			return NULL;
280 		} //end else
281 		if ( !PC_ExpectAnyToken( source, &token ) ) {
282 			FreeFuzzySeperators_r( firstfs );
283 			return NULL;
284 		} //end if
285 	} while ( strcmp( token.string, "}" ) );
286 	//
287 	if ( !founddefault ) {
288 		SourceWarning( source, "switch without default" );
289 		fs = (fuzzyseperator_t *) GetClearedMemory( sizeof( fuzzyseperator_t ) );
290 		fs->index = index;
291 		fs->value = MAX_INVENTORYVALUE;
292 		fs->weight = 0;
293 		fs->next = NULL;
294 		fs->child = NULL;
295 		if ( lastfs ) {
296 			lastfs->next = fs;
297 		} else { firstfs = fs;}
298 	} //end if
299 	  //
300 	return firstfs;
301 } //end of the function ReadFuzzySeperators_r
302 //===========================================================================
303 //
304 // Parameter:				-
305 // Returns:					-
306 // Changes Globals:		-
307 //===========================================================================
ReadWeightConfig(char * filename)308 weightconfig_t *ReadWeightConfig( char *filename ) {
309 	int newindent, avail = 0, n;
310 	token_t token;
311 	source_t *source;
312 	fuzzyseperator_t *fs;
313 	weightconfig_t *config = NULL;
314 #ifdef DEBUG
315 	int starttime;
316 
317 	starttime = Sys_MilliSeconds();
318 #endif //DEBUG
319 
320 	if ( !LibVarGetValue( "bot_reloadcharacters" ) ) {
321 		avail = -1;
322 		for ( n = 0; n < MAX_WEIGHT_FILES; n++ )
323 		{
324 			config = weightFileList[n];
325 			if ( !config ) {
326 				if ( avail == -1 ) {
327 					avail = n;
328 				} //end if
329 				continue;
330 			} //end if
331 			if ( strcmp( filename, config->filename ) == 0 ) {
332 				//botimport.Print( PRT_MESSAGE, "retained %s\n", filename );
333 				return config;
334 			} //end if
335 		} //end for
336 
337 		if ( avail == -1 ) {
338 			botimport.Print( PRT_ERROR, "weightFileList was full trying to load %s\n", filename );
339 			return NULL;
340 		} //end if
341 	} //end if
342 
343 	source = LoadSourceFile( filename );
344 	if ( !source ) {
345 		botimport.Print( PRT_ERROR, "counldn't load %s\n", filename );
346 		return NULL;
347 	} //end if
348 	  //
349 	config = (weightconfig_t *) GetClearedMemory( sizeof( weightconfig_t ) );
350 	config->numweights = 0;
351 	Q_strncpyz( config->filename, filename, sizeof( config->filename ) );
352 	//parse the item config file
353 	while ( PC_ReadToken( source, &token ) )
354 	{
355 		if ( !strcmp( token.string, "weight" ) ) {
356 			if ( config->numweights >= MAX_WEIGHTS ) {
357 				SourceWarning( source, "too many fuzzy weights" );
358 				break;
359 			} //end if
360 			if ( !PC_ExpectTokenType( source, TT_STRING, 0, &token ) ) {
361 				FreeWeightConfig( config );
362 				FreeSource( source );
363 				return NULL;
364 			} //end if
365 			StripDoubleQuotes( token.string );
366 			config->weights[config->numweights].name = (char *) GetClearedMemory( strlen( token.string ) + 1 );
367 			strcpy( config->weights[config->numweights].name, token.string );
368 			if ( !PC_ExpectAnyToken( source, &token ) ) {
369 				FreeWeightConfig( config );
370 				FreeSource( source );
371 				return NULL;
372 			} //end if
373 			newindent = qfalse;
374 			if ( !strcmp( token.string, "{" ) ) {
375 				newindent = qtrue;
376 				if ( !PC_ExpectAnyToken( source, &token ) ) {
377 					FreeWeightConfig( config );
378 					FreeSource( source );
379 					return NULL;
380 				} //end if
381 			} //end if
382 			if ( !strcmp( token.string, "switch" ) ) {
383 				fs = ReadFuzzySeperators_r( source );
384 				if ( !fs ) {
385 					FreeWeightConfig( config );
386 					FreeSource( source );
387 					return NULL;
388 				} //end if
389 				config->weights[config->numweights].firstseperator = fs;
390 			} //end if
391 			else if ( !strcmp( token.string, "return" ) ) {
392 				fs = (fuzzyseperator_t *) GetClearedMemory( sizeof( fuzzyseperator_t ) );
393 				fs->index = 0;
394 				fs->value = MAX_INVENTORYVALUE;
395 				fs->next = NULL;
396 				fs->child = NULL;
397 				if ( !ReadFuzzyWeight( source, fs ) ) {
398 					FreeMemory( fs );
399 					FreeWeightConfig( config );
400 					FreeSource( source );
401 					return NULL;
402 				} //end if
403 				config->weights[config->numweights].firstseperator = fs;
404 			} //end else if
405 			else
406 			{
407 				SourceError( source, "invalid name %s", token.string );
408 				FreeWeightConfig( config );
409 				FreeSource( source );
410 				return NULL;
411 			} //end else
412 			if ( newindent ) {
413 				if ( !PC_ExpectTokenString( source, "}" ) ) {
414 					FreeWeightConfig( config );
415 					FreeSource( source );
416 					return NULL;
417 				} //end if
418 			} //end if
419 			config->numweights++;
420 		} //end if
421 		else
422 		{
423 			SourceError( source, "invalid name %s", token.string );
424 			FreeWeightConfig( config );
425 			FreeSource( source );
426 			return NULL;
427 		} //end else
428 	} //end while
429 	  //free the source at the end of a pass
430 	FreeSource( source );
431 	//if the file was located in a pak file
432 	botimport.Print( PRT_MESSAGE, "loaded %s\n", filename );
433 #ifdef DEBUG
434 	if ( botDeveloper ) {
435 		botimport.Print( PRT_MESSAGE, "weights loaded in %d msec\n", Sys_MilliSeconds() - starttime );
436 	} //end if
437 #endif //DEBUG
438 	   //
439 	if ( !LibVarGetValue( "bot_reloadcharacters" ) ) {
440 		weightFileList[avail] = config;
441 	} //end if
442 	  //
443 	return config;
444 } //end of the function ReadWeightConfig
445 #if 0
446 //===========================================================================
447 //
448 // Parameter:				-
449 // Returns:					-
450 // Changes Globals:		-
451 //===========================================================================
452 qboolean WriteFuzzyWeight( FILE *fp, fuzzyseperator_t *fs ) {
453 	if ( fs->type == WT_BALANCE ) {
454 		if ( fprintf( fp, " return balance(" ) < 0 ) {
455 			return qfalse;
456 		}
457 		if ( !WriteFloat( fp, fs->weight ) ) {
458 			return qfalse;
459 		}
460 		if ( fprintf( fp, "," ) < 0 ) {
461 			return qfalse;
462 		}
463 		if ( !WriteFloat( fp, fs->minweight ) ) {
464 			return qfalse;
465 		}
466 		if ( fprintf( fp, "," ) < 0 ) {
467 			return qfalse;
468 		}
469 		if ( !WriteFloat( fp, fs->maxweight ) ) {
470 			return qfalse;
471 		}
472 		if ( fprintf( fp, ");\n" ) < 0 ) {
473 			return qfalse;
474 		}
475 	} //end if
476 	else
477 	{
478 		if ( fprintf( fp, " return " ) < 0 ) {
479 			return qfalse;
480 		}
481 		if ( !WriteFloat( fp, fs->weight ) ) {
482 			return qfalse;
483 		}
484 		if ( fprintf( fp, ";\n" ) < 0 ) {
485 			return qfalse;
486 		}
487 	} //end else
488 	return qtrue;
489 } //end of the function WriteFuzzyWeight
490 //===========================================================================
491 //
492 // Parameter:				-
493 // Returns:					-
494 // Changes Globals:		-
495 //===========================================================================
496 qboolean WriteFuzzySeperators_r( FILE *fp, fuzzyseperator_t *fs, int indent ) {
497 	if ( !WriteIndent( fp, indent ) ) {
498 		return qfalse;
499 	}
500 	if ( fprintf( fp, "switch(%d)\n", fs->index ) < 0 ) {
501 		return qfalse;
502 	}
503 	if ( !WriteIndent( fp, indent ) ) {
504 		return qfalse;
505 	}
506 	if ( fprintf( fp, "{\n" ) < 0 ) {
507 		return qfalse;
508 	}
509 	indent++;
510 	do
511 	{
512 		if ( !WriteIndent( fp, indent ) ) {
513 			return qfalse;
514 		}
515 		if ( fs->next ) {
516 			if ( fprintf( fp, "case %d:", fs->value ) < 0 ) {
517 				return qfalse;
518 			}
519 		} //end if
520 		else
521 		{
522 			if ( fprintf( fp, "default:" ) < 0 ) {
523 				return qfalse;
524 			}
525 		} //end else
526 		if ( fs->child ) {
527 			if ( fprintf( fp, "\n" ) < 0 ) {
528 				return qfalse;
529 			}
530 			if ( !WriteIndent( fp, indent ) ) {
531 				return qfalse;
532 			}
533 			if ( fprintf( fp, "{\n" ) < 0 ) {
534 				return qfalse;
535 			}
536 			if ( !WriteFuzzySeperators_r( fp, fs->child, indent + 1 ) ) {
537 				return qfalse;
538 			}
539 			if ( !WriteIndent( fp, indent ) ) {
540 				return qfalse;
541 			}
542 			if ( fs->next ) {
543 				if ( fprintf( fp, "} //end case\n" ) < 0 ) {
544 					return qfalse;
545 				}
546 			} //end if
547 			else
548 			{
549 				if ( fprintf( fp, "} //end default\n" ) < 0 ) {
550 					return qfalse;
551 				}
552 			} //end else
553 		} //end if
554 		else
555 		{
556 			if ( !WriteFuzzyWeight( fp, fs ) ) {
557 				return qfalse;
558 			}
559 		} //end else
560 		fs = fs->next;
561 	} while ( fs );
562 	indent--;
563 	if ( !WriteIndent( fp, indent ) ) {
564 		return qfalse;
565 	}
566 	if ( fprintf( fp, "} //end switch\n" ) < 0 ) {
567 		return qfalse;
568 	}
569 	return qtrue;
570 } //end of the function WriteItemFuzzyWeights_r
571 //===========================================================================
572 //
573 // Parameter:				-
574 // Returns:					-
575 // Changes Globals:		-
576 //===========================================================================
577 qboolean WriteWeightConfig( char *filename, weightconfig_t *config ) {
578 	int i;
579 	FILE *fp;
580 	weight_t *ifw;
581 
582 	fp = fopen( filename, "wb" );
583 	if ( !fp ) {
584 		return qfalse;
585 	}
586 
587 	for ( i = 0; i < config->numweights; i++ )
588 	{
589 		ifw = &config->weights[i];
590 		if ( fprintf( fp, "\nweight \"%s\"\n", ifw->name ) < 0 ) {
591 			return qfalse;
592 		}
593 		if ( fprintf( fp, "{\n" ) < 0 ) {
594 			return qfalse;
595 		}
596 		if ( ifw->firstseperator->index > 0 ) {
597 			if ( !WriteFuzzySeperators_r( fp, ifw->firstseperator, 1 ) ) {
598 				return qfalse;
599 			}
600 		} //end if
601 		else
602 		{
603 			if ( !WriteIndent( fp, 1 ) ) {
604 				return qfalse;
605 			}
606 			if ( !WriteFuzzyWeight( fp, ifw->firstseperator ) ) {
607 				return qfalse;
608 			}
609 		} //end else
610 		if ( fprintf( fp, "} //end weight\n" ) < 0 ) {
611 			return qfalse;
612 		}
613 	} //end for
614 	fclose( fp );
615 	return qtrue;
616 } //end of the function WriteWeightConfig
617 #endif
618 //===========================================================================
619 //
620 // Parameter:				-
621 // Returns:					-
622 // Changes Globals:		-
623 //===========================================================================
FindFuzzyWeight(weightconfig_t * wc,char * name)624 int FindFuzzyWeight( weightconfig_t *wc, char *name ) {
625 	int i;
626 
627 	for ( i = 0; i < wc->numweights; i++ )
628 	{
629 		if ( !strcmp( wc->weights[i].name, name ) ) {
630 			return i;
631 		} //end if
632 	} //end if
633 	return -1;
634 } //end of the function FindFuzzyWeight
635 //===========================================================================
636 //
637 // Parameter:				-
638 // Returns:					-
639 // Changes Globals:		-
640 //===========================================================================
FuzzyWeight_r(int * inventory,fuzzyseperator_t * fs)641 float FuzzyWeight_r( int *inventory, fuzzyseperator_t *fs ) {
642 	float scale, w1, w2;
643 
644 	if ( inventory[fs->index] < fs->value ) {
645 		if ( fs->child ) {
646 			return FuzzyWeight_r( inventory, fs->child );
647 		} else { return fs->weight;}
648 	} //end if
649 	else if ( fs->next ) {
650 		if ( inventory[fs->index] < fs->next->value ) {
651 			//first weight
652 			if ( fs->child ) {
653 				w1 = FuzzyWeight_r( inventory, fs->child );
654 			} else { w1 = fs->weight;}
655 			//second weight
656 			if ( fs->next->child ) {
657 				w2 = FuzzyWeight_r( inventory, fs->next->child );
658 			} else { w2 = fs->next->weight;}
659 			//the scale factor
660 			if(fs->next->value == MAX_INVENTORYVALUE) // is fs->next the default case?
661 				return w2;      // can't interpolate, return default weight
662 			else
663 				scale = (float) (inventory[fs->index] - fs->value) / (fs->next->value - fs->value);
664 			//scale between the two weights
665 			return (1 - scale) * w1 + scale * w2;
666 		} //end if
667 		return FuzzyWeight_r( inventory, fs->next );
668 	} //end else if
669 	return fs->weight;
670 } //end of the function FuzzyWeight_r
671 //===========================================================================
672 //
673 // Parameter:				-
674 // Returns:					-
675 // Changes Globals:		-
676 //===========================================================================
FuzzyWeightUndecided_r(int * inventory,fuzzyseperator_t * fs)677 float FuzzyWeightUndecided_r( int *inventory, fuzzyseperator_t *fs ) {
678 	float scale, w1, w2;
679 
680 	if ( inventory[fs->index] < fs->value ) {
681 		if ( fs->child ) {
682 			return FuzzyWeightUndecided_r( inventory, fs->child );
683 		} else { return fs->minweight + random() * ( fs->maxweight - fs->minweight );}
684 	} //end if
685 	else if ( fs->next ) {
686 		if ( inventory[fs->index] < fs->next->value ) {
687 			//first weight
688 			if ( fs->child ) {
689 				w1 = FuzzyWeightUndecided_r( inventory, fs->child );
690 			} else { w1 = fs->minweight + random() * ( fs->maxweight - fs->minweight );}
691 			//second weight
692 			if ( fs->next->child ) {
693 				w2 = FuzzyWeight_r( inventory, fs->next->child );
694 			} else { w2 = fs->next->minweight + random() * ( fs->next->maxweight - fs->next->minweight );}
695 			//the scale factor
696 			if(fs->next->value == MAX_INVENTORYVALUE) // is fs->next the default case?
697 				return w2;      // can't interpolate, return default weight
698 			else
699 				scale = (float) (inventory[fs->index] - fs->value) / (fs->next->value - fs->value);
700 			//scale between the two weights
701 			return (1 - scale) * w1 + scale * w2;
702 		} //end if
703 		return FuzzyWeightUndecided_r( inventory, fs->next );
704 	} //end else if
705 	return fs->weight;
706 } //end of the function FuzzyWeightUndecided_r
707 //===========================================================================
708 //
709 // Parameter:				-
710 // Returns:					-
711 // Changes Globals:		-
712 //===========================================================================
FuzzyWeight(int * inventory,weightconfig_t * wc,int weightnum)713 float FuzzyWeight( int *inventory, weightconfig_t *wc, int weightnum ) {
714 #ifdef EVALUATERECURSIVELY
715 	return FuzzyWeight_r( inventory, wc->weights[weightnum].firstseperator );
716 #else
717 	fuzzyseperator_t *s;
718 
719 	s = wc->weights[weightnum].firstseperator;
720 	if ( !s ) {
721 		return 0;
722 	}
723 	while ( 1 )
724 	{
725 		if ( inventory[s->index] < s->value ) {
726 			if ( s->child ) {
727 				s = s->child;
728 			} else { return s->weight;}
729 		} //end if
730 		else
731 		{
732 			if ( s->next ) {
733 				s = s->next;
734 			} else { return s->weight;}
735 		} //end else
736 	} //end if
737 	return 0;
738 #endif
739 } //end of the function FuzzyWeight
740 //===========================================================================
741 //
742 // Parameter:				-
743 // Returns:					-
744 // Changes Globals:		-
745 //===========================================================================
FuzzyWeightUndecided(int * inventory,weightconfig_t * wc,int weightnum)746 float FuzzyWeightUndecided( int *inventory, weightconfig_t *wc, int weightnum ) {
747 #ifdef EVALUATERECURSIVELY
748 	return FuzzyWeightUndecided_r( inventory, wc->weights[weightnum].firstseperator );
749 #else
750 	fuzzyseperator_t *s;
751 
752 	s = wc->weights[weightnum].firstseperator;
753 	if ( !s ) {
754 		return 0;
755 	}
756 	while ( 1 )
757 	{
758 		if ( inventory[s->index] < s->value ) {
759 			if ( s->child ) {
760 				s = s->child;
761 			} else { return s->minweight + random() * ( s->maxweight - s->minweight );}
762 		} //end if
763 		else
764 		{
765 			if ( s->next ) {
766 				s = s->next;
767 			} else { return s->minweight + random() * ( s->maxweight - s->minweight );}
768 		} //end else
769 	} //end if
770 	return 0;
771 #endif
772 } //end of the function FuzzyWeightUndecided
773 //===========================================================================
774 //
775 // Parameter:				-
776 // Returns:					-
777 // Changes Globals:		-
778 //===========================================================================
EvolveFuzzySeperator_r(fuzzyseperator_t * fs)779 void EvolveFuzzySeperator_r( fuzzyseperator_t *fs ) {
780 	if ( fs->child ) {
781 		EvolveFuzzySeperator_r( fs->child );
782 	} //end if
783 	else if ( fs->type == WT_BALANCE ) {
784 		//every once in a while an evolution leap occurs, mutation
785 		if ( random() < 0.01 ) {
786 			fs->weight += crandom() * ( fs->maxweight - fs->minweight );
787 		} else { fs->weight += crandom() * ( fs->maxweight - fs->minweight ) * 0.5;}
788 		//modify bounds if necesary because of mutation
789 		if ( fs->weight < fs->minweight ) {
790 			fs->minweight = fs->weight;
791 		} else if ( fs->weight > fs->maxweight ) {
792 			fs->maxweight = fs->weight;
793 		}
794 	} //end else if
795 	if ( fs->next ) {
796 		EvolveFuzzySeperator_r( fs->next );
797 	}
798 } //end of the function EvolveFuzzySeperator_r
799 //===========================================================================
800 //
801 // Parameter:				-
802 // Returns:					-
803 // Changes Globals:		-
804 //===========================================================================
EvolveWeightConfig(weightconfig_t * config)805 void EvolveWeightConfig( weightconfig_t *config ) {
806 	int i;
807 
808 	for ( i = 0; i < config->numweights; i++ )
809 	{
810 		EvolveFuzzySeperator_r( config->weights[i].firstseperator );
811 	} //end for
812 } //end of the function EvolveWeightConfig
813 //===========================================================================
814 //
815 // Parameter:				-
816 // Returns:					-
817 // Changes Globals:		-
818 //===========================================================================
ScaleFuzzySeperator_r(fuzzyseperator_t * fs,float scale)819 void ScaleFuzzySeperator_r( fuzzyseperator_t *fs, float scale ) {
820 	if ( fs->child ) {
821 		ScaleFuzzySeperator_r( fs->child, scale );
822 	} //end if
823 	else if ( fs->type == WT_BALANCE ) {
824 		//
825 		fs->weight = (float) (fs->maxweight + fs->minweight) * scale;
826 		//get the weight between bounds
827 		if ( fs->weight < fs->minweight ) {
828 			fs->weight = fs->minweight;
829 		} else if ( fs->weight > fs->maxweight ) {
830 			fs->weight = fs->maxweight;
831 		}
832 	} //end else if
833 	if ( fs->next ) {
834 		ScaleFuzzySeperator_r( fs->next, scale );
835 	}
836 } //end of the function ScaleFuzzySeperator_r
837 //===========================================================================
838 //
839 // Parameter:				-
840 // Returns:					-
841 // Changes Globals:		-
842 //===========================================================================
ScaleWeight(weightconfig_t * config,char * name,float scale)843 void ScaleWeight( weightconfig_t *config, char *name, float scale ) {
844 	int i;
845 
846 	if ( scale < 0 ) {
847 		scale = 0;
848 	} else if ( scale > 1 ) {
849 		scale = 1;
850 	}
851 	for ( i = 0; i < config->numweights; i++ )
852 	{
853 		if ( !strcmp( name, config->weights[i].name ) ) {
854 			ScaleFuzzySeperator_r( config->weights[i].firstseperator, scale );
855 			break;
856 		} //end if
857 	} //end for
858 } //end of the function ScaleWeight
859 //===========================================================================
860 //
861 // Parameter:				-
862 // Returns:					-
863 // Changes Globals:		-
864 //===========================================================================
ScaleFuzzySeperatorBalanceRange_r(fuzzyseperator_t * fs,float scale)865 void ScaleFuzzySeperatorBalanceRange_r( fuzzyseperator_t *fs, float scale ) {
866 	if ( fs->child ) {
867 		ScaleFuzzySeperatorBalanceRange_r( fs->child, scale );
868 	} //end if
869 	else if ( fs->type == WT_BALANCE ) {
870 		float mid = ( fs->minweight + fs->maxweight ) * 0.5;
871 		//get the weight between bounds
872 		fs->maxweight = mid + ( fs->maxweight - mid ) * scale;
873 		fs->minweight = mid + ( fs->minweight - mid ) * scale;
874 		if ( fs->maxweight < fs->minweight ) {
875 			fs->maxweight = fs->minweight;
876 		} //end if
877 	} //end else if
878 	if ( fs->next ) {
879 		ScaleFuzzySeperatorBalanceRange_r( fs->next, scale );
880 	}
881 } //end of the function ScaleFuzzySeperatorBalanceRange_r
882 //===========================================================================
883 //
884 // Parameter:				-
885 // Returns:					-
886 // Changes Globals:		-
887 //===========================================================================
ScaleFuzzyBalanceRange(weightconfig_t * config,float scale)888 void ScaleFuzzyBalanceRange( weightconfig_t *config, float scale ) {
889 	int i;
890 
891 	if ( scale < 0 ) {
892 		scale = 0;
893 	} else if ( scale > 100 ) {
894 		scale = 100;
895 	}
896 	for ( i = 0; i < config->numweights; i++ )
897 	{
898 		ScaleFuzzySeperatorBalanceRange_r( config->weights[i].firstseperator, scale );
899 	} //end for
900 } //end of the function ScaleFuzzyBalanceRange
901 //===========================================================================
902 //
903 // Parameter:				-
904 // Returns:					-
905 // Changes Globals:		-
906 //===========================================================================
InterbreedFuzzySeperator_r(fuzzyseperator_t * fs1,fuzzyseperator_t * fs2,fuzzyseperator_t * fsout)907 int InterbreedFuzzySeperator_r( fuzzyseperator_t *fs1, fuzzyseperator_t *fs2,
908 								fuzzyseperator_t *fsout ) {
909 	if ( fs1->child ) {
910 		if ( !fs2->child || !fsout->child ) {
911 			botimport.Print( PRT_ERROR, "cannot interbreed weight configs, unequal child\n" );
912 			return qfalse;
913 		} //end if
914 		if ( !InterbreedFuzzySeperator_r( fs2->child, fs2->child, fsout->child ) ) {
915 			return qfalse;
916 		} //end if
917 	} //end if
918 	else if ( fs1->type == WT_BALANCE ) {
919 		if ( fs2->type != WT_BALANCE || fsout->type != WT_BALANCE ) {
920 			botimport.Print( PRT_ERROR, "cannot interbreed weight configs, unequal balance\n" );
921 			return qfalse;
922 		} //end if
923 		fsout->weight = ( fs1->weight + fs2->weight ) / 2;
924 		if ( fsout->weight > fsout->maxweight ) {
925 			fsout->maxweight = fsout->weight;
926 		}
927 		if ( fsout->weight > fsout->minweight ) {
928 			fsout->minweight = fsout->weight;
929 		}
930 	} //end else if
931 	if ( fs1->next ) {
932 		if ( !fs2->next || !fsout->next ) {
933 			botimport.Print( PRT_ERROR, "cannot interbreed weight configs, unequal next\n" );
934 			return qfalse;
935 		} //end if
936 		if ( !InterbreedFuzzySeperator_r( fs1->next, fs2->next, fsout->next ) ) {
937 			return qfalse;
938 		} //end if
939 	} //end if
940 	return qtrue;
941 } //end of the function InterbreedFuzzySeperator_r
942 //===========================================================================
943 // config1 and config2 are interbreeded and stored in configout
944 //
945 // Parameter:				-
946 // Returns:					-
947 // Changes Globals:		-
948 //===========================================================================
InterbreedWeightConfigs(weightconfig_t * config1,weightconfig_t * config2,weightconfig_t * configout)949 void InterbreedWeightConfigs( weightconfig_t *config1, weightconfig_t *config2,
950 							  weightconfig_t *configout ) {
951 	int i;
952 
953 	if ( config1->numweights != config2->numweights ||
954 		 config1->numweights != configout->numweights ) {
955 		botimport.Print( PRT_ERROR, "cannot interbreed weight configs, unequal numweights\n" );
956 		return;
957 	} //end if
958 	for ( i = 0; i < config1->numweights; i++ )
959 	{
960 		InterbreedFuzzySeperator_r( config1->weights[i].firstseperator,
961 									config2->weights[i].firstseperator,
962 									configout->weights[i].firstseperator );
963 	} //end for
964 } //end of the function InterbreedWeightConfigs
965 //===========================================================================
966 //
967 // Parameter:			-
968 // Returns:				-
969 // Changes Globals:		-
970 //===========================================================================
BotShutdownWeights(void)971 void BotShutdownWeights( void ) {
972 	int i;
973 
974 	for ( i = 0; i < MAX_WEIGHT_FILES; i++ )
975 	{
976 		if ( weightFileList[i] ) {
977 			FreeWeightConfig2( weightFileList[i] );
978 			weightFileList[i] = NULL;
979 		} //end if
980 	} //end for
981 } //end of the function BotShutdownWeights
982