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_char.c
25  *
26  * desc:		bot characters
27  *
28  * $Archive: /MissionPack/code/botlib/be_ai_char.c $
29  *
30  *****************************************************************************/
31 
32 #include "../qcommon/q_shared.h"
33 #include "l_log.h"
34 #include "l_memory.h"
35 #include "l_utils.h"
36 #include "l_script.h"
37 #include "l_precomp.h"
38 #include "l_struct.h"
39 #include "l_libvar.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_char.h"
46 
47 #define MAX_CHARACTERISTICS		80
48 
49 #define CT_INTEGER				1
50 #define CT_FLOAT				2
51 #define CT_STRING				3
52 
53 #define DEFAULT_CHARACTER		"bots/default_c.c"
54 
55 //characteristic value
56 union cvalue
57 {
58 	int integer;
59 	float _float;
60 	char *string;
61 };
62 //a characteristic
63 typedef struct bot_characteristic_s
64 {
65 	char type;						//characteristic type
66 	union cvalue value;				//characteristic value
67 } bot_characteristic_t;
68 
69 //a bot character
70 typedef struct bot_character_s
71 {
72 	char filename[MAX_QPATH];
73 	float skill;
74 	bot_characteristic_t c[1];		//variable sized
75 } bot_character_t;
76 
77 bot_character_t *botcharacters[MAX_CLIENTS + 1];
78 
79 //========================================================================
80 //
81 // Parameter:			-
82 // Returns:				-
83 // Changes Globals:		-
84 //========================================================================
BotCharacterFromHandle(int handle)85 bot_character_t *BotCharacterFromHandle(int handle)
86 {
87 	if (handle <= 0 || handle > MAX_CLIENTS)
88 	{
89 		botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle);
90 		return NULL;
91 	} //end if
92 	if (!botcharacters[handle])
93 	{
94 		botimport.Print(PRT_FATAL, "invalid character %d\n", handle);
95 		return NULL;
96 	} //end if
97 	return botcharacters[handle];
98 } //end of the function BotCharacterFromHandle
99 //===========================================================================
100 //
101 // Parameter:			-
102 // Returns:				-
103 // Changes Globals:		-
104 //===========================================================================
BotDumpCharacter(bot_character_t * ch)105 void BotDumpCharacter(bot_character_t *ch)
106 {
107 	int i;
108 
109 	Log_Write("%s\n", ch->filename);
110 	Log_Write("skill %.1f\n", ch->skill);
111 	Log_Write("{\n");
112 	for (i = 0; i < MAX_CHARACTERISTICS; i++)
113 	{
114 		switch(ch->c[i].type)
115 		{
116 			case CT_INTEGER: Log_Write(" %4d %d\n", i, ch->c[i].value.integer); break;
117 			case CT_FLOAT: Log_Write(" %4d %f\n", i, ch->c[i].value._float); break;
118 			case CT_STRING: Log_Write(" %4d %s\n", i, ch->c[i].value.string); break;
119 		} //end case
120 	} //end for
121 	Log_Write("}\n");
122 } //end of the function BotDumpCharacter
123 //========================================================================
124 //
125 // Parameter:			-
126 // Returns:				-
127 // Changes Globals:		-
128 //========================================================================
BotFreeCharacterStrings(bot_character_t * ch)129 void BotFreeCharacterStrings(bot_character_t *ch)
130 {
131 	int i;
132 
133 	for (i = 0; i < MAX_CHARACTERISTICS; i++)
134 	{
135 		if (ch->c[i].type == CT_STRING)
136 		{
137 			FreeMemory(ch->c[i].value.string);
138 		} //end if
139 	} //end for
140 } //end of the function BotFreeCharacterStrings
141 //========================================================================
142 //
143 // Parameter:			-
144 // Returns:				-
145 // Changes Globals:		-
146 //========================================================================
BotFreeCharacter2(int handle)147 void BotFreeCharacter2(int handle)
148 {
149 	if (handle <= 0 || handle > MAX_CLIENTS)
150 	{
151 		botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle);
152 		return;
153 	} //end if
154 	if (!botcharacters[handle])
155 	{
156 		botimport.Print(PRT_FATAL, "invalid character %d\n", handle);
157 		return;
158 	} //end if
159 	BotFreeCharacterStrings(botcharacters[handle]);
160 	FreeMemory(botcharacters[handle]);
161 	botcharacters[handle] = NULL;
162 } //end of the function BotFreeCharacter2
163 //========================================================================
164 //
165 // Parameter:			-
166 // Returns:				-
167 // Changes Globals:		-
168 //========================================================================
BotFreeCharacter(int handle)169 void BotFreeCharacter(int handle)
170 {
171 	if (!LibVarGetValue("bot_reloadcharacters")) return;
172 	BotFreeCharacter2(handle);
173 } //end of the function BotFreeCharacter
174 //===========================================================================
175 //
176 // Parameter:			-
177 // Returns:				-
178 // Changes Globals:		-
179 //===========================================================================
BotDefaultCharacteristics(bot_character_t * ch,bot_character_t * defaultch)180 void BotDefaultCharacteristics(bot_character_t *ch, bot_character_t *defaultch)
181 {
182 	int i;
183 
184 	for (i = 0; i < MAX_CHARACTERISTICS; i++)
185 	{
186 		if (ch->c[i].type) continue;
187 		//
188 		if (defaultch->c[i].type == CT_FLOAT)
189 		{
190 			ch->c[i].type = CT_FLOAT;
191 			ch->c[i].value._float = defaultch->c[i].value._float;
192 		} //end if
193 		else if (defaultch->c[i].type == CT_INTEGER)
194 		{
195 			ch->c[i].type = CT_INTEGER;
196 			ch->c[i].value.integer = defaultch->c[i].value.integer;
197 		} //end else if
198 		else if (defaultch->c[i].type == CT_STRING)
199 		{
200 			ch->c[i].type = CT_STRING;
201 			ch->c[i].value.string = (char *) GetMemory(strlen(defaultch->c[i].value.string)+1);
202 			strcpy(ch->c[i].value.string, defaultch->c[i].value.string);
203 		} //end else if
204 	} //end for
205 } //end of the function BotDefaultCharacteristics
206 //===========================================================================
207 //
208 // Parameter:			-
209 // Returns:				-
210 // Changes Globals:		-
211 //===========================================================================
BotLoadCharacterFromFile(char * charfile,int skill)212 bot_character_t *BotLoadCharacterFromFile(char *charfile, int skill)
213 {
214 	int indent, index, foundcharacter;
215 	bot_character_t *ch;
216 	source_t *source;
217 	token_t token;
218 
219 	foundcharacter = qfalse;
220 	//a bot character is parsed in two phases
221 	PC_SetBaseFolder(BOTFILESBASEFOLDER);
222 	source = LoadSourceFile(charfile);
223 	if (!source)
224 	{
225 		botimport.Print(PRT_ERROR, "counldn't load %s\n", charfile);
226 		return NULL;
227 	} //end if
228 	ch = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) +
229 					MAX_CHARACTERISTICS * sizeof(bot_characteristic_t));
230 	strcpy(ch->filename, charfile);
231 	while(PC_ReadToken(source, &token))
232 	{
233 		if (!strcmp(token.string, "skill"))
234 		{
235 			if (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token))
236 			{
237 				FreeSource(source);
238 				BotFreeCharacterStrings(ch);
239 				FreeMemory(ch);
240 				return NULL;
241 			} //end if
242 			if (!PC_ExpectTokenString(source, "{"))
243 			{
244 				FreeSource(source);
245 				BotFreeCharacterStrings(ch);
246 				FreeMemory(ch);
247 				return NULL;
248 			} //end if
249 			//if it's the correct skill
250 			if (skill < 0 || token.intvalue == skill)
251 			{
252 				foundcharacter = qtrue;
253 				ch->skill = token.intvalue;
254 				while(PC_ExpectAnyToken(source, &token))
255 				{
256 					if (!strcmp(token.string, "}")) break;
257 					if (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER))
258 					{
259 						SourceError(source, "expected integer index, found %s\n", token.string);
260 						FreeSource(source);
261 						BotFreeCharacterStrings(ch);
262 						FreeMemory(ch);
263 						return NULL;
264 					} //end if
265 					index = token.intvalue;
266 					if (index < 0 || index > MAX_CHARACTERISTICS)
267 					{
268 						SourceError(source, "characteristic index out of range [0, %d]\n", MAX_CHARACTERISTICS);
269 						FreeSource(source);
270 						BotFreeCharacterStrings(ch);
271 						FreeMemory(ch);
272 						return NULL;
273 					} //end if
274 					if (ch->c[index].type)
275 					{
276 						SourceError(source, "characteristic %d already initialized\n", index);
277 						FreeSource(source);
278 						BotFreeCharacterStrings(ch);
279 						FreeMemory(ch);
280 						return NULL;
281 					} //end if
282 					if (!PC_ExpectAnyToken(source, &token))
283 					{
284 						FreeSource(source);
285 						BotFreeCharacterStrings(ch);
286 						FreeMemory(ch);
287 						return NULL;
288 					} //end if
289 					if (token.type == TT_NUMBER)
290 					{
291 						if (token.subtype & TT_FLOAT)
292 						{
293 							ch->c[index].value._float = token.floatvalue;
294 							ch->c[index].type = CT_FLOAT;
295 						} //end if
296 						else
297 						{
298 							ch->c[index].value.integer = token.intvalue;
299 							ch->c[index].type = CT_INTEGER;
300 						} //end else
301 					} //end if
302 					else if (token.type == TT_STRING)
303 					{
304 						StripDoubleQuotes(token.string);
305 						ch->c[index].value.string = GetMemory(strlen(token.string)+1);
306 						strcpy(ch->c[index].value.string, token.string);
307 						ch->c[index].type = CT_STRING;
308 					} //end else if
309 					else
310 					{
311 						SourceError(source, "expected integer, float or string, found %s\n", token.string);
312 						FreeSource(source);
313 						BotFreeCharacterStrings(ch);
314 						FreeMemory(ch);
315 						return NULL;
316 					} //end else
317 				} //end if
318 				break;
319 			} //end if
320 			else
321 			{
322 				indent = 1;
323 				while(indent)
324 				{
325 					if (!PC_ExpectAnyToken(source, &token))
326 					{
327 						FreeSource(source);
328 						BotFreeCharacterStrings(ch);
329 						FreeMemory(ch);
330 						return NULL;
331 					} //end if
332 					if (!strcmp(token.string, "{")) indent++;
333 					else if (!strcmp(token.string, "}")) indent--;
334 				} //end while
335 			} //end else
336 		} //end if
337 		else
338 		{
339 			SourceError(source, "unknown definition %s\n", token.string);
340 			FreeSource(source);
341 			BotFreeCharacterStrings(ch);
342 			FreeMemory(ch);
343 			return NULL;
344 		} //end else
345 	} //end while
346 	FreeSource(source);
347 	//
348 	if (!foundcharacter)
349 	{
350 		BotFreeCharacterStrings(ch);
351 		FreeMemory(ch);
352 		return NULL;
353 	} //end if
354 	return ch;
355 } //end of the function BotLoadCharacterFromFile
356 //===========================================================================
357 //
358 // Parameter:			-
359 // Returns:				-
360 // Changes Globals:		-
361 //===========================================================================
BotFindCachedCharacter(char * charfile,float skill)362 int BotFindCachedCharacter(char *charfile, float skill)
363 {
364 	int handle;
365 
366 	for (handle = 1; handle <= MAX_CLIENTS; handle++)
367 	{
368 		if ( !botcharacters[handle] ) continue;
369 		if ( strcmp( botcharacters[handle]->filename, charfile ) == 0 &&
370 			(skill < 0 || fabs(botcharacters[handle]->skill - skill) < 0.01) )
371 		{
372 			return handle;
373 		} //end if
374 	} //end for
375 	return 0;
376 } //end of the function BotFindCachedCharacter
377 //===========================================================================
378 //
379 // Parameter:			-
380 // Returns:				-
381 // Changes Globals:		-
382 //===========================================================================
BotLoadCachedCharacter(char * charfile,float skill,int reload)383 int BotLoadCachedCharacter(char *charfile, float skill, int reload)
384 {
385 	int handle, cachedhandle, intskill;
386 	bot_character_t *ch = NULL;
387 #ifdef DEBUG
388 	int starttime;
389 
390 	starttime = Sys_MilliSeconds();
391 #endif //DEBUG
392 
393 	//find a free spot for a character
394 	for (handle = 1; handle <= MAX_CLIENTS; handle++)
395 	{
396 		if (!botcharacters[handle]) break;
397 	} //end for
398 	if (handle > MAX_CLIENTS) return 0;
399 	//try to load a cached character with the given skill
400 	if (!reload)
401 	{
402 		cachedhandle = BotFindCachedCharacter(charfile, skill);
403 		if (cachedhandle)
404 		{
405 			botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile);
406 			return cachedhandle;
407 		} //end if
408 	} //end else
409 	//
410 	intskill = (int) (skill + 0.5);
411 	//try to load the character with the given skill
412 	ch = BotLoadCharacterFromFile(charfile, intskill);
413 	if (ch)
414 	{
415 		botcharacters[handle] = ch;
416 		//
417 		botimport.Print(PRT_MESSAGE, "loaded skill %d from %s\n", intskill, charfile);
418 #ifdef DEBUG
419 		if (botDeveloper)
420 		{
421 			botimport.Print(PRT_MESSAGE, "skill %d loaded in %d msec from %s\n", intskill, Sys_MilliSeconds() - starttime, charfile);
422 		} //end if
423 #endif //DEBUG
424 		return handle;
425 	} //end if
426 	//
427 	botimport.Print(PRT_WARNING, "couldn't find skill %d in %s\n", intskill, charfile);
428 	//
429 	if (!reload)
430 	{
431 		//try to load a cached default character with the given skill
432 		cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, skill);
433 		if (cachedhandle)
434 		{
435 			botimport.Print(PRT_MESSAGE, "loaded cached default skill %d from %s\n", intskill, charfile);
436 			return cachedhandle;
437 		} //end if
438 	} //end if
439 	//try to load the default character with the given skill
440 	ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, intskill);
441 	if (ch)
442 	{
443 		botcharacters[handle] = ch;
444 		botimport.Print(PRT_MESSAGE, "loaded default skill %d from %s\n", intskill, charfile);
445 		return handle;
446 	} //end if
447 	//
448 	if (!reload)
449 	{
450 		//try to load a cached character with any skill
451 		cachedhandle = BotFindCachedCharacter(charfile, -1);
452 		if (cachedhandle)
453 		{
454 			botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile);
455 			return cachedhandle;
456 		} //end if
457 	} //end if
458 	//try to load a character with any skill
459 	ch = BotLoadCharacterFromFile(charfile, -1);
460 	if (ch)
461 	{
462 		botcharacters[handle] = ch;
463 		botimport.Print(PRT_MESSAGE, "loaded skill %f from %s\n", ch->skill, charfile);
464 		return handle;
465 	} //end if
466 	//
467 	if (!reload)
468 	{
469 		//try to load a cached character with any skill
470 		cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, -1);
471 		if (cachedhandle)
472 		{
473 			botimport.Print(PRT_MESSAGE, "loaded cached default skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile);
474 			return cachedhandle;
475 		} //end if
476 	} //end if
477 	//try to load a character with any skill
478 	ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, -1);
479 	if (ch)
480 	{
481 		botcharacters[handle] = ch;
482 		botimport.Print(PRT_MESSAGE, "loaded default skill %f from %s\n", ch->skill, charfile);
483 		return handle;
484 	} //end if
485 	//
486 	botimport.Print(PRT_WARNING, "couldn't load any skill from %s\n", charfile);
487 	//couldn't load any character
488 	return 0;
489 } //end of the function BotLoadCachedCharacter
490 //===========================================================================
491 //
492 // Parameter:			-
493 // Returns:				-
494 // Changes Globals:		-
495 //===========================================================================
BotLoadCharacterSkill(char * charfile,float skill)496 int BotLoadCharacterSkill(char *charfile, float skill)
497 {
498 	int ch, defaultch;
499 
500 	defaultch = BotLoadCachedCharacter(DEFAULT_CHARACTER, skill, qfalse);
501 	ch = BotLoadCachedCharacter(charfile, skill, LibVarGetValue("bot_reloadcharacters"));
502 
503 	if (defaultch && ch)
504 	{
505 		BotDefaultCharacteristics(botcharacters[ch], botcharacters[defaultch]);
506 	} //end if
507 
508 	return ch;
509 } //end of the function BotLoadCharacterSkill
510 //===========================================================================
511 //
512 // Parameter:			-
513 // Returns:				-
514 // Changes Globals:		-
515 //===========================================================================
BotInterpolateCharacters(int handle1,int handle2,float desiredskill)516 int BotInterpolateCharacters(int handle1, int handle2, float desiredskill)
517 {
518 	bot_character_t *ch1, *ch2, *out;
519 	int i, handle;
520 	float scale;
521 
522 	ch1 = BotCharacterFromHandle(handle1);
523 	ch2 = BotCharacterFromHandle(handle2);
524 	if (!ch1 || !ch2)
525 		return 0;
526 	//find a free spot for a character
527 	for (handle = 1; handle <= MAX_CLIENTS; handle++)
528 	{
529 		if (!botcharacters[handle]) break;
530 	} //end for
531 	if (handle > MAX_CLIENTS) return 0;
532 	out = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) +
533 					MAX_CHARACTERISTICS * sizeof(bot_characteristic_t));
534 	out->skill = desiredskill;
535 	strcpy(out->filename, ch1->filename);
536 	botcharacters[handle] = out;
537 
538 	scale = (float) (desiredskill - ch1->skill) / (ch2->skill - ch1->skill);
539 	for (i = 0; i < MAX_CHARACTERISTICS; i++)
540 	{
541 		//
542 		if (ch1->c[i].type == CT_FLOAT && ch2->c[i].type == CT_FLOAT)
543 		{
544 			out->c[i].type = CT_FLOAT;
545 			out->c[i].value._float = ch1->c[i].value._float +
546 								(ch2->c[i].value._float - ch1->c[i].value._float) * scale;
547 		} //end if
548 		else if (ch1->c[i].type == CT_INTEGER)
549 		{
550 			out->c[i].type = CT_INTEGER;
551 			out->c[i].value.integer = ch1->c[i].value.integer;
552 		} //end else if
553 		else if (ch1->c[i].type == CT_STRING)
554 		{
555 			out->c[i].type = CT_STRING;
556 			out->c[i].value.string = (char *) GetMemory(strlen(ch1->c[i].value.string)+1);
557 			strcpy(out->c[i].value.string, ch1->c[i].value.string);
558 		} //end else if
559 	} //end for
560 	return handle;
561 } //end of the function BotInterpolateCharacters
562 //===========================================================================
563 //
564 // Parameter:			-
565 // Returns:				-
566 // Changes Globals:		-
567 //===========================================================================
BotLoadCharacter(char * charfile,float skill)568 int BotLoadCharacter(char *charfile, float skill)
569 {
570 	int firstskill, secondskill, handle;
571 
572 	//make sure the skill is in the valid range
573 	if (skill < 1.0) skill = 1.0;
574 	else if (skill > 5.0) skill = 5.0;
575 	//skill 1, 4 and 5 should be available in the character files
576 	if (skill == 1.0 || skill == 4.0 || skill == 5.0)
577 	{
578 		return BotLoadCharacterSkill(charfile, skill);
579 	} //end if
580 	//check if there's a cached skill
581 	handle = BotFindCachedCharacter(charfile, skill);
582 	if (handle)
583 	{
584 		botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile);
585 		return handle;
586 	} //end if
587 	if (skill < 4.0)
588 	{
589 		//load skill 1 and 4
590 		firstskill = BotLoadCharacterSkill(charfile, 1);
591 		if (!firstskill) return 0;
592 		secondskill = BotLoadCharacterSkill(charfile, 4);
593 		if (!secondskill) return firstskill;
594 	} //end if
595 	else
596 	{
597 		//load skill 4 and 5
598 		firstskill = BotLoadCharacterSkill(charfile, 4);
599 		if (!firstskill) return 0;
600 		secondskill = BotLoadCharacterSkill(charfile, 5);
601 		if (!secondskill) return firstskill;
602 	} //end else
603 	//interpolate between the two skills
604 	handle = BotInterpolateCharacters(firstskill, secondskill, skill);
605 	if (!handle) return 0;
606 	//write the character to the log file
607 	BotDumpCharacter(botcharacters[handle]);
608 	//
609 	return handle;
610 } //end of the function BotLoadCharacter
611 //===========================================================================
612 //
613 // Parameter:			-
614 // Returns:				-
615 // Changes Globals:		-
616 //===========================================================================
CheckCharacteristicIndex(int character,int index)617 int CheckCharacteristicIndex(int character, int index)
618 {
619 	bot_character_t *ch;
620 
621 	ch = BotCharacterFromHandle(character);
622 	if (!ch) return qfalse;
623 	if (index < 0 || index >= MAX_CHARACTERISTICS)
624 	{
625 		botimport.Print(PRT_ERROR, "characteristic %d does not exist\n", index);
626 		return qfalse;
627 	} //end if
628 	if (!ch->c[index].type)
629 	{
630 		botimport.Print(PRT_ERROR, "characteristic %d is not initialized\n", index);
631 		return qfalse;
632 	} //end if
633 	return qtrue;
634 } //end of the function CheckCharacteristicIndex
635 //===========================================================================
636 //
637 // Parameter:			-
638 // Returns:				-
639 // Changes Globals:		-
640 //===========================================================================
Characteristic_Float(int character,int index)641 float Characteristic_Float(int character, int index)
642 {
643 	bot_character_t *ch;
644 
645 	ch = BotCharacterFromHandle(character);
646 	if (!ch) return 0;
647 	//check if the index is in range
648 	if (!CheckCharacteristicIndex(character, index)) return 0;
649 	//an integer will be converted to a float
650 	if (ch->c[index].type == CT_INTEGER)
651 	{
652 		return (float) ch->c[index].value.integer;
653 	} //end if
654 	//floats are just returned
655 	else if (ch->c[index].type == CT_FLOAT)
656 	{
657 		return ch->c[index].value._float;
658 	} //end else if
659 	//cannot convert a string pointer to a float
660 	else
661 	{
662 		botimport.Print(PRT_ERROR, "characteristic %d is not a float\n", index);
663 		return 0;
664 	} //end else if
665 //	return 0;
666 } //end of the function Characteristic_Float
667 //===========================================================================
668 //
669 // Parameter:				-
670 // Returns:					-
671 // Changes Globals:		-
672 //===========================================================================
Characteristic_BFloat(int character,int index,float min,float max)673 float Characteristic_BFloat(int character, int index, float min, float max)
674 {
675 	float value;
676 	bot_character_t *ch;
677 
678 	ch = BotCharacterFromHandle(character);
679 	if (!ch) return 0;
680 	if (min > max)
681 	{
682 		botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %f and %f\n", index, min, max);
683 		return 0;
684 	} //end if
685 	value = Characteristic_Float(character, index);
686 	if (value < min) return min;
687 	if (value > max) return max;
688 	return value;
689 } //end of the function Characteristic_BFloat
690 //===========================================================================
691 //
692 // Parameter:			-
693 // Returns:				-
694 // Changes Globals:		-
695 //===========================================================================
Characteristic_Integer(int character,int index)696 int Characteristic_Integer(int character, int index)
697 {
698 	bot_character_t *ch;
699 
700 	ch = BotCharacterFromHandle(character);
701 	if (!ch) return 0;
702 	//check if the index is in range
703 	if (!CheckCharacteristicIndex(character, index)) return 0;
704 	//an integer will just be returned
705 	if (ch->c[index].type == CT_INTEGER)
706 	{
707 		return ch->c[index].value.integer;
708 	} //end if
709 	//floats are casted to integers
710 	else if (ch->c[index].type == CT_FLOAT)
711 	{
712 		return (int) ch->c[index].value._float;
713 	} //end else if
714 	else
715 	{
716 		botimport.Print(PRT_ERROR, "characteristic %d is not a integer\n", index);
717 		return 0;
718 	} //end else if
719 //	return 0;
720 } //end of the function Characteristic_Integer
721 //===========================================================================
722 //
723 // Parameter:			-
724 // Returns:				-
725 // Changes Globals:		-
726 //===========================================================================
Characteristic_BInteger(int character,int index,int min,int max)727 int Characteristic_BInteger(int character, int index, int min, int max)
728 {
729 	int value;
730 	bot_character_t *ch;
731 
732 	ch = BotCharacterFromHandle(character);
733 	if (!ch) return 0;
734 	if (min > max)
735 	{
736 		botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %d and %d\n", index, min, max);
737 		return 0;
738 	} //end if
739 	value = Characteristic_Integer(character, index);
740 	if (value < min) return min;
741 	if (value > max) return max;
742 	return value;
743 } //end of the function Characteristic_BInteger
744 //===========================================================================
745 //
746 // Parameter:			-
747 // Returns:				-
748 // Changes Globals:		-
749 //===========================================================================
Characteristic_String(int character,int index,char * buf,int size)750 void Characteristic_String(int character, int index, char *buf, int size)
751 {
752 	bot_character_t *ch;
753 
754 	ch = BotCharacterFromHandle(character);
755 	if (!ch) return;
756 	//check if the index is in range
757 	if (!CheckCharacteristicIndex(character, index)) return;
758 	//an integer will be converted to a float
759 	if (ch->c[index].type == CT_STRING)
760 	{
761 		strncpy(buf, ch->c[index].value.string, size-1);
762 		buf[size-1] = '\0';
763 		return;
764 	} //end if
765 	else
766 	{
767 		botimport.Print(PRT_ERROR, "characteristic %d is not a string\n", index);
768 		return;
769 	} //end else if
770 	return;
771 } //end of the function Characteristic_String
772 //===========================================================================
773 //
774 // Parameter:			-
775 // Returns:				-
776 // Changes Globals:		-
777 //===========================================================================
BotShutdownCharacters(void)778 void BotShutdownCharacters(void)
779 {
780 	int handle;
781 
782 	for (handle = 1; handle <= MAX_CLIENTS; handle++)
783 	{
784 		if (botcharacters[handle])
785 		{
786 			BotFreeCharacter2(handle);
787 		} //end if
788 	} //end for
789 } //end of the function BotShutdownCharacters
790 
791