Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

sv_bot.c (16211B)


      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 Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 // sv_bot.c
     23 
     24 #include "server.h"
     25 #include "../game/botlib.h"
     26 
     27 typedef struct bot_debugpoly_s
     28 {
     29 	int inuse;
     30 	int color;
     31 	int numPoints;
     32 	vec3_t points[128];
     33 } bot_debugpoly_t;
     34 
     35 static bot_debugpoly_t *debugpolygons;
     36 int bot_maxdebugpolys;
     37 
     38 extern botlib_export_t	*botlib_export;
     39 int	bot_enable;
     40 
     41 
     42 /*
     43 ==================
     44 SV_BotAllocateClient
     45 ==================
     46 */
     47 int SV_BotAllocateClient(void) {
     48 	int			i;
     49 	client_t	*cl;
     50 
     51 	// find a client slot
     52 	for ( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) {
     53 		if ( cl->state == CS_FREE ) {
     54 			break;
     55 		}
     56 	}
     57 
     58 	if ( i == sv_maxclients->integer ) {
     59 		return -1;
     60 	}
     61 
     62 	cl->gentity = SV_GentityNum( i );
     63 	cl->gentity->s.number = i;
     64 	cl->state = CS_ACTIVE;
     65 	cl->lastPacketTime = svs.time;
     66 	cl->netchan.remoteAddress.type = NA_BOT;
     67 	cl->rate = 16384;
     68 
     69 	return i;
     70 }
     71 
     72 /*
     73 ==================
     74 SV_BotFreeClient
     75 ==================
     76 */
     77 void SV_BotFreeClient( int clientNum ) {
     78 	client_t	*cl;
     79 
     80 	if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) {
     81 		Com_Error( ERR_DROP, "SV_BotFreeClient: bad clientNum: %i", clientNum );
     82 	}
     83 	cl = &svs.clients[clientNum];
     84 	cl->state = CS_FREE;
     85 	cl->name[0] = 0;
     86 	if ( cl->gentity ) {
     87 		cl->gentity->r.svFlags &= ~SVF_BOT;
     88 	}
     89 }
     90 
     91 /*
     92 ==================
     93 BotDrawDebugPolygons
     94 ==================
     95 */
     96 void BotDrawDebugPolygons(void (*drawPoly)(int color, int numPoints, float *points), int value) {
     97 	static cvar_t *bot_debug, *bot_groundonly, *bot_reachability, *bot_highlightarea;
     98 	bot_debugpoly_t *poly;
     99 	int i, parm0;
    100 
    101 	if (!debugpolygons)
    102 		return;
    103 	//bot debugging
    104 	if (!bot_debug) bot_debug = Cvar_Get("bot_debug", "0", 0);
    105 	//
    106 	if (bot_enable && bot_debug->integer) {
    107 		//show reachabilities
    108 		if (!bot_reachability) bot_reachability = Cvar_Get("bot_reachability", "0", 0);
    109 		//show ground faces only
    110 		if (!bot_groundonly) bot_groundonly = Cvar_Get("bot_groundonly", "1", 0);
    111 		//get the hightlight area
    112 		if (!bot_highlightarea) bot_highlightarea = Cvar_Get("bot_highlightarea", "0", 0);
    113 		//
    114 		parm0 = 0;
    115 		if (svs.clients[0].lastUsercmd.buttons & BUTTON_ATTACK) parm0 |= 1;
    116 		if (bot_reachability->integer) parm0 |= 2;
    117 		if (bot_groundonly->integer) parm0 |= 4;
    118 		botlib_export->BotLibVarSet("bot_highlightarea", bot_highlightarea->string);
    119 		botlib_export->Test(parm0, NULL, svs.clients[0].gentity->r.currentOrigin, 
    120 			svs.clients[0].gentity->r.currentAngles);
    121 	} //end if
    122 	//draw all debug polys
    123 	for (i = 0; i < bot_maxdebugpolys; i++) {
    124 		poly = &debugpolygons[i];
    125 		if (!poly->inuse) continue;
    126 		drawPoly(poly->color, poly->numPoints, (float *) poly->points);
    127 		//Com_Printf("poly %i, numpoints = %d\n", i, poly->numPoints);
    128 	}
    129 }
    130 
    131 /*
    132 ==================
    133 BotImport_Print
    134 ==================
    135 */
    136 void QDECL BotImport_Print(int type, char *fmt, ...)
    137 {
    138 	char str[2048];
    139 	va_list ap;
    140 
    141 	va_start(ap, fmt);
    142 	vsprintf(str, fmt, ap);
    143 	va_end(ap);
    144 
    145 	switch(type) {
    146 		case PRT_MESSAGE: {
    147 			Com_Printf("%s", str);
    148 			break;
    149 		}
    150 		case PRT_WARNING: {
    151 			Com_Printf(S_COLOR_YELLOW "Warning: %s", str);
    152 			break;
    153 		}
    154 		case PRT_ERROR: {
    155 			Com_Printf(S_COLOR_RED "Error: %s", str);
    156 			break;
    157 		}
    158 		case PRT_FATAL: {
    159 			Com_Printf(S_COLOR_RED "Fatal: %s", str);
    160 			break;
    161 		}
    162 		case PRT_EXIT: {
    163 			Com_Error(ERR_DROP, S_COLOR_RED "Exit: %s", str);
    164 			break;
    165 		}
    166 		default: {
    167 			Com_Printf("unknown print type\n");
    168 			break;
    169 		}
    170 	}
    171 }
    172 
    173 /*
    174 ==================
    175 BotImport_Trace
    176 ==================
    177 */
    178 void BotImport_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) {
    179 	trace_t trace;
    180 
    181 	SV_Trace(&trace, start, mins, maxs, end, passent, contentmask, qfalse);
    182 	//copy the trace information
    183 	bsptrace->allsolid = trace.allsolid;
    184 	bsptrace->startsolid = trace.startsolid;
    185 	bsptrace->fraction = trace.fraction;
    186 	VectorCopy(trace.endpos, bsptrace->endpos);
    187 	bsptrace->plane.dist = trace.plane.dist;
    188 	VectorCopy(trace.plane.normal, bsptrace->plane.normal);
    189 	bsptrace->plane.signbits = trace.plane.signbits;
    190 	bsptrace->plane.type = trace.plane.type;
    191 	bsptrace->surface.value = trace.surfaceFlags;
    192 	bsptrace->ent = trace.entityNum;
    193 	bsptrace->exp_dist = 0;
    194 	bsptrace->sidenum = 0;
    195 	bsptrace->contents = 0;
    196 }
    197 
    198 /*
    199 ==================
    200 BotImport_EntityTrace
    201 ==================
    202 */
    203 void BotImport_EntityTrace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int entnum, int contentmask) {
    204 	trace_t trace;
    205 
    206 	SV_ClipToEntity(&trace, start, mins, maxs, end, entnum, contentmask, qfalse);
    207 	//copy the trace information
    208 	bsptrace->allsolid = trace.allsolid;
    209 	bsptrace->startsolid = trace.startsolid;
    210 	bsptrace->fraction = trace.fraction;
    211 	VectorCopy(trace.endpos, bsptrace->endpos);
    212 	bsptrace->plane.dist = trace.plane.dist;
    213 	VectorCopy(trace.plane.normal, bsptrace->plane.normal);
    214 	bsptrace->plane.signbits = trace.plane.signbits;
    215 	bsptrace->plane.type = trace.plane.type;
    216 	bsptrace->surface.value = trace.surfaceFlags;
    217 	bsptrace->ent = trace.entityNum;
    218 	bsptrace->exp_dist = 0;
    219 	bsptrace->sidenum = 0;
    220 	bsptrace->contents = 0;
    221 }
    222 
    223 
    224 /*
    225 ==================
    226 BotImport_PointContents
    227 ==================
    228 */
    229 int BotImport_PointContents(vec3_t point) {
    230 	return SV_PointContents(point, -1);
    231 }
    232 
    233 /*
    234 ==================
    235 BotImport_inPVS
    236 ==================
    237 */
    238 int BotImport_inPVS(vec3_t p1, vec3_t p2) {
    239 	return SV_inPVS (p1, p2);
    240 }
    241 
    242 /*
    243 ==================
    244 BotImport_BSPEntityData
    245 ==================
    246 */
    247 char *BotImport_BSPEntityData(void) {
    248 	return CM_EntityString();
    249 }
    250 
    251 /*
    252 ==================
    253 BotImport_BSPModelMinsMaxsOrigin
    254 ==================
    255 */
    256 void BotImport_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t outmins, vec3_t outmaxs, vec3_t origin) {
    257 	clipHandle_t h;
    258 	vec3_t mins, maxs;
    259 	float max;
    260 	int	i;
    261 
    262 	h = CM_InlineModel(modelnum);
    263 	CM_ModelBounds(h, mins, maxs);
    264 	//if the model is rotated
    265 	if ((angles[0] || angles[1] || angles[2])) {
    266 		// expand for rotation
    267 
    268 		max = RadiusFromBounds(mins, maxs);
    269 		for (i = 0; i < 3; i++) {
    270 			mins[i] = -max;
    271 			maxs[i] = max;
    272 		}
    273 	}
    274 	if (outmins) VectorCopy(mins, outmins);
    275 	if (outmaxs) VectorCopy(maxs, outmaxs);
    276 	if (origin) VectorClear(origin);
    277 }
    278 
    279 /*
    280 ==================
    281 BotImport_GetMemory
    282 ==================
    283 */
    284 void *BotImport_GetMemory(int size) {
    285 	void *ptr;
    286 
    287 	ptr = Z_TagMalloc( size, TAG_BOTLIB );
    288 	return ptr;
    289 }
    290 
    291 /*
    292 ==================
    293 BotImport_FreeMemory
    294 ==================
    295 */
    296 void BotImport_FreeMemory(void *ptr) {
    297 	Z_Free(ptr);
    298 }
    299 
    300 /*
    301 =================
    302 BotImport_HunkAlloc
    303 =================
    304 */
    305 void *BotImport_HunkAlloc( int size ) {
    306 	if( Hunk_CheckMark() ) {
    307 		Com_Error( ERR_DROP, "SV_Bot_HunkAlloc: Alloc with marks already set\n" );
    308 	}
    309 	return Hunk_Alloc( size, h_high );
    310 }
    311 
    312 /*
    313 ==================
    314 BotImport_DebugPolygonCreate
    315 ==================
    316 */
    317 int BotImport_DebugPolygonCreate(int color, int numPoints, vec3_t *points) {
    318 	bot_debugpoly_t *poly;
    319 	int i;
    320 
    321 	if (!debugpolygons)
    322 		return 0;
    323 
    324 	for (i = 1; i < bot_maxdebugpolys; i++) 	{
    325 		if (!debugpolygons[i].inuse)
    326 			break;
    327 	}
    328 	if (i >= bot_maxdebugpolys)
    329 		return 0;
    330 	poly = &debugpolygons[i];
    331 	poly->inuse = qtrue;
    332 	poly->color = color;
    333 	poly->numPoints = numPoints;
    334 	Com_Memcpy(poly->points, points, numPoints * sizeof(vec3_t));
    335 	//
    336 	return i;
    337 }
    338 
    339 /*
    340 ==================
    341 BotImport_DebugPolygonShow
    342 ==================
    343 */
    344 void BotImport_DebugPolygonShow(int id, int color, int numPoints, vec3_t *points) {
    345 	bot_debugpoly_t *poly;
    346 
    347 	if (!debugpolygons) return;
    348 	poly = &debugpolygons[id];
    349 	poly->inuse = qtrue;
    350 	poly->color = color;
    351 	poly->numPoints = numPoints;
    352 	Com_Memcpy(poly->points, points, numPoints * sizeof(vec3_t));
    353 }
    354 
    355 /*
    356 ==================
    357 BotImport_DebugPolygonDelete
    358 ==================
    359 */
    360 void BotImport_DebugPolygonDelete(int id)
    361 {
    362 	if (!debugpolygons) return;
    363 	debugpolygons[id].inuse = qfalse;
    364 }
    365 
    366 /*
    367 ==================
    368 BotImport_DebugLineCreate
    369 ==================
    370 */
    371 int BotImport_DebugLineCreate(void) {
    372 	vec3_t points[1];
    373 	return BotImport_DebugPolygonCreate(0, 0, points);
    374 }
    375 
    376 /*
    377 ==================
    378 BotImport_DebugLineDelete
    379 ==================
    380 */
    381 void BotImport_DebugLineDelete(int line) {
    382 	BotImport_DebugPolygonDelete(line);
    383 }
    384 
    385 /*
    386 ==================
    387 BotImport_DebugLineShow
    388 ==================
    389 */
    390 void BotImport_DebugLineShow(int line, vec3_t start, vec3_t end, int color) {
    391 	vec3_t points[4], dir, cross, up = {0, 0, 1};
    392 	float dot;
    393 
    394 	VectorCopy(start, points[0]);
    395 	VectorCopy(start, points[1]);
    396 	//points[1][2] -= 2;
    397 	VectorCopy(end, points[2]);
    398 	//points[2][2] -= 2;
    399 	VectorCopy(end, points[3]);
    400 
    401 
    402 	VectorSubtract(end, start, dir);
    403 	VectorNormalize(dir);
    404 	dot = DotProduct(dir, up);
    405 	if (dot > 0.99 || dot < -0.99) VectorSet(cross, 1, 0, 0);
    406 	else CrossProduct(dir, up, cross);
    407 
    408 	VectorNormalize(cross);
    409 
    410 	VectorMA(points[0], 2, cross, points[0]);
    411 	VectorMA(points[1], -2, cross, points[1]);
    412 	VectorMA(points[2], -2, cross, points[2]);
    413 	VectorMA(points[3], 2, cross, points[3]);
    414 
    415 	BotImport_DebugPolygonShow(line, color, 4, points);
    416 }
    417 
    418 /*
    419 ==================
    420 SV_BotClientCommand
    421 ==================
    422 */
    423 void BotClientCommand( int client, char *command ) {
    424 	SV_ExecuteClientCommand( &svs.clients[client], command, qtrue );
    425 }
    426 
    427 /*
    428 ==================
    429 SV_BotFrame
    430 ==================
    431 */
    432 void SV_BotFrame( int time ) {
    433 	if (!bot_enable) return;
    434 	//NOTE: maybe the game is already shutdown
    435 	if (!gvm) return;
    436 	VM_Call( gvm, BOTAI_START_FRAME, time );
    437 }
    438 
    439 /*
    440 ===============
    441 SV_BotLibSetup
    442 ===============
    443 */
    444 int SV_BotLibSetup( void ) {
    445 	if (!bot_enable) {
    446 		return 0;
    447 	}
    448 
    449 	if ( !botlib_export ) {
    450 		Com_Printf( S_COLOR_RED "Error: SV_BotLibSetup without SV_BotInitBotLib\n" );
    451 		return -1;
    452 	}
    453 
    454 	return botlib_export->BotLibSetup();
    455 }
    456 
    457 /*
    458 ===============
    459 SV_ShutdownBotLib
    460 
    461 Called when either the entire server is being killed, or
    462 it is changing to a different game directory.
    463 ===============
    464 */
    465 int SV_BotLibShutdown( void ) {
    466 
    467 	if ( !botlib_export ) {
    468 		return -1;
    469 	}
    470 
    471 	return botlib_export->BotLibShutdown();
    472 }
    473 
    474 /*
    475 ==================
    476 SV_BotInitCvars
    477 ==================
    478 */
    479 void SV_BotInitCvars(void) {
    480 
    481 	Cvar_Get("bot_enable", "1", 0);						//enable the bot
    482 	Cvar_Get("bot_developer", "0", CVAR_CHEAT);			//bot developer mode
    483 	Cvar_Get("bot_debug", "0", CVAR_CHEAT);				//enable bot debugging
    484 	Cvar_Get("bot_maxdebugpolys", "2", 0);				//maximum number of debug polys
    485 	Cvar_Get("bot_groundonly", "1", 0);					//only show ground faces of areas
    486 	Cvar_Get("bot_reachability", "0", 0);				//show all reachabilities to other areas
    487 	Cvar_Get("bot_visualizejumppads", "0", CVAR_CHEAT);	//show jumppads
    488 	Cvar_Get("bot_forceclustering", "0", 0);			//force cluster calculations
    489 	Cvar_Get("bot_forcereachability", "0", 0);			//force reachability calculations
    490 	Cvar_Get("bot_forcewrite", "0", 0);					//force writing aas file
    491 	Cvar_Get("bot_aasoptimize", "0", 0);				//no aas file optimisation
    492 	Cvar_Get("bot_saveroutingcache", "0", 0);			//save routing cache
    493 	Cvar_Get("bot_thinktime", "100", CVAR_CHEAT);		//msec the bots thinks
    494 	Cvar_Get("bot_reloadcharacters", "0", 0);			//reload the bot characters each time
    495 	Cvar_Get("bot_testichat", "0", 0);					//test ichats
    496 	Cvar_Get("bot_testrchat", "0", 0);					//test rchats
    497 	Cvar_Get("bot_testsolid", "0", CVAR_CHEAT);			//test for solid areas
    498 	Cvar_Get("bot_testclusters", "0", CVAR_CHEAT);		//test the AAS clusters
    499 	Cvar_Get("bot_fastchat", "0", 0);					//fast chatting bots
    500 	Cvar_Get("bot_nochat", "0", 0);						//disable chats
    501 	Cvar_Get("bot_pause", "0", CVAR_CHEAT);				//pause the bots thinking
    502 	Cvar_Get("bot_report", "0", CVAR_CHEAT);			//get a full report in ctf
    503 	Cvar_Get("bot_grapple", "0", 0);					//enable grapple
    504 	Cvar_Get("bot_rocketjump", "1", 0);					//enable rocket jumping
    505 	Cvar_Get("bot_challenge", "0", 0);					//challenging bot
    506 	Cvar_Get("bot_minplayers", "0", 0);					//minimum players in a team or the game
    507 	Cvar_Get("bot_interbreedchar", "", CVAR_CHEAT);		//bot character used for interbreeding
    508 	Cvar_Get("bot_interbreedbots", "10", CVAR_CHEAT);	//number of bots used for interbreeding
    509 	Cvar_Get("bot_interbreedcycle", "20", CVAR_CHEAT);	//bot interbreeding cycle
    510 	Cvar_Get("bot_interbreedwrite", "", CVAR_CHEAT);	//write interbreeded bots to this file
    511 }
    512 
    513 /*
    514 ==================
    515 SV_BotInitBotLib
    516 ==================
    517 */
    518 void SV_BotInitBotLib(void) {
    519 	botlib_import_t	botlib_import;
    520 
    521 	if ( !Cvar_VariableValue("fs_restrict") && !Sys_CheckCD() ) {
    522 		Com_Error( ERR_NEED_CD, "Game CD not in drive" );
    523 	}
    524 
    525 	if (debugpolygons) Z_Free(debugpolygons);
    526 	bot_maxdebugpolys = Cvar_VariableIntegerValue("bot_maxdebugpolys");
    527 	debugpolygons = Z_Malloc(sizeof(bot_debugpoly_t) * bot_maxdebugpolys);
    528 
    529 	botlib_import.Print = BotImport_Print;
    530 	botlib_import.Trace = BotImport_Trace;
    531 	botlib_import.EntityTrace = BotImport_EntityTrace;
    532 	botlib_import.PointContents = BotImport_PointContents;
    533 	botlib_import.inPVS = BotImport_inPVS;
    534 	botlib_import.BSPEntityData = BotImport_BSPEntityData;
    535 	botlib_import.BSPModelMinsMaxsOrigin = BotImport_BSPModelMinsMaxsOrigin;
    536 	botlib_import.BotClientCommand = BotClientCommand;
    537 
    538 	//memory management
    539 	botlib_import.GetMemory = BotImport_GetMemory;
    540 	botlib_import.FreeMemory = BotImport_FreeMemory;
    541 	botlib_import.AvailableMemory = Z_AvailableMemory;
    542 	botlib_import.HunkAlloc = BotImport_HunkAlloc;
    543 
    544 	// file system access
    545 	botlib_import.FS_FOpenFile = FS_FOpenFileByMode;
    546 	botlib_import.FS_Read = FS_Read2;
    547 	botlib_import.FS_Write = FS_Write;
    548 	botlib_import.FS_FCloseFile = FS_FCloseFile;
    549 	botlib_import.FS_Seek = FS_Seek;
    550 
    551 	//debug lines
    552 	botlib_import.DebugLineCreate = BotImport_DebugLineCreate;
    553 	botlib_import.DebugLineDelete = BotImport_DebugLineDelete;
    554 	botlib_import.DebugLineShow = BotImport_DebugLineShow;
    555 
    556 	//debug polygons
    557 	botlib_import.DebugPolygonCreate = BotImport_DebugPolygonCreate;
    558 	botlib_import.DebugPolygonDelete = BotImport_DebugPolygonDelete;
    559 
    560 	botlib_export = (botlib_export_t *)GetBotLibAPI( BOTLIB_API_VERSION, &botlib_import );
    561 	assert(botlib_export); 	// bk001129 - somehow we end up with a zero import.
    562 }
    563 
    564 
    565 //
    566 //  * * * BOT AI CODE IS BELOW THIS POINT * * *
    567 //
    568 
    569 /*
    570 ==================
    571 SV_BotGetConsoleMessage
    572 ==================
    573 */
    574 int SV_BotGetConsoleMessage( int client, char *buf, int size )
    575 {
    576 	client_t	*cl;
    577 	int			index;
    578 
    579 	cl = &svs.clients[client];
    580 	cl->lastPacketTime = svs.time;
    581 
    582 	if ( cl->reliableAcknowledge == cl->reliableSequence ) {
    583 		return qfalse;
    584 	}
    585 
    586 	cl->reliableAcknowledge++;
    587 	index = cl->reliableAcknowledge & ( MAX_RELIABLE_COMMANDS - 1 );
    588 
    589 	if ( !cl->reliableCommands[index][0] ) {
    590 		return qfalse;
    591 	}
    592 
    593 	Q_strncpyz( buf, cl->reliableCommands[index], size );
    594 	return qtrue;
    595 }
    596 
    597 #if 0
    598 /*
    599 ==================
    600 EntityInPVS
    601 ==================
    602 */
    603 int EntityInPVS( int client, int entityNum ) {
    604 	client_t			*cl;
    605 	clientSnapshot_t	*frame;
    606 	int					i;
    607 
    608 	cl = &svs.clients[client];
    609 	frame = &cl->frames[cl->netchan.outgoingSequence & PACKET_MASK];
    610 	for ( i = 0; i < frame->num_entities; i++ )	{
    611 		if ( svs.snapshotEntities[(frame->first_entity + i) % svs.numSnapshotEntities].number == entityNum ) {
    612 			return qtrue;
    613 		}
    614 	}
    615 	return qfalse;
    616 }
    617 #endif
    618 
    619 /*
    620 ==================
    621 SV_BotGetSnapshotEntity
    622 ==================
    623 */
    624 int SV_BotGetSnapshotEntity( int client, int sequence ) {
    625 	client_t			*cl;
    626 	clientSnapshot_t	*frame;
    627 
    628 	cl = &svs.clients[client];
    629 	frame = &cl->frames[cl->netchan.outgoingSequence & PACKET_MASK];
    630 	if (sequence < 0 || sequence >= frame->num_entities) {
    631 		return -1;
    632 	}
    633 	return svs.snapshotEntities[(frame->first_entity + sequence) % svs.numSnapshotEntities].number;
    634 }
    635