The Return to Castle Wolfenstein Single Player sources as originally released under... master
authorTravis Bradshaw <travis.bradshaw@idsoftware.com>
Tue, 31 Jan 2012 20:51:32 +0000 (14:51 -0600)
committerTravis Bradshaw <travis.bradshaw@idsoftware.com>
Tue, 31 Jan 2012 20:51:32 +0000 (14:51 -0600)
620 files changed:
COPYING.txt [new file with mode: 0644]
README.txt [new file with mode: 0644]
main/ui/menudef.h [new file with mode: 0644]
src/Makefile [new file with mode: 0644]
src/botai/ai_chat.c [new file with mode: 0644]
src/botai/ai_chat.h [new file with mode: 0644]
src/botai/ai_cmd.c [new file with mode: 0644]
src/botai/ai_cmd.h [new file with mode: 0644]
src/botai/ai_dmnet.c [new file with mode: 0644]
src/botai/ai_dmnet.h [new file with mode: 0644]
src/botai/ai_dmq3.c [new file with mode: 0644]
src/botai/ai_dmq3.h [new file with mode: 0644]
src/botai/ai_main.c [new file with mode: 0644]
src/botai/ai_main.h [new file with mode: 0644]
src/botai/ai_team.c [new file with mode: 0644]
src/botai/ai_team.h [new file with mode: 0644]
src/botai/botai.h [new file with mode: 0644]
src/botai/chars.h [new file with mode: 0644]
src/botai/inv.h [new file with mode: 0644]
src/botai/match.h [new file with mode: 0644]
src/botai/syn.h [new file with mode: 0644]
src/botlib/aasfile.h [new file with mode: 0644]
src/botlib/be_aas.h [new file with mode: 0644]
src/botlib/be_aas_bsp.h [new file with mode: 0644]
src/botlib/be_aas_bspq3.c [new file with mode: 0644]
src/botlib/be_aas_cluster.c [new file with mode: 0644]
src/botlib/be_aas_cluster.h [new file with mode: 0644]
src/botlib/be_aas_debug.c [new file with mode: 0644]
src/botlib/be_aas_debug.h [new file with mode: 0644]
src/botlib/be_aas_def.h [new file with mode: 0644]
src/botlib/be_aas_entity.c [new file with mode: 0644]
src/botlib/be_aas_entity.h [new file with mode: 0644]
src/botlib/be_aas_file.c [new file with mode: 0644]
src/botlib/be_aas_file.h [new file with mode: 0644]
src/botlib/be_aas_funcs.h [new file with mode: 0644]
src/botlib/be_aas_main.c [new file with mode: 0644]
src/botlib/be_aas_main.h [new file with mode: 0644]
src/botlib/be_aas_move.c [new file with mode: 0644]
src/botlib/be_aas_move.h [new file with mode: 0644]
src/botlib/be_aas_optimize.c [new file with mode: 0644]
src/botlib/be_aas_optimize.h [new file with mode: 0644]
src/botlib/be_aas_reach.c [new file with mode: 0644]
src/botlib/be_aas_reach.h [new file with mode: 0644]
src/botlib/be_aas_route.c [new file with mode: 0644]
src/botlib/be_aas_route.h [new file with mode: 0644]
src/botlib/be_aas_routealt.c [new file with mode: 0644]
src/botlib/be_aas_routealt.h [new file with mode: 0644]
src/botlib/be_aas_routetable.c [new file with mode: 0644]
src/botlib/be_aas_routetable.h [new file with mode: 0644]
src/botlib/be_aas_sample.c [new file with mode: 0644]
src/botlib/be_aas_sample.h [new file with mode: 0644]
src/botlib/be_ai_char.c [new file with mode: 0644]
src/botlib/be_ai_chat.c [new file with mode: 0644]
src/botlib/be_ai_gen.c [new file with mode: 0644]
src/botlib/be_ai_goal.c [new file with mode: 0644]
src/botlib/be_ai_move.c [new file with mode: 0644]
src/botlib/be_ai_weap.c [new file with mode: 0644]
src/botlib/be_ai_weight.c [new file with mode: 0644]
src/botlib/be_ai_weight.h [new file with mode: 0644]
src/botlib/be_ea.c [new file with mode: 0644]
src/botlib/be_interface.c [new file with mode: 0644]
src/botlib/be_interface.h [new file with mode: 0644]
src/botlib/botlib.h [new file with mode: 0644]
src/botlib/botlib.vcproj [new file with mode: 0644]
src/botlib/l_crc.c [new file with mode: 0644]
src/botlib/l_crc.h [new file with mode: 0644]
src/botlib/l_libvar.c [new file with mode: 0644]
src/botlib/l_libvar.h [new file with mode: 0644]
src/botlib/l_log.c [new file with mode: 0644]
src/botlib/l_log.h [new file with mode: 0644]
src/botlib/l_memory.c [new file with mode: 0644]
src/botlib/l_memory.h [new file with mode: 0644]
src/botlib/l_precomp.c [new file with mode: 0644]
src/botlib/l_precomp.h [new file with mode: 0644]
src/botlib/l_script.c [new file with mode: 0644]
src/botlib/l_script.h [new file with mode: 0644]
src/botlib/l_struct.c [new file with mode: 0644]
src/botlib/l_struct.h [new file with mode: 0644]
src/botlib/l_utils.h [new file with mode: 0644]
src/bspc/_files.c [new file with mode: 0644]
src/bspc/aas_areamerging.c [new file with mode: 0644]
src/bspc/aas_areamerging.h [new file with mode: 0644]
src/bspc/aas_cfg.c [new file with mode: 0644]
src/bspc/aas_cfg.h [new file with mode: 0644]
src/bspc/aas_create.c [new file with mode: 0644]
src/bspc/aas_create.h [new file with mode: 0644]
src/bspc/aas_edgemelting.c [new file with mode: 0644]
src/bspc/aas_edgemelting.h [new file with mode: 0644]
src/bspc/aas_facemerging.c [new file with mode: 0644]
src/bspc/aas_facemerging.h [new file with mode: 0644]
src/bspc/aas_file.c [new file with mode: 0644]
src/bspc/aas_file.h [new file with mode: 0644]
src/bspc/aas_gsubdiv.c [new file with mode: 0644]
src/bspc/aas_gsubdiv.h [new file with mode: 0644]
src/bspc/aas_map.c [new file with mode: 0644]
src/bspc/aas_map.h [new file with mode: 0644]
src/bspc/aas_prunenodes.c [new file with mode: 0644]
src/bspc/aas_prunenodes.h [new file with mode: 0644]
src/bspc/aas_store.c [new file with mode: 0644]
src/bspc/aas_store.h [new file with mode: 0644]
src/bspc/be_aas_bspc.c [new file with mode: 0644]
src/bspc/be_aas_bspc.h [new file with mode: 0644]
src/bspc/brushbsp.c [new file with mode: 0644]
src/bspc/bspc.c [new file with mode: 0644]
src/bspc/csg.c [new file with mode: 0644]
src/bspc/faces.c [new file with mode: 0644]
src/bspc/glfile.c [new file with mode: 0644]
src/bspc/l_bsp_ent.c [new file with mode: 0644]
src/bspc/l_bsp_ent.h [new file with mode: 0644]
src/bspc/l_bsp_hl.c [new file with mode: 0644]
src/bspc/l_bsp_hl.h [new file with mode: 0644]
src/bspc/l_bsp_q1.c [new file with mode: 0644]
src/bspc/l_bsp_q1.h [new file with mode: 0644]
src/bspc/l_bsp_q2.c [new file with mode: 0644]
src/bspc/l_bsp_q2.h [new file with mode: 0644]
src/bspc/l_bsp_q3.c [new file with mode: 0644]
src/bspc/l_bsp_q3.h [new file with mode: 0644]
src/bspc/l_bsp_sin.c [new file with mode: 0644]
src/bspc/l_bsp_sin.h [new file with mode: 0644]
src/bspc/l_cmd.c [new file with mode: 0644]
src/bspc/l_cmd.h [new file with mode: 0644]
src/bspc/l_log.c [new file with mode: 0644]
src/bspc/l_log.h [new file with mode: 0644]
src/bspc/l_math.c [new file with mode: 0644]
src/bspc/l_math.h [new file with mode: 0644]
src/bspc/l_mem.c [new file with mode: 0644]
src/bspc/l_mem.h [new file with mode: 0644]
src/bspc/l_poly.c [new file with mode: 0644]
src/bspc/l_poly.h [new file with mode: 0644]
src/bspc/l_qfiles.c [new file with mode: 0644]
src/bspc/l_qfiles.h [new file with mode: 0644]
src/bspc/l_threads.c [new file with mode: 0644]
src/bspc/l_threads.h [new file with mode: 0644]
src/bspc/l_utils.c [new file with mode: 0644]
src/bspc/l_utils.h [new file with mode: 0644]
src/bspc/leakfile.c [new file with mode: 0644]
src/bspc/map.c [new file with mode: 0644]
src/bspc/map_hl.c [new file with mode: 0644]
src/bspc/map_q1.c [new file with mode: 0644]
src/bspc/map_q2.c [new file with mode: 0644]
src/bspc/map_q3.c [new file with mode: 0644]
src/bspc/map_sin.c [new file with mode: 0644]
src/bspc/nodraw.c [new file with mode: 0644]
src/bspc/portals.c [new file with mode: 0644]
src/bspc/prtfile.c [new file with mode: 0644]
src/bspc/q2files.h [new file with mode: 0644]
src/bspc/q3files.h [new file with mode: 0644]
src/bspc/qbsp.h [new file with mode: 0644]
src/bspc/qfiles.h [new file with mode: 0644]
src/bspc/sinfiles.h [new file with mode: 0644]
src/bspc/textures.c [new file with mode: 0644]
src/bspc/tree.c [new file with mode: 0644]
src/bspc/writebsp.c [new file with mode: 0644]
src/cgame/cg_consolecmds.c [new file with mode: 0644]
src/cgame/cg_draw.c [new file with mode: 0644]
src/cgame/cg_drawtools.c [new file with mode: 0644]
src/cgame/cg_effects.c [new file with mode: 0644]
src/cgame/cg_ents.c [new file with mode: 0644]
src/cgame/cg_event.c [new file with mode: 0644]
src/cgame/cg_flamethrower.c [new file with mode: 0644]
src/cgame/cg_info.c [new file with mode: 0644]
src/cgame/cg_local.h [new file with mode: 0644]
src/cgame/cg_localents.c [new file with mode: 0644]
src/cgame/cg_main.c [new file with mode: 0644]
src/cgame/cg_marks.c [new file with mode: 0644]
src/cgame/cg_newDraw.c [new file with mode: 0644]
src/cgame/cg_particles.c [new file with mode: 0644]
src/cgame/cg_players.c [new file with mode: 0644]
src/cgame/cg_playerstate.c [new file with mode: 0644]
src/cgame/cg_predict.c [new file with mode: 0644]
src/cgame/cg_public.h [new file with mode: 0644]
src/cgame/cg_scoreboard.c [new file with mode: 0644]
src/cgame/cg_servercmds.c [new file with mode: 0644]
src/cgame/cg_snapshot.c [new file with mode: 0644]
src/cgame/cg_sound.c [new file with mode: 0644]
src/cgame/cg_syscalls.c [new file with mode: 0644]
src/cgame/cg_trails.c [new file with mode: 0644]
src/cgame/cg_view.c [new file with mode: 0644]
src/cgame/cg_weapons.c [new file with mode: 0644]
src/cgame/cgame.def [new file with mode: 0644]
src/cgame/cgame.vcproj [new file with mode: 0644]
src/cgame/tr_types.h [new file with mode: 0644]
src/client/cl_cgame.c [new file with mode: 0644]
src/client/cl_cin.c [new file with mode: 0644]
src/client/cl_console.c [new file with mode: 0644]
src/client/cl_input.c [new file with mode: 0644]
src/client/cl_keys.c [new file with mode: 0644]
src/client/cl_main.c [new file with mode: 0644]
src/client/cl_net_chan.c [new file with mode: 0644]
src/client/cl_parse.c [new file with mode: 0644]
src/client/cl_scrn.c [new file with mode: 0644]
src/client/cl_ui.c [new file with mode: 0644]
src/client/client.h [new file with mode: 0644]
src/client/keys.h [new file with mode: 0644]
src/client/snd_adpcm.c [new file with mode: 0644]
src/client/snd_dma.c [new file with mode: 0644]
src/client/snd_local.h [new file with mode: 0644]
src/client/snd_mem.c [new file with mode: 0644]
src/client/snd_mix.c [new file with mode: 0644]
src/client/snd_public.h [new file with mode: 0644]
src/client/snd_wavelet.c [new file with mode: 0644]
src/extractfuncs/ChangeLog [new file with mode: 0644]
src/extractfuncs/Conscript [new file with mode: 0644]
src/extractfuncs/extractfuncs.bat [new file with mode: 0644]
src/extractfuncs/extractfuncs.c [new file with mode: 0644]
src/extractfuncs/extractfuncs.vcproj [new file with mode: 0644]
src/extractfuncs/l_log.c [new file with mode: 0644]
src/extractfuncs/l_log.h [new file with mode: 0644]
src/extractfuncs/l_memory.c [new file with mode: 0644]
src/extractfuncs/l_memory.h [new file with mode: 0644]
src/extractfuncs/l_precomp.c [new file with mode: 0644]
src/extractfuncs/l_precomp.h [new file with mode: 0644]
src/extractfuncs/l_script.c [new file with mode: 0644]
src/extractfuncs/l_script.h [new file with mode: 0644]
src/ft2/ahangles.c [new file with mode: 0644]
src/ft2/ahangles.h [new file with mode: 0644]
src/ft2/ahglobal.c [new file with mode: 0644]
src/ft2/ahglobal.h [new file with mode: 0644]
src/ft2/ahglyph.c [new file with mode: 0644]
src/ft2/ahglyph.h [new file with mode: 0644]
src/ft2/ahhint.c [new file with mode: 0644]
src/ft2/ahhint.h [new file with mode: 0644]
src/ft2/ahloader.h [new file with mode: 0644]
src/ft2/ahmodule.c [new file with mode: 0644]
src/ft2/ahmodule.h [new file with mode: 0644]
src/ft2/ahoptim.c [new file with mode: 0644]
src/ft2/ahoptim.h [new file with mode: 0644]
src/ft2/ahtypes.h [new file with mode: 0644]
src/ft2/autohint.h [new file with mode: 0644]
src/ft2/freetype.h [new file with mode: 0644]
src/ft2/ftcalc.c [new file with mode: 0644]
src/ft2/ftcalc.h [new file with mode: 0644]
src/ft2/ftconfig.h [new file with mode: 0644]
src/ft2/ftdebug.c [new file with mode: 0644]
src/ft2/ftdebug.h [new file with mode: 0644]
src/ft2/ftdriver.h [new file with mode: 0644]
src/ft2/fterrors.h [new file with mode: 0644]
src/ft2/ftextend.c [new file with mode: 0644]
src/ft2/ftextend.h [new file with mode: 0644]
src/ft2/ftglyph.c [new file with mode: 0644]
src/ft2/ftglyph.h [new file with mode: 0644]
src/ft2/ftgrays.c [new file with mode: 0644]
src/ft2/ftgrays.h [new file with mode: 0644]
src/ft2/ftimage.h [new file with mode: 0644]
src/ft2/ftinit.c [new file with mode: 0644]
src/ft2/ftlist.c [new file with mode: 0644]
src/ft2/ftlist.h [new file with mode: 0644]
src/ft2/ftmemory.h [new file with mode: 0644]
src/ft2/ftmm.c [new file with mode: 0644]
src/ft2/ftmm.h [new file with mode: 0644]
src/ft2/ftmodule.h [new file with mode: 0644]
src/ft2/ftnames.c [new file with mode: 0644]
src/ft2/ftnames.h [new file with mode: 0644]
src/ft2/ftobjs.c [new file with mode: 0644]
src/ft2/ftobjs.h [new file with mode: 0644]
src/ft2/ftoption.h [new file with mode: 0644]
src/ft2/ftoutln.c [new file with mode: 0644]
src/ft2/ftoutln.h [new file with mode: 0644]
src/ft2/ftraster.c [new file with mode: 0644]
src/ft2/ftraster.h [new file with mode: 0644]
src/ft2/ftrend1.c [new file with mode: 0644]
src/ft2/ftrend1.h [new file with mode: 0644]
src/ft2/ftrender.h [new file with mode: 0644]
src/ft2/ftsmooth.c [new file with mode: 0644]
src/ft2/ftsmooth.h [new file with mode: 0644]
src/ft2/ftstream.c [new file with mode: 0644]
src/ft2/ftstream.h [new file with mode: 0644]
src/ft2/ftsystem.c [new file with mode: 0644]
src/ft2/ftsystem.h [new file with mode: 0644]
src/ft2/fttypes.h [new file with mode: 0644]
src/ft2/psnames.h [new file with mode: 0644]
src/ft2/sfdriver.c [new file with mode: 0644]
src/ft2/sfdriver.h [new file with mode: 0644]
src/ft2/sfnt.h [new file with mode: 0644]
src/ft2/sfobjs.c [new file with mode: 0644]
src/ft2/sfobjs.h [new file with mode: 0644]
src/ft2/t1tables.h [new file with mode: 0644]
src/ft2/ttcmap.c [new file with mode: 0644]
src/ft2/ttcmap.h [new file with mode: 0644]
src/ft2/ttdriver.c [new file with mode: 0644]
src/ft2/ttdriver.h [new file with mode: 0644]
src/ft2/tterrors.h [new file with mode: 0644]
src/ft2/ttgload.c [new file with mode: 0644]
src/ft2/ttgload.h [new file with mode: 0644]
src/ft2/ttinterp.c [new file with mode: 0644]
src/ft2/ttinterp.h [new file with mode: 0644]
src/ft2/ttload.c [new file with mode: 0644]
src/ft2/ttload.h [new file with mode: 0644]
src/ft2/ttnameid.h [new file with mode: 0644]
src/ft2/ttobjs.c [new file with mode: 0644]
src/ft2/ttobjs.h [new file with mode: 0644]
src/ft2/ttpload.c [new file with mode: 0644]
src/ft2/ttpload.h [new file with mode: 0644]
src/ft2/ttpost.c [new file with mode: 0644]
src/ft2/ttpost.h [new file with mode: 0644]
src/ft2/ttsbit.c [new file with mode: 0644]
src/ft2/ttsbit.h [new file with mode: 0644]
src/ft2/tttables.h [new file with mode: 0644]
src/ft2/tttags.h [new file with mode: 0644]
src/ft2/tttypes.h [new file with mode: 0644]
src/game/ai_cast.c [new file with mode: 0644]
src/game/ai_cast.h [new file with mode: 0644]
src/game/ai_cast_characters.c [new file with mode: 0644]
src/game/ai_cast_debug.c [new file with mode: 0644]
src/game/ai_cast_events.c [new file with mode: 0644]
src/game/ai_cast_fight.c [new file with mode: 0644]
src/game/ai_cast_fight.h [new file with mode: 0644]
src/game/ai_cast_func_attack.c [new file with mode: 0644]
src/game/ai_cast_func_boss1.c [new file with mode: 0644]
src/game/ai_cast_funcs.c [new file with mode: 0644]
src/game/ai_cast_global.h [new file with mode: 0644]
src/game/ai_cast_script.c [new file with mode: 0644]
src/game/ai_cast_script_actions.c [new file with mode: 0644]
src/game/ai_cast_script_ents.c [new file with mode: 0644]
src/game/ai_cast_sight.c [new file with mode: 0644]
src/game/ai_cast_think.c [new file with mode: 0644]
src/game/be_aas.h [new file with mode: 0644]
src/game/be_ai_char.h [new file with mode: 0644]
src/game/be_ai_chat.h [new file with mode: 0644]
src/game/be_ai_gen.h [new file with mode: 0644]
src/game/be_ai_goal.h [new file with mode: 0644]
src/game/be_ai_move.h [new file with mode: 0644]
src/game/be_ai_weap.h [new file with mode: 0644]
src/game/be_ea.h [new file with mode: 0644]
src/game/bg_animation.c [new file with mode: 0644]
src/game/bg_lib.c [new file with mode: 0644]
src/game/bg_local.h [new file with mode: 0644]
src/game/bg_misc.c [new file with mode: 0644]
src/game/bg_pmove.c [new file with mode: 0644]
src/game/bg_public.h [new file with mode: 0644]
src/game/bg_slidemove.c [new file with mode: 0644]
src/game/botlib.h [new file with mode: 0644]
src/game/g_active.c [new file with mode: 0644]
src/game/g_alarm.c [new file with mode: 0644]
src/game/g_bot.c [new file with mode: 0644]
src/game/g_client.c [new file with mode: 0644]
src/game/g_cmds.c [new file with mode: 0644]
src/game/g_combat.c [new file with mode: 0644]
src/game/g_func_decs.h [new file with mode: 0644]
src/game/g_funcs.h [new file with mode: 0644]
src/game/g_items.c [new file with mode: 0644]
src/game/g_local.h [new file with mode: 0644]
src/game/g_main.c [new file with mode: 0644]
src/game/g_mem.c [new file with mode: 0644]
src/game/g_misc.c [new file with mode: 0644]
src/game/g_missile.c [new file with mode: 0644]
src/game/g_mover.c [new file with mode: 0644]
src/game/g_props.c [new file with mode: 0644]
src/game/g_public.h [new file with mode: 0644]
src/game/g_save.c [new file with mode: 0644]
src/game/g_script.c [new file with mode: 0644]
src/game/g_script_actions.c [new file with mode: 0644]
src/game/g_session.c [new file with mode: 0644]
src/game/g_spawn.c [new file with mode: 0644]
src/game/g_svcmds.c [new file with mode: 0644]
src/game/g_syscalls.c [new file with mode: 0644]
src/game/g_target.c [new file with mode: 0644]
src/game/g_team.c [new file with mode: 0644]
src/game/g_team.h [new file with mode: 0644]
src/game/g_tramcar.c [new file with mode: 0644]
src/game/g_trigger.c [new file with mode: 0644]
src/game/g_utils.c [new file with mode: 0644]
src/game/g_weapon.c [new file with mode: 0644]
src/game/game.def [new file with mode: 0644]
src/game/game.vcproj [new file with mode: 0644]
src/game/q_math.c [new file with mode: 0644]
src/game/q_shared.c [new file with mode: 0644]
src/game/q_shared.h [new file with mode: 0644]
src/game/surfaceflags.h [new file with mode: 0644]
src/idLib/idAudio.h [new file with mode: 0644]
src/idLib/idAudioHardware.h [new file with mode: 0644]
src/idLib/idLib.h [new file with mode: 0644]
src/idLib/idQ3Asnd.cpp [new file with mode: 0644]
src/idLib/idSound.cpp [new file with mode: 0644]
src/idLib/idSound.h [new file with mode: 0644]
src/idLib/idSpeaker.cpp [new file with mode: 0644]
src/idLib/idSpeaker.h [new file with mode: 0644]
src/idLib/sys/win32/eax.h [new file with mode: 0644]
src/idLib/sys/win32/eaxguid.lib [new file with mode: 0644]
src/idLib/sys/win32/win_snd.cpp [new file with mode: 0644]
src/jpeg-6/jcapimin.c [new file with mode: 0644]
src/jpeg-6/jccoefct.c [new file with mode: 0644]
src/jpeg-6/jccolor.c [new file with mode: 0644]
src/jpeg-6/jcdctmgr.c [new file with mode: 0644]
src/jpeg-6/jchuff.c [new file with mode: 0644]
src/jpeg-6/jchuff.h [new file with mode: 0644]
src/jpeg-6/jcinit.c [new file with mode: 0644]
src/jpeg-6/jcmainct.c [new file with mode: 0644]
src/jpeg-6/jcmarker.c [new file with mode: 0644]
src/jpeg-6/jcmaster.c [new file with mode: 0644]
src/jpeg-6/jcomapi.c [new file with mode: 0644]
src/jpeg-6/jconfig.h [new file with mode: 0644]
src/jpeg-6/jcparam.c [new file with mode: 0644]
src/jpeg-6/jcphuff.c [new file with mode: 0644]
src/jpeg-6/jcprepct.c [new file with mode: 0644]
src/jpeg-6/jcsample.c [new file with mode: 0644]
src/jpeg-6/jctrans.c [new file with mode: 0644]
src/jpeg-6/jdapimin.c [new file with mode: 0644]
src/jpeg-6/jdapistd.c [new file with mode: 0644]
src/jpeg-6/jdatadst.c [new file with mode: 0644]
src/jpeg-6/jdatasrc.c [new file with mode: 0644]
src/jpeg-6/jdcoefct.c [new file with mode: 0644]
src/jpeg-6/jdcolor.c [new file with mode: 0644]
src/jpeg-6/jdct.h [new file with mode: 0644]
src/jpeg-6/jddctmgr.c [new file with mode: 0644]
src/jpeg-6/jdhuff.c [new file with mode: 0644]
src/jpeg-6/jdhuff.h [new file with mode: 0644]
src/jpeg-6/jdinput.c [new file with mode: 0644]
src/jpeg-6/jdmainct.c [new file with mode: 0644]
src/jpeg-6/jdmarker.c [new file with mode: 0644]
src/jpeg-6/jdmaster.c [new file with mode: 0644]
src/jpeg-6/jdpostct.c [new file with mode: 0644]
src/jpeg-6/jdsample.c [new file with mode: 0644]
src/jpeg-6/jdtrans.c [new file with mode: 0644]
src/jpeg-6/jerror.c [new file with mode: 0644]
src/jpeg-6/jerror.h [new file with mode: 0644]
src/jpeg-6/jfdctflt.c [new file with mode: 0644]
src/jpeg-6/jidctflt.c [new file with mode: 0644]
src/jpeg-6/jinclude.h [new file with mode: 0644]
src/jpeg-6/jmemmgr.c [new file with mode: 0644]
src/jpeg-6/jmemnobs.c [new file with mode: 0644]
src/jpeg-6/jmemsys.h [new file with mode: 0644]
src/jpeg-6/jmorecfg.h [new file with mode: 0644]
src/jpeg-6/jpegint.h [new file with mode: 0644]
src/jpeg-6/jpeglib.h [new file with mode: 0644]
src/jpeg-6/jutils.c [new file with mode: 0644]
src/jpeg-6/jversion.h [new file with mode: 0644]
src/macosx/BuildRelease [new file with mode: 0644]
src/macosx/CGMouseDeltaFix.h [new file with mode: 0644]
src/macosx/CGMouseDeltaFix.m [new file with mode: 0644]
src/macosx/CGPrivateAPI.h [new file with mode: 0644]
src/macosx/GenerateQGL.pl [new file with mode: 0644]
src/macosx/Performance.rtf [new file with mode: 0644]
src/macosx/Q3Controller.h [new file with mode: 0644]
src/macosx/Q3Controller.m [new file with mode: 0644]
src/macosx/Quake3.icns [new file with mode: 0644]
src/macosx/Quake3.nib/classes.nib [new file with mode: 0644]
src/macosx/Quake3.nib/info.nib [new file with mode: 0644]
src/macosx/Quake3.nib/objects.nib [new file with mode: 0644]
src/macosx/RecordDemo.zsh [new file with mode: 0644]
src/macosx/WolfSP.pbproj/bungi.pbxuser [new file with mode: 0644]
src/macosx/WolfSP.pbproj/duane.pbxuser [new file with mode: 0644]
src/macosx/WolfSP.pbproj/johnson.pbxuser [new file with mode: 0644]
src/macosx/WolfSP.pbproj/project.pbxproj [new file with mode: 0644]
src/macosx/WolfSP.pbproj/zaphod.pbxuser [new file with mode: 0644]
src/macosx/banner.jpg [new file with mode: 0644]
src/macosx/macosx_display.h [new file with mode: 0644]
src/macosx/macosx_display.m [new file with mode: 0644]
src/macosx/macosx_glimp.h [new file with mode: 0644]
src/macosx/macosx_glimp.m [new file with mode: 0644]
src/macosx/macosx_glsmp_mutex.m [new file with mode: 0644]
src/macosx/macosx_glsmp_null.m [new file with mode: 0644]
src/macosx/macosx_glsmp_ports.m [new file with mode: 0644]
src/macosx/macosx_input.m [new file with mode: 0644]
src/macosx/macosx_local.h [new file with mode: 0644]
src/macosx/macosx_qgl.h [new file with mode: 0644]
src/macosx/macosx_sndcore.m [new file with mode: 0644]
src/macosx/macosx_snddma.m [new file with mode: 0644]
src/macosx/macosx_sys.m [new file with mode: 0644]
src/macosx/macosx_timers.h [new file with mode: 0644]
src/macosx/macosx_timers.m [new file with mode: 0644]
src/macosx/timedemo.zsh [new file with mode: 0644]
src/qcommon/cm_load.c [new file with mode: 0644]
src/qcommon/cm_local.h [new file with mode: 0644]
src/qcommon/cm_patch.c [new file with mode: 0644]
src/qcommon/cm_patch.h [new file with mode: 0644]
src/qcommon/cm_polylib.c [new file with mode: 0644]
src/qcommon/cm_polylib.h [new file with mode: 0644]
src/qcommon/cm_public.h [new file with mode: 0644]
src/qcommon/cm_test.c [new file with mode: 0644]
src/qcommon/cm_trace.c [new file with mode: 0644]
src/qcommon/cmd.c [new file with mode: 0644]
src/qcommon/common.c [new file with mode: 0644]
src/qcommon/cvar.c [new file with mode: 0644]
src/qcommon/files.c [new file with mode: 0644]
src/qcommon/huffman.c [new file with mode: 0644]
src/qcommon/md4.c [new file with mode: 0644]
src/qcommon/msg.c [new file with mode: 0644]
src/qcommon/net_chan.c [new file with mode: 0644]
src/qcommon/qcommon.h [new file with mode: 0644]
src/qcommon/qfiles.h [new file with mode: 0644]
src/qcommon/unzip.c [new file with mode: 0644]
src/qcommon/unzip.h [new file with mode: 0644]
src/qcommon/vm.c [new file with mode: 0644]
src/qcommon/vm_interpreted.c [new file with mode: 0644]
src/qcommon/vm_local.h [new file with mode: 0644]
src/qcommon/vm_x86.c [new file with mode: 0644]
src/renderer/anorms256.h [new file with mode: 0644]
src/renderer/qgl.h [new file with mode: 0644]
src/renderer/qgl_linked.h [new file with mode: 0644]
src/renderer/ref_trin.def [new file with mode: 0644]
src/renderer/renderer.vcproj [new file with mode: 0644]
src/renderer/tr_animation.c [new file with mode: 0644]
src/renderer/tr_backend.c [new file with mode: 0644]
src/renderer/tr_bsp.c [new file with mode: 0644]
src/renderer/tr_cmds.c [new file with mode: 0644]
src/renderer/tr_cmesh.c [new file with mode: 0644]
src/renderer/tr_curve.c [new file with mode: 0644]
src/renderer/tr_flares.c [new file with mode: 0644]
src/renderer/tr_font.c [new file with mode: 0644]
src/renderer/tr_image.c [new file with mode: 0644]
src/renderer/tr_init.c [new file with mode: 0644]
src/renderer/tr_light.c [new file with mode: 0644]
src/renderer/tr_local.h [new file with mode: 0644]
src/renderer/tr_main.c [new file with mode: 0644]
src/renderer/tr_marks.c [new file with mode: 0644]
src/renderer/tr_mesh.c [new file with mode: 0644]
src/renderer/tr_model.c [new file with mode: 0644]
src/renderer/tr_noise.c [new file with mode: 0644]
src/renderer/tr_public.h [new file with mode: 0644]
src/renderer/tr_scene.c [new file with mode: 0644]
src/renderer/tr_shade.c [new file with mode: 0644]
src/renderer/tr_shade_calc.c [new file with mode: 0644]
src/renderer/tr_shader.c [new file with mode: 0644]
src/renderer/tr_shadows.c [new file with mode: 0644]
src/renderer/tr_sky.c [new file with mode: 0644]
src/renderer/tr_surface.c [new file with mode: 0644]
src/renderer/tr_world.c [new file with mode: 0644]
src/server/server.h [new file with mode: 0644]
src/server/sv_bot.c [new file with mode: 0644]
src/server/sv_ccmds.c [new file with mode: 0644]
src/server/sv_client.c [new file with mode: 0644]
src/server/sv_game.c [new file with mode: 0644]
src/server/sv_init.c [new file with mode: 0644]
src/server/sv_main.c [new file with mode: 0644]
src/server/sv_net_chan.c [new file with mode: 0644]
src/server/sv_snapshot.c [new file with mode: 0644]
src/server/sv_world.c [new file with mode: 0644]
src/splines/Splines.vcproj [new file with mode: 0644]
src/splines/math_angles.cpp [new file with mode: 0644]
src/splines/math_angles.h [new file with mode: 0644]
src/splines/math_matrix.cpp [new file with mode: 0644]
src/splines/math_matrix.h [new file with mode: 0644]
src/splines/math_quaternion.cpp [new file with mode: 0644]
src/splines/math_quaternion.h [new file with mode: 0644]
src/splines/math_vector.cpp [new file with mode: 0644]
src/splines/math_vector.h [new file with mode: 0644]
src/splines/q_parse.cpp [new file with mode: 0644]
src/splines/q_shared.cpp [new file with mode: 0644]
src/splines/q_splineshared.h [new file with mode: 0644]
src/splines/splines.cpp [new file with mode: 0644]
src/splines/splines.h [new file with mode: 0644]
src/splines/util_list.h [new file with mode: 0644]
src/splines/util_str.cpp [new file with mode: 0644]
src/splines/util_str.h [new file with mode: 0644]
src/ui/keycodes.h [new file with mode: 0644]
src/ui/ui.def [new file with mode: 0644]
src/ui/ui.vcproj [new file with mode: 0644]
src/ui/ui_atoms.c [new file with mode: 0644]
src/ui/ui_gameinfo.c [new file with mode: 0644]
src/ui/ui_local.h [new file with mode: 0644]
src/ui/ui_main.c [new file with mode: 0644]
src/ui/ui_players.c [new file with mode: 0644]
src/ui/ui_public.h [new file with mode: 0644]
src/ui/ui_shared.c [new file with mode: 0644]
src/ui/ui_shared.h [new file with mode: 0644]
src/ui/ui_syscalls.c [new file with mode: 0644]
src/ui/ui_util.c [new file with mode: 0644]
src/unix/ChangeLog [new file with mode: 0644]
src/unix/Conscript-bspc [new file with mode: 0644]
src/unix/Conscript-cgame [new file with mode: 0644]
src/unix/Conscript-client [new file with mode: 0644]
src/unix/Conscript-game [new file with mode: 0644]
src/unix/Conscript-setup [new file with mode: 0644]
src/unix/Conscript-ui [new file with mode: 0644]
src/unix/Construct [new file with mode: 0644]
src/unix/README.EULA [new file with mode: 0644]
src/unix/README.Linux [new file with mode: 0644]
src/unix/README.Q3Test [new file with mode: 0644]
src/unix/bspc.vpj [new file with mode: 0644]
src/unix/build_setup.sh [new file with mode: 0644]
src/unix/build_tarball.sh [new file with mode: 0644]
src/unix/cgame.vpj [new file with mode: 0644]
src/unix/client.vpj [new file with mode: 0644]
src/unix/cons [new file with mode: 0644]
src/unix/extractfuncs.vpj [new file with mode: 0644]
src/unix/ftol.nasm [new file with mode: 0644]
src/unix/game.vpj [new file with mode: 0644]
src/unix/linux_common.c [new file with mode: 0644]
src/unix/linux_glimp.c [new file with mode: 0644]
src/unix/linux_joystick.c [new file with mode: 0644]
src/unix/linux_local.h [new file with mode: 0644]
src/unix/linux_qgl.c [new file with mode: 0644]
src/unix/linux_snd.c [new file with mode: 0644]
src/unix/matha.s [new file with mode: 0644]
src/unix/pcons-2.3.1 [new file with mode: 0644]
src/unix/qasm.h [new file with mode: 0644]
src/unix/quake3.xpm [new file with mode: 0644]
src/unix/snapvector.nasm [new file with mode: 0644]
src/unix/snd_mixa.s [new file with mode: 0644]
src/unix/sys_dosa.s [new file with mode: 0644]
src/unix/ui.vpj [new file with mode: 0644]
src/unix/unix_glw.h [new file with mode: 0644]
src/unix/unix_main.c [new file with mode: 0644]
src/unix/unix_net.c [new file with mode: 0644]
src/unix/unix_shared.c [new file with mode: 0644]
src/unix/vm_x86.c [new file with mode: 0644]
src/unix/vm_x86a.s [new file with mode: 0644]
src/unix/wolf.vpw [new file with mode: 0644]
src/win32/background.bmp [new file with mode: 0644]
src/win32/clear.bmp [new file with mode: 0644]
src/win32/glw_win.h [new file with mode: 0644]
src/win32/qe3.ico [new file with mode: 0644]
src/win32/resource.h [new file with mode: 0644]
src/win32/save/win_snd.c [new file with mode: 0644]
src/win32/win_gamma.c [new file with mode: 0644]
src/win32/win_glimp.c [new file with mode: 0644]
src/win32/win_input.c [new file with mode: 0644]
src/win32/win_local.h [new file with mode: 0644]
src/win32/win_main.c [new file with mode: 0644]
src/win32/win_net.c [new file with mode: 0644]
src/win32/win_qgl.c [new file with mode: 0644]
src/win32/win_shared.c [new file with mode: 0644]
src/win32/win_snd.c [new file with mode: 0644]
src/win32/win_syscon.c [new file with mode: 0644]
src/win32/win_wndproc.c [new file with mode: 0644]
src/win32/winquake.rc [new file with mode: 0644]
src/wolf.sln [new file with mode: 0644]
src/wolf.vcproj [new file with mode: 0644]
uncrustify.cfg [new file with mode: 0644]

diff --git a/COPYING.txt b/COPYING.txt
new file mode 100644 (file)
index 0000000..2449be1
--- /dev/null
@@ -0,0 +1,643 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+
+
+ADDITIONAL TERMS APPLICABLE TO THE RETURN TO CASTLE WOLFENSTEIN SINGLE PLAYER GPL SOURCE CODE.  
+
+       The following additional terms (\93Additional Terms\94) supplement and modify the GNU General Public License, Version 3 (\93GPL\94) applicable to the Return to Castle Wolfenstein single player GPL Source Code (\93RTCW SP Source Code\94).  In addition to the terms and conditions of the GPL, the RTCW SP Source Code is subject to the further restrictions below.
+
+1. Replacement of Section 15.  Section 15 of the GPL shall be deleted in its entirety and replaced with the following:
+
+\9315. Disclaimer of Warranty.  
+
+THE PROGRAM IS PROVIDED WITHOUT ANY WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, TITLE AND MERCHANTABILITY.  THE PROGRAM IS BEING DELIVERED OR MADE AVAILABLE \93AS IS\94\93WITH ALL FAULTS\94 AND WITHOUT WARRANTY OR REPRESENTATION.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\94
+
+2. Replacement of Section 16.  Section 16 of the GPL shall be deleted in its entirety and replaced with the following:
+
+\9316.   LIMITATION OF LIABILITY.  
+
+UNDER NO CIRCUMSTANCES SHALL ANY COPYRIGHT HOLDER OR ITS AFFILIATES, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, FOR ANY DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, DIRECT, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL OR PUNITIVE DAMAGES ARISING FROM, OUT OF OR IN CONNECTION WITH THE USE OR INABILITY TO USE THE PROGRAM OR OTHER DEALINGS WITH THE PROGRAM(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), WHETHER OR NOT ANY COPYRIGHT HOLDER OR SUCH OTHER PARTY RECEIVES NOTICE OF ANY SUCH DAMAGES AND WHETHER OR NOT SUCH DAMAGES COULD HAVE BEEN FORESEEN.\94
+
+3. LEGAL NOTICES; NO TRADEMARK LICENSE; ORIGIN.  You must reproduce faithfully all trademark, copyright and other proprietary and legal notices on any copies of the Program or any other required author attributions.  This license does not grant you rights to use any copyright holder or any other party\92s name, logo, or trademarks.  Neither the name of the copyright holder or its affiliates, or any other party who modifies and/or conveys the Program may be used to endorse or promote products derived from this software without specific prior written permission.  The origin of the Program must not be misrepresented; you must not claim that you wrote the original Program.  Altered source versions must be plainly marked as such, and must not be misrepresented as being the original Program.
+
+4. INDEMNIFICATION.  IF YOU CONVEY A COVERED WORK AND AGREE WITH ANY RECIPIENT OF THAT COVERED WORK THAT YOU WILL ASSUME ANY LIABILITY FOR THAT COVERED WORK, YOU HEREBY AGREE TO INDEMNIFY, DEFEND AND HOLD HARMLESS THE OTHER LICENSORS AND AUTHORS OF THAT COVERED WORK FOR ANY DAMAEGS, DEMANDS, CLAIMS, LOSSES, CAUSES OF ACTION, LAWSUITS, JUDGMENTS EXPENSES (INCLUDING WITHOUT LIMITATION REASONABLE ATTORNEYS' FEES AND EXPENSES) OR ANY OTHER LIABLITY ARISING FROM, RELATED TO OR IN CONNECTION WITH YOUR ASSUMPTIONS OF LIABILITY.
diff --git a/README.txt b/README.txt
new file mode 100644 (file)
index 0000000..9d27f39
--- /dev/null
@@ -0,0 +1,133 @@
+Return to Castle Wolfenstein single player GPL source release
+=============================================================
+
+This file contains the following sections:
+
+GENERAL NOTES
+LICENSE
+
+GENERAL NOTES
+=============
+
+Game data and patching:
+-----------------------
+
+This source release does not contain any game data, the game data is still
+covered by the original EULA and must be obeyed as usual.
+
+You must patch the game to the latest version.
+
+Note that RTCW is available from the Steam store at
+http://store.steampowered.com/app/9010/
+
+Linux note: due to the game CD containing only a Windows version of the game,
+you must install and update the game using WINE to get the game data.
+
+Compiling on win32:
+-------------------
+
+A Visual C++ 2008 project is provided in src\wolf.sln.
+The solution file is compatible with the Express release of Visual C++.
+
+You will need to execute src\extractfuncs\extractfuncs.bat to generate src\game\g_save.c
+
+You can test your binaries by replacing WolfSP.exe, qagamex86.dll, cgamex86.dll, uix86.dll at the top of the RTCW install
+
+Compiling on GNU/Linux x86:
+---------------------------
+
+Go to the src/unix directory, and run the cons script
+(cons is a perl based precursor to scons, this is what we were using at the time)
+
+Run ./cons -h to review build options. Use ./cons -- release to compile in release mode.
+
+If problems occur, consult the internet.
+
+Other platforms, updated source code, security issues:
+------------------------------------------------------
+
+If you have obtained this source code several weeks after the time of release
+(August 2010), it is likely that you can find modified and improved
+versions of the engine in various open source projects across the internet.
+Depending what is your interest with the source code, those may be a better
+starting point.
+
+
+LICENSE
+=======
+
+See COPYING.txt for the GNU GENERAL PUBLIC LICENSE
+
+ADDITIONAL TERMS:  The Return to Castle Wolfenstein single player GPL 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 GPL which accompanied the RTCW SP Source Code.  If not, please request a copy in writing from id Software at id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
+
+EXCLUDED CODE:  The code described below and contained in the Return to Castle Wolfenstein single player GPL Source Code release is not part of the Program covered by the GPL and is expressly excluded from its terms.  You are solely responsible for obtaining from the copyright holder a license for such code and complying with the applicable license terms.
+
+IO on .zip files using portions of zlib
+---------------------------------------------------------------------------
+lines  file(s)
+4301   src/qcommon/unzip.c
+Copyright (C) 1998 Gilles Vollant
+zlib is Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+MD4 Message-Digest Algorithm
+-----------------------------------------------------------------------------
+lines   file(s)
+289     src/qcommon/md4.c
+Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved.
+
+License to copy and use this software is granted provided that it is identified
+as the <93>RSA Data Security, Inc. MD4 Message-Digest Algorithm<94> in all mater
+ial mentioning or referencing this software or this function.
+License is also granted to make and use derivative works provided that such work
+s are identified as <93>derived from the RSA Data Security, Inc. MD4 Message-Dig
+est Algorithm<94> in all material mentioning or referencing the derived work.
+RSA Data Security, Inc. makes no representations concerning either the merchanta
+bility of this software or the suitability of this software for any particular p
+urpose. It is provided <93>as is<94> without express or implied warranty of any
+kind.
+
+JPEG library
+-----------------------------------------------------------------------------
+src/jpeg-6
+Copyright (C) 1991-1995, Thomas G. Lane
+
+Permission is hereby granted to use, copy, modify, and distribute this
+software (or portions thereof) for any purpose, without fee, subject to these
+conditions:
+(1) If any part of the source code for this software is distributed, then this
+README file must be included, with this copyright and no-warranty notice
+unaltered; and any additions, deletions, or changes to the original files
+must be clearly indicated in accompanying documentation.
+(2) If only executable code is distributed, then the accompanying
+documentation must state that "this software is based in part on the work of
+the Independent JPEG Group".
+(3) Permission for use of this software is granted only if the user accepts
+full responsibility for any undesirable consequences; the authors accept
+NO LIABILITY for damages of any kind.
+
+These conditions apply to any software derived from or based on the IJG code,
+not just to the unmodified library.  If you use our work, you ought to
+acknowledge us.
+
+NOTE: unfortunately the README that came with our copy of the library has
+been lost, so the one from release 6b is included instead. There are a few
+'glue type' modifications to the library to make it easier to use from
+the engine, but otherwise the dependency can be easily cleaned up to a
+better release of the library.
+
diff --git a/main/ui/menudef.h b/main/ui/menudef.h
new file mode 100644 (file)
index 0000000..aab0320
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+===========================================================================
+
+Return to Castle Wolfenstein single player GPL Source Code
+Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. 
+
+This file is part of the Return to Castle Wolfenstein single player GPL Source Code (\93RTCW SP Source Code\94).  
+
+RTCW SP Source Code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+RTCW SP Source Code is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
+
+In addition, the RTCW SP 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 SP Source Code.  If not, please request a copy in writing from id Software at the address below.
+
+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.
+
+===========================================================================
+*/
+
+
+#define ITEM_TYPE_TEXT              0       // simple text
+#define ITEM_TYPE_BUTTON            1       // button, basically text with a border
+#define ITEM_TYPE_RADIOBUTTON       2       // toggle button, may be grouped
+#define ITEM_TYPE_CHECKBOX          3       // check box
+#define ITEM_TYPE_EDITFIELD         4       // editable text, associated with a cvar
+#define ITEM_TYPE_COMBO             5       // drop down list
+#define ITEM_TYPE_LISTBOX           6       // scrollable list
+#define ITEM_TYPE_MODEL             7       // model
+#define ITEM_TYPE_OWNERDRAW         8       // owner draw, name specs what it is
+#define ITEM_TYPE_NUMERICFIELD      9       // editable text, associated with a cvar
+#define ITEM_TYPE_SLIDER            10      // mouse speed, volume, etc.
+#define ITEM_TYPE_YESNO             11      // yes no cvar setting
+#define ITEM_TYPE_MULTI             12      // multiple list setting, enumerated
+#define ITEM_TYPE_BIND              13      // multiple list setting, enumerated
+#define ITEM_TYPE_MENUMODEL         14      // special menu model
+#define ITEM_TYPE_VALIDFILEFIELD    15      // text must be valid for use in a dos filename
+
+#define ITEM_ALIGN_LEFT             0       // left alignment
+#define ITEM_ALIGN_CENTER           1       // center alignment
+#define ITEM_ALIGN_RIGHT            2       // right alignment
+
+#define ITEM_TEXTSTYLE_NORMAL           0   // normal text
+#define ITEM_TEXTSTYLE_BLINK            1   // fast blinking
+#define ITEM_TEXTSTYLE_PULSE            2   // slow pulsing
+#define ITEM_TEXTSTYLE_SHADOWED         3   // drop shadow ( need a color for this )
+#define ITEM_TEXTSTYLE_OUTLINED         4   // drop shadow ( need a color for this )
+#define ITEM_TEXTSTYLE_OUTLINESHADOWED  5   // drop shadow ( need a color for this )
+#define ITEM_TEXTSTYLE_SHADOWEDMORE     6   // drop shadow ( need a color for this )
+
+#define WINDOW_BORDER_NONE          0       // no border
+#define WINDOW_BORDER_FULL          1       // full border based on border color ( single pixel )
+#define WINDOW_BORDER_HORZ          2       // horizontal borders only
+#define WINDOW_BORDER_VERT          3       // vertical borders only
+#define WINDOW_BORDER_KCGRADIENT    4       // horizontal border using the gradient bars
+
+#define WINDOW_STYLE_EMPTY          0       // no background
+#define WINDOW_STYLE_FILLED         1       // filled with background color
+#define WINDOW_STYLE_GRADIENT       2       // gradient bar based on background color
+#define WINDOW_STYLE_SHADER         3       // gradient bar based on background color
+#define WINDOW_STYLE_TEAMCOLOR      4       // team color
+#define WINDOW_STYLE_CINEMATIC      5       // cinematic
+
+#define MENU_TRUE           1       // uh.. true
+#define MENU_FALSE          0       // and false
+
+#define HUD_VERTICAL        0x00
+#define HUD_HORIZONTAL      0x01
+
+#define RANGETYPE_ABSOLUTE  0
+#define RANGETYPE_RELATIVE  1
+
+// list box element types
+#define LISTBOX_TEXT        0x00
+#define LISTBOX_IMAGE       0x01
+
+// list feeders
+#define FEEDER_HEADS                0x00    // model heads
+#define FEEDER_MAPS                 0x01    // text maps based on game type
+#define FEEDER_SERVERS              0x02    // servers
+#define FEEDER_CLANS                0x03    // clan names
+#define FEEDER_ALLMAPS              0x04    // all maps available, in graphic format
+#define FEEDER_REDTEAM_LIST         0x05    // red team members
+#define FEEDER_BLUETEAM_LIST        0x06    // blue team members
+#define FEEDER_PLAYER_LIST          0x07    // players
+#define FEEDER_TEAM_LIST            0x08    // team members for team voting
+#define FEEDER_MODS                 0x09    // team members for team voting
+#define FEEDER_DEMOS                0x0a    // team members for team voting
+#define FEEDER_SCOREBOARD           0x0b    // team members for team voting
+#define FEEDER_Q3HEADS              0x0c    // model heads
+#define FEEDER_SERVERSTATUS         0x0d    // server status
+#define FEEDER_FINDPLAYER           0x0e    // find player
+#define FEEDER_CINEMATICS           0x0f    // cinematics
+#define FEEDER_SAVEGAMES            0x10    // savegames
+#define FEEDER_PICKSPAWN            0x11    // NERVE - SMF - wolf mp pick spawn point
+
+// display flags
+#define CG_SHOW_BLUE_TEAM_HAS_REDFLAG       0x00000001
+#define CG_SHOW_RED_TEAM_HAS_BLUEFLAG       0x00000002
+#define CG_SHOW_ANYTEAMGAME                 0x00000004
+#define CG_SHOW_HARVESTER                   0x00000008
+#define CG_SHOW_ONEFLAG                     0x00000010
+#define CG_SHOW_CTF                         0x00000020
+#define CG_SHOW_OBELISK                     0x00000040
+#define CG_SHOW_HEALTHCRITICAL              0x00000080
+#define CG_SHOW_SINGLEPLAYER                0x00000100
+#define CG_SHOW_TOURNAMENT                  0x00000200
+#define CG_SHOW_DURINGINCOMINGVOICE         0x00000400
+#define CG_SHOW_IF_PLAYER_HAS_FLAG          0x00000800
+#define CG_SHOW_LANPLAYONLY                 0x00001000
+#define CG_SHOW_MINED                       0x00002000
+#define CG_SHOW_HEALTHOK                    0x00004000
+#define CG_SHOW_TEAMINFO                    0x00008000
+#define CG_SHOW_NOTEAMINFO                  0x00010000
+#define CG_SHOW_OTHERTEAMHASFLAG            0x00020000
+#define CG_SHOW_YOURTEAMHASENEMYFLAG        0x00040000
+#define CG_SHOW_ANYNONTEAMGAME              0x00080000
+//(SA)
+#define CG_SHOW_TEXTASINT                   0x00200000
+#define CG_SHOW_HIGHLIGHTED                 0x00100000
+
+#define CG_SHOW_NOT_V_BINOC                 0x00200000  //----(SA)     added   // hide on binoc huds
+#define CG_SHOW_NOT_V_SNIPER                0x00400000  //----(SA)     added   // hide on sniper huds
+#define CG_SHOW_NOT_V_SNOOPER               0x00800000  //----(SA)     added   // hide on snooper huds
+#define CG_SHOW_NOT_V_FGSCOPE               0x01000000  //----(SA)     added   // hide on fg42 scope huds
+#define CG_SHOW_NOT_V_CLEAR                 0x02000000  //----(SA)     added   // hide on normal, full-view huds
+
+#define CG_SHOW_2DONLY                      0x10000000
+
+
+#define UI_SHOW_LEADER                      0x00000001
+#define UI_SHOW_NOTLEADER                   0x00000002
+#define UI_SHOW_FAVORITESERVERS             0x00000004
+#define UI_SHOW_ANYNONTEAMGAME              0x00000008
+#define UI_SHOW_ANYTEAMGAME                 0x00000010
+#define UI_SHOW_NEWHIGHSCORE                0x00000020
+#define UI_SHOW_DEMOAVAILABLE               0x00000040
+#define UI_SHOW_NEWBESTTIME                 0x00000080
+#define UI_SHOW_FFA                         0x00000100
+#define UI_SHOW_NOTFFA                      0x00000200
+#define UI_SHOW_NETANYNONTEAMGAME           0x00000400
+#define UI_SHOW_NETANYTEAMGAME              0x00000800
+#define UI_SHOW_NOTFAVORITESERVERS          0x00001000
+
+// font types
+#define UI_FONT_DEFAULT         0   // auto-chose betwen big/reg/small
+#define UI_FONT_NORMAL          1
+#define UI_FONT_BIG             2
+#define UI_FONT_SMALL           3
+#define UI_FONT_HANDWRITING     4
+
+// owner draw types
+// ideally these should be done outside of this file but
+// this makes it much easier for the macro expansion to
+// convert them for the designers ( from the .menu files )
+#define CG_OWNERDRAW_BASE           1
+#define CG_PLAYER_ARMOR_ICON        1
+#define CG_PLAYER_ARMOR_VALUE       2
+#define CG_PLAYER_HEAD              3
+#define CG_PLAYER_HEALTH            4
+#define CG_PLAYER_AMMO_ICON         5
+#define CG_PLAYER_AMMO_VALUE        6
+#define CG_SELECTEDPLAYER_HEAD      7
+#define CG_SELECTEDPLAYER_NAME      8
+#define CG_SELECTEDPLAYER_LOCATION  9
+#define CG_SELECTEDPLAYER_STATUS    10
+#define CG_SELECTEDPLAYER_WEAPON    11
+#define CG_SELECTEDPLAYER_POWERUP   12
+
+#define CG_FLAGCARRIER_HEAD         13
+#define CG_FLAGCARRIER_NAME         14
+#define CG_FLAGCARRIER_LOCATION     15
+#define CG_FLAGCARRIER_STATUS       16
+#define CG_FLAGCARRIER_WEAPON       17
+#define CG_FLAGCARRIER_POWERUP      18
+
+#define CG_PLAYER_ITEM              19
+#define CG_PLAYER_SCORE             20
+
+#define CG_BLUE_FLAGHEAD            21
+#define CG_BLUE_FLAGSTATUS          22
+#define CG_BLUE_FLAGNAME            23
+#define CG_RED_FLAGHEAD             24
+#define CG_RED_FLAGSTATUS           25
+#define CG_RED_FLAGNAME             26
+
+#define CG_BLUE_SCORE               27
+#define CG_RED_SCORE                28
+#define CG_RED_NAME                 29
+#define CG_BLUE_NAME                30
+#define CG_HARVESTER_SKULLS         31  // only shows in harvester
+#define CG_ONEFLAG_STATUS           32  // only shows in one flag
+#define CG_PLAYER_LOCATION          33
+#define CG_TEAM_COLOR               34
+#define CG_CTF_POWERUP              35
+
+#define CG_AREA_POWERUP             36
+#define CG_AREA_LAGOMETER           37  // painted with old system
+#define CG_PLAYER_HASFLAG           38
+#define CG_GAME_TYPE                39  // not done
+
+#define CG_SELECTEDPLAYER_ARMOR     40
+#define CG_SELECTEDPLAYER_HEALTH    41
+#define CG_PLAYER_STATUS            42
+#define CG_FRAGGED_MSG              43  // painted with old system
+#define CG_PROXMINED_MSG            44  // painted with old system
+#define CG_AREA_FPSINFO             45  // painted with old system
+#define CG_AREA_SYSTEMCHAT          46  // painted with old system
+#define CG_AREA_TEAMCHAT            47  // painted with old system
+#define CG_AREA_CHAT                48  // painted with old system
+#define CG_GAME_STATUS              49
+#define CG_KILLER                   50
+#define CG_PLAYER_ARMOR_ICON2D      51
+#define CG_PLAYER_AMMO_ICON2D       52
+#define CG_ACCURACY                 53
+#define CG_ASSISTS                  54
+#define CG_DEFEND                   55
+#define CG_EXCELLENT                56
+#define CG_IMPRESSIVE               57
+#define CG_PERFECT                  58
+#define CG_GAUNTLET                 59
+#define CG_SPECTATORS               60
+#define CG_TEAMINFO                 61
+#define CG_VOICE_HEAD               62
+#define CG_VOICE_NAME               63
+#define CG_PLAYER_HASFLAG2D         64
+#define CG_HARVESTER_SKULLS2D       65  // only shows in harvester
+#define CG_CAPFRAGLIMIT             66
+#define CG_1STPLACE                 67
+#define CG_2NDPLACE                 68
+#define CG_CAPTURES                 69
+
+// (SA) adding
+#define CG_PLAYER_AMMOCLIP_VALUE    70
+#define CG_PLAYER_WEAPON_ICON2D     71
+#define CG_CURSORHINT               72
+#define CG_STAMINA                  73
+#define CG_PLAYER_WEAPON_HEAT       74
+#define CG_PLAYER_POWERUP           75
+#define CG_PLAYER_HOLDABLE          76
+#define CG_PLAYER_INVENTORY         77
+#define CG_AREA_WEAPON              78  // draw weapons here
+#define CG_AREA_HOLDABLE            79
+#define CG_CURSORHINT_STATUS        80  // like 'health' bar when pointing at a func_explosive
+#define CG_PLAYER_WEAPON_STABILITY  81  // shows aimSpreadScale value
+#define CG_NEWMESSAGE               82  // 'you got mail!'     //----(SA)      added
+
+#define UI_OWNERDRAW_BASE           200
+#define UI_HANDICAP                 200
+#define UI_EFFECTS                  201
+#define UI_PLAYERMODEL              202
+#define UI_CLANNAME                 203
+#define UI_CLANLOGO                 204
+#define UI_GAMETYPE                 205
+#define UI_MAPPREVIEW               206
+#define UI_SKILL                    207
+#define UI_BLUETEAMNAME             208
+#define UI_REDTEAMNAME              209
+#define UI_BLUETEAM1                210
+#define UI_BLUETEAM2                211
+#define UI_BLUETEAM3                212
+#define UI_BLUETEAM4                213
+#define UI_BLUETEAM5                214
+#define UI_REDTEAM1                 215
+#define UI_REDTEAM2                 216
+#define UI_REDTEAM3                 217
+#define UI_REDTEAM4                 218
+#define UI_REDTEAM5                 219
+#define UI_NETSOURCE                220
+#define UI_NETMAPPREVIEW            221
+#define UI_NETFILTER                222
+#define UI_TIER                     223
+#define UI_OPPONENTMODEL            224
+#define UI_TIERMAP1                 225
+#define UI_TIERMAP2                 226
+#define UI_TIERMAP3                 227
+#define UI_PLAYERLOGO               228
+#define UI_OPPONENTLOGO             229
+#define UI_PLAYERLOGO_METAL         230
+#define UI_OPPONENTLOGO_METAL       231
+#define UI_PLAYERLOGO_NAME          232
+#define UI_OPPONENTLOGO_NAME        233
+#define UI_TIER_MAPNAME             234
+#define UI_TIER_GAMETYPE            235
+#define UI_ALLMAPS_SELECTION        236
+#define UI_OPPONENT_NAME            237
+#define UI_VOTE_KICK                238
+#define UI_BOTNAME                  239
+#define UI_BOTSKILL                 240
+#define UI_REDBLUE                  241
+#define UI_CROSSHAIR                242
+#define UI_SELECTEDPLAYER           243
+#define UI_MAPCINEMATIC             244
+#define UI_NETGAMETYPE              245
+#define UI_NETMAPCINEMATIC          246
+#define UI_SERVERREFRESHDATE        247
+#define UI_SERVERMOTD               248
+#define UI_GLINFO                   249
+#define UI_KEYBINDSTATUS            250
+#define UI_CLANCINEMATIC            251
+#define UI_MAP_TIMETOBEAT           252
+#define UI_JOINGAMETYPE             253
+#define UI_PREVIEWCINEMATIC         254
+#define UI_STARTMAPCINEMATIC        255
+#define UI_MAPS_SELECTION           256
+
+#define UI_MENUMODEL                257
+#define UI_SAVEGAME_SHOT            258
+
+// NERVE - SMF
+#define UI_LIMBOCHAT                259
+// -NERVE - SMF
+
+#define UI_LEVELSHOT                260
+#define UI_LOADSTATUSBAR            261
+#define UI_SAVEGAMENAME             262
+#define UI_SAVEGAMEINFO             263
+
+#define VOICECHAT_GETFLAG           "getflag"                // command someone to get the flag
+#define VOICECHAT_OFFENSE           "offense"                // command someone to go on offense
+#define VOICECHAT_DEFEND            "defend"             // command someone to go on defense
+#define VOICECHAT_DEFENDFLAG        "defendflag"         // command someone to defend the flag
+#define VOICECHAT_PATROL            "patrol"             // command someone to go on patrol (roam)
+#define VOICECHAT_CAMP              "camp"                   // command someone to camp (we don't have sounds for this one)
+#define VOICECHAT_FOLLOWME          "followme"               // command someone to follow you
+#define VOICECHAT_RETURNFLAG        "returnflag"         // command someone to return our flag
+#define VOICECHAT_FOLLOWFLAGCARRIER "followflagcarrier"  // command someone to follow the flag carrier
+#define VOICECHAT_YES               "yes"                    // yes, affirmative, etc.
+#define VOICECHAT_NO                "no"                 // no, negative, etc.
+#define VOICECHAT_ONGETFLAG         "ongetflag"          // I'm getting the flag
+#define VOICECHAT_ONOFFENSE         "onoffense"          // I'm on offense
+#define VOICECHAT_ONDEFENSE         "ondefense"          // I'm on defense
+#define VOICECHAT_ONPATROL          "onpatrol"               // I'm on patrol (roaming)
+#define VOICECHAT_ONCAMPING         "oncamp"             // I'm camping somewhere
+#define VOICECHAT_ONFOLLOW          "onfollow"               // I'm following
+#define VOICECHAT_ONFOLLOWCARRIER   "onfollowcarrier"        // I'm following the flag carrier
+#define VOICECHAT_ONRETURNFLAG      "onreturnflag"           // I'm returning our flag
+#define VOICECHAT_INPOSITION        "inposition"         // I'm in position
+#define VOICECHAT_IHAVEFLAG         "ihaveflag"          // I have the flag
+#define VOICECHAT_BASEATTACK        "baseattack"         // the base is under attack
+#define VOICECHAT_ENEMYHASFLAG      "enemyhasflag"           // the enemy has our flag (CTF)
+#define VOICECHAT_STARTLEADER       "startleader"            // I'm the leader
+#define VOICECHAT_STOPLEADER        "stopleader"         // I resign leadership
+#define VOICECHAT_WHOISLEADER       "whoisleader"            // who is the team leader
+#define VOICECHAT_WANTONDEFENSE     "wantondefense"      // I want to be on defense
+#define VOICECHAT_WANTONOFFENSE     "wantonoffense"      // I want to be on offense
+#define VOICECHAT_KILLINSULT        "kill_insult"            // I just killed you
+#define VOICECHAT_TAUNT             "taunt"              // I want to taunt you
+#define VOICECHAT_DEATHINSULT       "death_insult"           // you just killed me
+#define VOICECHAT_KILLGAUNTLET      "kill_gauntlet"      // I just killed you with the gauntlet
+#define VOICECHAT_PRAISE            "praise"             // you did something good
+
+// NERVE - SMF - wolf multiplayer class/item selection mechanism
+#define WM_START_SELECT         0
+
+#define WM_SELECT_TEAM          1
+#define WM_SELECT_CLASS         2
+#define WM_SELECT_WEAPON        3
+#define WM_SELECT_PISTOL        4
+#define WM_SELECT_GRENADE       5
+#define WM_SELECT_ITEM1         6
+
+#define WM_AXIS                 1
+#define WM_ALLIES               2
+#define WM_SPECTATOR            3
+
+#define WM_SOLDIER              1
+#define WM_MEDIC                2
+#define WM_LIEUTENANT           3
+#define WM_ENGINEER             4
+
+#define WM_PISTOL_1911          1
+#define WM_PISTOL_LUGER         2
+
+#define WM_WEAPON_MP40          3
+#define WM_WEAPON_THOMPSON      4
+#define WM_WEAPON_STEN          5
+#define WM_WEAPON_MAUSER        6
+#define WM_WEAPON_GARAND        7
+#define WM_WEAPON_PANZERFAUST   8
+#define WM_WEAPON_VENOM         9
+#define WM_WEAPON_FLAMETHROWER  10
+
+#define WM_PINEAPPLE_GRENADE    11
+#define WM_STICK_GRENADE        12
+// -NERVE - SMF
diff --git a/src/Makefile b/src/Makefile
new file mode 100644 (file)
index 0000000..8c2913a
--- /dev/null
@@ -0,0 +1,3 @@
+# nasty ugly to get build system working from Anjuta
+all:
+       cd unix && (if [ `hostname` == antares ] ; then (./pcons-2.3.1 -j4) ; else ./cons ; fi)
diff --git a/src/botai/ai_chat.c b/src/botai/ai_chat.c
new file mode 100644 (file)
index 0000000..58d313e
--- /dev/null
@@ -0,0 +1,1313 @@
+/*
+===========================================================================
+
+Return to Castle Wolfenstein single player GPL Source Code
+Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. 
+
+This file is part of the Return to Castle Wolfenstein single player GPL Source Code (\93RTCW SP Source Code\94).  
+
+RTCW SP Source Code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+RTCW SP Source Code is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
+
+In addition, the RTCW SP 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 SP Source Code.  If not, please request a copy in writing from id Software at the address below.
+
+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.
+
+===========================================================================
+*/
+
+
+/*****************************************************************************
+ * name:               ai_chat.c
+ *
+ * desc:               Quake3 bot AI
+ *
+ *
+ *****************************************************************************/
+
+#include "../game/g_local.h"
+#include "../game/botlib.h"
+#include "../game/be_aas.h"
+#include "../game/be_ea.h"
+#include "../game/be_ai_char.h"
+#include "../game/be_ai_chat.h"
+#include "../game/be_ai_gen.h"
+#include "../game/be_ai_goal.h"
+#include "../game/be_ai_move.h"
+#include "../game/be_ai_weap.h"
+#include "../botai/botai.h"
+//
+#include "ai_main.h"
+#include "ai_dmq3.h"
+#include "ai_chat.h"
+#include "ai_cmd.h"
+#include "ai_dmnet.h"
+//
+#include "chars.h"               //characteristics
+#include "inv.h"             //indexes into the inventory
+#include "syn.h"             //synonyms
+#include "match.h"               //string matching types and vars
+
+
+/*
+==================
+BotNumActivePlayers
+==================
+*/
+int BotNumActivePlayers( void ) {
+       int i, num;
+       char buf[MAX_INFO_STRING];
+       static int maxclients;
+
+       if ( !maxclients ) {
+               maxclients = trap_Cvar_VariableIntegerValue( "sv_maxclients" );
+       }
+
+       num = 0;
+       for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) {
+               trap_GetConfigstring( CS_PLAYERS + i, buf, sizeof( buf ) );
+               //if no config string or no name
+               if ( !strlen( buf ) || !strlen( Info_ValueForKey( buf, "n" ) ) ) {
+                       continue;
+               }
+               //skip spectators
+               if ( atoi( Info_ValueForKey( buf, "t" ) ) == TEAM_SPECTATOR ) {
+                       continue;
+               }
+               //
+               num++;
+       }
+       return num;
+}
+
+/*
+==================
+BotIsFirstInRankings
+==================
+*/
+int BotIsFirstInRankings( bot_state_t *bs ) {
+       int i, score;
+       char buf[MAX_INFO_STRING];
+       static int maxclients;
+       playerState_t ps;
+
+       if ( !maxclients ) {
+               maxclients = trap_Cvar_VariableIntegerValue( "sv_maxclients" );
+       }
+
+       score = bs->cur_ps.persistant[PERS_SCORE];
+       for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) {
+               trap_GetConfigstring( CS_PLAYERS + i, buf, sizeof( buf ) );
+               //if no config string or no name
+               if ( !strlen( buf ) || !strlen( Info_ValueForKey( buf, "n" ) ) ) {
+                       continue;
+               }
+               //skip spectators
+               if ( atoi( Info_ValueForKey( buf, "t" ) ) == TEAM_SPECTATOR ) {
+                       continue;
+               }
+               //
+               BotAI_GetClientState( i, &ps );
+               if ( score < ps.persistant[PERS_SCORE] ) {
+                       return qfalse;
+               }
+       }
+       return qtrue;
+}
+
+/*
+==================
+BotIsLastInRankings
+==================
+*/
+int BotIsLastInRankings( bot_state_t *bs ) {
+       int i, score;
+       char buf[MAX_INFO_STRING];
+       static int maxclients;
+       playerState_t ps;
+
+       if ( !maxclients ) {
+               maxclients = trap_Cvar_VariableIntegerValue( "sv_maxclients" );
+       }
+
+       score = bs->cur_ps.persistant[PERS_SCORE];
+       for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) {
+               trap_GetConfigstring( CS_PLAYERS + i, buf, sizeof( buf ) );
+               //if no config string or no name
+               if ( !strlen( buf ) || !strlen( Info_ValueForKey( buf, "n" ) ) ) {
+                       continue;
+               }
+               //skip spectators
+               if ( atoi( Info_ValueForKey( buf, "t" ) ) == TEAM_SPECTATOR ) {
+                       continue;
+               }
+               //
+               BotAI_GetClientState( i, &ps );
+               if ( score > ps.persistant[PERS_SCORE] ) {
+                       return qfalse;
+               }
+       }
+       return qtrue;
+}
+
+/*
+==================
+BotFirstClientInRankings
+==================
+*/
+char *BotFirstClientInRankings( void ) {
+       int i, bestscore, bestclient;
+       char buf[MAX_INFO_STRING];
+       static char name[32];
+       static int maxclients;
+       playerState_t ps;
+
+       if ( !maxclients ) {
+               maxclients = trap_Cvar_VariableIntegerValue( "sv_maxclients" );
+       }
+
+       bestscore = -999999;
+       bestclient = 0;
+       for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) {
+               trap_GetConfigstring( CS_PLAYERS + i, buf, sizeof( buf ) );
+               //if no config string or no name
+               if ( !strlen( buf ) || !strlen( Info_ValueForKey( buf, "n" ) ) ) {
+                       continue;
+               }
+               //skip spectators
+               if ( atoi( Info_ValueForKey( buf, "t" ) ) == TEAM_SPECTATOR ) {
+                       continue;
+               }
+               //
+               BotAI_GetClientState( i, &ps );
+               if ( ps.persistant[PERS_SCORE] > bestscore ) {
+                       bestscore = ps.persistant[PERS_SCORE];
+                       bestclient = i;
+               }
+       }
+       EasyClientName( bestclient, name, 32 );
+       return name;
+}
+
+/*
+==================
+BotLastClientInRankings
+==================
+*/
+char *BotLastClientInRankings( void ) {
+       int i, worstscore, bestclient;
+       char buf[MAX_INFO_STRING];
+       static char name[32];
+       static int maxclients;
+       playerState_t ps;
+
+       if ( !maxclients ) {
+               maxclients = trap_Cvar_VariableIntegerValue( "sv_maxclients" );
+       }
+
+       worstscore = 999999;
+       bestclient = 0;
+       for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) {
+               trap_GetConfigstring( CS_PLAYERS + i, buf, sizeof( buf ) );
+               //if no config string or no name
+               if ( !strlen( buf ) || !strlen( Info_ValueForKey( buf, "n" ) ) ) {
+                       continue;
+               }
+               //skip spectators
+               if ( atoi( Info_ValueForKey( buf, "t" ) ) == TEAM_SPECTATOR ) {
+                       continue;
+               }
+               //
+               BotAI_GetClientState( i, &ps );
+               if ( ps.persistant[PERS_SCORE] < worstscore ) {
+                       worstscore = ps.persistant[PERS_SCORE];
+                       bestclient = i;
+               }
+       }
+       EasyClientName( bestclient, name, 32 );
+       return name;
+}
+
+/*
+==================
+BotRandomOpponentName
+==================
+*/
+char *BotRandomOpponentName( bot_state_t *bs ) {
+       int i, count;
+       char buf[MAX_INFO_STRING];
+       int opponents[MAX_CLIENTS], numopponents;
+       static int maxclients;
+       static char name[32];
+
+       if ( !maxclients ) {
+               maxclients = trap_Cvar_VariableIntegerValue( "sv_maxclients" );
+       }
+
+       numopponents = 0;
+       opponents[0] = 0;
+       for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) {
+               if ( i == bs->client ) {
+                       continue;
+               }
+               //
+               trap_GetConfigstring( CS_PLAYERS + i, buf, sizeof( buf ) );
+               //if no config string or no name
+               if ( !strlen( buf ) || !strlen( Info_ValueForKey( buf, "n" ) ) ) {
+                       continue;
+               }
+               //skip spectators
+               if ( atoi( Info_ValueForKey( buf, "t" ) ) == TEAM_SPECTATOR ) {
+                       continue;
+               }
+               //skip team mates
+               if ( BotSameTeam( bs, i ) ) {
+                       continue;
+               }
+               //
+               opponents[numopponents] = i;
+               numopponents++;
+       }
+       count = random() * numopponents;
+       for ( i = 0; i < numopponents; i++ ) {
+               count--;
+               if ( count <= 0 ) {
+                       EasyClientName( opponents[i], name, sizeof( name ) );
+                       return name;
+               }
+       }
+       EasyClientName( opponents[0], name, sizeof( name ) );
+       return name;
+}
+
+/*
+==================
+BotMapTitle
+==================
+*/
+
+char *BotMapTitle( void ) {
+       char info[1024];
+       static char mapname[128];
+
+       trap_GetServerinfo( info, sizeof( info ) );
+
+       strncpy( mapname, Info_ValueForKey( info, "mapname" ), sizeof( mapname ) - 1 );
+       mapname[sizeof( mapname ) - 1] = '\0';
+
+       return mapname;
+}
+
+
+/*
+==================
+BotWeaponNameForMeansOfDeath
+==================
+*/
+
+char *BotWeaponNameForMeansOfDeath( int mod ) {
+       switch ( mod ) {
+       case MOD_SHOTGUN: return "Shotgun";
+       case MOD_GAUNTLET: return "Gauntlet";
+       case MOD_MACHINEGUN: return "Machinegun";
+       case MOD_GRENADE:
+       case MOD_GRENADE_SPLASH: return "Grenade Launcher";
+       case MOD_ROCKET:
+       case MOD_ROCKET_SPLASH: return "Rocket Launcher";
+       case MOD_RAILGUN: return "Railgun";
+       case MOD_LIGHTNING: return "Lightning Gun";
+       case MOD_BFG:
+       case MOD_BFG_SPLASH: return "BFG10K";
+       case MOD_GRAPPLE: return "Grapple";
+       default: return "[unknown weapon]";
+       }
+}
+
+/*
+==================
+BotRandomWeaponName
+==================
+*/
+char *BotRandomWeaponName( void ) {
+       int rnd;
+
+       rnd = random() * 8.9;
+       switch ( rnd ) {
+       case 0: return "Gauntlet";
+       case 1: return "Shotgun";
+       case 2: return "Machinegun";
+       case 3: return "Grenade Launcher";
+       case 4: return "Rocket Launcher";
+       case 5: return "Plasmagun";
+       case 6: return "Railgun";
+       case 7: return "Lightning Gun";
+       default: return "BFG10K";
+       }
+}
+
+/*
+==================
+BotValidChatPosition
+==================
+*/
+int BotValidChatPosition( bot_state_t *bs ) {
+       vec3_t point, start, end, mins, maxs;
+       bsp_trace_t trace;
+
+       //if the bot is dead all positions are valid
+       if ( BotIsDead( bs ) ) {
+               return qtrue;
+       }
+       //must be on the ground
+       //if (bs->cur_ps.groundEntityNum != ENTITYNUM_NONE) return qfalse;
+       //do not chat if in lava or slime
+       VectorCopy( bs->origin, point );
+       point[2] -= 24;
+       if ( trap_PointContents( point,bs->entitynum ) & ( CONTENTS_LAVA | CONTENTS_SLIME ) ) {
+               return qfalse;
+       }
+       //do not chat if under water
+       VectorCopy( bs->origin, point );
+       point[2] += 32;
+       if ( trap_PointContents( point,bs->entitynum ) & MASK_WATER ) {
+               return qfalse;
+       }
+       //must be standing on the world entity
+       VectorCopy( bs->origin, start );
+       VectorCopy( bs->origin, end );
+       start[2] += 1;
+       end[2] -= 10;
+       trap_AAS_PresenceTypeBoundingBox( PRESENCE_CROUCH, mins, maxs );
+       BotAI_Trace( &trace, start, mins, maxs, end, bs->client, MASK_SOLID );
+       if ( trace.ent != ENTITYNUM_WORLD ) {
+               return qfalse;
+       }
+       //the bot is in a position where it can chat
+       return qtrue;
+}
+
+/*
+==================
+BotChat_EnterGame
+==================
+*/
+int BotChat_EnterGame( bot_state_t *bs ) {
+       char name[32];
+       float rnd;
+
+       if ( bot_nochat.integer ) {
+               return qfalse;
+       }
+       if ( bs->lastchat_time > trap_AAS_Time() - 3 ) {
+               return qfalse;
+       }
+       //don't chat in teamplay
+       if ( TeamPlayIsOn() ) {
+               return qfalse;
+       }
+       rnd = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CHAT_ENTEREXITGAME, 0, 1 );
+       if ( !bot_fastchat.integer ) {
+               if ( random() > rnd ) {
+                       return qfalse;
+               }
+       }
+       if ( BotNumActivePlayers() <= 1 ) {
+               return qfalse;
+       }
+       if ( !BotValidChatPosition( bs ) ) {
+               return qfalse;
+       }
+       BotAI_BotInitialChat( bs, "game_enter",
+                                                 EasyClientName( bs->client, name, 32 ), // 0
+                                                 BotRandomOpponentName( bs ),  // 1
+                                                 "[invalid var]",          // 2
+                                                 "[invalid var]",          // 3
+                                                 BotMapTitle(),                // 4
+                                                 NULL );
+       bs->lastchat_time = trap_AAS_Time();
+       bs->chatto = CHAT_ALL;
+       return qtrue;
+}
+
+/*
+==================
+BotChat_ExitGame
+==================
+*/
+int BotChat_ExitGame( bot_state_t *bs ) {
+       char name[32];
+       float rnd;
+
+       if ( bot_nochat.integer ) {
+               return qfalse;
+       }
+       if ( bs->lastchat_time > trap_AAS_Time() - 3 ) {
+               return qfalse;
+       }
+       //don't chat in teamplay
+       if ( TeamPlayIsOn() ) {
+               return qfalse;
+       }
+       rnd = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CHAT_ENTEREXITGAME, 0, 1 );
+       if ( !bot_fastchat.integer ) {
+               if ( random() > rnd ) {
+                       return qfalse;
+               }
+       }
+       if ( BotNumActivePlayers() <= 1 ) {
+               return qfalse;
+       }
+       //
+       BotAI_BotInitialChat( bs, "game_exit",
+                                                 EasyClientName( bs->client, name, 32 ), // 0
+                                                 BotRandomOpponentName( bs ),  // 1
+                                                 "[invalid var]",          // 2
+                                                 "[invalid var]",          // 3
+                                                 BotMapTitle(),                // 4
+                                                 NULL );
+       bs->lastchat_time = trap_AAS_Time();
+       bs->chatto = CHAT_ALL;
+       return qtrue;
+}
+
+/*
+==================
+BotChat_StartLevel
+==================
+*/
+int BotChat_StartLevel( bot_state_t *bs ) {
+       char name[32];
+       float rnd;
+
+       if ( bot_nochat.integer ) {
+               return qfalse;
+       }
+       if ( BotIsObserver( bs ) ) {
+               return qfalse;
+       }
+       if ( bs->lastchat_time > trap_AAS_Time() - 3 ) {
+               return qfalse;
+       }
+       //don't chat in teamplay
+       if ( TeamPlayIsOn() ) {
+               return qfalse;
+       }
+       rnd = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CHAT_STARTENDLEVEL, 0, 1 );
+       if ( !bot_fastchat.integer ) {
+               if ( random() > rnd ) {
+                       return qfalse;
+               }
+       }
+       if ( BotNumActivePlayers() <= 1 ) {
+               return qfalse;
+       }
+       BotAI_BotInitialChat( bs, "level_start",
+                                                 EasyClientName( bs->client, name, 32 ), // 0
+                                                 NULL );
+       bs->lastchat_time = trap_AAS_Time();
+       bs->chatto = CHAT_ALL;
+       return qtrue;
+}
+
+/*
+==================
+BotChat_EndLevel
+==================
+*/
+int BotChat_EndLevel( bot_state_t *bs ) {
+       char name[32];
+       float rnd;
+
+       if ( bot_nochat.integer ) {
+               return qfalse;
+       }
+       if ( BotIsObserver( bs ) ) {
+               return qfalse;
+       }
+       if ( bs->lastchat_time > trap_AAS_Time() - 3 ) {
+               return qfalse;
+       }
+       //don't chat in teamplay
+       if ( TeamPlayIsOn() ) {
+               return qfalse;
+       }
+       rnd = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CHAT_STARTENDLEVEL, 0, 1 );
+       if ( !bot_fastchat.integer ) {
+               if ( random() > rnd ) {
+                       return qfalse;
+               }
+       }
+       if ( BotNumActivePlayers() <= 1 ) {
+               return qfalse;
+       }
+       //
+       if ( BotIsFirstInRankings( bs ) ) {
+               BotAI_BotInitialChat( bs, "level_end_victory",
+                                                         EasyClientName( bs->client, name, 32 ), // 0
+                                                         BotRandomOpponentName( bs ), // 1
+                                                         "[invalid var]",      // 2
+                                                         BotLastClientInRankings(), // 3
+                                                         BotMapTitle(),            // 4
+                                                         NULL );
+       } else if ( BotIsLastInRankings( bs ) )       {
+               BotAI_BotInitialChat( bs, "level_end_lose",
+                                                         EasyClientName( bs->client, name, 32 ), // 0
+                                                         BotRandomOpponentName( bs ), // 1
+                                                         BotFirstClientInRankings(), // 2
+                                                         "[invalid var]",      // 3
+                                                         BotMapTitle(),            // 4
+                                                         NULL );
+       } else {
+               BotAI_BotInitialChat( bs, "level_end",
+                                                         EasyClientName( bs->client, name, 32 ), // 0
+                                                         BotRandomOpponentName( bs ), // 1
+                                                         BotFirstClientInRankings(), // 2
+                                                         BotLastClientInRankings(), // 3
+                                                         BotMapTitle(),            // 4
+                                                         NULL );
+       }
+       bs->lastchat_time = trap_AAS_Time();
+       bs->chatto = CHAT_ALL;
+       return qtrue;
+}
+
+/*
+==================
+BotChat_Death
+==================
+*/
+int BotChat_Death( bot_state_t *bs ) {
+       char name[32];
+       float rnd;
+
+       if ( bot_nochat.integer ) {
+               return qfalse;
+       }
+       if ( bs->lastchat_time > trap_AAS_Time() - 3 ) {
+               return qfalse;
+       }
+       rnd = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CHAT_DEATH, 0, 1 );
+       //if fast chatting is off
+       if ( !bot_fastchat.integer ) {
+               if ( random() > rnd ) {
+                       return qfalse;
+               }
+       }
+       if ( BotNumActivePlayers() <= 1 ) {
+               return qfalse;
+       }
+       //
+       if ( bs->lastkilledby >= 0 && bs->lastkilledby < MAX_CLIENTS ) {
+               EasyClientName( bs->lastkilledby, name, 32 );
+       } else {
+               strcpy( name, "[world]" );
+       }
+       //
+       if ( TeamPlayIsOn() && BotSameTeam( bs, bs->lastkilledby ) ) {
+               if ( bs->lastkilledby == bs->client ) {
+                       return qfalse;
+               }
+               BotAI_BotInitialChat( bs, "death_teammate", name, NULL );
+               bs->chatto = CHAT_TEAM;
+       } else
+       {
+               //don't chat in teamplay
+               if ( TeamPlayIsOn() ) {
+                       return qfalse;
+               }
+               //
+               if ( bs->botdeathtype == MOD_WATER ) {
+                       BotAI_BotInitialChat( bs, "death_drown", BotRandomOpponentName( bs ), NULL );
+               } else if ( bs->botdeathtype == MOD_SLIME ) {
+                       BotAI_BotInitialChat( bs, "death_slime", BotRandomOpponentName( bs ), NULL );
+               } else if ( bs->botdeathtype == MOD_LAVA ) {
+                       BotAI_BotInitialChat( bs, "death_lava", BotRandomOpponentName( bs ), NULL );
+               } else if ( bs->botdeathtype == MOD_FALLING ) {
+                       BotAI_BotInitialChat( bs, "death_cratered", BotRandomOpponentName( bs ), NULL );
+               } else if ( bs->botsuicide || //all other suicides by own weapon
+                                       bs->botdeathtype == MOD_CRUSH ||
+                                       bs->botdeathtype == MOD_SUICIDE ||
+                                       bs->botdeathtype == MOD_TARGET_LASER ||
+                                       bs->botdeathtype == MOD_TRIGGER_HURT ||
+                                       bs->botdeathtype == MOD_UNKNOWN ) {
+                       BotAI_BotInitialChat( bs, "death_suicide", BotRandomOpponentName( bs ), NULL );
+               } else if ( bs->botdeathtype == MOD_TELEFRAG ) {
+                       BotAI_BotInitialChat( bs, "death_telefrag", name, NULL );
+               } else {
+                       if ( ( bs->botdeathtype == MOD_GAUNTLET ||
+                                  bs->botdeathtype == MOD_RAILGUN ||
+                                  bs->botdeathtype == MOD_BFG ||
+                                  bs->botdeathtype == MOD_BFG_SPLASH ) && random() < 0.5 ) {
+
+                               if ( bs->botdeathtype == MOD_GAUNTLET ) {
+                                       BotAI_BotInitialChat( bs, "death_gauntlet",
+                                                                                 name,                                 // 0
+                                                                                 BotWeaponNameForMeansOfDeath( bs->botdeathtype ), // 1
+                                                                                 NULL );
+                               } else if ( bs->botdeathtype == MOD_RAILGUN ) {
+                                       BotAI_BotInitialChat( bs, "death_rail",
+                                                                                 name,                                 // 0
+                                                                                 BotWeaponNameForMeansOfDeath( bs->botdeathtype ), // 1
+                                                                                 NULL );
+                               } else {
+                                       BotAI_BotInitialChat( bs, "death_bfg",
+                                                                                 name,                                 // 0
+                                                                                 BotWeaponNameForMeansOfDeath( bs->botdeathtype ), // 1
+                                                                                 NULL );
+                               }
+                       }
+                       //choose between insult and praise
+                       else if ( random() < trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CHAT_INSULT, 0, 1 ) ) {
+                               BotAI_BotInitialChat( bs, "death_insult",
+                                                                         name,                                     // 0
+                                                                         BotWeaponNameForMeansOfDeath( bs->botdeathtype ), // 1
+                                                                         NULL );
+                       } else {
+                               BotAI_BotInitialChat( bs, "death_praise",
+                                                                         name,                                     // 0
+                                                                         BotWeaponNameForMeansOfDeath( bs->botdeathtype ), // 1
+                                                                         NULL );
+                       }
+               }
+               bs->chatto = CHAT_ALL;
+       }
+       bs->lastchat_time = trap_AAS_Time();
+       return qtrue;
+}
+
+/*
+==================
+BotChat_Kill
+==================
+*/
+int BotChat_Kill( bot_state_t *bs ) {
+       char name[32];
+       float rnd;
+
+       if ( bot_nochat.integer ) {
+               return qfalse;
+       }
+       if ( bs->lastchat_time > trap_AAS_Time() - 3 ) {
+               return qfalse;
+       }
+       rnd = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CHAT_KILL, 0, 1 );
+       //if fast chat is off
+       if ( !bot_fastchat.integer ) {
+               if ( random() > rnd ) {
+                       return qfalse;
+               }
+       }
+       if ( bs->lastkilledplayer == bs->client ) {
+               return qfalse;
+       }
+       if ( BotNumActivePlayers() <= 1 ) {
+               return qfalse;
+       }
+       if ( !BotValidChatPosition( bs ) ) {
+               return qfalse;
+       }
+       //
+       EasyClientName( bs->lastkilledplayer, name, 32 );
+       //
+       bs->chatto = CHAT_ALL;
+       if ( TeamPlayIsOn() && BotSameTeam( bs, bs->lastkilledplayer ) ) {
+               BotAI_BotInitialChat( bs, "kill_teammate", name, NULL );
+               bs->chatto = CHAT_TEAM;
+       } else
+       {
+               //don't chat in teamplay
+               if ( TeamPlayIsOn() ) {
+                       return qfalse;
+               }
+               //
+               if ( bs->enemydeathtype == MOD_GAUNTLET ) {
+                       BotAI_BotInitialChat( bs, "kill_gauntlet", name, NULL );
+               } else if ( bs->enemydeathtype == MOD_RAILGUN )     {
+                       BotAI_BotInitialChat( bs, "kill_rail", name, NULL );
+               } else if ( bs->enemydeathtype == MOD_TELEFRAG )     {
+                       BotAI_BotInitialChat( bs, "kill_telefrag", name, NULL );
+               }
+               //choose between insult and praise
+               else if ( random() < trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CHAT_INSULT, 0, 1 ) ) {
+                       BotAI_BotInitialChat( bs, "kill_insult", name, NULL );
+               } else {
+                       BotAI_BotInitialChat( bs, "kill_praise", name, NULL );
+               }
+       }
+       bs->lastchat_time = trap_AAS_Time();
+       return qtrue;
+}
+
+/*
+==================
+BotChat_EnemySuicide
+==================
+*/
+int BotChat_EnemySuicide( bot_state_t *bs ) {
+       char name[32];
+       float rnd;
+
+       if ( bot_nochat.integer ) {
+               return qfalse;
+       }
+       if ( bs->lastchat_time > trap_AAS_Time() - 3 ) {
+               return qfalse;
+       }
+       if ( BotNumActivePlayers() <= 1 ) {
+               return qfalse;
+       }
+       //
+       rnd = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CHAT_KILL, 0, 1 );
+       //don't chat in teamplay
+       if ( TeamPlayIsOn() ) {
+               return qfalse;
+       }
+       //if fast chat is off
+       if ( !bot_fastchat.integer ) {
+               if ( random() > rnd ) {
+                       return qfalse;
+               }
+       }
+       if ( !BotValidChatPosition( bs ) ) {
+               return qfalse;
+       }
+       //
+       if ( bs->enemy >= 0 ) {
+               EasyClientName( bs->enemy, name, 32 );
+       } else { strcpy( name, "" );}
+       BotAI_BotInitialChat( bs, "enemy_suicide", name, NULL );
+       bs->lastchat_time = trap_AAS_Time();
+       bs->chatto = CHAT_ALL;
+       return qtrue;
+}
+
+/*
+==================
+BotChat_HitTalking
+==================
+*/
+int BotChat_HitTalking( bot_state_t *bs ) {
+       char name[32], *weap;
+       int lasthurt_client;
+       float rnd;
+
+       if ( bot_nochat.integer ) {
+               return qfalse;
+       }
+       if ( bs->lastchat_time > trap_AAS_Time() - 3 ) {
+               return qfalse;
+       }
+       if ( BotNumActivePlayers() <= 1 ) {
+               return qfalse;
+       }
+       lasthurt_client = g_entities[bs->client].client->lasthurt_client;
+       if ( !lasthurt_client ) {
+               return qfalse;
+       }
+       if ( lasthurt_client == bs->client ) {
+               return qfalse;
+       }
+       //
+       if ( lasthurt_client < 0 || lasthurt_client >= MAX_CLIENTS ) {
+               return qfalse;
+       }
+       //
+       rnd = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CHAT_HITTALKING, 0, 1 );
+       //don't chat in teamplay
+       if ( TeamPlayIsOn() ) {
+               return qfalse;
+       }
+       //if fast chat is off
+       if ( !bot_fastchat.integer ) {
+               if ( random() > rnd * 0.5 ) {
+                       return qfalse;
+               }
+       }
+       if ( !BotValidChatPosition( bs ) ) {
+               return qfalse;
+       }
+       //
+       ClientName( g_entities[bs->client].client->lasthurt_client, name, sizeof( name ) );
+       weap = BotWeaponNameForMeansOfDeath( g_entities[bs->client].client->lasthurt_client );
+       //
+       BotAI_BotInitialChat( bs, "hit_talking", name, weap, NULL );
+       bs->lastchat_time = trap_AAS_Time();
+       bs->chatto = CHAT_ALL;
+       return qtrue;
+}
+
+/*
+==================
+BotChat_HitNoDeath
+==================
+*/
+int BotChat_HitNoDeath( bot_state_t *bs ) {
+       char name[32], *weap;
+       float rnd;
+       int lasthurt_client;
+       aas_entityinfo_t entinfo;
+
+       lasthurt_client = g_entities[bs->client].client->lasthurt_client;
+       if ( !lasthurt_client ) {
+               return qfalse;
+       }
+       if ( lasthurt_client == bs->client ) {
+               return qfalse;
+       }
+       //
+       if ( lasthurt_client < 0 || lasthurt_client >= MAX_CLIENTS ) {
+               return qfalse;
+       }
+       //
+       if ( bot_nochat.integer ) {
+               return qfalse;
+       }
+       if ( bs->lastchat_time > trap_AAS_Time() - 3 ) {
+               return qfalse;
+       }
+       if ( BotNumActivePlayers() <= 1 ) {
+               return qfalse;
+       }
+       rnd = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CHAT_HITNODEATH, 0, 1 );
+       //don't chat in teamplay
+       if ( TeamPlayIsOn() ) {
+               return qfalse;
+       }
+       //if fast chat is off
+       if ( !bot_fastchat.integer ) {
+               if ( random() > rnd * 0.5 ) {
+                       return qfalse;
+               }
+       }
+       if ( !BotValidChatPosition( bs ) ) {
+               return qfalse;
+       }
+       //if the enemy is visible
+       if ( BotEntityVisible( bs->client, bs->eye, bs->viewangles, 360, bs->enemy ) ) {
+               return qfalse;
+       }
+       //
+       BotEntityInfo( bs->enemy, &entinfo );
+       if ( EntityIsShooting( &entinfo ) ) {
+               return qfalse;
+       }
+       //
+       ClientName( lasthurt_client, name, sizeof( name ) );
+       weap = BotWeaponNameForMeansOfDeath( g_entities[bs->client].client->lasthurt_mod );
+       //
+       BotAI_BotInitialChat( bs, "hit_nodeath", name, weap, NULL );
+       bs->lastchat_time = trap_AAS_Time();
+       bs->chatto = CHAT_ALL;
+       return qtrue;
+}
+
+/*
+==================
+BotChat_HitNoKill
+==================
+*/
+int BotChat_HitNoKill( bot_state_t *bs ) {
+       char name[32], *weap;
+       float rnd;
+       aas_entityinfo_t entinfo;
+
+       if ( bot_nochat.integer ) {
+               return qfalse;
+       }
+       if ( bs->lastchat_time > trap_AAS_Time() - 3 ) {
+               return qfalse;
+       }
+       if ( BotNumActivePlayers() <= 1 ) {
+               return qfalse;
+       }
+       rnd = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CHAT_HITNOKILL, 0, 1 );
+       //don't chat in teamplay
+       if ( TeamPlayIsOn() ) {
+               return qfalse;
+       }
+       //if fast chat is off
+       if ( !bot_fastchat.integer ) {
+               if ( random() > rnd * 0.5 ) {
+                       return qfalse;
+               }
+       }
+       if ( !BotValidChatPosition( bs ) ) {
+               return qfalse;
+       }
+       //if the enemy is visible
+       if ( BotEntityVisible( bs->client, bs->eye, bs->viewangles, 360, bs->enemy ) ) {
+               return qfalse;
+       }
+       //
+       BotEntityInfo( bs->enemy, &entinfo );
+       if ( EntityIsShooting( &entinfo ) ) {
+               return qfalse;
+       }
+       //
+       ClientName( bs->enemy, name, sizeof( name ) );
+       weap = BotWeaponNameForMeansOfDeath( g_entities[bs->enemy].client->lasthurt_mod );
+       //
+       BotAI_BotInitialChat( bs, "hit_nokill", name, weap, NULL );
+       bs->lastchat_time = trap_AAS_Time();
+       bs->chatto = CHAT_ALL;
+       return qtrue;
+}
+
+/*
+==================
+BotChat_Random
+==================
+*/
+int BotChat_Random( bot_state_t *bs ) {
+       float rnd;
+       char name[32];
+
+       if ( bot_nochat.integer ) {
+               return qfalse;
+       }
+       if ( BotIsObserver( bs ) ) {
+               return qfalse;
+       }
+       if ( bs->lastchat_time > trap_AAS_Time() - 3 ) {
+               return qfalse;
+       }
+       //don't chat in teamplay
+       if ( TeamPlayIsOn() ) {
+               return qfalse;
+       }
+       //don't chat when doing something important :)
+       if ( bs->ltgtype == LTG_TEAMHELP ||
+                bs->ltgtype == LTG_TEAMACCOMPANY ||
+                bs->ltgtype == LTG_RUSHBASE ) {
+               return qfalse;
+       }
+       //
+       rnd = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CHAT_RANDOM, 0, 1 );
+       if ( random() > bs->thinktime * 0.1 ) {
+               return qfalse;
+       }
+       if ( !bot_fastchat.integer ) {
+               if ( random() > rnd ) {
+                       return qfalse;
+               }
+               if ( random() > 0.25 ) {
+                       return qfalse;
+               }
+       }
+       if ( BotNumActivePlayers() <= 1 ) {
+               return qfalse;
+       }
+       if ( !BotValidChatPosition( bs ) ) {
+               return qfalse;
+       }
+       //
+       if ( bs->lastkilledplayer == bs->client ) {
+               strcpy( name, BotRandomOpponentName( bs ) );
+       } else {
+               EasyClientName( bs->lastkilledplayer, name, sizeof( name ) );
+       }
+       //
+       if ( random() < trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CHAT_MISC, 0, 1 ) ) {
+               BotAI_BotInitialChat( bs, "random_misc",
+                                                         BotRandomOpponentName( bs ), // 0
+                                                         name,             // 1
+                                                         "[invalid var]", // 2
+                                                         "[invalid var]", // 3
+                                                         BotMapTitle(),    // 4
+                                                         BotRandomWeaponName(), // 5
+                                                         NULL );
+       } else {
+               BotAI_BotInitialChat( bs, "random_insult",
+                                                         BotRandomOpponentName( bs ), // 0
+                                                         name,             // 1
+                                                         "[invalid var]", // 2
+                                                         "[invalid var]", // 3
+                                                         BotMapTitle(),    // 4
+                                                         BotRandomWeaponName(), // 5
+                                                         NULL );
+       }
+       bs->lastchat_time = trap_AAS_Time();
+       bs->chatto = CHAT_ALL;
+       return qtrue;
+}
+
+/*
+==================
+BotChatTime
+==================
+*/
+float BotChatTime( bot_state_t *bs ) {
+       int cpm;
+
+       cpm = trap_Characteristic_BInteger( bs->character, CHARACTERISTIC_CHAT_CPM, 1, 4000 );
+
+       return 2.0; //(float) trap_BotChatLength(bs->cs) * 30 / cpm;
+}
+
+/*
+==================
+BotChatTest
+==================
+*/
+void BotChatTest( bot_state_t *bs ) {
+
+       char name[32];
+       char *weap;
+       int num, i;
+
+       num = trap_BotNumInitialChats( bs->cs, "game_enter" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "game_enter",
+                                                         EasyClientName( bs->client, name, 32 ), // 0
+                                                         BotRandomOpponentName( bs ),  // 1
+                                                         "[invalid var]",          // 2
+                                                         "[invalid var]",          // 3
+                                                         BotMapTitle(),                // 4
+                                                         NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "game_exit" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "game_exit",
+                                                         EasyClientName( bs->client, name, 32 ), // 0
+                                                         BotRandomOpponentName( bs ),  // 1
+                                                         "[invalid var]",          // 2
+                                                         "[invalid var]",          // 3
+                                                         BotMapTitle(),                // 4
+                                                         NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "level_start" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "level_start",
+                                                         EasyClientName( bs->client, name, 32 ), // 0
+                                                         NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "level_end_victory" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "level_end_victory",
+                                                         EasyClientName( bs->client, name, 32 ), // 0
+                                                         BotRandomOpponentName( bs ), // 1
+                                                         BotFirstClientInRankings(), // 2
+                                                         BotLastClientInRankings(), // 3
+                                                         BotMapTitle(),            // 4
+                                                         NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "level_end_lose" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "level_end_lose",
+                                                         EasyClientName( bs->client, name, 32 ), // 0
+                                                         BotRandomOpponentName( bs ), // 1
+                                                         BotFirstClientInRankings(), // 2
+                                                         BotLastClientInRankings(), // 3
+                                                         BotMapTitle(),            // 4
+                                                         NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "level_end" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "level_end",
+                                                         EasyClientName( bs->client, name, 32 ), // 0
+                                                         BotRandomOpponentName( bs ), // 1
+                                                         BotFirstClientInRankings(), // 2
+                                                         BotLastClientInRankings(), // 3
+                                                         BotMapTitle(),            // 4
+                                                         NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       EasyClientName( bs->lastkilledby, name, sizeof( name ) );
+       num = trap_BotNumInitialChats( bs->cs, "death_drown" );
+       for ( i = 0; i < num; i++ )
+       {
+               //
+               BotAI_BotInitialChat( bs, "death_drown", name, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "death_slime" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "death_slime", name, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "death_lava" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "death_lava", name, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "death_cratered" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "death_cratered", name, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "death_suicide" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "death_suicide", name, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "death_telefrag" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "death_telefrag", name, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "death_gauntlet" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "death_gauntlet",
+                                                         name,                                 // 0
+                                                         BotWeaponNameForMeansOfDeath( bs->botdeathtype ), // 1
+                                                         NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "death_rail" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "death_rail",
+                                                         name,                                 // 0
+                                                         BotWeaponNameForMeansOfDeath( bs->botdeathtype ), // 1
+                                                         NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "death_bfg" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "death_bfg",
+                                                         name,                                 // 0
+                                                         BotWeaponNameForMeansOfDeath( bs->botdeathtype ), // 1
+                                                         NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "death_insult" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "death_insult",
+                                                         name,                                     // 0
+                                                         BotWeaponNameForMeansOfDeath( bs->botdeathtype ), // 1
+                                                         NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "death_praise" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "death_praise",
+                                                         name,                                     // 0
+                                                         BotWeaponNameForMeansOfDeath( bs->botdeathtype ), // 1
+                                                         NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       //
+       EasyClientName( bs->lastkilledplayer, name, 32 );
+       //
+       num = trap_BotNumInitialChats( bs->cs, "kill_gauntlet" );
+       for ( i = 0; i < num; i++ )
+       {
+               //
+               BotAI_BotInitialChat( bs, "kill_gauntlet", name, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "kill_rail" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "kill_rail", name, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "kill_telefrag" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "kill_telefrag", name, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "kill_insult" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "kill_insult", name, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "kill_praise" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "kill_praise", name, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "enemy_suicide" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "enemy_suicide", name, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       ClientName( g_entities[bs->client].client->lasthurt_client, name, sizeof( name ) );
+       weap = BotWeaponNameForMeansOfDeath( g_entities[bs->client].client->lasthurt_client );
+       num = trap_BotNumInitialChats( bs->cs, "hit_talking" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "hit_talking", name, weap, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "hit_nodeath" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "hit_nodeath", name, weap, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "hit_nokill" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "hit_nokill", name, weap, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       //
+       if ( bs->lastkilledplayer == bs->client ) {
+               strcpy( name, BotRandomOpponentName( bs ) );
+       } else {
+               EasyClientName( bs->lastkilledplayer, name, sizeof( name ) );
+       }
+       //
+       num = trap_BotNumInitialChats( bs->cs, "random_misc" );
+       for ( i = 0; i < num; i++ )
+       {
+               //
+               BotAI_BotInitialChat( bs, "random_misc",
+                                                         BotRandomOpponentName( bs ), // 0
+                                                         name,             // 1
+                                                         "[invalid var]", // 2
+                                                         "[invalid var]", // 3
+                                                         BotMapTitle(),    // 4
+                                                         BotRandomWeaponName(), // 5
+                                                         NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+       num = trap_BotNumInitialChats( bs->cs, "random_insult" );
+       for ( i = 0; i < num; i++ )
+       {
+               BotAI_BotInitialChat( bs, "random_insult",
+                                                         BotRandomOpponentName( bs ), // 0
+                                                         name,             // 1
+                                                         "[invalid var]", // 2
+                                                         "[invalid var]", // 3
+                                                         BotMapTitle(),    // 4
+                                                         BotRandomWeaponName(), // 5
+                                                         NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
+       }
+}
diff --git a/src/botai/ai_chat.h b/src/botai/ai_chat.h
new file mode 100644 (file)
index 0000000..1b97caf
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+===========================================================================
+
+Return to Castle Wolfenstein single player GPL Source Code
+Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. 
+
+This file is part of the Return to Castle Wolfenstein single player GPL Source Code (\93RTCW SP Source Code\94).  
+
+RTCW SP Source Code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+RTCW SP Source Code is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
+
+In addition, the RTCW SP 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 SP Source Code.  If not, please request a copy in writing from id Software at the address below.
+
+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.
+
+===========================================================================
+*/
+
+
+/*****************************************************************************
+ * name:               ai_chat.h
+ *
+ * desc:               Quake3 bot AI
+ *
+ *
+ *****************************************************************************/
+
+//
+int BotChat_EnterGame( bot_state_t *bs );
+//
+int BotChat_ExitGame( bot_state_t *bs );
+//
+int BotChat_StartLevel( bot_state_t *bs );
+//
+int BotChat_EndLevel( bot_state_t *bs );
+//
+int BotChat_HitTalking( bot_state_t *bs );
+//
+int BotChat_HitNoDeath( bot_state_t *bs );
+//
+int BotChat_HitNoKill( bot_state_t *bs );
+//
+int BotChat_Death( bot_state_t *bs );
+//
+int BotChat_Kill( bot_state_t *bs );
+//
+int BotChat_EnemySuicide( bot_state_t *bs );
+//
+int BotChat_Random( bot_state_t *bs );
+// time the selected chat takes to type in
+float BotChatTime( bot_state_t *bs );
+// returns true if the bot can chat at the current position
+int BotValidChatPosition( bot_state_t *bs );
+// test the initial bot chats
+void BotChatTest( bot_state_t *bs );
+
diff --git a/src/botai/ai_cmd.c b/src/botai/ai_cmd.c
new file mode 100644 (file)
index 0000000..2ddd5d8
--- /dev/null
@@ -0,0 +1,1645 @@
+/*
+===========================================================================
+
+Return to Castle Wolfenstein single player GPL Source Code
+Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. 
+
+This file is part of the Return to Castle Wolfenstein single player GPL Source Code (\93RTCW SP Source Code\94).  
+
+RTCW SP Source Code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+RTCW SP Source Code is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
+
+In addition, the RTCW SP 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 SP Source Code.  If not, please request a copy in writing from id Software at the address below.
+
+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.
+
+===========================================================================
+*/
+
+
+/*****************************************************************************
+ * name:               ai_cmd.c
+ *
+ * desc:               Quake3 bot AI
+ *
+ *
+ *****************************************************************************/
+
+#include "../game/g_local.h"
+#include "../game/botlib.h"
+#include "../game/be_aas.h"
+#include "../game/be_ea.h"
+#include "../game/be_ai_char.h"
+#include "../game/be_ai_chat.h"
+#include "../game/be_ai_gen.h"
+#include "../game/be_ai_goal.h"
+#include "../game/be_ai_move.h"
+#include "../game/be_ai_weap.h"
+#include "../botai/botai.h"
+//
+#include "ai_main.h"
+#include "ai_dmq3.h"
+#include "ai_chat.h"
+#include "ai_cmd.h"
+#include "ai_dmnet.h"
+//
+#include "chars.h"               //characteristics
+#include "inv.h"             //indexes into the inventory
+#include "syn.h"             //synonyms
+#include "match.h"               //string matching types and vars
+
+
+#ifdef DEBUG
+/*
+==================
+BotPrintTeamGoal
+==================
+*/
+void BotPrintTeamGoal( bot_state_t *bs ) {
+       char netname[MAX_NETNAME];
+       float t;
+
+       ClientName( bs->client, netname, sizeof( netname ) );
+       t = bs->teamgoal_time - trap_AAS_Time();
+       switch ( bs->ltgtype ) {
+       case LTG_TEAMHELP:
+       {
+               BotAI_Print( PRT_MESSAGE, "%s: I'm gonna help a team mate for %1.0f secs\n", netname, t );
+               break;
+       }
+       case LTG_TEAMACCOMPANY:
+       {
+               BotAI_Print( PRT_MESSAGE, "%s: I'm gonna accompany a team mate for %1.0f secs\n", netname, t );
+               break;
+       }
+       case LTG_GETFLAG:
+       {
+               BotAI_Print( PRT_MESSAGE, "%s: I'm gonna get the flag for %1.0f secs\n", netname, t );
+               break;
+       }
+       case LTG_RUSHBASE:
+       {
+               BotAI_Print( PRT_MESSAGE, "%s: I'm gonna rush to the base for %1.0f secs\n", netname, t );
+               break;
+       }
+       case LTG_RETURNFLAG:
+       {
+               BotAI_Print( PRT_MESSAGE, "%s: I'm gonna try to return the flag for %1.0f secs\n", netname, t );
+               break;
+       }
+       case LTG_DEFENDKEYAREA:
+       {
+               BotAI_Print( PRT_MESSAGE, "%s: I'm gonna defend a key area for %1.0f secs\n", netname, t );
+               break;
+       }
+       case LTG_GETITEM:
+       {
+               BotAI_Print( PRT_MESSAGE, "%s: I'm gonna get an item for %1.0f secs\n", netname, t );
+               break;
+       }
+       case LTG_KILL:
+       {
+               BotAI_Print( PRT_MESSAGE, "%s: I'm gonna kill someone for %1.0f secs\n", netname, t );
+               break;
+       }
+       case LTG_CAMP:
+       case LTG_CAMPORDER:
+       {
+               BotAI_Print( PRT_MESSAGE, "%s: I'm gonna camp for %1.0f secs\n", netname, t );
+               break;
+       }
+       case LTG_PATROL:
+       {
+               BotAI_Print( PRT_MESSAGE, "%s: I'm gonna patrol for %1.0f secs\n", netname, t );
+               break;
+       }
+       default:
+       {
+               if ( bs->ctfroam_time > trap_AAS_Time() ) {
+                       t = bs->ctfroam_time - trap_AAS_Time();
+                       BotAI_Print( PRT_MESSAGE, "%s: I'm gonna roam for %1.0f secs\n", netname, t );
+               } else {
+                       BotAI_Print( PRT_MESSAGE, "%s: I've got a regular goal\n", netname );
+               }
+       }
+       }
+}
+#endif //DEBUG
+
+/*
+==================
+BotGetItemTeamGoal
+
+FIXME: add stuff like "upper rocket launcher"
+"the rl near the railgun", "lower grenade launcher" etc.
+==================
+*/
+int BotGetItemTeamGoal( char *goalname, bot_goal_t *goal ) {
+       int i;
+
+       if ( !strlen( goalname ) ) {
+               return qfalse;
+       }
+       i = -1;
+       do {
+               i = trap_BotGetLevelItemGoal( i, goalname, goal );
+               if ( i > 0 ) { // && !AvoidGoalTime(&bs->gs, goal.number))
+                       return qtrue;
+               }
+       } while ( i > 0 );
+       return qfalse;
+}
+
+/*
+==================
+BotGetMessageTeamGoal
+==================
+*/
+int BotGetMessageTeamGoal( bot_state_t *bs, char *goalname, bot_goal_t *goal ) {
+       bot_waypoint_t *cp;
+
+       if ( BotGetItemTeamGoal( goalname, goal ) ) {
+               return qtrue;
+       }
+
+       cp = BotFindWayPoint( bs->checkpoints, goalname );
+       if ( cp ) {
+               memcpy( goal, &cp->goal, sizeof( bot_goal_t ) );
+               return qtrue;
+       }
+       return qfalse;
+}
+
+/*
+==================
+BotGetTime
+==================
+*/
+float BotGetTime( bot_match_t *match ) {
+       bot_match_t timematch;
+       char timestring[MAX_MESSAGE_SIZE];
+       float t;
+
+       //if the matched string has a time
+       if ( match->subtype & ST_TIME ) {
+               //get the time string
+               trap_BotMatchVariable( match, TIME, timestring, MAX_MESSAGE_SIZE );
+               //match it to find out if the time is in seconds or minutes
+               if ( trap_BotFindMatch( timestring, &timematch, MTCONTEXT_TIME ) ) {
+                       if ( timematch.type == MSG_FOREVER ) {
+                               t = 99999999;
+                       } else {
+                               trap_BotMatchVariable( &timematch, TIME, timestring, MAX_MESSAGE_SIZE );
+                               if ( timematch.type == MSG_MINUTES ) {
+                                       t = atof( timestring ) * 60;
+                               } else if ( timematch.type == MSG_SECONDS ) {
+                                       t = atof( timestring );
+                               } else { t = 0;}
+                       }
+                       //if there's a valid time
+                       if ( t > 0 ) {
+                               return trap_AAS_Time() + t;
+                       }
+               }
+       }
+       return 0;
+}
+
+/*
+==================
+FindClientByName
+==================
+*/
+int FindClientByName( char *name ) {
+       int i;
+       char buf[MAX_INFO_STRING];
+       static int maxclients;
+
+       if ( !maxclients ) {
+               maxclients = trap_Cvar_VariableIntegerValue( "sv_maxclients" );
+       }
+       for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) {
+               ClientName( i, buf, sizeof( buf ) );
+               if ( !Q_stricmp( buf, name ) ) {
+                       return i;
+               }
+       }
+       for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) {
+               ClientName( i, buf, sizeof( buf ) );
+               if ( stristr( buf, name ) ) {
+                       return i;
+               }
+       }
+       return -1;
+}
+
+/*
+==================
+FindEnemyByName
+==================
+*/
+int FindEnemyByName( bot_state_t *bs, char *name ) {
+       int i;
+       char buf[MAX_INFO_STRING];
+       static int maxclients;
+
+       if ( !maxclients ) {
+               maxclients = trap_Cvar_VariableIntegerValue( "sv_maxclients" );
+       }
+       for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) {
+               if ( BotSameTeam( bs, i ) ) {
+                       continue;
+               }
+               ClientName( i, buf, sizeof( buf ) );
+               if ( !Q_stricmp( buf, name ) ) {
+                       return i;
+               }
+       }
+       for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) {
+               if ( BotSameTeam( bs, i ) ) {
+                       continue;
+               }
+               ClientName( i, buf, sizeof( buf ) );
+               if ( stristr( buf, name ) ) {
+                       return i;
+               }
+       }
+       return -1;
+}
+
+/*
+==================
+NumPlayersOnSameTeam
+==================
+*/
+int NumPlayersOnSameTeam( bot_state_t *bs ) {
+       int i, num;
+       char buf[MAX_INFO_STRING];
+       static int maxclients;
+
+       if ( !maxclients ) {
+               maxclients = trap_Cvar_VariableIntegerValue( "sv_maxclients" );
+       }
+
+       num = 0;
+       for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) {
+               trap_GetConfigstring( CS_PLAYERS + i, buf, MAX_INFO_STRING );
+               if ( strlen( buf ) ) {
+                       if ( BotSameTeam( bs, i + 1 ) ) {
+                               num++;
+                       }
+               }
+       }
+       return num;
+}
+
+/*
+==================
+TeamPlayIsOn
+==================
+*/
+int BotGetPatrolWaypoints( bot_state_t *bs, bot_match_t *match ) {
+       char keyarea[MAX_MESSAGE_SIZE];
+       int patrolflags;
+       bot_waypoint_t *wp, *newwp, *newpatrolpoints;
+       bot_match_t keyareamatch;
+       bot_goal_t goal;
+
+       newpatrolpoints = NULL;
+       patrolflags = 0;
+       //
+       trap_BotMatchVariable( match, KEYAREA, keyarea, MAX_MESSAGE_SIZE );
+       //
+       while ( 1 ) {
+               if ( !trap_BotFindMatch( keyarea, &keyareamatch, MTCONTEXT_PATROLKEYAREA ) ) {
+                       trap_EA_SayTeam( bs->client, "what do you say?" );
+                       BotFreeWaypoints( newpatrolpoints );
+                       bs->patrolpoints = NULL;
+                       return qfalse;
+               }
+               trap_BotMatchVariable( &keyareamatch, KEYAREA, keyarea, MAX_MESSAGE_SIZE );
+               if ( !BotGetMessageTeamGoal( bs, keyarea, &goal ) ) {
+                       //BotAI_BotInitialChat(bs, "cannotfind", keyarea, NULL);
+                       //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
+                       BotFreeWaypoints( newpatrolpoints );
+                       bs->patrolpoints = NULL;
+                       return qfalse;
+               }
+               //create a new waypoint
+               newwp = BotCreateWayPoint( keyarea, goal.origin, goal.areanum );
+               //add the waypoint to the patrol points
+               newwp->next = NULL;
+               for ( wp = newpatrolpoints; wp && wp->next; wp = wp->next ) ;
+               if ( !wp ) {
+                       newpatrolpoints = newwp;
+                       newwp->prev = NULL;
+               } else {
+                       wp->next = newwp;
+                       newwp->prev = wp;
+               }
+               //
+               if ( keyareamatch.subtype & ST_BACK ) {
+                       patrolflags = PATROL_LOOP;
+                       break;
+               } else if ( keyareamatch.subtype & ST_REVERSE )     {
+                       patrolflags = PATROL_REVERSE;
+                       break;
+               } else if ( keyareamatch.subtype & ST_MORE )     {
+                       trap_BotMatchVariable( &keyareamatch, MORE, keyarea, MAX_MESSAGE_SIZE );
+               } else {
+                       break;
+               }
+       }
+       //
+       if ( !newpatrolpoints || !newpatrolpoints->next ) {
+               trap_EA_SayTeam( bs->client, "I need more key points to patrol\n" );
+               BotFreeWaypoints( newpatrolpoints );
+               newpatrolpoints = NULL;
+               return qfalse;
+       }
+       //
+       BotFreeWaypoints( bs->patrolpoints );
+       bs->patrolpoints = newpatrolpoints;
+       //
+       bs->curpatrolpoint = bs->patrolpoints;
+       bs->patrolflags = patrolflags;
+       //
+       return qtrue;
+}
+
+/*
+==================
+BotAddressedToBot
+==================
+*/
+int BotAddressedToBot( bot_state_t *bs, bot_match_t *match ) {
+       char addressedto[MAX_MESSAGE_SIZE];
+       char netname[MAX_MESSAGE_SIZE];
+       char name[MAX_MESSAGE_SIZE];
+       char botname[128];
+       int client;
+       bot_match_t addresseematch;
+
+       trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) );
+       client = ClientFromName( netname );
+       if ( client < 0 ) {
+               return qfalse;
+       }
+       if ( !BotSameTeam( bs, client ) ) {
+               return qfalse;
+       }
+       //if the message is addressed to someone
+       if ( match->subtype & ST_ADDRESSED ) {
+               trap_BotMatchVariable( match, ADDRESSEE, addressedto, sizeof( addressedto ) );
+               //the name of this bot
+               ClientName( bs->client, botname, 128 );
+               //
+               while ( trap_BotFindMatch( addressedto, &addresseematch, MTCONTEXT_ADDRESSEE ) ) {
+                       if ( addresseematch.type == MSG_EVERYONE ) {
+                               return qtrue;
+                       } else if ( addresseematch.type == MSG_MULTIPLENAMES )     {
+                               trap_BotMatchVariable( &addresseematch, TEAMMATE, name, sizeof( name ) );
+                               if ( strlen( name ) ) {
+                                       if ( stristr( botname, name ) ) {
+                                               return qtrue;
+                                       }
+                                       if ( stristr( bs->subteam, name ) ) {
+                                               return qtrue;
+                                       }
+                               }
+                               trap_BotMatchVariable( &addresseematch, MORE, addressedto, MAX_MESSAGE_SIZE );
+                       } else {
+                               trap_BotMatchVariable( &addresseematch, TEAMMATE, name, MAX_MESSAGE_SIZE );
+                               if ( strlen( name ) ) {
+                                       if ( stristr( botname, name ) ) {
+                                               return qtrue;
+                                       }
+                                       if ( stristr( bs->subteam, name ) ) {
+                                               return qtrue;
+                                       }
+                               }
+                               break;
+                       }
+               }
+               //Com_sprintf(buf, sizeof(buf), "not addressed to me but %s", addressedto);
+               //trap_EA_Say(bs->client, buf);
+               return qfalse;
+       } else {
+               //make sure not everyone reacts to this message
+               if ( random() > (float ) 1.0 / ( NumPlayersOnSameTeam( bs ) - 1 ) ) {
+                       return qfalse;
+               }
+       }
+       return qtrue;
+}
+
+/*
+==================
+BotGPSToPosition
+==================
+*/
+int BotGPSToPosition( char *buf, vec3_t position ) {
+       int i, j = 0;
+       int num, sign;
+
+       for ( i = 0; i < 3; i++ ) {
+               num = 0;
+               while ( buf[j] == ' ' ) j++;
+               if ( buf[j] == '-' ) {
+                       j++;
+                       sign = -1;
+               } else {
+                       sign = 1;
+               }
+               while ( buf[j] ) {
+                       if ( buf[j] >= '0' && buf[j] <= '9' ) {
+                               num = num * 10 + buf[j] - '0';
+                               j++;
+                       } else {
+                               j++;
+                               break;
+                       }
+               }
+               BotAI_Print( PRT_MESSAGE, "%d\n", sign * num );
+               position[i] = (float) sign * num;
+       }
+       return qtrue;
+}
+
+/*
+==================
+BotMatch_HelpAccompany
+==================
+*/
+void BotMatch_HelpAccompany( bot_state_t *bs, bot_match_t *match ) {
+       int client, other, areanum;
+       char teammate[MAX_MESSAGE_SIZE], netname[MAX_MESSAGE_SIZE];
+       char itemname[MAX_MESSAGE_SIZE];
+       bot_match_t teammatematch;
+       aas_entityinfo_t entinfo;
+
+       if ( !TeamPlayIsOn() ) {
+               return;
+       }
+       //if not addressed to this bot
+       if ( !BotAddressedToBot( bs, match ) ) {
+               return;
+       }
+       //get the team mate name
+       trap_BotMatchVariable( match, TEAMMATE, teammate, sizeof( teammate ) );
+       //get the client to help
+       if ( trap_BotFindMatch( teammate, &teammatematch, MTCONTEXT_TEAMMATE ) &&
+                //if someone asks for him or herself
+                teammatematch.type == MSG_ME ) {
+               //get the netname
+               trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) );
+               client = ClientFromName( netname );
+               other = qfalse;
+       } else {
+               //asked for someone else
+               client = FindClientByName( teammate );
+               //if this is the bot self
+               if ( client == bs->client ) {
+                       other = qfalse;
+               } else if ( !BotSameTeam( bs, client ) )       {
+                       //FIXME: say "I don't help the enemy"
+                       return;
+               } else {
+                       other = qtrue;
+               }
+       }
+       //if the bot doesn't know who to help (FindClientByName returned -1)
+       if ( client < 0 ) {
+               if ( other ) {
+                       BotAI_BotInitialChat( bs, "whois", teammate, NULL );
+               } else { BotAI_BotInitialChat( bs, "whois", netname, NULL );}
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+               return;
+       }
+       //don't help or accompany yourself
+       if ( client == bs->client ) {
+               return;
+       }
+       //
+       bs->teamgoal.entitynum = -1;
+       BotEntityInfo( client, &entinfo );
+       //if info is valid (in PVS)
+       if ( entinfo.valid ) {
+               areanum = BotPointAreaNum( entinfo.origin );
+               if ( areanum && trap_AAS_AreaReachability( areanum ) ) {
+                       bs->teamgoal.entitynum = client;
+                       bs->teamgoal.areanum = areanum;
+                       VectorCopy( entinfo.origin, bs->teamgoal.origin );
+                       VectorSet( bs->teamgoal.mins, -8, -8, -8 );
+                       VectorSet( bs->teamgoal.maxs, 8, 8, 8 );
+               }
+       }
+       //if no teamgoal yet
+       if ( bs->teamgoal.entitynum < 0 ) {
+               //if near an item
+               if ( match->subtype & ST_NEARITEM ) {
+                       //get the match variable
+                       trap_BotMatchVariable( match, ITEM, itemname, sizeof( itemname ) );
+                       //
+                       if ( !BotGetMessageTeamGoal( bs, itemname, &bs->teamgoal ) ) {
+                               //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL);
+                               //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
+                               return;
+                       }
+               }
+       }
+       //
+       if ( bs->teamgoal.entitynum < 0 ) {
+               if ( other ) {
+                       BotAI_BotInitialChat( bs, "whereis", teammate, NULL );
+               } else { BotAI_BotInitialChat( bs, "whereareyou", netname, NULL );}
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+               return;
+       }
+       //the team mate
+       bs->teammate = client;
+       //last time the team mate was assumed visible
+       bs->teammatevisible_time = trap_AAS_Time();
+       //set the time to send a message to the team mates
+       bs->teammessage_time = trap_AAS_Time() + 2 * random();
+       //get the team goal time
+       bs->teamgoal_time = BotGetTime( match );
+       //set the ltg type
+       if ( match->type == MSG_HELP ) {
+               bs->ltgtype = LTG_TEAMHELP;
+               if ( !bs->teamgoal_time ) {
+                       bs->teamgoal_time = trap_AAS_Time() + TEAM_HELP_TIME;
+               }
+       } else {
+               bs->ltgtype = LTG_TEAMACCOMPANY;
+               if ( !bs->teamgoal_time ) {
+                       bs->teamgoal_time = trap_AAS_Time() + TEAM_ACCOMPANY_TIME;
+               }
+               bs->formation_dist = 3.5 * 32;      //3.5 meter
+               bs->arrive_time = 0;
+       }
+#ifdef DEBUG
+       BotPrintTeamGoal( bs );
+#endif //DEBUG
+}
+
+/*
+==================
+BotMatch_DefendKeyArea
+==================
+*/
+void BotMatch_DefendKeyArea( bot_state_t *bs, bot_match_t *match ) {
+       char itemname[MAX_MESSAGE_SIZE];
+
+       if ( !TeamPlayIsOn() ) {
+               return;
+       }
+       //if not addressed to this bot
+       if ( !BotAddressedToBot( bs, match ) ) {
+               return;
+       }
+       //get the match variable
+       trap_BotMatchVariable( match, KEYAREA, itemname, sizeof( itemname ) );
+       //
+       if ( !BotGetMessageTeamGoal( bs, itemname, &bs->teamgoal ) ) {
+               //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL);
+               //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
+               return;
+       }
+       //set the time to send a message to the team mates
+       bs->teammessage_time = trap_AAS_Time() + 2 * random();
+       //set the ltg type
+       bs->ltgtype = LTG_DEFENDKEYAREA;
+       //get the team goal time
+       bs->teamgoal_time = BotGetTime( match );
+       //set the team goal time
+       if ( !bs->teamgoal_time ) {
+               bs->teamgoal_time = trap_AAS_Time() + TEAM_DEFENDKEYAREA_TIME;
+       }
+       //away from defending
+       bs->defendaway_time = 0;
+#ifdef DEBUG
+       BotPrintTeamGoal( bs );
+#endif //DEBUG
+}
+
+/*
+==================
+BotMatch_GetItem
+==================
+*/
+void BotMatch_GetItem( bot_state_t *bs, bot_match_t *match ) {
+       char itemname[MAX_MESSAGE_SIZE];
+
+       if ( !TeamPlayIsOn() ) {
+               return;
+       }
+       //if not addressed to this bot
+       if ( !BotAddressedToBot( bs, match ) ) {
+               return;
+       }
+       //get the match variable
+       trap_BotMatchVariable( match, ITEM, itemname, sizeof( itemname ) );
+       //
+       if ( !BotGetMessageTeamGoal( bs, itemname, &bs->teamgoal ) ) {
+               //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL);
+               //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
+               return;
+       }
+       //set the time to send a message to the team mates
+       bs->teammessage_time = trap_AAS_Time() + 2 * random();
+       //set the ltg type
+       bs->ltgtype = LTG_GETITEM;
+       //set the team goal time
+       bs->teamgoal_time = trap_AAS_Time() + TEAM_GETITEM_TIME;
+#ifdef DEBUG
+       BotPrintTeamGoal( bs );
+#endif //DEBUG
+}
+
+/*
+==================
+BotMatch_Camp
+==================
+*/
+void BotMatch_Camp( bot_state_t *bs, bot_match_t *match ) {
+       int client, areanum;
+       char netname[MAX_MESSAGE_SIZE];
+       char itemname[MAX_MESSAGE_SIZE];
+       aas_entityinfo_t entinfo;
+
+       if ( !TeamPlayIsOn() ) {
+               return;
+       }
+       //if not addressed to this bot
+       if ( !BotAddressedToBot( bs, match ) ) {
+               return;
+       }
+       //
+       trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) );
+       //asked for someone else
+       client = FindClientByName( netname );
+       //if there's no valid client with this name
+       if ( client < 0 ) {
+               BotAI_BotInitialChat( bs, "whois", netname, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+               return;
+       }
+       //get the match variable
+       trap_BotMatchVariable( match, KEYAREA, itemname, sizeof( itemname ) );
+       //in CTF it could be the base
+       if ( match->subtype & ST_THERE ) {
+               //camp at the spot the bot is currently standing
+               bs->teamgoal.entitynum = bs->entitynum;
+               bs->teamgoal.areanum = bs->areanum;
+               VectorCopy( bs->origin, bs->teamgoal.origin );
+               VectorSet( bs->teamgoal.mins, -8, -8, -8 );
+               VectorSet( bs->teamgoal.maxs, 8, 8, 8 );
+       } else if ( match->subtype & ST_HERE )     {
+               //if this is the bot self
+               if ( client == bs->client ) {
+                       return;
+               }
+               //
+               bs->teamgoal.entitynum = -1;
+               BotEntityInfo( client, &entinfo );
+               //if info is valid (in PVS)
+               if ( entinfo.valid ) {
+                       areanum = BotPointAreaNum( entinfo.origin );
+                       if ( areanum && trap_AAS_AreaReachability( areanum ) ) {
+                               //NOTE: just cheat and assume the bot knows where the person is
+                               //if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, client)) {
+                               bs->teamgoal.entitynum = client;
+                               bs->teamgoal.areanum = areanum;
+                               VectorCopy( entinfo.origin, bs->teamgoal.origin );
+                               VectorSet( bs->teamgoal.mins, -8, -8, -8 );
+                               VectorSet( bs->teamgoal.maxs, 8, 8, 8 );
+                               //}
+                       }
+               }
+               //if the other is not visible
+               if ( bs->teamgoal.entitynum < 0 ) {
+                       BotAI_BotInitialChat( bs, "whereareyou", netname, NULL );
+                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       return;
+               }
+       } else if ( !BotGetMessageTeamGoal( bs, itemname, &bs->teamgoal ) )       {
+               //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL);
+               //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
+               return;
+       }
+       //set the time to send a message to the team mates
+       bs->teammessage_time = trap_AAS_Time() + 2 * random();
+       //set the ltg type
+       bs->ltgtype = LTG_CAMPORDER;
+       //get the team goal time
+       bs->teamgoal_time = BotGetTime( match );
+       //set the team goal time
+       if ( !bs->teamgoal_time ) {
+               bs->teamgoal_time = trap_AAS_Time() + TEAM_CAMP_TIME;
+       }
+       //the teammate that requested the camping
+       bs->teammate = client;
+       //not arrived yet
+       bs->arrive_time = 0;
+       //
+#ifdef DEBUG
+       BotPrintTeamGoal( bs );
+#endif //DEBUG
+}
+
+/*
+==================
+BotMatch_Patrol
+==================
+*/
+void BotMatch_Patrol( bot_state_t *bs, bot_match_t *match ) {
+       if ( !TeamPlayIsOn() ) {
+               return;
+       }
+       //if not addressed to this bot
+       if ( !BotAddressedToBot( bs, match ) ) {
+               return;
+       }
+       //get the patrol waypoints
+       if ( !BotGetPatrolWaypoints( bs, match ) ) {
+               return;
+       }
+       //set the time to send a message to the team mates
+       bs->teammessage_time = trap_AAS_Time() + 2 * random();
+       //set the ltg type
+       bs->ltgtype = LTG_PATROL;
+       //get the team goal time
+       bs->teamgoal_time = BotGetTime( match );
+       //set the team goal time if not set already
+       if ( !bs->teamgoal_time ) {
+               bs->teamgoal_time = trap_AAS_Time() + TEAM_PATROL_TIME;
+       }
+       //
+#ifdef DEBUG
+       BotPrintTeamGoal( bs );
+#endif //DEBUG
+}
+
+/*
+==================
+BotMatch_GetFlag
+==================
+*/
+void BotMatch_GetFlag( bot_state_t *bs, bot_match_t *match ) {
+       //if not in CTF mode
+       if ( gametype != GT_CTF || !ctf_redflag.areanum || !ctf_blueflag.areanum ) {
+               return;
+       }
+       //if not addressed to this bot
+       if ( !BotAddressedToBot( bs, match ) ) {
+               return;
+       }
+       //set the time to send a message to the team mates
+       bs->teammessage_time = trap_AAS_Time() + 2 * random();
+       //set the ltg type
+       bs->ltgtype = LTG_GETFLAG;
+       //set the team goal time
+       bs->teamgoal_time = trap_AAS_Time() + CTF_GETFLAG_TIME;
+#ifdef DEBUG
+       BotPrintTeamGoal( bs );
+#endif //DEBUG
+}
+
+/*
+==================
+BotMatch_RushBase
+==================
+*/
+void BotMatch_RushBase( bot_state_t *bs, bot_match_t *match ) {
+       //if not in CTF mode
+       if ( gametype != GT_CTF || !ctf_redflag.areanum || !ctf_blueflag.areanum ) {
+               return;
+       }
+       //if not addressed to this bot
+       if ( !BotAddressedToBot( bs, match ) ) {
+               return;
+       }
+       //set the time to send a message to the team mates
+       bs->teammessage_time = trap_AAS_Time() + 2 * random();
+       //set the ltg type
+       bs->ltgtype = LTG_RUSHBASE;
+       //set the team goal time
+       bs->teamgoal_time = trap_AAS_Time() + CTF_RUSHBASE_TIME;
+       bs->rushbaseaway_time = 0;
+#ifdef DEBUG
+       BotPrintTeamGoal( bs );
+#endif //DEBUG
+}
+
+
+/*
+==================
+BotMatch_ReturnFlag
+==================
+*/
+void BotMatch_ReturnFlag( bot_state_t *bs, bot_match_t *match ) {
+       //if not in CTF mode
+       if ( gametype != GT_CTF ) {
+               return;
+       }
+       //if not addressed to this bot
+       if ( !BotAddressedToBot( bs, match ) ) {
+               return;
+       }
+       //set the time to send a message to the team mates
+       bs->teammessage_time = trap_AAS_Time() + 2 * random();
+       //set the ltg type
+       bs->ltgtype = LTG_RETURNFLAG;
+       //set the team goal time
+       bs->teamgoal_time = trap_AAS_Time() + CTF_RETURNFLAG_TIME;
+       bs->rushbaseaway_time = 0;
+#ifdef DEBUG
+       BotPrintTeamGoal( bs );
+#endif //DEBUG
+}
+
+/*
+==================
+BotMatch_JoinSubteam
+==================
+*/
+void BotMatch_JoinSubteam( bot_state_t *bs, bot_match_t *match ) {
+       char teammate[MAX_MESSAGE_SIZE];
+
+       if ( !TeamPlayIsOn() ) {
+               return;
+       }
+       //if not addressed to this bot
+       if ( !BotAddressedToBot( bs, match ) ) {
+               return;
+       }
+       //get the sub team name
+       trap_BotMatchVariable( match, TEAMNAME, teammate, MAX_MESSAGE_SIZE );
+       //set the sub team name
+       strncpy( bs->subteam, teammate, 32 );
+       bs->subteam[31] = '\0';
+       //
+       BotAI_BotInitialChat( bs, "joinedteam", teammate, NULL );
+       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+}
+
+/*
+==================
+BotMatch_LeaveSubteam
+==================
+*/
+void BotMatch_LeaveSubteam( bot_state_t *bs, bot_match_t *match ) {
+       if ( !TeamPlayIsOn() ) {
+               return;
+       }
+       //if not addressed to this bot
+       if ( !BotAddressedToBot( bs, match ) ) {
+               return;
+       }
+       //
+       if ( strlen( bs->subteam ) ) {
+               BotAI_BotInitialChat( bs, "leftteam", bs->subteam, NULL );
+       } //end if
+       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+       strcpy( bs->subteam, "" );
+}
+
+/*
+==================
+BotMatch_LeaveSubteam
+==================
+*/
+void BotMatch_WhichTeam( bot_state_t *bs, bot_match_t *match ) {
+       if ( !TeamPlayIsOn() ) {
+               return;
+       }
+       //if not addressed to this bot
+       if ( !BotAddressedToBot( bs, match ) ) {
+               return;
+       }
+       //
+       if ( strlen( bs->subteam ) ) {
+               BotAI_BotInitialChat( bs, "inteam", bs->subteam, NULL );
+       } else {
+               BotAI_BotInitialChat( bs, "noteam", NULL );
+       }
+       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+}
+
+/*
+==================
+BotMatch_CheckPoint
+==================
+*/
+void BotMatch_CheckPoint( bot_state_t *bs, bot_match_t *match ) {
+       int areanum;
+       char buf[MAX_MESSAGE_SIZE];
+       vec3_t position;
+       bot_waypoint_t *cp;
+
+       if ( !TeamPlayIsOn() ) {
+               return;
+       }
+       //
+       trap_BotMatchVariable( match, POSITION, buf, MAX_MESSAGE_SIZE );
+       VectorClear( position );
+       //BotGPSToPosition(buf, position);
+       sscanf( buf, "%f %f %f", &position[0], &position[1], &position[2] );
+       position[2] += 0.5;
+       areanum = BotPointAreaNum( position );
+       if ( !areanum ) {
+               if ( BotAddressedToBot( bs, match ) ) {
+                       BotAI_BotInitialChat( bs, "checkpoint_invalid", NULL );
+                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+               }
+               return;
+       }
+       //
+       trap_BotMatchVariable( match, NAME, buf, MAX_MESSAGE_SIZE );
+       //check if there already exists a checkpoint with this name
+       cp = BotFindWayPoint( bs->checkpoints, buf );
+       if ( cp ) {
+               if ( cp->next ) {
+                       cp->next->prev = cp->prev;
+               }
+               if ( cp->prev ) {
+                       cp->prev->next = cp->next;
+               } else { bs->checkpoints = cp->next;}
+               cp->inuse = qfalse;
+       }
+       //create a new check point
+       cp = BotCreateWayPoint( buf, position, areanum );
+       //add the check point to the bot's known chech points
+       cp->next = bs->checkpoints;
+       if ( bs->checkpoints ) {
+               bs->checkpoints->prev = cp;
+       }
+       bs->checkpoints = cp;
+       //
+       if ( BotAddressedToBot( bs, match ) ) {
+               Com_sprintf( buf, sizeof( buf ), "%1.0f %1.0f %1.0f", cp->goal.origin[0],
+                                        cp->goal.origin[1],
+                                        cp->goal.origin[2] );
+
+               BotAI_BotInitialChat( bs, "checkpoint_confirm", cp->name, buf, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+       }
+}
+
+/*
+==================
+BotMatch_FormationSpace
+==================
+*/
+void BotMatch_FormationSpace( bot_state_t *bs, bot_match_t *match ) {
+       char buf[MAX_MESSAGE_SIZE];
+       float space;
+
+       if ( !TeamPlayIsOn() ) {
+               return;
+       }
+       //if not addressed to this bot
+       if ( !BotAddressedToBot( bs, match ) ) {
+               return;
+       }
+       //
+       trap_BotMatchVariable( match, NUMBER, buf, MAX_MESSAGE_SIZE );
+       //if it's the distance in feet
+       if ( match->subtype & ST_FEET ) {
+               space = 0.3048 * 32 * atof( buf );
+       }
+       //else it's in meters
+       else {space = 32 * atof( buf );}
+       //check if the formation intervening space is valid
+       if ( space < 48 || space > 500 ) {
+               space = 100;
+       }
+       bs->formation_dist = space;
+}
+
+/*
+==================
+BotMatch_Dismiss
+==================
+*/
+void BotMatch_Dismiss( bot_state_t *bs, bot_match_t *match ) {
+       if ( !TeamPlayIsOn() ) {
+               return;
+       }
+       //if not addressed to this bot
+       if ( !BotAddressedToBot( bs, match ) ) {
+               return;
+       }
+       //
+       bs->ltgtype = 0;
+       bs->lead_time = 0;
+       //
+       BotAI_BotInitialChat( bs, "dismissed", NULL );
+       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+}
+
+/*
+==================
+BotMatch_StartTeamLeaderShip
+==================
+*/
+void BotMatch_StartTeamLeaderShip( bot_state_t *bs, bot_match_t *match ) {
+       int client;
+       char teammate[MAX_MESSAGE_SIZE];
+
+       if ( !TeamPlayIsOn() ) {
+               return;
+       }
+       //if chats for him or herself
+       if ( match->subtype & ST_I ) {
+               //get the team mate that will be the team leader
+               trap_BotMatchVariable( match, NETNAME, teammate, sizeof( teammate ) );
+               strncpy( bs->teamleader, teammate, sizeof( bs->teamleader ) );
+               bs->teamleader[sizeof( bs->teamleader )] = '\0';
+       }
+       //chats for someone else
+       else {
+               //get the team mate that will be the team leader
+               trap_BotMatchVariable( match, TEAMMATE, teammate, sizeof( teammate ) );
+               client = FindClientByName( teammate );
+               if ( client >= 0 ) {
+                       ClientName( client, bs->teamleader, sizeof( bs->teamleader ) );
+               }
+       }
+}
+
+/*
+==================
+BotMatch_StopTeamLeaderShip
+==================
+*/
+void BotMatch_StopTeamLeaderShip( bot_state_t *bs, bot_match_t *match ) {
+       int client;
+       char teammate[MAX_MESSAGE_SIZE];
+       char netname[MAX_MESSAGE_SIZE];
+
+       if ( !TeamPlayIsOn() ) {
+               return;
+       }
+       //get the team mate that stops being the team leader
+       trap_BotMatchVariable( match, TEAMMATE, teammate, sizeof( teammate ) );
+       //if chats for him or herself
+       if ( match->subtype & ST_I ) {
+               trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) );
+               client = FindClientByName( netname );
+       }
+       //chats for someone else
+       else {
+               client = FindClientByName( teammate );
+       } //end else
+       if ( client >= 0 ) {
+               if ( !Q_stricmp( bs->teamleader, ClientName( client, netname, sizeof( netname ) ) ) ) {
+                       bs->teamleader[0] = '\0';
+               }
+       }
+}
+
+/*
+==================
+BotMatch_WhoIsTeamLeader
+==================
+*/
+void BotMatch_WhoIsTeamLeader( bot_state_t *bs, bot_match_t *match ) {
+       char netname[MAX_MESSAGE_SIZE];
+
+       if ( !TeamPlayIsOn() ) {
+               return;
+       }
+
+       ClientName( bs->client, netname, sizeof( netname ) );
+       //if this bot IS the team leader
+       if ( !Q_stricmp( netname, bs->teamleader ) ) {
+               trap_EA_SayTeam( bs->client, "I'm the team leader\n" );
+       }
+}
+
+/*
+==================
+BotMatch_WhatAreYouDoing
+==================
+*/
+void BotMatch_WhatAreYouDoing( bot_state_t *bs, bot_match_t *match ) {
+       char netname[MAX_MESSAGE_SIZE];
+       char goalname[MAX_MESSAGE_SIZE];
+
+       //if not addressed to this bot
+       if ( !BotAddressedToBot( bs, match ) ) {
+               return;
+       }
+       //
+       switch ( bs->ltgtype ) {
+       case LTG_TEAMHELP:
+       {
+               trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) );
+               EasyClientName( bs->teammate, netname, MAX_MESSAGE_SIZE );
+               BotAI_BotInitialChat( bs, "helping", netname, NULL );
+               break;
+       }
+       case LTG_TEAMACCOMPANY:
+       {
+               trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) );
+               EasyClientName( bs->teammate, netname, MAX_MESSAGE_SIZE );
+               BotAI_BotInitialChat( bs, "accompanying", netname, NULL );
+               break;
+       }
+       case LTG_DEFENDKEYAREA:
+       {
+               trap_BotGoalName( bs->teamgoal.number, goalname, sizeof( goalname ) );
+               BotAI_BotInitialChat( bs, "defending", goalname, NULL );
+               break;
+       }
+       case LTG_GETITEM:
+       {
+               trap_BotGoalName( bs->teamgoal.number, goalname, sizeof( goalname ) );
+               BotAI_BotInitialChat( bs, "gettingitem", goalname, NULL );
+               break;
+       }
+       case LTG_KILL:
+       {
+               ClientName( bs->teamgoal.entitynum, netname, sizeof( netname ) );
+               BotAI_BotInitialChat( bs, "killing", netname, NULL );
+               break;
+       }
+       case LTG_CAMP:
+       case LTG_CAMPORDER:
+       {
+               BotAI_BotInitialChat( bs, "camping", NULL );
+               break;
+       }
+       case LTG_PATROL:
+       {
+               BotAI_BotInitialChat( bs, "patrolling", NULL );
+               break;
+       }
+       case LTG_GETFLAG:
+       {
+               BotAI_BotInitialChat( bs, "capturingflag", NULL );
+               break;
+       }
+       case LTG_RUSHBASE:
+       {
+               BotAI_BotInitialChat( bs, "rushingbase", NULL );
+               break;
+       }
+       case LTG_RETURNFLAG:
+       {
+               BotAI_BotInitialChat( bs, "returningflag", NULL );
+               break;
+       }
+       default:
+       {
+               BotAI_BotInitialChat( bs, "roaming", NULL );
+               break;
+       }
+       }
+       //chat what the bot is doing
+       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+}
+
+/*
+==================
+BotMatch_WhatIsMyCommand
+==================
+*/
+void BotMatch_WhatIsMyCommand( bot_state_t *bs, bot_match_t *match ) {
+       char netname[MAX_NETNAME];
+
+       ClientName( bs->client, netname, sizeof( netname ) );
+       if ( Q_stricmp( netname, bs->teamleader ) != 0 ) {
+               return;
+       }
+       bs->forceorders = qtrue;
+}
+
+/*
+==================
+BotNearestVisibleItem
+==================
+*/
+float BotNearestVisibleItem( bot_state_t *bs, char *itemname, bot_goal_t *goal ) {
+       int i;
+       char name[64];
+       bot_goal_t tmpgoal;
+       float dist, bestdist;
+       vec3_t dir;
+       bsp_trace_t trace;
+
+       bestdist = 999999;
+       i = -1;
+       do {
+               i = trap_BotGetLevelItemGoal( i, itemname, &tmpgoal );
+               trap_BotGoalName( tmpgoal.number, name, sizeof( name ) );
+               if ( Q_stricmp( itemname, name ) != 0 ) {
+                       continue;
+               }
+               VectorSubtract( tmpgoal.origin, bs->origin, dir );
+               dist = VectorLength( dir );
+               if ( dist < bestdist ) {
+                       //trace from start to end
+                       BotAI_Trace( &trace, bs->eye, NULL, NULL, tmpgoal.origin, bs->client, CONTENTS_SOLID | CONTENTS_PLAYERCLIP );
+                       if ( trace.fraction >= 1.0 ) {
+                               bestdist = dist;
+                               memcpy( goal, &tmpgoal, sizeof( bot_goal_t ) );
+                       }
+               }
+       } while ( i > 0 );
+       return bestdist;
+}
+
+/*
+==================
+BotMatch_WhereAreYou
+==================
+*/
+void BotMatch_WhereAreYou( bot_state_t *bs, bot_match_t *match ) {
+       float dist, bestdist;
+       int i, bestitem, redflagtt, blueflagtt, redtobluett;
+       bot_goal_t goal;
+       char *nearbyitems[] = {
+               "Shotgun",
+               "Grenade Launcher",
+               "Rocket Launcher",
+               "Plasmagun",
+               "Railgun",
+               "Lightning Gun",
+               "BFG10K",
+               "Quad Damage",
+               "Regeneration",
+               "Battle Suit",
+               "Speed",
+               "Invisibility",
+               "Flight",
+               "Armor",
+               "Heavy Armor",
+               "Red Flag",
+               "Blue Flag",
+               NULL
+       };
+       //
+       if ( !TeamPlayIsOn() ) {
+               return;
+       }
+       //if not addressed to this bot
+       if ( !BotAddressedToBot( bs, match ) ) {
+               return;
+       }
+
+       bestitem = -1;
+       bestdist = 999999;
+       for ( i = 0; nearbyitems[i]; i++ ) {
+               dist = BotNearestVisibleItem( bs, nearbyitems[i], &goal );
+               if ( dist < bestdist ) {
+                       bestdist = dist;
+                       bestitem = i;
+               }
+       }
+       if ( bestitem != -1 ) {
+               if ( gametype == GT_CTF ) {
+                       redflagtt = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, ctf_redflag.areanum, TFL_DEFAULT );
+                       blueflagtt = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, ctf_blueflag.areanum, TFL_DEFAULT );
+                       redtobluett = trap_AAS_AreaTravelTimeToGoalArea( ctf_redflag.areanum, ctf_redflag.origin, ctf_blueflag.areanum, TFL_DEFAULT );
+                       if ( redflagtt < ( redflagtt + blueflagtt ) * 0.4 ) {
+                               BotAI_BotInitialChat( bs, "ctflocation", nearbyitems[bestitem], "red", NULL );
+                       } else if ( blueflagtt < ( redflagtt + blueflagtt ) * 0.4 )       {
+                               BotAI_BotInitialChat( bs, "ctflocation", nearbyitems[bestitem], "blue", NULL );
+                       } else {
+                               BotAI_BotInitialChat( bs, "location", nearbyitems[bestitem], NULL );
+                       }
+               } else {
+                       BotAI_BotInitialChat( bs, "location", nearbyitems[bestitem], NULL );
+               }
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+       }
+}
+
+/*
+==================
+BotMatch_LeadTheWay
+==================
+*/
+void BotMatch_LeadTheWay( bot_state_t *bs, bot_match_t *match ) {
+       aas_entityinfo_t entinfo;
+       char netname[MAX_MESSAGE_SIZE], teammate[MAX_MESSAGE_SIZE];
+       int client, areanum, other;
+
+       if ( !TeamPlayIsOn() ) {
+               return;
+       }
+       //if not addressed to this bot
+       if ( !BotAddressedToBot( bs, match ) ) {
+               return;
+       }
+       //if someone asks for someone else
+       if ( match->subtype & ST_SOMEONE ) {
+               //get the team mate name
+               trap_BotMatchVariable( match, TEAMMATE, teammate, sizeof( teammate ) );
+               client = FindClientByName( teammate );
+               //if this is the bot self
+               if ( client == bs->client ) {
+                       other = qfalse;
+               } else if ( !BotSameTeam( bs, client ) )       {
+                       //FIXME: say "I don't help the enemy"
+                       return;
+               } else {
+                       other = qtrue;
+               }
+       } else {
+               //get the netname
+               trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) );
+               client = ClientFromName( netname );
+               other = qfalse;
+       }
+       //if the bot doesn't know who to help (FindClientByName returned -1)
+       if ( client < 0 ) {
+               BotAI_BotInitialChat( bs, "whois", netname, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+               return;
+       }
+       //
+       bs->lead_teamgoal.entitynum = -1;
+       BotEntityInfo( client, &entinfo );
+       //if info is valid (in PVS)
+       if ( entinfo.valid ) {
+               areanum = BotPointAreaNum( entinfo.origin );
+               if ( areanum && trap_AAS_AreaReachability( areanum ) ) {
+                       bs->lead_teamgoal.entitynum = client;
+                       bs->lead_teamgoal.areanum = areanum;
+                       VectorCopy( entinfo.origin, bs->lead_teamgoal.origin );
+                       VectorSet( bs->lead_teamgoal.mins, -8, -8, -8 );
+                       VectorSet( bs->lead_teamgoal.maxs, 8, 8, 8 );
+               }
+       }
+
+       if ( bs->teamgoal.entitynum < 0 ) {
+               if ( other ) {
+                       BotAI_BotInitialChat( bs, "whereis", teammate, NULL );
+               } else { BotAI_BotInitialChat( bs, "whereareyou", netname, NULL );}
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+               return;
+       }
+       bs->lead_teammate = client;
+       bs->lead_time = trap_AAS_Time() + TEAM_LEAD_TIME;
+       bs->leadvisible_time = 0;
+       bs->leadmessage_time = -( trap_AAS_Time() + 2 * random() );
+}
+
+/*
+==================
+BotMatch_Kill
+==================
+*/
+void BotMatch_Kill( bot_state_t *bs, bot_match_t *match ) {
+       char enemy[MAX_MESSAGE_SIZE];
+       int client;
+
+       if ( !TeamPlayIsOn() ) {
+               return;
+       }
+       //if not addressed to this bot
+       if ( !BotAddressedToBot( bs, match ) ) {
+               return;
+       }
+
+       trap_BotMatchVariable( match, ENEMY, enemy, sizeof( enemy ) );
+       //
+       client = FindEnemyByName( bs, enemy );
+       if ( client < 0 ) {
+               BotAI_BotInitialChat( bs, "whois", enemy, NULL );
+               trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+               return;
+       }
+       bs->teamgoal.entitynum = client;
+       //set the time to send a message to the team mates
+       bs->teammessage_time = trap_AAS_Time() + 2 * random();
+       //set the ltg type
+       bs->ltgtype = LTG_KILL;
+       //set the team goal time
+       bs->teamgoal_time = trap_AAS_Time() + TEAM_KILL_SOMEONE;
+#ifdef DEBUG
+       BotPrintTeamGoal( bs );
+#endif //DEBUG
+}
+
+/*
+==================
+BotMatch_CTF
+==================
+*/
+void BotMatch_CTF( bot_state_t *bs, bot_match_t *match ) {
+
+       char flag[128], netname[MAX_NETNAME];
+
+       trap_BotMatchVariable( match, FLAG, flag, sizeof( flag ) );
+       if ( match->subtype & ST_GOTFLAG ) {
+               if ( !Q_stricmp( flag, "red" ) ) {
+                       bs->redflagstatus = 1;
+                       if ( BotCTFTeam( bs ) == CTF_TEAM_BLUE ) {
+                               trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) );
+                               bs->flagcarrier = ClientFromName( netname );
+                       }
+               } else {
+                       bs->blueflagstatus = 1;
+                       if ( BotCTFTeam( bs ) == CTF_TEAM_RED ) {
+                               trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) );
+                               bs->flagcarrier = ClientFromName( netname );
+                       }
+               }
+               bs->flagstatuschanged = 1;
+       } else if ( match->subtype & ST_CAPTUREDFLAG )     {
+               bs->redflagstatus = 0;
+               bs->blueflagstatus = 0;
+               bs->flagcarrier = 0;
+               bs->flagstatuschanged = 1;
+       } else if ( match->subtype & ST_RETURNEDFLAG )     {
+               if ( !Q_stricmp( flag, "red" ) ) {
+                       bs->redflagstatus = 0;
+               } else { bs->blueflagstatus = 0;}
+               bs->flagstatuschanged = 1;
+       }
+}
+
+/*
+==================
+BotMatchMessage
+==================
+*/
+int BotMatchMessage( bot_state_t *bs, char *message ) {
+       bot_match_t match;
+
+       match.type = 0;
+       //if it is an unknown message
+       if ( !trap_BotFindMatch( message, &match, MTCONTEXT_ENTERGAME
+                                                        | MTCONTEXT_INITIALTEAMCHAT
+                                                        | MTCONTEXT_CTF ) ) {
+               return qfalse;
+       }
+       //react to the found message
+       switch ( match.type ) {
+       case MSG_HELP:                      //someone calling for help
+       case MSG_ACCOMPANY:                 //someone calling for company
+       {
+               BotMatch_HelpAccompany( bs, &match );
+               break;
+       }
+       case MSG_DEFENDKEYAREA:             //teamplay defend a key area
+       {
+               BotMatch_DefendKeyArea( bs, &match );
+               break;
+       }
+       case MSG_CAMP:                      //camp somewhere
+       {
+               BotMatch_Camp( bs, &match );
+               break;
+       }
+       case MSG_PATROL:                    //patrol between several key areas
+       {
+               BotMatch_Patrol( bs, &match );
+               break;
+       }
+#ifdef CTF
+       case MSG_GETFLAG:                   //ctf get the enemy flag
+       {
+               BotMatch_GetFlag( bs, &match );
+               break;
+       }
+       case MSG_RUSHBASE:                  //ctf rush to the base
+       {
+               BotMatch_RushBase( bs, &match );
+               break;
+       }
+       case MSG_RETURNFLAG:
+       {
+               BotMatch_ReturnFlag( bs, &match );
+               break;
+       }
+#endif //CTF
+       case MSG_GETITEM:
+       {
+               BotMatch_GetItem( bs, &match );
+               break;
+       }
+       case MSG_JOINSUBTEAM:               //join a sub team
+       {
+               BotMatch_JoinSubteam( bs, &match );
+               break;
+       }
+       case MSG_LEAVESUBTEAM:              //leave a sub team
+       {
+               BotMatch_LeaveSubteam( bs, &match );
+               break;
+       }
+       case MSG_WHICHTEAM:
+       {
+               BotMatch_WhichTeam( bs, &match );
+               break;
+       }
+       case MSG_CHECKPOINT:                //remember a check point
+       {
+               BotMatch_CheckPoint( bs, &match );
+               break;
+       }
+       case MSG_CREATENEWFORMATION:        //start the creation of a new formation
+       {
+               trap_EA_SayTeam( bs->client, "the part of my brain to create formations has been damaged" );
+               break;
+       }
+       case MSG_FORMATIONPOSITION:         //tell someone his/her position in the formation
+       {
+               trap_EA_SayTeam( bs->client, "the part of my brain to create formations has been damaged" );
+               break;
+       }
+       case MSG_FORMATIONSPACE:            //set the formation space
+       {
+               BotMatch_FormationSpace( bs, &match );
+               break;
+       }
+       case MSG_DOFORMATION:               //form a certain formation
+       {
+               break;
+       }
+       case MSG_DISMISS:                   //dismiss someone
+       {
+               BotMatch_Dismiss( bs, &match );
+               break;
+       }
+       case MSG_STARTTEAMLEADERSHIP:       //someone will become the team leader
+       {
+               BotMatch_StartTeamLeaderShip( bs, &match );
+               break;
+       }
+       case MSG_STOPTEAMLEADERSHIP:        //someone will stop being the team leader
+       {
+               BotMatch_StopTeamLeaderShip( bs, &match );
+               break;
+       }
+       case MSG_WHOISTEAMLAEDER:
+       {
+               BotMatch_WhoIsTeamLeader( bs, &match );
+               break;
+       }
+       case MSG_WHATAREYOUDOING:           //ask a bot what he/she is doing
+       {
+               BotMatch_WhatAreYouDoing( bs, &match );
+               break;
+       }
+       case MSG_WHATISMYCOMMAND:
+       {
+               BotMatch_WhatIsMyCommand( bs, &match );
+               break;
+       }
+       case MSG_WHEREAREYOU:
+       {
+               BotMatch_WhereAreYou( bs, &match );
+               break;
+       }
+       case MSG_LEADTHEWAY:
+       {
+               BotMatch_LeadTheWay( bs, &match );
+               break;
+       }
+       case MSG_KILL:
+       {
+               BotMatch_Kill( bs, &match );
+               break;
+       }
+       case MSG_ENTERGAME:                 //someone entered the game
+       {
+               //NOTE: eliza chats will catch this
+               //BotMatchVariable(&match, NETNAME, netname);
+               //Com_sprintf(buf, sizeof(buf), "heya %s", netname);
+               //EA_Say(bs->client, buf);
+               break;
+       }
+       case MSG_CTF:
+       {
+               BotMatch_CTF( bs, &match );
+               break;
+       }
+       case MSG_WAIT:
+       {
+               break;
+       }
+       default:
+       {
+               BotAI_Print( PRT_MESSAGE, "unknown match type\n" );
+               break;
+       }
+       }
+       return qtrue;
+}
diff --git a/src/botai/ai_cmd.h b/src/botai/ai_cmd.h
new file mode 100644 (file)
index 0000000..eb91c36
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+===========================================================================
+
+Return to Castle Wolfenstein single player GPL Source Code
+Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. 
+
+This file is part of the Return to Castle Wolfenstein single player GPL Source Code (\93RTCW SP Source Code\94).  
+
+RTCW SP Source Code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+RTCW SP Source Code is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
+
+In addition, the RTCW SP 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 SP Source Code.  If not, please request a copy in writing from id Software at the address below.
+
+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.
+
+===========================================================================
+*/
+
+
+/*****************************************************************************
+ * name:               ai_cmd.h
+ *
+ * desc:               Quake3 bot AI
+ *
+ *
+ *****************************************************************************/
+
+int BotMatchMessage( bot_state_t *bs, char *message );
+void BotPrintTeamGoal( bot_state_t *bs );
+
diff --git a/src/botai/ai_dmnet.c b/src/botai/ai_dmnet.c
new file mode 100644 (file)
index 0000000..1b19160
--- /dev/null
@@ -0,0 +1,2045 @@
+/*
+===========================================================================
+
+Return to Castle Wolfenstein single player GPL Source Code
+Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. 
+
+This file is part of the Return to Castle Wolfenstein single player GPL Source Code (\93RTCW SP Source Code\94).  
+
+RTCW SP Source Code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+RTCW SP Source Code is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
+
+In addition, the RTCW SP 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 SP Source Code.  If not, please request a copy in writing from id Software at the address below.
+
+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.
+
+===========================================================================
+*/
+
+
+/*****************************************************************************
+ * name:               ai_dmnet.c
+ *
+ * desc:               Quake3 bot AI
+ *
+ *
+ *****************************************************************************/
+
+#include "../game/g_local.h"
+#include "../game/botlib.h"
+#include "../game/be_aas.h"
+#include "../game/be_ea.h"
+#include "../game/be_ai_char.h"
+#include "../game/be_ai_chat.h"
+#include "../game/be_ai_gen.h"
+#include "../game/be_ai_goal.h"
+#include "../game/be_ai_move.h"
+#include "../game/be_ai_weap.h"
+#include "../botai/botai.h"
+//
+#include "ai_main.h"
+#include "ai_dmq3.h"
+#include "ai_chat.h"
+#include "ai_cmd.h"
+#include "ai_dmnet.h"
+//data file headers
+#include "chars.h"           //characteristics
+#include "inv.h"         //indexes into the inventory
+#include "syn.h"         //synonyms
+#include "match.h"           //string matching types and vars
+
+//goal flag, see be_ai_goal.h for the other GFL_*
+#define GFL_AIR         16
+
+int numnodeswitches;
+char nodeswitch[MAX_NODESWITCHES + 1][144];
+
+#define LOOKAHEAD_DISTANCE      300
+
+/*
+==================
+BotResetNodeSwitches
+==================
+*/
+void BotResetNodeSwitches( void ) {
+       numnodeswitches = 0;
+}
+
+/*
+==================
+BotDumpNodeSwitches
+==================
+*/
+void BotDumpNodeSwitches( bot_state_t *bs ) {
+       int i;
+       char netname[MAX_NETNAME];
+
+       ClientName( bs->client, netname, sizeof( netname ) );
+       BotAI_Print( PRT_MESSAGE, "%s at %1.1f switched more than %d AI nodes\n", netname, trap_AAS_Time(), MAX_NODESWITCHES );
+       for ( i = 0; i < numnodeswitches; i++ ) {
+               BotAI_Print( PRT_MESSAGE, nodeswitch[i] );
+       }
+       BotAI_Print( PRT_FATAL, "" );
+}
+
+/*
+==================
+BotRecordNodeSwitch
+==================
+*/
+void BotRecordNodeSwitch( bot_state_t *bs, char *node, char *str ) {
+       char netname[MAX_NETNAME];
+
+       ClientName( bs->client, netname, sizeof( netname ) );
+       Com_sprintf( nodeswitch[numnodeswitches], 144, "%s at %2.1f entered %s: %s\n", netname, trap_AAS_Time(), node, str );
+#ifdef DEBUG
+       if ( 0 ) {
+               BotAI_Print( PRT_MESSAGE, nodeswitch[numnodeswitches] );
+       }
+#endif //DEBUG
+       numnodeswitches++;
+}
+
+/*
+==================
+BotGetAirGoal
+==================
+*/
+int BotGetAirGoal( bot_state_t *bs, bot_goal_t *goal ) {
+       bsp_trace_t bsptrace;
+       vec3_t end, mins = {-15, -15, -2}, maxs = {15, 15, 2};
+       int areanum;
+
+       //trace up until we hit solid
+       VectorCopy( bs->origin, end );
+       end[2] += 1000;
+       BotAI_Trace( &bsptrace, bs->origin, mins, maxs, end, bs->entitynum, CONTENTS_SOLID | CONTENTS_PLAYERCLIP );
+       //trace down until we hit water
+       VectorCopy( bsptrace.endpos, end );
+       BotAI_Trace( &bsptrace, end, mins, maxs, bs->origin, bs->entitynum, CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA );
+       //if we found the water surface
+       if ( bsptrace.fraction > 0 ) {
+               areanum = BotPointAreaNum( bsptrace.endpos );
+               if ( areanum ) {
+                       VectorCopy( bsptrace.endpos, goal->origin );
+                       goal->origin[2] -= 2;
+                       goal->areanum = areanum;
+                       goal->mins[0] = -15;
+                       goal->mins[1] = -15;
+                       goal->mins[2] = -1;
+                       goal->maxs[0] = 15;
+                       goal->maxs[1] = 15;
+                       goal->maxs[2] = 1;
+                       goal->flags = GFL_AIR;
+                       goal->number = 0;
+                       goal->iteminfo = 0;
+                       goal->entitynum = 0;
+                       return qtrue;
+               }
+       }
+       return qfalse;
+}
+
+/*
+==================
+BotGoForAir
+==================
+*/
+int BotGoForAir( bot_state_t *bs, int tfl, bot_goal_t *ltg, float range ) {
+       bot_goal_t goal;
+
+       //if the bot needs air
+       if ( bs->lastair_time < trap_AAS_Time() - 6 ) {
+               //
+#ifdef DEBUG
+               //BotAI_Print(PRT_MESSAGE, "going for air\n");
+#endif //DEBUG
+          //if we can find an air goal
+               if ( BotGetAirGoal( bs, &goal ) ) {
+                       trap_BotPushGoal( bs->gs, &goal );
+                       return qtrue;
+               } else {
+                       //get a nearby goal outside the water
+                       while ( trap_BotChooseNBGItem( bs->gs, bs->origin, bs->inventory, tfl, ltg, range ) ) {
+                               trap_BotGetTopGoal( bs->gs, &goal );
+                               //if the goal is not in water
+                               if ( !( trap_AAS_PointContents( goal.origin ) & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) ) {
+                                       return qtrue;
+                               }
+                               trap_BotPopGoal( bs->gs );
+                       }
+                       trap_BotResetAvoidGoals( bs->gs );
+               }
+       }
+       return qfalse;
+}
+
+/*
+==================
+BotNearbyGoal
+==================
+*/
+int BotNearbyGoal( bot_state_t *bs, int tfl, bot_goal_t *ltg, float range ) {
+       int ret;
+
+       if ( BotGoForAir( bs, tfl, ltg, range ) ) {
+               return qtrue;
+       }
+       //
+       ret = trap_BotChooseNBGItem( bs->gs, bs->origin, bs->inventory, tfl, ltg, range );
+       /*
+       if (ret)
+       {
+               char buf[128];
+               //get the goal at the top of the stack
+               trap_BotGetTopGoal(bs->gs, &goal);
+               trap_BotGoalName(goal.number, buf, sizeof(buf));
+               BotAI_Print(PRT_MESSAGE, "%1.1f: new nearby goal %s\n", trap_AAS_Time(), buf);
+       }
+       */
+       return ret;
+}
+
+/*
+==================
+BotReachedGoal
+==================
+*/
+int BotReachedGoal( bot_state_t *bs, bot_goal_t *goal ) {
+       if ( goal->flags & GFL_ITEM ) {
+               //if touching the goal
+               if ( trap_BotTouchingGoal( bs->origin, goal ) ) {
+                       return qtrue;
+               }
+               //if the goal isn't there
+               if ( trap_BotItemGoalInVisButNotVisible( bs->entitynum, bs->eye, bs->viewangles, goal ) ) {
+                       return qtrue;
+               }
+               //if in the goal area and below or above the goal and not swimming
+               if ( bs->areanum == goal->areanum ) {
+                       if ( bs->origin[0] > goal->origin[0] + goal->mins[0] && bs->origin[0] < goal->origin[0] + goal->maxs[0] ) {
+                               if ( bs->origin[1] > goal->origin[1] + goal->mins[1] && bs->origin[1] < goal->origin[1] + goal->maxs[1] ) {
+                                       if ( !trap_AAS_Swimming( bs->origin ) ) {
+                                               return qtrue;
+                                       }
+                               }
+                       }
+               }
+       } else if ( goal->flags & GFL_AIR )     {
+               //if touching the goal
+               if ( trap_BotTouchingGoal( bs->origin, goal ) ) {
+                       return qtrue;
+               }
+               //if the bot got air
+               if ( bs->lastair_time > trap_AAS_Time() - 1 ) {
+                       return qtrue;
+               }
+       } else {
+               //if touching the goal
+               if ( trap_BotTouchingGoal( bs->origin, goal ) ) {
+                       return qtrue;
+               }
+       }
+       return qfalse;
+}
+
+/*
+==================
+BotGetItemLongTermGoal
+==================
+*/
+int BotGetItemLongTermGoal( bot_state_t *bs, int tfl, bot_goal_t *goal ) {
+       //if the bot has no goal
+       if ( !trap_BotGetTopGoal( bs->gs, goal ) ) {
+               //BotAI_Print(PRT_MESSAGE, "no ltg on stack\n");
+               bs->ltg_time = 0;
+       }
+       //if the bot touches the current goal
+       else if ( BotReachedGoal( bs, goal ) ) {
+               BotChooseWeapon( bs );
+               bs->ltg_time = 0;
+       }
+       //if it is time to find a new long term goal
+       if ( bs->ltg_time < trap_AAS_Time() ) {
+               //pop the current goal from the stack
+               trap_BotPopGoal( bs->gs );
+               //BotAI_Print(PRT_MESSAGE, "%s: choosing new ltg\n", ClientName(bs->client, netname, sizeof(netname)));
+               //choose a new goal
+               //BotAI_Print(PRT_MESSAGE, "%6.1f client %d: BotChooseLTGItem\n", trap_AAS_Time(), bs->client);
+               if ( trap_BotChooseLTGItem( bs->gs, bs->origin, bs->inventory, tfl ) ) {
+                       /*
+                       char buf[128];
+                       //get the goal at the top of the stack
+                       trap_BotGetTopGoal(bs->gs, goal);
+                       trap_BotGoalName(goal->number, buf, sizeof(buf));
+                       BotAI_Print(PRT_MESSAGE, "%1.1f: new long term goal %s\n", trap_AAS_Time(), buf);
+                       */
+                       bs->ltg_time = trap_AAS_Time() + 20;
+               } else { //the bot gets sorta stuck with all the avoid timings, shouldn't happen though
+                               //
+#ifdef DEBUG
+                       char netname[128];
+
+                       BotAI_Print( PRT_MESSAGE, "%s: no valid ltg (probably stuck)\n", ClientName( bs->client, netname, sizeof( netname ) ) );
+#endif
+                       //trap_BotDumpAvoidGoals(bs->gs);
+                       //reset the avoid goals and the avoid reach
+                       trap_BotResetAvoidGoals( bs->gs );
+                       trap_BotResetAvoidReach( bs->ms );
+               }
+               //get the goal at the top of the stack
+               return trap_BotGetTopGoal( bs->gs, goal );
+       }
+       return qtrue;
+}
+
+/*
+==================
+BotGetLongTermGoal
+
+we could also create a seperate AI node for every long term goal type
+however this saves us a lot of code
+==================
+*/
+int BotGetLongTermGoal( bot_state_t *bs, int tfl, int retreat, bot_goal_t *goal ) {
+       vec3_t target, dir;
+       char netname[MAX_NETNAME];
+       char buf[MAX_MESSAGE_SIZE];
+       int areanum;
+       float croucher;
+       aas_entityinfo_t entinfo;
+       bot_waypoint_t *wp;
+
+       if ( bs->ltgtype == LTG_TEAMHELP && !retreat ) {
+               //check for bot typing status message
+               if ( bs->teammessage_time && bs->teammessage_time < trap_AAS_Time() ) {
+                       BotAI_BotInitialChat( bs, "help_start", EasyClientName( bs->teammate, netname, sizeof( netname ) ), NULL );
+                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       bs->teammessage_time = 0;
+               }
+               //if trying to help the team mate for more than a minute
+               if ( bs->teamgoal_time < trap_AAS_Time() ) {
+                       bs->ltgtype = 0;
+               }
+               //if the team mate IS visible for quite some time
+               if ( bs->teammatevisible_time < trap_AAS_Time() - 10 ) {
+                       bs->ltgtype = 0;
+               }
+               //get entity information of the companion
+               BotEntityInfo( bs->teammate, &entinfo );
+               //if the team mate is visible
+               if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->teammate ) ) {
+                       //if close just stand still there
+                       VectorSubtract( entinfo.origin, bs->origin, dir );
+                       if ( VectorLength( dir ) < 100 ) {
+                               trap_BotResetAvoidReach( bs->ms );
+                               return qfalse;
+                       }
+               } else {
+                       //last time the bot was NOT visible
+                       bs->teammatevisible_time = trap_AAS_Time();
+               }
+               //if the entity information is valid (entity in PVS)
+               if ( entinfo.valid ) {
+                       areanum = BotPointAreaNum( entinfo.origin );
+                       if ( areanum && trap_AAS_AreaReachability( areanum ) ) {
+                               //update team goal
+                               bs->teamgoal.entitynum = bs->teammate;
+                               bs->teamgoal.areanum = areanum;
+                               VectorCopy( entinfo.origin, bs->teamgoal.origin );
+                               VectorSet( bs->teamgoal.mins, -8, -8, -8 );
+                               VectorSet( bs->teamgoal.maxs, 8, 8, 8 );
+                       }
+               }
+               memcpy( goal, &bs->teamgoal, sizeof( bot_goal_t ) );
+               return qtrue;
+       }
+       //if the bot accompanies someone
+       if ( bs->ltgtype == LTG_TEAMACCOMPANY && !retreat ) {
+               //check for bot typing status message
+               if ( bs->teammessage_time && bs->teammessage_time < trap_AAS_Time() ) {
+                       BotAI_BotInitialChat( bs, "accompany_start", EasyClientName( bs->teammate, netname, sizeof( netname ) ), NULL );
+                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       bs->teammessage_time = 0;
+               }
+               //if accompanying the companion for 3 minutes
+               if ( bs->teamgoal_time < trap_AAS_Time() ) {
+                       BotAI_BotInitialChat( bs, "accompany_stop", EasyClientName( bs->teammate, netname, sizeof( netname ) ), NULL );
+                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       bs->ltgtype = 0;
+               }
+               //get entity information of the companion
+               BotEntityInfo( bs->teammate, &entinfo );
+               //if the companion is visible
+               if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->teammate ) ) {
+                       //update visible time
+                       bs->teammatevisible_time = trap_AAS_Time();
+                       VectorSubtract( entinfo.origin, bs->origin, dir );
+                       if ( VectorLength( dir ) < bs->formation_dist ) {
+                               //check if the bot wants to crouch
+                               //don't crouch if crouched less than 5 seconds ago
+                               if ( bs->attackcrouch_time < trap_AAS_Time() - 5 ) {
+                                       croucher = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CROUCHER, 0, 1 );
+                                       if ( random() < bs->thinktime * croucher ) {
+                                               bs->attackcrouch_time = trap_AAS_Time() + 5 + croucher * 15;
+                                       }
+                               }
+                               //don't crouch when swimming
+                               if ( trap_AAS_Swimming( bs->origin ) ) {
+                                       bs->attackcrouch_time = trap_AAS_Time() - 1;
+                               }
+                               //if not arrived yet or arived some time ago
+                               if ( bs->arrive_time < trap_AAS_Time() - 2 ) {
+                                       //if not arrived yet
+                                       if ( !bs->arrive_time ) {
+                                               trap_EA_Gesture( bs->client );
+                                               BotAI_BotInitialChat( bs, "accompany_arrive", EasyClientName( bs->teammate, netname, sizeof( netname ) ), NULL );
+                                               trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                                               bs->arrive_time = trap_AAS_Time();
+                                       }
+                                       //if the bot wants to crouch
+                                       else if ( bs->attackcrouch_time > trap_AAS_Time() ) {
+                                               trap_EA_Crouch( bs->client );
+                                       }
+                                       //else do some model taunts
+                                       else if ( random() < bs->thinktime * 0.3 ) {
+                                               //do a gesture :)
+                                               trap_EA_Gesture( bs->client );
+                                       }
+                               }
+                               //if just arrived look at the companion
+                               if ( bs->arrive_time > trap_AAS_Time() - 2 ) {
+                                       VectorSubtract( entinfo.origin, bs->origin, dir );
+                                       vectoangles( dir, bs->ideal_viewangles );
+                                       bs->ideal_viewangles[2] *= 0.5;
+                               }
+                               //else look strategically around for enemies
+                               else if ( random() < bs->thinktime * 0.8 ) {
+                                       BotRoamGoal( bs, target );
+                                       VectorSubtract( target, bs->origin, dir );
+                                       vectoangles( dir, bs->ideal_viewangles );
+                                       bs->ideal_viewangles[2] *= 0.5;
+                               }
+                               //check if the bot wants to go for air
+                               if ( BotGoForAir( bs, bs->tfl, &bs->teamgoal, 400 ) ) {
+                                       trap_BotResetLastAvoidReach( bs->ms );
+                                       //get the goal at the top of the stack
+                                       //trap_BotGetTopGoal(bs->gs, &tmpgoal);
+                                       //trap_BotGoalName(tmpgoal.number, buf, 144);
+                                       //BotAI_Print(PRT_MESSAGE, "new nearby goal %s\n", buf);
+                                       //time the bot gets to pick up the nearby goal item
+                                       bs->nbg_time = trap_AAS_Time() + 8;
+                                       AIEnter_Seek_NBG( bs );
+                                       return qfalse;
+                               }
+                               //
+                               trap_BotResetAvoidReach( bs->ms );
+                               return qfalse;
+                       }
+               }
+               //if the entity information is valid (entity in PVS)
+               if ( entinfo.valid ) {
+                       areanum = BotPointAreaNum( entinfo.origin );
+                       if ( areanum && trap_AAS_AreaReachability( areanum ) ) {
+                               //update team goal so bot will accompany
+                               bs->teamgoal.entitynum = bs->teammate;
+                               bs->teamgoal.areanum = areanum;
+                               VectorCopy( entinfo.origin, bs->teamgoal.origin );
+                               VectorSet( bs->teamgoal.mins, -8, -8, -8 );
+                               VectorSet( bs->teamgoal.maxs, 8, 8, 8 );
+                       }
+               }
+               //the goal the bot should go for
+               memcpy( goal, &bs->teamgoal, sizeof( bot_goal_t ) );
+               //if the companion is NOT visible for too long
+               if ( bs->teammatevisible_time < trap_AAS_Time() - 60 ) {
+                       BotAI_BotInitialChat( bs, "accompany_cannotfind", EasyClientName( bs->teammate, netname, sizeof( netname ) ), NULL );
+                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       bs->ltgtype = 0;
+               }
+               return qtrue;
+       }
+       //
+       if ( bs->ltgtype == LTG_DEFENDKEYAREA ) {
+               if ( trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin,
+                                                                                               bs->teamgoal.areanum, TFL_DEFAULT ) > bs->defendaway_range ) {
+                       bs->defendaway_time = 0;
+               }
+       }
+       //if defending a key area
+       if ( bs->ltgtype == LTG_DEFENDKEYAREA && !retreat &&
+                bs->defendaway_time < trap_AAS_Time() ) {
+               //check for bot typing status message
+               if ( bs->teammessage_time && bs->teammessage_time < trap_AAS_Time() ) {
+                       trap_BotGoalName( bs->teamgoal.number, buf, sizeof( buf ) );
+                       BotAI_BotInitialChat( bs, "defend_start", buf, NULL );
+                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       bs->teammessage_time = 0;
+               }
+               //set the bot goal
+               memcpy( goal, &bs->teamgoal, sizeof( bot_goal_t ) );
+               //stop after 2 minutes
+               if ( bs->teamgoal_time < trap_AAS_Time() ) {
+                       trap_BotGoalName( bs->teamgoal.number, buf, sizeof( buf ) );
+                       BotAI_BotInitialChat( bs, "defend_stop", buf, NULL );
+                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       bs->ltgtype = 0;
+               }
+               //if very close... go away for some time
+               VectorSubtract( goal->origin, bs->origin, dir );
+               if ( VectorLength( dir ) < 70 ) {
+                       trap_BotResetAvoidReach( bs->ms );
+                       bs->defendaway_time = trap_AAS_Time() + 2 + 5 * random();
+                       bs->defendaway_range = 300;
+               }
+               return qtrue;
+       }
+       //going to kill someone
+       if ( bs->ltgtype == LTG_KILL && !retreat ) {
+               //check for bot typing status message
+               if ( bs->teammessage_time && bs->teammessage_time < trap_AAS_Time() ) {
+                       EasyClientName( bs->teamgoal.entitynum, buf, sizeof( buf ) );
+                       BotAI_BotInitialChat( bs, "kill_start", buf, NULL );
+                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       bs->teammessage_time = 0;
+               }
+               //
+               if ( bs->lastkilledplayer == bs->teamgoal.entitynum ) {
+                       EasyClientName( bs->teamgoal.entitynum, buf, sizeof( buf ) );
+                       BotAI_BotInitialChat( bs, "kill_done", buf, NULL );
+                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       bs->lastkilledplayer = -1;
+                       bs->ltgtype = 0;
+               }
+               //
+               if ( bs->teamgoal_time < trap_AAS_Time() ) {
+                       bs->ltgtype = 0;
+               }
+               //just roam around
+               return BotGetItemLongTermGoal( bs, tfl, goal );
+       }
+       //get an item
+       if ( bs->ltgtype == LTG_GETITEM && !retreat ) {
+               //check for bot typing status message
+               if ( bs->teammessage_time && bs->teammessage_time < trap_AAS_Time() ) {
+                       trap_BotGoalName( bs->teamgoal.number, buf, sizeof( buf ) );
+                       BotAI_BotInitialChat( bs, "getitem_start", buf, NULL );
+                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       bs->teammessage_time = 0;
+               }
+               //set the bot goal
+               memcpy( goal, &bs->teamgoal, sizeof( bot_goal_t ) );
+               //stop after some time
+               if ( bs->teamgoal_time < trap_AAS_Time() ) {
+                       bs->ltgtype = 0;
+               }
+               //
+               if ( trap_BotItemGoalInVisButNotVisible( bs->entitynum, bs->eye, bs->viewangles, goal ) ) {
+                       trap_BotGoalName( bs->teamgoal.number, buf, sizeof( buf ) );
+                       BotAI_BotInitialChat( bs, "getitem_notthere", buf, NULL );
+                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       bs->ltgtype = 0;
+               } else if ( BotReachedGoal( bs, goal ) )       {
+                       trap_BotGoalName( bs->teamgoal.number, buf, sizeof( buf ) );
+                       BotAI_BotInitialChat( bs, "getitem_gotit", buf, NULL );
+                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       bs->ltgtype = 0;
+               }
+               return qtrue;
+       }
+       //if camping somewhere
+       if ( ( bs->ltgtype == LTG_CAMP || bs->ltgtype == LTG_CAMPORDER ) && !retreat ) {
+               //check for bot typing status message
+               if ( bs->teammessage_time && bs->teammessage_time < trap_AAS_Time() ) {
+                       if ( bs->ltgtype == LTG_CAMPORDER ) {
+                               BotAI_BotInitialChat( bs, "camp_start", EasyClientName( bs->teammate, netname, sizeof( netname ) ), NULL );
+                               trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       }
+                       bs->teammessage_time = 0;
+               }
+               //set the bot goal
+               memcpy( goal, &bs->teamgoal, sizeof( bot_goal_t ) );
+               //
+               if ( bs->teamgoal_time < trap_AAS_Time() ) {
+                       if ( bs->ltgtype == LTG_CAMPORDER ) {
+                               BotAI_BotInitialChat( bs, "camp_stop", NULL );
+                               trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       }
+                       bs->ltgtype = 0;
+               }
+               //if really near the camp spot
+               VectorSubtract( goal->origin, bs->origin, dir );
+               if ( VectorLength( dir ) < 60 ) {
+                       //if not arrived yet
+                       if ( !bs->arrive_time ) {
+                               if ( bs->ltgtype == LTG_CAMPORDER ) {
+                                       BotAI_BotInitialChat( bs, "camp_arrive", EasyClientName( bs->teammate, netname, sizeof( netname ) ), NULL );
+                                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                               }
+                               bs->arrive_time = trap_AAS_Time();
+                       }
+                       //look strategically around for enemies
+                       if ( random() < bs->thinktime * 0.8 ) {
+                               BotRoamGoal( bs, target );
+                               VectorSubtract( target, bs->origin, dir );
+                               vectoangles( dir, bs->ideal_viewangles );
+                               bs->ideal_viewangles[2] *= 0.5;
+                       }
+                       //check if the bot wants to crouch
+                       //don't crouch if crouched less than 5 seconds ago
+                       if ( bs->attackcrouch_time < trap_AAS_Time() - 5 ) {
+                               croucher = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CROUCHER, 0, 1 );
+                               if ( random() < bs->thinktime * croucher ) {
+                                       bs->attackcrouch_time = trap_AAS_Time() + 5 + croucher * 15;
+                               }
+                       }
+                       //if the bot wants to crouch
+                       if ( bs->attackcrouch_time > trap_AAS_Time() ) {
+                               trap_EA_Crouch( bs->client );
+                       }
+                       //don't crouch when swimming
+                       if ( trap_AAS_Swimming( bs->origin ) ) {
+                               bs->attackcrouch_time = trap_AAS_Time() - 1;
+                       }
+                       //make sure the bot is not gonna drown
+                       if ( trap_PointContents( bs->eye,bs->entitynum ) & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
+                               if ( bs->ltgtype == LTG_CAMPORDER ) {
+                                       BotAI_BotInitialChat( bs, "camp_stop", NULL );
+                                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                               }
+                               bs->ltgtype = 0;
+                       }
+                       //
+                       if ( bs->camp_range > 0 ) {
+                               //FIXME: move around a bit
+                       }
+                       //
+                       trap_BotResetAvoidReach( bs->ms );
+                       return qfalse;
+               }
+               return qtrue;
+       }
+       //patrolling along several waypoints
+       if ( bs->ltgtype == LTG_PATROL && !retreat ) {
+               //check for bot typing status message
+               if ( bs->teammessage_time && bs->teammessage_time < trap_AAS_Time() ) {
+                       strcpy( buf, "" );
+                       for ( wp = bs->patrolpoints; wp; wp = wp->next ) {
+                               strcat( buf, wp->name );
+                               if ( wp->next ) {
+                                       strcat( buf, " to " );
+                               }
+                       }
+                       BotAI_BotInitialChat( bs, "patrol_start", buf, NULL );
+                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       bs->teammessage_time = 0;
+               }
+               //
+               if ( !bs->curpatrolpoint ) {
+                       bs->ltgtype = 0;
+                       return qfalse;
+               }
+               //if the bot touches the current goal
+               if ( trap_BotTouchingGoal( bs->origin, &bs->curpatrolpoint->goal ) ) {
+                       if ( bs->patrolflags & PATROL_BACK ) {
+                               if ( bs->curpatrolpoint->prev ) {
+                                       bs->curpatrolpoint = bs->curpatrolpoint->prev;
+                               } else {
+                                       bs->curpatrolpoint = bs->curpatrolpoint->next;
+                                       bs->patrolflags &= ~PATROL_BACK;
+                               }
+                       } else {
+                               if ( bs->curpatrolpoint->next ) {
+                                       bs->curpatrolpoint = bs->curpatrolpoint->next;
+                               } else {
+                                       bs->curpatrolpoint = bs->curpatrolpoint->prev;
+                                       bs->patrolflags |= PATROL_BACK;
+                               }
+                       }
+               }
+               //stop after 5 minutes
+               if ( bs->teamgoal_time < trap_AAS_Time() ) {
+                       BotAI_BotInitialChat( bs, "patrol_stop", NULL );
+                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       bs->ltgtype = 0;
+               }
+               if ( !bs->curpatrolpoint ) {
+                       bs->ltgtype = 0;
+                       return qfalse;
+               }
+               memcpy( goal, &bs->curpatrolpoint->goal, sizeof( bot_goal_t ) );
+               return qtrue;
+       }
+#ifdef CTF
+       //if going for enemy flag
+       if ( bs->ltgtype == LTG_GETFLAG ) {
+               //check for bot typing status message
+               if ( bs->teammessage_time && bs->teammessage_time < trap_AAS_Time() ) {
+                       BotAI_BotInitialChat( bs, "captureflag_start", NULL );
+                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       bs->teammessage_time = 0;
+               }
+               //
+               switch ( BotCTFTeam( bs ) ) {
+               case CTF_TEAM_RED: *goal = ctf_blueflag; break;
+               case CTF_TEAM_BLUE: *goal = ctf_redflag; break;
+               default: bs->ltgtype = 0; return qfalse;
+               }
+               //if touching the flag
+               if ( trap_BotTouchingGoal( bs->origin, goal ) ) {
+                       bs->ltgtype = 0;
+               }
+               //stop after 3 minutes
+               if ( bs->teamgoal_time < trap_AAS_Time() ) {
+#ifdef DEBUG
+                       BotAI_Print( PRT_MESSAGE, "%s: I quit getting the flag\n", ClientName( bs->client, netname, sizeof( netname ) ) );
+#endif //DEBUG
+                       bs->ltgtype = 0;
+               }
+               return qtrue;
+       }
+       //if rushing to the base
+       if ( bs->ltgtype == LTG_RUSHBASE && bs->rushbaseaway_time < trap_AAS_Time() ) {
+               switch ( BotCTFTeam( bs ) ) {
+               case CTF_TEAM_RED: *goal = ctf_redflag; break;
+               case CTF_TEAM_BLUE: *goal = ctf_blueflag; break;
+               default: bs->ltgtype = 0; return qfalse;
+               }
+               //quit rushing after 2 minutes
+               if ( bs->teamgoal_time < trap_AAS_Time() ) {
+                       bs->ltgtype = 0;
+               }
+               //if touching the base flag the bot should loose the enemy flag
+               if ( trap_BotTouchingGoal( bs->origin, goal ) ) {
+                       //if the bot is still carrying the enemy flag then the
+                       //base flag is gone, now just walk near the base a bit
+                       if ( BotCTFCarryingFlag( bs ) ) {
+                               trap_BotResetAvoidReach( bs->ms );
+                               bs->rushbaseaway_time = trap_AAS_Time() + 5 + 10 * random();
+                               //FIXME: add chat to tell the others to get back the flag
+                       } else {
+                               bs->ltgtype = 0;
+                       }
+               }
+               return qtrue;
+       }
+       //returning flag
+       if ( bs->ltgtype == LTG_RETURNFLAG ) {
+               //check for bot typing status message
+               if ( bs->teammessage_time && bs->teammessage_time < trap_AAS_Time() ) {
+                       EasyClientName( bs->teamgoal.entitynum, buf, sizeof( buf ) );
+                       BotAI_BotInitialChat( bs, "returnflag_start", buf, NULL );
+                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       bs->teammessage_time = 0;
+               }
+               //
+               if ( bs->teamgoal_time < trap_AAS_Time() ) {
+                       bs->ltgtype = 0;
+               }
+               //just roam around
+               return BotGetItemLongTermGoal( bs, tfl, goal );
+       }
+#endif //CTF
+          //normal goal stuff
+       return BotGetItemLongTermGoal( bs, tfl, goal );
+}
+
+/*
+==================
+BotLongTermGoal
+==================
+*/
+int BotLongTermGoal( bot_state_t *bs, int tfl, int retreat, bot_goal_t *goal ) {
+       aas_entityinfo_t entinfo;
+       char teammate[MAX_MESSAGE_SIZE];
+       float dist;
+       int areanum;
+       vec3_t dir;
+
+       //FIXME: also have air long term goals?
+       //
+       //if the bot is leading someone and not retreating
+       if ( bs->lead_time > 0 && !retreat ) {
+               if ( bs->lead_time < trap_AAS_Time() ) {
+                       //FIXME: add chat to tell the team mate that he/she's on his/her own
+                       bs->lead_time = 0;
+                       return BotGetLongTermGoal( bs, tfl, retreat, goal );
+               }
+               //
+               if ( bs->leadmessage_time < 0 && -bs->leadmessage_time < trap_AAS_Time() ) {
+                       BotAI_BotInitialChat( bs, "followme", EasyClientName( bs->lead_teammate, teammate, sizeof( teammate ) ), NULL );
+                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                       bs->leadmessage_time = trap_AAS_Time();
+               }
+               //get entity information of the companion
+               BotEntityInfo( bs->lead_teammate, &entinfo );
+               //
+               if ( entinfo.valid ) {
+                       areanum = BotPointAreaNum( entinfo.origin );
+                       if ( areanum && trap_AAS_AreaReachability( areanum ) ) {
+                               //update team goal
+                               bs->lead_teamgoal.entitynum = bs->lead_teammate;
+                               bs->lead_teamgoal.areanum = areanum;
+                               VectorCopy( entinfo.origin, bs->lead_teamgoal.origin );
+                               VectorSet( bs->lead_teamgoal.mins, -8, -8, -8 );
+                               VectorSet( bs->lead_teamgoal.maxs, 8, 8, 8 );
+                       }
+               }
+               //if the team mate is visible
+               if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->lead_teammate ) ) {
+                       bs->leadvisible_time = trap_AAS_Time();
+               }
+               //if the team mate is not visible for 1 seconds
+               if ( bs->leadvisible_time < trap_AAS_Time() - 1 ) {
+                       bs->leadbackup_time = trap_AAS_Time() + 2;
+               }
+               //distance towards the team mate
+               VectorSubtract( bs->origin, bs->lead_teamgoal.origin, dir );
+               dist = VectorLength( dir );
+               //if backing up towards the team mate
+               if ( bs->leadbackup_time > trap_AAS_Time() ) {
+                       if ( bs->leadmessage_time < trap_AAS_Time() - 20 ) {
+                               BotAI_BotInitialChat( bs, "followme", EasyClientName( bs->lead_teammate, teammate, sizeof( teammate ) ), NULL );
+                               trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                               bs->leadmessage_time = trap_AAS_Time();
+                       }
+                       //if very close to the team mate
+                       if ( dist < 100 ) {
+                               bs->leadbackup_time = 0;
+                       }
+                       //the bot should go back to the team mate
+                       memcpy( goal, &bs->lead_teamgoal, sizeof( bot_goal_t ) );
+                       return qtrue;
+               } else {
+                       //if quite distant from the team mate
+                       if ( dist > 500 ) {
+                               if ( bs->leadmessage_time < trap_AAS_Time() - 20 ) {
+                                       BotAI_BotInitialChat( bs, "followme", EasyClientName( bs->lead_teammate, teammate, sizeof( teammate ) ), NULL );
+                                       trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
+                                       bs->leadmessage_time = trap_AAS_Time();
+                               }
+                               //look at the team mate
+                               VectorSubtract( entinfo.origin, bs->origin, dir );
+                               vectoangles( dir, bs->ideal_viewangles );
+                               bs->ideal_viewangles[2] *= 0.5;
+                               //just wait for the team mate
+                               return qfalse;
+                       }
+               }
+       }
+       return BotGetLongTermGoal( bs, tfl, retreat, goal );
+}
+
+/*
+==================
+AIEnter_Intermission
+==================
+*/
+void AIEnter_Intermission( bot_state_t *bs ) {
+       BotRecordNodeSwitch( bs, "intermission", "" );
+       //reset the bot state
+       BotResetState( bs );
+       //check for end level chat
+       if ( BotChat_EndLevel( bs ) ) {
+               trap_BotEnterChat( bs->cs, bs->client, bs->chatto );
+       }
+       bs->ainode = AINode_Intermission;
+}
+
+/*
+==================
+AINode_Intermission
+==================
+*/
+int AINode_Intermission( bot_state_t *bs ) {
+       //if the intermission ended
+       if ( !BotIntermission( bs ) ) {
+               if ( BotChat_StartLevel( bs ) ) {
+                       bs->stand_time = trap_AAS_Time() + BotChatTime( bs );
+               } else {
+                       bs->stand_time = trap_AAS_Time() + 2;
+               }
+               AIEnter_Stand( bs );
+       }
+       return qtrue;
+}
+
+/*
+==================
+AIEnter_Observer
+==================
+*/
+void AIEnter_Observer( bot_state_t *bs ) {
+       BotRecordNodeSwitch( bs, "observer", "" );
+       //reset the bot state
+       BotResetState( bs );
+       bs->ainode = AINode_Observer;
+}
+
+/*
+==================
+AINode_Observer
+==================
+*/
+int AINode_Observer( bot_state_t *bs ) {
+       //if the bot left observer mode
+       if ( !BotIsObserver( bs ) ) {
+               AIEnter_Stand( bs );
+       }
+       return qtrue;
+}
+
+/*
+==================
+AIEnter_Stand
+==================
+*/
+void AIEnter_Stand( bot_state_t *bs ) {
+       BotRecordNodeSwitch( bs, "stand", "" );
+       bs->standfindenemy_time = trap_AAS_Time() + 1;
+       bs->ainode = AINode_Stand;
+}
+
+/*
+==================
+AINode_Stand
+==================
+*/
+int AINode_Stand( bot_state_t *bs ) {
+
+       //if the bot's health decreased
+       if ( bs->lastframe_health > bs->inventory[INVENTORY_HEALTH] ) {
+               if ( BotChat_HitTalking( bs ) ) {
+                       bs->standfindenemy_time = trap_AAS_Time() + BotChatTime( bs ) + 0.1;
+                       bs->stand_time = trap_AAS_Time() + BotChatTime( bs ) + 0.1;
+               }
+       }
+       if ( bs->standfindenemy_time < trap_AAS_Time() ) {
+               if ( BotFindEnemy( bs, -1 ) ) {
+                       AIEnter_Battle_Fight( bs );
+                       return qfalse;
+               }
+               bs->standfindenemy_time = trap_AAS_Time() + 1;
+       }
+       trap_EA_Talk( bs->client );
+       if ( bs->stand_time < trap_AAS_Time() ) {
+               trap_BotEnterChat( bs->cs, bs->client, bs->chatto );
+               AIEnter_Seek_LTG( bs );
+               return qfalse;
+       }
+       //
+       return qtrue;
+}
+
+/*
+==================
+AIEnter_Respawn
+==================
+*/
+void AIEnter_Respawn( bot_state_t *bs ) {
+       BotRecordNodeSwitch( bs, "respawn", "" );
+       //reset some states
+       trap_BotResetMoveState( bs->ms );
+       trap_BotResetGoalState( bs->gs );
+       trap_BotResetAvoidGoals( bs->gs );
+       trap_BotResetAvoidReach( bs->ms );
+       //if the bot wants to chat
+       if ( BotChat_Death( bs ) ) {
+               bs->respawn_time = trap_AAS_Time() + BotChatTime( bs );
+               bs->respawnchat_time = trap_AAS_Time();
+       } else {
+               bs->respawn_time = trap_AAS_Time() + 1 + random();
+               bs->respawnchat_time = 0;
+       }
+       //set respawn state
+       bs->respawn_wait = qfalse;
+       bs->ainode = AINode_Respawn;
+}
+
+/*
+==================
+AINode_Respawn
+==================
+*/
+int AINode_Respawn( bot_state_t *bs ) {
+       if ( bs->respawn_wait ) {
+               if ( !BotIsDead( bs ) ) {
+                       AIEnter_Seek_LTG( bs );
+               } else {
+                       trap_EA_Respawn( bs->client );
+               }
+       } else if ( bs->respawn_time < trap_AAS_Time() )     {
+               //wait until respawned
+               bs->respawn_wait = qtrue;
+               //elementary action respawn
+               trap_EA_Respawn( bs->client );
+               //
+               if ( bs->respawnchat_time ) {
+                       trap_BotEnterChat( bs->cs, bs->client, bs->chatto );
+                       bs->enemy = -1;
+               }
+       }
+       if ( bs->respawnchat_time && bs->respawnchat_time < trap_AAS_Time() - 0.5 ) {
+               trap_EA_Talk( bs->client );
+       }
+       //
+       return qtrue;
+}
+
+/*
+==================
+AIEnter_Seek_ActivateEntity
+==================
+*/
+void AIEnter_Seek_ActivateEntity( bot_state_t *bs ) {
+       BotRecordNodeSwitch( bs, "activate entity", "" );
+       bs->ainode = AINode_Seek_ActivateEntity;
+}
+
+/*
+==================
+AINode_Seek_Activate_Entity
+==================
+*/
+int AINode_Seek_ActivateEntity( bot_state_t *bs ) {
+       bot_goal_t *goal;
+       vec3_t target, dir;
+       bot_moveresult_t moveresult;
+
+       if ( BotIsObserver( bs ) ) {
+               AIEnter_Observer( bs );
+               return qfalse;
+       }
+       //if in the intermission
+       if ( BotIntermission( bs ) ) {
+               AIEnter_Intermission( bs );
+               return qfalse;
+       }
+       //respawn if dead
+       if ( BotIsDead( bs ) ) {
+               AIEnter_Respawn( bs );
+               return qfalse;
+       }
+       //
+       bs->tfl = TFL_DEFAULT;
+       if ( bot_grapple.integer ) {
+               bs->tfl |= TFL_GRAPPLEHOOK;
+       }
+       //if in lava or slime the bot should be able to get out
+       if ( BotInLava( bs ) ) {
+               bs->tfl |= TFL_LAVA;
+       }
+       if ( BotInSlime( bs ) ) {
+               bs->tfl |= TFL_SLIME;
+       }
+       //map specific code
+       BotMapScripts( bs );
+       //no enemy
+       bs->enemy = -1;
+       //
+       goal = &bs->activategoal;
+       //if the bot has no goal
+       if ( !goal ) {
+               bs->activate_time = 0;
+       }
+       //if the bot touches the current goal
+       else if ( trap_BotTouchingGoal( bs->origin, goal ) ) {
+               BotChooseWeapon( bs );
+#ifdef DEBUG
+               BotAI_Print( PRT_MESSAGE, "touched button or trigger\n" );
+#endif //DEBUG
+               bs->activate_time = 0;
+       }
+       //
+       if ( bs->activate_time < trap_AAS_Time() ) {
+               AIEnter_Seek_NBG( bs );
+               return qfalse;
+       }
+       //initialize the movement state
+       BotSetupForMovement( bs );
+       //move towards the goal
+       trap_BotMoveToGoal( &moveresult, bs->ms, goal, bs->tfl );
+       //if the movement failed
+       if ( moveresult.failure ) {
+               //reset the avoid reach, otherwise bot is stuck in current area
+               trap_BotResetAvoidReach( bs->ms );
+               bs->nbg_time = 0;
+       }
+       //check if the bot is blocked
+       BotAIBlocked( bs, &moveresult, qtrue );
+       //
+       if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) {
+               VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles );
+       }
+       //if waiting for something
+       else if ( moveresult.flags & MOVERESULT_WAITING ) {
+               if ( random() < bs->thinktime * 0.8 ) {
+                       BotRoamGoal( bs, target );
+                       VectorSubtract( target, bs->origin, dir );
+                       vectoangles( dir, bs->ideal_viewangles );
+                       bs->ideal_viewangles[2] *= 0.5;
+               }
+       } else if ( !( bs->flags & BFL_IDEALVIEWSET ) )       {
+               if ( trap_BotMovementViewTarget( bs->ms, goal, bs->tfl, 300, target ) ) {
+                       VectorSubtract( target, bs->origin, dir );
+                       vectoangles( dir, bs->ideal_viewangles );
+               } else {
+                       //vectoangles(moveresult.movedir, bs->ideal_viewangles);
+               }
+               bs->ideal_viewangles[2] *= 0.5;
+       }
+       //if the weapon is used for the bot movement
+       if ( moveresult.flags & MOVERESULT_MOVEMENTWEAPON ) {
+               bs->weaponnum = moveresult.weapon;
+       }
+       //if there is an enemy
+       if ( BotFindEnemy( bs, -1 ) ) {
+               if ( BotWantsToRetreat( bs ) ) {
+                       //keep the current long term goal and retreat
+                       AIEnter_Battle_NBG( bs );
+               } else {
+                       trap_BotResetLastAvoidReach( bs->ms );
+                       //empty the goal stack
+                       trap_BotEmptyGoalStack( bs->gs );
+                       //go fight
+                       AIEnter_Battle_Fight( bs );
+               }
+       }
+       return qtrue;
+}
+
+/*
+==================
+AIEnter_Seek_NBG
+==================
+*/
+void AIEnter_Seek_NBG( bot_state_t *bs ) {
+       bot_goal_t goal;
+       char buf[144];
+
+       if ( trap_BotGetTopGoal( bs->gs, &goal ) ) {
+               trap_BotGoalName( goal.number, buf, 144 );
+               BotRecordNodeSwitch( bs, "seek NBG", buf );
+       } else {
+               BotRecordNodeSwitch( bs, "seek NBG", "no goal" );
+       }
+       bs->ainode = AINode_Seek_NBG;
+}
+
+/*
+==================
+AINode_Seek_NBG
+==================
+*/
+int AINode_Seek_NBG( bot_state_t *bs ) {
+       bot_goal_t goal;
+       vec3_t target, dir;
+       bot_moveresult_t moveresult;
+
+       if ( BotIsObserver( bs ) ) {
+               AIEnter_Observer( bs );
+               return qfalse;
+       }
+       //if in the intermission
+       if ( BotIntermission( bs ) ) {
+               AIEnter_Intermission( bs );
+               return qfalse;
+       }
+       //respawn if dead
+       if ( BotIsDead( bs ) ) {
+               AIEnter_Respawn( bs );
+               return qfalse;
+       }
+    &n